Skip to content

Commit

Permalink
Added export to an IDEAS.md file.
Browse files Browse the repository at this point in the history
Defaults to ## style headings and outputs whatever format the last file import used
  • Loading branch information
insin committed Feb 24, 2015
1 parent b98cc2d commit 51b4026
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 24 deletions.
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
`##` headings now win if both supported heading types are detected when
importing a file.

Added export to an IDEAS.md file - defaults to exporting `##` headings, but will
export headings matching the format used in the last file import.

Fixed trimming of whitespace when importing.

Switched to centred layout.
Expand Down
109 changes: 97 additions & 12 deletions app.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,50 @@ var escapeHTML = (() => {
return (text) => text.replace(escapeRE, escaper)
})()

var unescapeHTML = (() => {
var unescapeRE = /&(?:amp|gt|lt);/g
var unescapes = {'&amp;': '&', '&gt;': '>', '&lt;': '<'}
var unescaper = (match) => unescapes[match]
return (text) => text.replace(unescapeRE, unescaper)
})()

var linebreaksToBr = (() => {
var linebreaksRE = /\r\n|\r|\n/g
return (text) => text.replace(linebreaksRE, '<br>')
})()

var brsToLinebreak = (() => {
var brRE = /<br>/g
return (text) => text.replace(brRE, '\n')
})()

function utf8ToBase64(text) {
return window.btoa(unescape(encodeURIComponent(text)))
}

function exportFile(text, filename) {
var a = document.createElement('a')
var base64 = utf8ToBase64(text)

if ('download' in a) {
a.href = 'data:text/markdown;base64,' + base64
a.download = filename
var event = document.createEvent('MouseEvents')
event.initMouseEvent('click', true, true, window, 1, 0, 0, 0, 0,
false, false, false, false, 0, null)
a.dispatchEvent(event)
}
else if (typeof navigator.msSaveBlob == 'function') {
navigator.msSaveBlob(new Blob([text], {
type: 'text/markdown;charset=utf-8;'
}), filename)
}
else {
window.location.href =
'data:application/octet-stream;base64,' + base64
}
}

// =================================================================== Store ===

// Section ids are re-assigned by index on load - we just need to ensure they're
Expand All @@ -52,6 +91,7 @@ var ID_SEED = 0

var GENERAL_KEY = 'imd:general'
var SECTIONS_KEY = 'imd:sections'
var EXPORT_FORMAT_KEY = 'imd:export'

var DEFAULT_SECTION = {section: '[section]', ideas: '[ideas]'}

Expand All @@ -75,14 +115,23 @@ function saveSections(sections) {
localStorage.setItem(SECTIONS_KEY, JSON.stringify(sections))
}

function loadExportFormat() {
return localStorage.getItem(EXPORT_FORMAT_KEY) || 'hash'
}

function saveExportFormat(exportFormat) {
localStorage.setItem(EXPORT_FORMAT_KEY, exportFormat)
}

var IdeasStore = {
general: loadGeneral(),
sections: loadSections(),
exportFormat: loadExportFormat(),
newSectionId: null,

get() {
var {general, sections, newSectionId} = this
return {general, sections, newSectionId}
var {general, sections, exportFormat, newSectionId} = this
return {general, sections, exportFormat, newSectionId}
},

import(state) {
Expand All @@ -92,8 +141,10 @@ var IdeasStore = {
section.ideas = linebreaksToBr(escapeHTML(section.ideas))
return section
})
this.exportFormat = state.exportFormat
saveGeneral(this.general)
saveSections(this.sections)
saveExportFormat(this.exportFormat)
this.notifyChange()
},

Expand Down Expand Up @@ -123,6 +174,12 @@ var IdeasStore = {
this.notifyChange()
},

editExportFormat(exportFormat) {
this.exportFormat = exportFormat
saveExportFormat(this.exportFormat)
this.notifyChange()
},

notifyChange() {
console.warn('IdeasStore: no change listener registered!')
}
Expand All @@ -133,11 +190,18 @@ var hashHeadingsRE = /^##([^#][^\n]*)/gm

function parseFileContents(text) {
var parts
if (underlineHeadingsRE.test(text)) {
var exportFormat
if (hashHeadingsRE.test(text)) {
parts = text.split(hashHeadingsRE)
exportFormat = 'hash'
}
else if (underlineHeadingsRE.test(text)) {
parts = text.split(underlineHeadingsRE)
exportFormat = 'underline'
}
else if (hashHeadingsRE.test(text)) {
parts = text.split(hashHeadingsRE)
else {
alert('Could not find any headings with ## prefixes or == underlines.')
return
}
var general = trim(parts[0])
var sections = []
Expand All @@ -146,7 +210,23 @@ function parseFileContents(text) {
var ideas = trim(parts[i + 1])
sections.push({section, ideas})
}
return {general, sections}
return {general, sections, exportFormat}
}

function createFileContents(general, sections, style) {
var parts = [unescapeHTML(brsToLinebreak(general))]
sections.forEach(section => {
var name = unescapeHTML(section.section)
if (style == 'hash') {
parts.push(`## ${name}`)
}
else if (style == 'underline') {
var underline = name.split(/./g).join('=')
parts.push(`${name}\n${underline}`)
}
parts.push(unescapeHTML(brsToLinebreak(section.ideas)))
})
return parts.join('\n\n')
}

// ============================================================== Components ===
Expand Down Expand Up @@ -179,6 +259,12 @@ var Ideas = React.createClass({
IdeasStore.addSection()
},

_export(e) {
var {general, sections, exportFormat} = this.state
var contents = createFileContents(general, sections, exportFormat)
exportFile(contents, 'IDEAS.md')
},

_onBlur(e, html) {
IdeasStore.editGeneral(html)
},
Expand All @@ -205,11 +291,10 @@ var Ideas = React.createClass({

render() {
return <div className="Ideas">
<Button className="Ideas__add"
onClick={this._addSection}
title="Add section">
+
</Button>
<div className="Ideas__buttons">
<Button onClick={this._addSection} title="Add section">+</Button>
<Button onClick={this._export} title="Export to file"></Button>
</div>
<div className="Ideas__general">
<PlainEditable
html={this.state.general}
Expand Down Expand Up @@ -259,7 +344,7 @@ var Section = React.createClass({
<Button className="Section__remove"
onClick={this._onRemove}
title="Remove section">
&mdash;
&ndash;
</Button>
<PlainEditable
autoFocus={this.props.isNew}
Expand Down
21 changes: 11 additions & 10 deletions style.css
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,15 @@ footer a {
.Button {
border-radius: 50%;
border: 1px solid #f8f8f2;
boz-sizing: border-box;
box-sizing: border-box;
cursor: pointer;
display: inline-block;
font-size: 24px;
height: 32px;
opacity: .5;
text-align: center;
transition: all .333s;
line-height: 28px;
width: 32px;
}
.Button:hover {
Expand All @@ -52,10 +53,15 @@ footer a {
padding: 16px;
}
.Ideas {

position: relative;


}
.Ideas__buttons {
float: left;
width: 32px;
}
.Ideas__buttons .Button {
margin-bottom: 8px;
opacity: .25;
}
.Ideas__general {
padding-left: 44px;
Expand All @@ -65,12 +71,7 @@ footer a {
padding: 8px 12px;
}
.Ideas__sections {
}
.Ideas__add {
left: 0;
opacity: .25;
position: absolute;
top: 0;
clear: both;
}

.Section {
Expand Down
4 changes: 3 additions & 1 deletion test/hash.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ more ideas

## last section

ideas
ideas

✓ à la mode
20 changes: 20 additions & 0 deletions test/mixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
general 1

general 2

## section

ideas

### subheading

more ideas

subheading
==========

## last section

ideas

✓ à la mode
4 changes: 3 additions & 1 deletion test/underline.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ empty section
last section
============

ideas
ideas

✓ à la mode

0 comments on commit 51b4026

Please sign in to comment.