Showing preview only (217K chars total). Download the full file or copy to clipboard to get everything.
Repository: byhestia/springseed
Branch: 2.x
Commit: 371c73550888
Files: 47
Total size: 204.7 KB
Directory structure:
gitextract_ku31l5mv/
├── .gitattributes
├── .gitignore
├── .gitmodules
├── Cakefile
├── Gemfile
├── Guardfile
├── LICENSE
├── Makefile
├── Procfile
├── README.md
├── app/
│ ├── controllers/
│ │ ├── account.coffee
│ │ ├── browser.coffee
│ │ ├── editor.coffee
│ │ ├── modal.coffee
│ │ ├── note.item.coffee
│ │ ├── notebook.item.coffee
│ │ ├── panel.coffee
│ │ ├── popover.coffee
│ │ ├── settings.coffee
│ │ ├── sidebar.coffee
│ │ ├── sync.coffee
│ │ └── upgrader.coffee
│ ├── index.coffee
│ ├── init.coffee
│ ├── lib/
│ │ ├── setup.coffee
│ │ └── splitter.js
│ ├── models/
│ │ ├── .gitkeep
│ │ ├── note.coffee
│ │ └── notebook.coffee
│ └── views/
│ ├── .gitkeep
│ ├── note.handlebars
│ └── notebook.handlebars
├── css/
│ ├── font.scss
│ ├── keyframes.scss
│ ├── new.scss
│ └── normalize.scss
├── package.json
├── public/
│ ├── about.html
│ ├── default.json
│ ├── emojify.js
│ ├── handlebars.runtime.js
│ ├── images/
│ │ └── emoji/
│ │ └── emojify.css
│ ├── index.html
│ └── sublime.css
└── src/
├── AboutWindow.coffee
├── Springseed.coffee
└── main.coffee
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
* text
# Binary Files.
*.png binary
*.jpg binary
*.gif binary
*.svg binary
*.ttf binary
================================================
FILE: .gitignore
================================================
*~
javascript/*.js
.sass-cache
node_modules
*.sublime-project
*.sublime-workspace
codekit-config.json
libpeerconnection.log
# Becuase mh0 needs to compress for the debian packaging
*.nw
public/application.css
public/application.js
src/*.js
app/views/*.js
.srclib-cache
================================================
FILE: .gitmodules
================================================
[submodule "app/lib/socket.io-client"]
path = app/lib/socket.io-client
url = https://github.com/LearnBoost/socket.io-client.git
================================================
FILE: Cakefile
================================================
Scrunch = require 'coffee-scrunch'
uglify = require 'uglify-js'
server = require 'node-static'
http = require 'http'
fs = require 'fs'
sass = require 'node-sass'
watch = require 'node-watch'
# Configuration
config =
port: 9294
public: 'public'
js:
folder: 'app/'
input: 'app/init.coffee'
output: 'public/application.js'
min: 'public/application.js'
sass:
input: 'css/new.scss'
output: 'public/application.css'
# Options
option '-p', '--port [port]', 'Set port for cake server'
option '-w', '--watch', 'Watch the folder for changes'
compile =
sass: (options) ->
sasscompile = ->
console.log 'Compiling SASS'
sass.render {
file: config.sass.input
success: (css) ->
fs.writeFile config.sass.output, css, (err) ->
console.warn err if err
error: (err) ->
console.warn err if err
outputStyle: 'compressed'
}
if options.watch
watch config.sass.input, sasscompile
sasscompile()
coffee: (options) ->
if options.watch
watch config.js.folder, ->
process.stdout.write 'Compiling CoffeeScript: '
Scrunch(config.js).then ->
process.stdout.write 'Done!\n'
process.stdout.write 'Compiling CoffeeScript: '
Scrunch(config.js).then ->
process.stdout.write 'Done!\n'
minify: ->
js = uglify.minify(config.js.output).code
fs.writeFile config.js.min, js
# ===============================================================
# Tasks
# ===============================================================
task 'server', 'Start server', (options) ->
# Start Server
port = options.port or config.port
file= new(server.Server)(config.public)
server = http.createServer (req, res) ->
req.addListener( 'end', ->
file.serve(req, res)
).resume()
server.listen(port)
console.log "Server started on :#{port}"
# Compile files
compile.sass(options)
compile.coffee(options)
task 'build', 'Compile CoffeeScript', compile.coffee
task 'minify', 'Minify application.js', compile.minify
task 'style', 'Compile Stylesheets', compile.sass
================================================
FILE: Gemfile
================================================
source 'https://rubygems.org'
# Automatically run `make` everytime a file is changed, see Guardfile for internals.
gem 'guard'
gem 'guard-shell'
gem 'sass'
================================================
FILE: Guardfile
================================================
# Only used for development!
# Always run using bundle! (`bundle exec guard`)
guard :shell do
watch(/.*\.coffee$/) do
p `make clean` # clean everything.
p `make` # recompile everything.
`pkill -f ~/atom-shell/atom` # Kill all Springseed processes automatically, quite crude, just matches all processes using a `~/atom-shell` ¯\_(ツ)_/¯
p `~/atom-shell/atom ~/springseed` # restart springseed, again, crude, but does the job.
end
end
================================================
FILE: LICENSE
================================================
The MIT License (MIT)
Copyright (c) 2013-2014 Jono Cooper
Copyright (c) 2013-2014 Micheal Harker
Copyright (c) 2013-2014 Caffeinated Code <http://caffeinatedco.de>
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: Makefile
================================================
node_bin = ./node_modules/.bin
spsd = app/init.coffee
atom = $(wildcard src/*.coffee)
css = css/new.scss
spsd_out = public/application.js
atom_out = $(atom:%.coffee=%.js)
css_out = public/application.css
all: npm build-atom handlebars style build-app
build-atom: $(atom_out)
src/%.js: src/%.coffee
@$(node_bin)/coffee -bc $<
build-app: $(spsd_out)
$(spsd_out): $(spsd)
@$(node_bin)/browserify -t coffeeify $< > $@
handlebars:
@$(node_bin)/handlebars app/views/note.handlebars -f app/views/note.js
@$(node_bin)/handlebars app/views/notebook.handlebars -f app/views/notebook.js
style:
@sass $(css) $(css_out)
npm:
@npm install .
clean:
@rm -f $(spsd_out) $(atom_out) $(css_out)
================================================
FILE: Procfile
================================================
web: serveup ./public
================================================
FILE: README.md
================================================
# Springseed
[](http://waffle.io/byhestia/springseed)
**Current version: 2.0**
Springseed is the simple and easy way to take your notes.
# Binaries
A fairly up-to-date build is available [here](https://xack.xyz/misc/springseed.zip).
1. Extract `springseed.zip` into its own directory.
2. `./run.sh` **should** download atom-shell, extract it, then run the app.
If there are any issues, please don't hesitate to submit a bug report.
## Preparing Springseed
Springseed is now based on the awesome work of the people at GitHub and as
such we use the fantastic `electron` framework to get stuff done.
### Installing `electron`
(If you already have `electron` jump to the next section)
Latest builds of `electron` are available [here](https://github.com/atom/electron/releases).
* Download the appropriate file based on your operating system and the system architecture.
* Unzip the downloaded file. This must create an executable file named `electron`.
### Building this code
We have introduced a new build system based on the GNU Makefile build system. Should the
build below fail, you should run `make clean` before trying again because some
make operations won't complete if they've errored. Nothing we can do to fix
this. :smile:
sudo gem install sass
git submodule update --init
make
## Running Springseed
To start Springseed, run the command `<path-to-electron-executable>/electron <springseed-build-directory>` where `<springseed-build-directory>` is the directory where you executed the three commands in **Building this code** section
If you're feeling awesome, you should contribute either with code or a
[donation][1]. Check out the [issue tracker][2] and tackle an issue.
Springseed is written in CoffeeScript and uses Spine.JS for MVC.
## Official website
<http://getspringseed.com>
Copyright © 2013-2014 [Caffeinated Code][3]<br>
Copyright © 2014 [Hestia][4]
Open source under the [MIT license][5].
[1]: http://getspringseed.com/donate
[2]: https://github.com/byhestia/springseed
[3]: http://www.caffeinatedco.de/
[4]: http://byhestia.com/
[5]: http://opensource.org/licenses/MIT
================================================
FILE: app/controllers/account.coffee
================================================
Spine = require 'spine'
class Account
constructor: ->
@signin: (username, password) ->
# Sign in to the Springseed API. It should return a token. Save it.
localStorage.login = JSON.stringify {'username': username, 'password': password}
if not username and not password
# Try signing in with stored credidentials.
$.get 'http://api.getspringseed.com/login', JSON.parse(localStorage.login), (data) =>
if data['status'] is 200
localStorage.token = data['token']
return true # Logged in
else if data['status'] > 400
return false
else
# Try signing in with the credidentials we were given.
$.get 'http://api.getspringseed.com/login', {'username': username, 'password', password}, (data) =>
if data['status'] is 200
localStorage.token = data['token']
return true # Logged in
else if data['status'] > 400
return false
@isSignedIn: () ->
return true if localStorage.token
return false if not localStorage.token
@enableChecks: () ->
$.get 'http://api.getspringseed.com/me', {token: localStorage.token}, (data) =>
if data['status'] is 403
# Get a new valid token - for the old one has expired.
localStorage.removeItem 'token'
@signin()
else
console.log data
localStorage.data = JSON.stringify({'username': data.username, 'first_name': data.first_name, 'last_name': data.last_name, 'pro': data.pro})
setTimeout(@enableChecks, 300000);
@get: () ->
return JSON.parse(localStorage.data)
@signout: () ->
localStorage.removeItem 'token'
localStorage.removeItem 'login'
module.exports = Account
================================================
FILE: app/controllers/browser.coffee
================================================
Spine = require 'spine'
# Models
Note = require '../models/note.coffee'
Notebook = require '../models/notebook.coffee'
# Controllers
NoteItem = require '../controllers/note.item.coffee'
class Browser extends Spine.Controller
template: (->
require '../views/note.js'
Handlebars.templates['note']
)()
elements:
'ul': 'noteBrowser'
'span': 'title'
events:
'click #new': 'newNote'
constructor: ->
super
Note.bind "create", @addOne
Notebook.bind "changeNotebook", @changeNotebook
addOne: (note) =>
# We should always be in the right list, but doesn't hurt to check
if typeof(Notebook.current) isnt "undefined"
if note.notebook is Notebook.current.id and (Notebook.current.category is "all" or note.category is Notebook.find(Notebook.current.id).categories[Notebook.current.category])
note.date = note.prettyDate()
@noteBrowser.prepend @template note
view = new NoteItem
el: @noteBrowser.find("#note-#{ note.id }")
note: note
newNote: (e) =>
if Notebook.current.id isnt "all"
# Create the note meta
note = Note.create
name: "Untitled Note"
starred: false
excerpt: "This is your new blank note - add some content!"
notebook: Notebook.current.id
category: if Notebook.current.category is "all" then Notebook.find(Notebook.current.id).categories[0] else Notebook.find(Notebook.current.id).categories[Notebook.current.category]
date: Math.round(new Date().getTime()/1000)
# Set the content with the special function
note.saveNote "# This is your new blank note\nAdd some content!", ->
# Select it and throw it into editable mode
note.trigger("changeNote")
note.trigger("openNote")
else
Modal.get('newNote').run()
changeNotebook: (notebook) =>
dateSort = (a, b) ->
return b.date - a.date
if notebook.search is true or notebook.search is not undefined
noteList = ""
for note in notebook.result
note.date = note.prettyDate
noteList += @template note
@noteBrowser.html noteList
else
noteList = ""
for note in Note.filter(Notebook.current.id, Notebook.current.category).sort(dateSort)
note.date = note.prettyDate()
noteList += @template note
@noteBrowser.html(noteList)
# Defers for speed
window.requestAnimationFrame( =>
for note in Note.filter(Notebook.current.id, Notebook.current.category)
view = new NoteItem
el: @noteBrowser.find("#note-#{ note.id }")
note: note
)
module.exports = Browser
================================================
FILE: app/controllers/editor.coffee
================================================
Spine = require 'spine'
marked = require 'marked'
hljs = require 'highlight.js'
# Models
Note = require '../models/note.coffee'
# Controllers
window.Modal = require '../controllers/modal.coffee'
class Editor extends Spine.Controller
elements:
".headerwrap .left input": "title"
".headerwrap .left time": "time"
"#contentread": "contentread"
"#contentwrite > .inner": "contentwrite"
".headerwrap .edit": "toggle"
"#psuedoinput": "psuedoinput"
".headerwrap i.star": "star"
events:
"click .headerwrap .edit": "toggleMode"
"click .headerwrap .revert": "revert"
"keydown #contentwrite > .inner": "keydown"
#"paste #contentwrite > .inner": "paste"
"dblclick #contentread": "toggleMode"
"click header .right .delete": "deleteNote"
"click header .star": "starNote"
"textInput section.inner": "inputHandler" # used for emojis
"blur section.inner": "blurHandler" # again, used for emojis
starNote: ->
note = Note.find(Note.current.id)
if note.starred is "true"
note.updateAttribute("starred", "false")
@star.addClass("fa-star-o")
@star.removeClass("fa-star")
@star.removeClass("starred")
else
note.updateAttribute("starred", "true")
@star.removeClass("fa-star-o")
@star.addClass("fa-star")
@star.addClass("starred")
deleteNote: (e) ->
note = Note.find(Note.current.id)
$(".delete-container span.name").text(note.name)
Modal.get("delete").run()
constructor: ->
super
Note.bind("changeNote", @enable)
Note.bind "openNote", =>
@toggleMode()
Note.bind("revert", @revertNote)
@.bind("checkSel", @checkSelWrap)
emojify.setConfig {
ignored_tags: {
'CODE': 1
}
}
marked.setOptions {
highlight: (code, lang) ->
try
if lang
return hljs.highlight(lang.toLowerCase(), code).value
else
return hljs.highlightAuto(code).value
catch error
code
tables: true
sanitize: true
smartLists: true
smartypants: true
}
@controls = $("#editorcontrols")
@controls.find('#bold').click @formatBold
@controls.find('#italics').click @formatItalics
@controls.find('#heading').click @formatHead
enable: (note) =>
# Put back into the right mode
@toggleMode() if @mode is "edit"
# Loads note
Note.current = note
if note isnt undefined
currentNote = Note.find(note.id)
@el.removeClass("deselected")
@title.val currentNote.name
@time.text currentNote.prettyDate(true)
if currentNote.starred is "true"
@star.attr('class', 'star fa fa-star starred')
else
@star.attr('class', 'star fa fa-star-o')
# Content
currentNote.loadNote (content) =>
@contentread.html marked(content)
@contentwrite.text content
# this needs to be bound
$('#contentread a').attr('target', '_blank').click ->
return false
else
@el.addClass("deselected")
setInterval(@wc, 1000)
@mode = "preview"
wc: ->
text = $('#contentwrite section').text()
wc = $(marked(text)).text().split(' ').length
$('.wc').text(wc)
toggleMode: (save) ->
if @mode is "preview" # enable the editor
# UI bits and bobs
@el.addClass("edit")
@toggle.find("i").addClass("fa-lock")
@toggle.find("i").removeClass("fa-pencil")
@toggle.find("i")[0].parentNode.title = "Preview Note"
@title.prop "disabled", false
@mode = "edit"
# Focus the text area
@contentwrite.focus()
else # disable the editor
if save is false and Note.current isnt undefined # We're not saving the content (revert button)
currentNote = Note.find(Note.current.id)
currentNote.loadNote (content) =>
@contentwrite.text content
else
@controls.hide()
# Copy the text in
noteText = @contentwrite.text()
@contentread.html marked(noteText)
$('#contentread a').attr('target', '_blank').click ->
return false
# Save it
if Note.current isnt undefined
currentNote = Note.find(Note.current.id)
# Excerpts nicely
info = noteText
if info.length > 90
info = info.substring(0, 100)
lastIndex = info.lastIndexOf(" ")
info = info.substring(0, lastIndex) + "…"
info = $(marked(info)).text()
info = info.split("\n").join(" ")
# Update Spine
currentNote.updateAttributes {
"name": @title.val()
"excerpt": info
"date": Math.round(new Date()/1000)
}
# Update IndexedDB
currentNote.saveNote(noteText)
# The opposite
@el.removeClass("edit")
@toggle.find("i").removeClass("fa-lock")
@toggle.find("i").addClass("fa-pencil")
@toggle.find("i")[0].parentNode.title = "Edit Note"
@title.prop "disabled", true
@mode = "preview"
@time.text currentNote.prettyDate(true)
revert: ->
if @mode isnt "preview"
Modal.get('revert').run()
revertNote: =>
@toggleMode(false)
# Pops the text into the contenteditable
insertText: (text) ->
sel = window.getSelection()
range = sel.getRangeAt(0)
range.deleteContents()
textNode = document.createTextNode(text)
range.insertNode(textNode)
range.setStartAfter(textNode)
sel.removeAllRanges()
sel.addRange(range)
keydown: (e) ->
# Some keys are special
if e.keyCode is 13 #return
#e.preventDefault()
@insertText "\n"
else if e.keyCode is 9 #tab
e.preventDefault()
@insertText " "
else if e.keyCode is 27 #escape key
e.preventDefault()
@toggleMode()
#paste: (e) ->
# # Keeps the range for later
# @range = window.getSelection().getRangeAt(0)
# # Paste it into a textarea (removes formatting)
# #@psuedoinput.val("").focus()
# # As the paste event isn't instant, put it back in a few secs.
# setTimeout( =>
# s = window.getSelection()
# s.removeAllRanges()
# s.addRange(@range)
# @insertText @psuedoinput.val()
# , 10)
checkSelWrap: ->
setTimeout =>
@checkSel()
, 50
checkSel: ->
if @mode is "preview"
@controls.hide()
else
sel = window.getSelection()
if sel.toString().trim() is ''
@controls.hide()
else
@sel = sel
@selrange = sel.getRangeAt(0)
@controls.show()
toppos = @selrange.getBoundingClientRect().top - 55 - @controls.height() + 'px'
leftpos = @selrange.getBoundingClientRect().right - (@controls.width() / 2) + 'px'
@controls.css {top: toppos, left: leftpos}
newString: (str) ->
@selrange.surroundContents(document.createElement("span"))
text = @el.find("span").text()
@el.find("span").text(str).contents().unwrap()
formatBold: (e) =>
str = @selrange.toString()
if str.substring(0,2) is "**" and str.substring(str.length-2,str.length) is "**"
@newString(str.substring(2,str.length-2))
else
@sel.collapseToStart()
@sel.modify("move", "backward", "character")
@sel.modify("move", "backward", "character")
@sel.modify("extend", "forward", "word")
@sel.modify("extend", "forward", "character")
@sel.modify("extend", "forward", "character")
@selrange = @sel.getRangeAt(0)
str = @selrange.toString()
if str.substring(0,2) is "**" and str.substring(str.length-2,str.length) is "**"
@newString(str.substring(2,str.length-2))
else
@sel.collapseToStart()
@sel.modify("move", "forward", "character")
@sel.modify("move", "forward", "character")
@sel.modify("extend", "forward", "word")
@selrange = @sel.getRangeAt(0)
str = @selrange.toString()
@newString("**" + str + "**")
formatItalics: (e) =>
str = @selrange.toString()
if str.substring(0,1) is "*" and str.substring(str.length-1,str.length) is "*"
@newString(str.substring(1,str.length-1))
else
@sel.collapseToStart()
@sel.modify("move", "backward", "character")
@sel.modify("extend", "forward", "word")
@sel.modify("extend", "forward", "character")
@selrange = @sel.getRangeAt(0)
str = @selrange.toString()
if str.substring(0,1) is "*" and str.substring(str.length-1,str.length) is "*"
@newString(str.substring(1,str.length-1))
else
@sel.collapseToStart()
@sel.modify("move", "forward", "character")
@sel.modify("extend", "forward", "word")
@selrange = @sel.getRangeAt(0)
str = @selrange.toString()
@newString("*#{str}*")
formatHead: (e) =>
@sel.modify("extend", "backward", "paragraphboundary")
@sel.modify("extend", "forward", "paragraphboundary")
@selrange = @sel.getRangeAt(0)
if @selrange.toString().substring(0,3) isnt "###"
@selrange.surroundContents(document.createElement("span"))
text = @el.find("span").text()
@el.find("span").text("#"+text+"").contents().unwrap()
else
@selrange.surroundContents(document.createElement("span"))
text = @el.find("span").text()
@el.find("span").text(text.substring(3)).contents().unwrap()
inputHandler: (e) ->
# Handle Emoji creation
if e.originalEvent.data == ":" # capture colon key
setTimeout (->
emojify.run() # ALL the emojis!
), 100
blurHandler: (e) ->
for i in $('section.inner img.emoji')
$(i).replaceWith(i.title)
module.exports = Editor
================================================
FILE: app/controllers/modal.coffee
================================================
Spine = require 'spine'
$ = Spine.$
# Needed.
Note = require '../models/note.coffee'
Notebook = require '../models/notebook.coffee'
# Base Modal Class.
class Modal extends Spine.Controller
constructor: (opts) ->
super
# Probably shouldn't be in here, but whatever
Spine.bind 'sync:meta', =>
modals['syncmeta'].run()
state: off
show: ->
return unless @state is off
@state = on
@el.show(0).addClass 'show'
if @onShow then @onShow()
setTimeout ( =>
@el.on "click.modal", (event) =>
if event.target.className.indexOf('modals') > -1
@hide()
), 500
hide: ->
return unless @state is on
@state = off
@el.removeClass 'show'
setTimeout ( =>
@el.hide(0)
if @onHide then @onHide()
), 350
@el.off 'click.modal'
modals = []
module.exports =
get: (name) ->
# Return a Modal object. Like a pro.
return modals[name]
init: ->
# Do init stuff here.
# Like, uh, getting modals sorted. Yolo.
modals['delete'] = new Modal
el: $('.modal.delete')
events:
'click .true': 'delete'
'click .false': 'hide'
run: ->
@show()
delete: ->
# Taken from controllers/panel.coffee.
if Note.current isnt undefined
currentNote = Note.find(Note.current.id)
# Take it out of editmode
Note.trigger 'changeNote'
currentNote.destroy()
@hide()
modals['newNote'] = new Modal
el: $('.newNote')
events:
'click .gotit': 'hide'
run: ->
@show()
modals['revert'] = new Modal
el: $('.modal.revert')
events:
'click .true': 'revert'
'click .false': 'hide'
run: ->
@show()
revert: ->
Note.trigger 'revert'
@hide()
modals['syncmeta'] = new Modal
el: $('.modal.syncmeta')
events:
'click .destroyclient': 'destroyclient'
'click .destroyserver': 'destroyserver'
destroyclient: ->
Sync.firstSync("destroyclient")
@hide()
destroyserver: ->
Sync.firstSync("destroyserver")
@hide()
run: ->
@show()
modals['deleteNotebook'] = new Modal
el: $('.modal.deleteNotebook')
events:
'click .true': 'delete'
'click .false': 'hide'
run: (notebookid, @category) ->
@notebook = Notebook.find(notebookid)
numberofNotes = Note.filter(notebookid, category).length;
if @category is "all"
@el.find('.type').text "Notebook"
@el.find('i').text @notebook.name
else
@el.find('.type').text "Subcategory"
@el.find('i').text @notebook.categories[@category]
@el.find('.numberofNotes').text(numberofNotes)
if numberofNotes > 1
@el.find('.oneOrMore').show()
else
@el.find('.oneOrMore').hide()
if numberofNotes > 0
@show()
else
@delete()
delete: ->
Note.trigger 'changeNote'
if @category is "all"
@notebook.destroy()
else
@notebook.subcategoryDestroy(@category)
@hide()
modals['renameNotebook'] = new Modal
el: $('.modal.renameNotebook')
events:
'click .true': 'rename'
'click .false': 'hide'
run: (@notebookid, @category) ->
# Subcategories need a proper lookup
input = @el.find('input')
if @category is 'all'
input.val(Notebook.find(@notebookid).name)
else
input.val(Notebook.find(@notebookid).categories[@category])
@show()
rename: ->
# Backslashes only seem to be an issue here
input = @el.find('input').val()
input = input.replace(/\\/g, '')
notebook = Notebook.find(@notebookid)
# Check for Empty String
if input isnt ''
if @category is 'all'
notebook.updateAttribute 'name', input
else
# Subcategories need to be cloned then updated
# Also, I wish there was a better way to do pointers.
arr = notebook.categories.slice(0)
arr[@category] = input
notebook.updateAttribute 'categories', arr
Notebook.trigger 'refresh'
@hide()
modals['pleaseLogIn'] = new Modal
el: $('.modal.pleaseLogIn')
events:
'click .false': 'hide'
hide: ->
@hide()
================================================
FILE: app/controllers/note.item.coffee
================================================
Spine = require 'spine'
# Models
Note = require '../models/note.coffee'
class NoteItem extends Spine.Controller
elements:
"h2": "title"
"time": "time"
"span": "excerpt"
events:
"click": "select"
"dragstart": "startdrag"
"dragend": "stopdrag"
constructor: ->
super
@note.bind "changeNote", @changeNote
@note.bind "change", @updateNote
@note.bind "destroy", @deleteNote
select: ->
Note.trigger "changeNote", {id: @note.id}
# console.log 'unemojified - change'
changeNote: =>
@el.parent().find(".selected").removeClass("selected")
@el.addClass("selected")
setTimeout (->
emojify.run() # ALL the emojis!
), 100
updateNote: =>
@title.text @note.name
@time.text @note.prettyDate() + " -"
@excerpt.text @note.excerpt
deleteNote: =>
@el.remove()
startdrag: (e) =>
@el.css {opacity: 0.4}
noteid = $(e.target).attr('id').replace("note-", "")
e.originalEvent.dataTransfer.setData('noteid', noteid)
stopdrag: (e) =>
@el.css {opacity: 1}
module.exports = NoteItem
================================================
FILE: app/controllers/notebook.item.coffee
================================================
Spine = require 'spine'
# Models
Notebook = require '../models/notebook.coffee'
class NotebookItem extends Spine.Controller
elements:
'ul': 'category'
events:
'click': 'expand'
'contextmenu': 'expand'
'contextmenu': 'toggleMore'
'click .icon': 'newCategory'
'dragenter': 'onDragEnter'
'dragleave': 'onDragLeave'
'dragover': 'onDragOver'
'drop': 'onDrop'
constructor: ->
super
if @notebook.id isnt 'all'
@notebook.bind 'changeNotebook', @changeNotebook
@notebook.bind 'update', @update
@notebook.bind 'destroy', @destroy
expand: (e) =>
# Categories
if $(e.target).attr('data-category')
Notebook.trigger('changeNotebook', {id: @notebook.id, category: $(e.target).attr('data-category')})
else
Notebook.trigger('changeNotebook', {id: @notebook.id, category: 'all'})
# Hacky, but whatever.
@changeNotebook({id: 'all', category: 'all'}) if @notebook.id is 'all'
toggleMore: (e) =>
@expand(e)
e.preventDefault()
if !$(e.target).hasClass('icon') and @notebook.id isnt 'all'
$('.popover-mask').show()
target = $(e.target).parent()
if $(e.target).attr('data-category') is 'all'
return
# Category
else if $(e.target).attr('data-category')
$('.delete-popover').css(
left: target.outerWidth(),
top: $(e.target).offset().top-($('.delete-popover').height()/3)
).attr('data-notebook', @notebook.id
).attr('data-category', $(e.target).attr('data-category')
).show()
# Book
else
$('.delete-popover').css(
left: target.outerWidth(),
top: @el.offset().top
).attr('data-notebook', @notebook.id
).attr('data-category', 'all'
).show()
changeNotebook: (notebook) =>
@el.parent()
.children()
.removeClass('expanded selected')
@el.addClass('selected')
# Only show the categories if there's more than one.
@el.addClass('expanded') if @notebook.categories.length > 1
# Select the right one
@category.find('li').removeClass('selected')
@el.find("[data-category='#{notebook.category}']").addClass('selected')
newCategory: (e) ->
$('.popover-mask').show()
target = $(e.target).parent()
$('.category-popover').css({left: target.outerWidth(), top: target.offset().top}).show()
.find('input').val('').focus()
update: =>
# Subcategories
str = '<li data-category="all" class="selected">All Notes</li>'
for category, i in @notebook.categories
str += "<li data-category=#{i}>#{category}</li>"
@category.html(str)
@el.addClass('expanded') if @notebook.categories.length > 1
destroy: =>
@el.remove()
onDragEnter: (e) =>
e.preventDefault()
if ($(e.target).attr('data-category') and $(e.target).attr('data-category') isnt 'all') or $(e.currentTarget).attr('id') isnt 'notebook-all'
$(e.target).addClass('dragover')
onDragLeave: (e) =>
e.preventDefault()
$(e.target).removeClass('dragover')
onDragOver: (e) =>
e.preventDefault()
if @notebook.id is 'all' or $(e.target).attr('data-category') is 'all'
# No.
return false
onDrop: (e) =>
# Create the note in this Notebook, delete the old one.
if @notebook.id is 'all' or $(e.target).attr('data-category') is 'all'
return false;
else
noteid = e.originalEvent.dataTransfer.getData('noteid')
note = Note.find(noteid)
if $(e.target).attr('data-category')
category = $(e.target).text()
$(e.target).removeClass('dragover') # Reset CSS changes
else
category = @notebook.categories[0] # Default Category
note.updateAttributes {
'notebook': @notebook.id,
'category': category
}
# Finally, refresh!
Notebook.trigger 'changeNotebook', {id: Notebook.current.id, category: Notebook.current.category}
module.exports = NotebookItem
================================================
FILE: app/controllers/panel.coffee
================================================
Spine = require 'spine'
# Models
Note = require '../models/note.coffee'
Notebook = require '../models/notebook.coffee'
Modal = require '../controllers/modal.coffee'
Settings = require '../controllers/settings.coffee'
Account = require '../controllers/account.coffee'
class Panel extends Spine.Controller
elements:
"#loginbox": "login"
events:
"dblclick": "maximize"
"click #decor img": "windowControl"
"keyup #search input": "search"
"click #loginbox .name": "userSettings"
maximized: false
constructor: ->
super
Note.bind "changeNote", @toggleNote
Notebook.bind "changeNotebook", @toggleNotebook
# if win
# win.on 'maximize', =>
# @maximized = true
# win.on 'unmaximize', =>
# @maximized = false
setInterval ->
if Account.isSignedIn()
$('#loginbox').find('.name').text(Account.get().first_name + " " + Account.get().last_name)
if Account.get().pro
$('#loginbox').find('.gopro').text('you\'re pro')
else
$('#loginbox').find('.name').text("log in")
$('#loginbox').find('.gopro').text('go pro')
,100
# Resizes the panel seperator
browser = $("#browser")
$(".splitter.split-right").on "mouseup", =>
@noteControls.width((browser.width()-4))
userSettings: (e) ->
Settings.get().show("account")
windowControl: (e) ->
# switch e.currentTarget.className
# when "close"
# win.close()
# when "minimize"
# win.minimize()
# when "maximize"
# @maximize()
maximize: ->
win.maximize() if @maximized is false
win.unmaximize() if @maximized is true
toggleNote: (note) =>
# if note isnt undefined
# @noteControls.removeClass "disabled"
# else
# @noteControls.addClass "disabled"
toggleNotebook: (notebook) =>
# if notebook.id is "all"
# @noteControls.addClass "all"
# else
# @noteControls.removeClass "all"
search: (e) =>
# This feels so ugly :/
searchstring = $(e.target).val()
Note.search(searchstring)
module.exports = Panel
================================================
FILE: app/controllers/popover.coffee
================================================
Spine = require 'spine'
# Models
Notebook = require '../models/notebook.coffee'
Modal = require './modal.coffee'
class Popover extends Spine.Controller
elements:
".delete-popover": "categoryPopover"
".delete-popover input": "categoryInput"
".delete-popover": "delpopover"
events:
"click": "hidePopover"
"contextmenu": "hidePopover"
"click .category-popover button": "addCategory"
"keyup .category-popover input": "addCategory"
"click .delete-popover #deleteNotebook": "deleteNotebook"
"click .delete-popover #renameNotebook": "renameNotebook"
"keyup .delete-popover #addCat": "addCategory"
"focusout .delete-popover #addCat": "cleanField"
constructor: ->
super
hidePopover: (e) ->
# Not sure where to put this, but it is global
e.preventDefault()
@el.hide().children().hide() if $(e.target)[0].nodeName isnt "INPUT"
cleanField: (e) ->
$(e.target)[0].value = ""
addCategory: (e) ->
if e.type is "keyup" and e.which is 13
# adds the new category on
notebook = Notebook.find(Notebook.current.id)
cat = notebook.categories
name = @categoryInput.val().replace(/\\/g, '')
if name isnt ''
cat.push(name)
notebook.updateAttribute("categories", cat)
@cleanField(e)
@el.hide()
renameNotebook: (e) =>
# All renaming gets implemented in modal.coffee
notebookid = @delpopover.attr('data-notebook')
categoryid = @delpopover.attr('data-category')
Modal.get('renameNotebook').run(notebookid, categoryid)
deleteNotebook: (e) =>
# All deletion gets implemented in modal.coffee
notebookid = @delpopover.attr('data-notebook')
categoryid = @delpopover.attr('data-category')
Modal.get('deleteNotebook').run(notebookid, categoryid)
module.exports = Popover
================================================
FILE: app/controllers/settings.coffee
================================================
Spine = require 'spine'
$ = Spine.$
shell = window.require('shell') if window.require
Sync = require './sync.coffee'
Account = require '../controllers/account.coffee'
class Settings extends Spine.Controller
elements:
'.sync #signin': 'signinbtn'
'.sync #signout': 'signoutbtn'
'.account #signin': 'signinacc'
'.sync .username': 'username'
'.account .name': 'accusername'
'.about': 'aboutPage'
'.general': 'generalPage'
'.sync .signedin': 'signedin'
'.sync .signedout': 'signedout'
'.account .signedin': 'accsignedin'
'.account .signedout': 'accsignedout'
'#accusername': 'usernameinput'
'#accpassword': 'passwordinput'
'.account .signedin #pro': 'pro'
'.account .signedin #alreadypro': 'alreadypro'
events:
'click .tabs li': 'tabs'
'click .sync #signin': 'signin'
'click .sync #signout': 'signout'
'click .account #signin': 'accountSignin'
'click .account #signout': 'accountSignout'
state: off
constructor: ->
super
# A really bad hack
Spine.bind 'sync:authorized', =>
@hide()
@signedout.hide()
@signedin.show()
$.ajax(
Sync.generateRequest {request: "me"}
).done((data) =>
@username.text data.email
)
Spine.bind 'sync:unauthorized', =>
@signedout.show()
@signedin.hide()
@signinbtn.text 'Sign In'
setInterval () =>
if Account.isSignedIn()
@accsignedout.hide()
@accsignedin.show()
@accusername.text(Account.get().first_name + " " + Account.get().last_name)
if Account.get().pro
@pro.hide()
@alreadypro.show()
else
@accsignedin.hide()
@accsignedout.show()
@pro.show()
@alreadypro.hide()
,100
accountSignin: ->
if Account.signin(@usernameinput.val(), @passwordinput.val())
if Account.isSignedIn()
@hide()
Account.enableChecks()
else
@signinacc.text "Wrong Username/Password"
setTimeout () =>
@signinacc.text "Sign in"
, 5000
accountSignout: ->
Account.signout()
tabs: (e) ->
# This is ugly. Shoot me later. Could not think of a better implementation.
@el.find('.current').removeClass 'current'
@el.find('div.'+$(e.target).addClass('current').attr('data-id')).addClass 'current'
show: (tab) ->
return unless @state is off
@state = on
@el.show(0).addClass("show")
setTimeout ( =>
@el.on "click.modal", (event) =>
if event.target.className.indexOf('modal') > -1 then @hide()
), 500
if Account.isSignedIn()
@accsignedin.show()
@accsignedout.hide()
else
@accsignedout.show()
@accsignedin.hide()
if tab is not null
$('.tabs ul li[data-id="'+tab+'"]').click()
hide: ->
return unless @state is on
@state = off
@el.removeClass("show")
setTimeout ( => @el.hide(0)), 350
@el.off("click.modal")
signin: ->
@signinbtn.text 'Connecting...'
Sync.auth (data) =>
shell.openExternal(data.url)
signout: ->
Sync.signOut()
settings = null # Temp
module.exports =
get: ->
return settings
init: ->
settings = new Settings
el: $('.modal.preferences')
================================================
FILE: app/controllers/sidebar.coffee
================================================
Spine = require 'spine'
# Models
Note = require '../models/note.coffee'
Notebook = require '../models/notebook.coffee'
# Controllers
NotebookItem = require './notebook.item.coffee'
Settings = require './settings.coffee'
Sync = require './sync.coffee'
class Sidebar extends Spine.Controller
template: (->
require '../views/notebook.js'
Handlebars.templates['notebook']
)()
events:
"keyup input": "new"
"click #settings": "toggleSettings"
"click #sync": "doSync"
elements:
"ul": "list"
"input": "input"
"#settings": "settings"
"#sync": "sync"
constructor: ->
super
Notebook.bind "create", @addOne
Notebook.bind "changeNotebook", @change
Notebook.bind "refresh", @refresh
Notebook.bind "destroy", @destroy
# Starts and stops the animation
Spine.bind 'sync:start', =>
@sync.addClass 'spin'
Spine.bind 'sync:stop', =>
@sync.removeClass 'spin'
addOne: (notebook) =>
@list.append @template notebook
view = new NotebookItem
el: @list.find("#notebook-#{ notebook.id }")
notebook: notebook
change: (notebook) =>
# This is defined here, or some weird shit happens
# Annoyingly, this is a two step process
if Note.current
if Note.current.persist
setTimeout( =>
Note.trigger "changeNote", Note.current
, 100)
else
Note.trigger "changeNote"
else
Note.trigger "changeNote"
Notebook.current = notebook
refresh: () =>
# Called on load from indexeddb
html = @template {id: "all", name: "All Notes"}
for notebook in Notebook.all()
html += @template notebook
@list.html(html)
# Defers for speed
window.requestAnimationFrame( =>
# All Notes
new NotebookItem
el: @list.find("#notebook-all")
notebook: {id: "all", name: "All Notes", categories: []}
# Normal Notes
for notebook in Notebook.all()
view = new NotebookItem
el: @list.find("#notebook-#{ notebook.id }")
notebook: notebook
# This feels so bad :(
if Notebook.current
Note.current.persist = true if Note.current
# Doubley bad
if Notebook.current.id is "all"
$("#notebook-all").trigger("click")
else
Notebook.trigger('changeNotebook', Notebook.current)
else
$("#notebook-all").trigger("click")
)
destroy: ->
# This is bad practice, but I knew this would happen.
# We'll revamp it when we get Smart Lists
$("#notebook-all").trigger("click")
new: (e) ->
val = @input.val()
if e.which is 13 and val
# Make a new Notebook
newNotebook = Notebook.create
name: val
categories: ["General"]
date: Math.round(new Date()/1000)
# Select that notebook for opening
Notebook.trigger "changeNotebook", {id: newNotebook.id, category: "all"}
@input.val ""
toggleSettings: (e) ->
Settings.get().show("sync")
doSync: ->
if localStorage.oauth
Sync.doSync()
else
@toggleSettings()
module.exports = Sidebar
================================================
FILE: app/controllers/sync.coffee
================================================
Spine = @Spine or require 'spine'
io = require '../lib/socket.io-client/dist/socket.io.js'
Model = Spine.Model
# Connection states
OFFLINE = 0
IN_PROGRESS = 1
ONLINE = 2
window.Sync =
oauth: JSON.parse localStorage.oauth or '{"service": "undefined"}'
queue: JSON.parse localStorage.Queue or '{"Note": {}, "Notebook": {}}'
syncMeta: {}
# Run a Sync on a Timeout
# Clears if another request is put through
timeoutSync: ->
clearTimeout Sync.timeoutId
Sync.timeoutId = setTimeout ->
Sync.doSync() if localStorage.oauth
, 5000
# Hold pending actions
pending: []
# Run pending actions
_clearPending: ->
for [fn, that, args] in @pending by - 1
fn.apply(that, args)
@pending.length--
# If connection is not ready, then we will wait until it is
defer: (self, fn, args...) ->
if @state is ONLINE
fn.apply(self, args)
else
@pending.push [fn, self, args]
state: OFFLINE
signOut: ->
# Deletes everything. Should work right?
localStorage.removeItem 'oauth'
Sync.oauth = JSON.parse localStorage.oauth or '{"service": "undefined"}'
Spine.trigger 'sync:unauthorized'
Spine.trigger 'sync:stop'
# analytics
anal: ->
$.ajax(
Sync.generateRequest({request: "me"})
).done (info) ->
anal =
name: info.display_name
email: info.email
countryCode: info.country
language: navigator.language
platform: navigator.platform
version: localStorage.version
# $.get("http://api.getspringseed.com/client", anal)
auth: (callback) ->
# Saves data after getting it from the auth server
onData = (data) ->
# Calc expiration date
data.expires = new Date().getTime() + parseInt(data.expires_in)*1000 if data.hasOwnProperty("expires_in")
Sync.oauth = data
localStorage.oauth = JSON.stringify(data)
Spine.trigger 'sync:authorized'
# Weird? Do a sync on error. Nope. We're checking if a meta file exists and then asking the user if they
# want to copy from the server, or copy to the server. I did tests and if we don't have this step, we get
# some really weird fuckery. File systems are great, yes. But databases are sometimes better.
$.ajax(
Sync.generateRequest
request: "download"
filename: "meta"
).done((data) ->
Spine.trigger 'sync:meta'
).error((data) ->
Sync.doSync()
)
if Sync.oauth.service is "undefined"
# There's something weird, even thought the sockets should die.
if Sync.socket
Sync.socket.disconnect()
Sync.socket.socket.reconnect()
else
Sync.socket = io.connect("https://springseed.azurewebsites.net:443")
Sync.socket.on "meta", (data) ->
console.log('meta', data)
callback(data)
Sync.socket.on "authorized", (data) ->
onData(data)
Sync.socket.disconnect() # Free up server resources
# If it expires in the next ten minutes, we'll refresh the token
else if Sync.oauth.service is "skydrive" and new Date().getTime() > (Sync.oauth.expires - 6000000)
$.ajax(
Sync.generateRequest
request: "refresh"
).done((data) ->
onData(data)
callback() if callback
)
# There's two options of destroying stuff.
# Destroy the server, or destroy the client.
firstSync: (method) ->
deferred = new $.Deferred()
if method is "destroyserver"
$.ajax(
Sync.generateRequest
request: "destroy"
filename: "meta"
dataType: "text"
).done (data) ->
# Do the real initial sync
Sync.doSync()
else if method is "destroyclient"
# Delete all the notes in the app
Notebook.all().forEach (notebook) ->
notebook.destroy()
# Then delete the queue so nothing tries to sync
Sync.queue = {"Note": {}, "Notebook": {}}
Sync.saveQueue()
# Do the real initial sync
Sync.doSync()
preSync: ->
deferred = new $.Deferred()
if Sync.oauth.service is "skydrive"
$.ajax(
Sync.generateRequest
request: "findappdir"
).done((data) ->
for item in data.data
# Break when we find the springseed folder
if item.name.toLowerCase() is "springseed"
Sync.syncMeta.folderid = item.id
break
$.ajax(
Sync.generateRequest
request: "listappdir"
).done((data) ->
Sync.syncMeta.map = {}
for item in data.data
Sync.syncMeta.map[item.name] = item.id
deferred.resolve()
)
)
else
deferred.resolve()
return deferred.promise()
# The main syncing function.
# It downloads the meta, then updates it to latest version as well as doing various other io.
# It's magic. Trust me.
doSync: ->
Spine.trigger 'sync:start'
# Downloads a meta file.
$.ajax(
Sync.generateRequest
request: "download"
filename: "meta"
).done((data) ->
# Merge all the magical data together
result = Sync.merger(JSON.parse(Sync.exportData()), data, Sync.queue)
# First, we rename the keys in our current DB
promises = []
result[1].Note.forEach (item) ->
promises.push((->
deferred = $.Deferred()
Note.find(item[0]).loadNote (oldcontent) ->
trans = Sync.db.transaction(["notes"], "readwrite")
store = trans.objectStore "notes"
request = store.put(oldcontent, item[1])
request.onsuccess = (e) =>
Note.find(item[0]).deleteNote ->
deferred.resolve()
return deferred.promise()
)())
# All files renamed, doing da internet IO.
$.when.apply($, promises).then ->
promises = []
dbrequests = []
trans = Sync.db.transaction(["notes"], "readwrite")
store = trans.objectStore "notes"
result[2].Note.forEach (item) ->
promises.push((->
deferred = $.Deferred()
switch item[0]
when "upload"
itemid = item[1]
dbrequests[itemid] = store.get(itemid)
dbrequests[itemid].onsuccess = (e) =>
# Upload file to my butt.
$.ajax(
Sync.generateRequest
request: "upload"
filename: itemid
dataType: "text"
data: e.target.result
).done (data) ->
deferred.resolve()
when "download"
# Download from my butt
$.ajax(
Sync.generateRequest
request: "download"
filename: item[1]
dataType: "text"
).done (data) ->
# We now have to store the new data in the database.
trans = Sync.db.transaction(["notes"], "readwrite")
store = trans.objectStore "notes"
request = store.put(data, item[1])
request.onsuccess = (e) =>
deferred.resolve()
when "destroy"
dbrequests[item[1]] = store.delete(item[1])
file = item[1]
# Delete in the butt
$.ajax(
Sync.generateRequest
request: "destroy"
filename: file
dataType: "text"
).done (data) ->
deferred.resolve()
return deferred.promise()
)())
# Almost all io done, replace metas now.
$.when.apply($, promises).then ->
# Copy into the app
Sync.importData JSON.stringify(result[0])
# Send it to my butt yo
$.ajax(
Sync.generateRequest
request: "upload"
filename: "meta"
data: JSON.stringify(result[0])
).done (data) ->
console.log("all done! Delete the queue")
Sync.queue = {"Note": {}, "Notebook": {}}
Sync.saveQueue()
Spine.trigger 'sync:stop'
).error (data) ->
if data.status is 401
console.log "the bearer token is wrong. deleting token & please reauth"
Sync.signOut()
else if data.status is 404
console.log "meta does not exist. uploading a new meta w/ every single file"
promises = []
$(Note.all()).each (i, e) ->
e.loadNote (data) ->
# make ajax request based on inputs from current loop element
promises.push($.ajax(
Sync.generateRequest
request: "upload"
filename: e.id
dataType: "text"
data: data
)
)
# stuff
$.when(promises).done ->
console.log 'All files uploaded, uploading the meta file'
$.ajax(
Sync.generateRequest
request: "upload"
filename: "meta"
data: Sync.exportData()
).done (data) ->
console.log("all done! Delete the queue")
Sync.queue = {"Note": {}, "Notebook": {}}
Sync.saveQueue()
Spine.trigger 'sync:stop'
Note.trigger 'refresh'
# It's *slightly* nicer having a function generating requests
generateRequest: (opts) ->
if Sync.oauth.service is "dropbox"
params =
crossDomain: true
dataType: opts.dataType || "json"
beforeSend: (xhr) ->
xhr.setRequestHeader "Authorization", "Bearer " + Sync.oauth.access_token
if opts.request is "me"
params.type = "get"
params.url = "https://api.dropbox.com/1/account/info"
else if opts.request is "download"
params.type = "get"
params.url = "https://api-content.dropbox.com/1/files/sandbox/" + opts.filename + ".seed"
else if opts.request is "upload"
params.type = "put"
params.contentType = opts.contentType || "application/octet-stream"
params.url = "https://api-content.dropbox.com/1/files_put/sandbox/" + opts.filename + ".seed"
params.data = opts.data
else if opts.request is "destroy"
params.type = "post"
params.url = "https://api.dropbox.com/1/fileops/delete"
params.data = {
root: "sandbox"
path: opts.filename + ".seed"
}
else if Sync.oauth.service is "skydrive"
params =
crossDomain: true
dataType: opts.dataType || "json"
if opts.request is "refresh"
params.type = "get"
params.url = "https://springseed.azurewebsites.net:443/refresh/skydrive?code=" + Sync.oauth.refresh_token
else if opts.request is "me"
params.type = "get"
params.url = "https://apis.live.net/v5.0/me?access_token=" + Sync.oauth.access_token
else if opts.request is "root"
params.type = "get"
params.url = "https://apis.live.net/v5.0/me/skydrive?access_token=" + Sync.oauth.access_token
else if opts.request is "makeappdir"
params.type = "post"
params.contentType = "application/json"
params.url = "https://apis.live.net/v5.0/me/skydrive/my_documents?access_token=" + Sync.oauth.access_token
params.data = '{"name": "Springseed", "description": "Springseed App Data"}'
else if opts.request is "findappdir"
params.type = "get"
params.url = "https://apis.live.net/v5.0/me/skydrive/my_documents/files?access_token=" + Sync.oauth.access_token
else if opts.request is "listappdir"
params.type = "get"
params.url = "https://apis.live.net/v5.0/" + Sync.syncMeta.folderid + "/files?access_token=" + Sync.oauth.access_token
else if opts.request is "download"
params.type = "get"
params.url = "https://apis.live.net/v5.0/" + Sync.syncMeta.map[opts.filename + ".seed"] + "/content?access_token=" + Sync.oauth.access_token
params # return
connect: (fn) ->
# Only run connect once
if @state is OFFLINE
@state = IN_PROGRESS
request = indexedDB.open("springseed", 1)
request.onupgradeneeded = (e) =>
@db = e.target.result
# We're going to just be storing all the spine stuff in one store,
# just because I cbf seperating the Keys.
@db.createObjectStore("meta")
# We'll make another store to seperate the note s
@db.createObjectStore("notes")
request.onsuccess = (e) =>
Sync.db = e.target.result
@state = ONLINE
fn() if typeof fn is 'function'
@_clearPending()
# Check an event, and if it is a model update add it to the queue
addToQueue: (event, args) ->
if @queue[args.constructor.name][args.id] and @queue[args.constructor.name][args.id][0] is "create"
# create + destroy = nothing
if event is "destroy"
delete @queue[args.constructor.name][args.id]
else
# Otherwiswe, add it to the queue
@queue[args.constructor.name][args.id] = [event, Math.round(new Date()/1000)]
@saveQueue()
saveQueue: ->
# Save queue to localstorage
localStorage.Queue = JSON.stringify @queue
exportData: (keys=["Note", "Notebook"]) ->
# Export local data to JSON
output = {
"Note": Note.toJSON()
"Notebook": Notebook.toJSON()
}
JSON.stringify(output)
importData: (obj) ->
input = JSON.parse(obj)
# We have to save it to the db, otherwise it just hangs around in memory.
Note.refresh(input.Note, clear: true)
Notebook.refresh(input.Notebook, clear: true)
Note.saveLocal()
Notebook.saveLocal()
# Merges the client & server, using a queue
# Spits out the result, id changes & fs changes
merger: (client, server, queue) ->
# Creates an array index
indexer = (db) ->
result = {}
for k,v of db
# Sets Up Index
result[k] = {}
result[k].max = 0
# Indexes Items
for key, index in db[k]
result[k][key.id] = index
# Checks for the highest number
num = parseInt(key.id.substring(2, key.id.length))
result[k].max = num if num > result[k].max
result #return
clientindex = indexer(client)
resultant = JSON.parse(JSON.stringify(server))
resultantindex = indexer(resultant)
namechanges = {"Notebook": [], "Note": []}
fschanges = {"Notebook": [], "Note": []}
# Add Changes from client
for type of queue
for key, value of queue[type]
# Ensures that the key exists
switch value[0]
when "create"
if clientindex[type][key]
# we copy the change into the resultant
oldId = key
newId = "c-" + (resultantindex[type].max += 1)
client[type][clientindex[type][key]].id = newId
resultant[type].push(client[type][clientindex[type][key]])
# update the index
resultantindex[type][newId] = resultant[type].length
# add to fschanges
fschanges[type].push(["upload", newId])
# add to name changes
namechanges[type].push([oldId, newId]) if oldId isnt newId
when "update"
if clientindex[type][key]
# If the item update was before the update on the server, create a conflicted copy
if value[1] < resultant[type][resultantindex[type][key]].date
# bad case of DRY here
# we copy the change into the resultant
oldId = key
newId = "c-" + (resultantindex[type].max += 1)
client[type][clientindex[type][key]].id = newId
client[type][clientindex[type][key]].name += " (Conflicted Copy)"
resultant[type].push(client[type][clientindex[type][key]])
# update the index
resultantindex[type][newId] = resultant[type].length
# add to fschanges
fschanges[type].push(["download", resultant[type][resultantindex[type][key]].id])
fschanges[type].push(["upload", newId])
# add to name changes
namechanges[type].push([oldId, newId]) if oldId isnt newId
else
# copies the new change in
resultant[type][resultantindex[type][key]] = client[type][clientindex[type][key]]
fschanges[type].push(["upload", resultant[type][resultantindex[type][key]].id])
when "destroy"
# If the item was after before the delete event, don't do anything
if value[1] > resultant[type][resultantindex[type][key]].date
# tell the server to delete it
fschanges[type].push(["destroy", resultant[type][resultantindex[type][key]].id])
# destroy the change from the resultant
resultant[type].splice(resultantindex[type][key], 1)
# reindex
resultantindex = indexer(resultant)
# Er, well if notebook name id changes we have to change note ids
# @consindo is an idiot. Please send him angry messages.
# This code is also annoying because it's not a keyvalue pair. That's what happens if you loop try to dry.
# So, we'll build an index
notebooknamechangesindex = {}
for item in namechanges.Notebook
notebooknamechangesindex[item[0]] = item[1]
# Simple, right? Now we loop throught the notes and change the ids
for k, v of resultant.Note
if notebooknamechangesindex[v.notebook] isnt undefined
v.notebook = notebooknamechangesindex[v.notebook]
resultant.Note[k] = v
# All the changes from the client should be added at this point
# Now we detect the differences between the server copy and our client.
# Basically, find timestamps that are newer on the server and stuff
# that exists on the server but not the client.
# We start by cloning the resultant
original = JSON.parse(JSON.stringify(resultant))
originalindex = indexer(original)
# Removing all the stuff that is changed
for type of fschanges
for item in fschanges[type]
original[type].splice(originalindex[type][item[1]], 1)
originalindex = indexer(original)
for type of original
for key, value of original[type]
# We only need to do this for stuff with dates, not anything else
if value.date
if client[type][clientindex[type][value.id]]
if client[type][clientindex[type][value.id]].date < value.date
# Server is newer than client, download from server
fschanges[type].push(["download", value.id])
else
# Doesn't exist on client, download from server
fschanges[type].push(["download", value.id])
return [resultant, namechanges, fschanges]
# Just in case you need any default values
class Base
Model.Sync =
extended: ->
console.log '%c> Setting up sync for %s', 'font-weight: bold', this.name
@fetch ->
console.log '%c> Calling fetch', 'background: #eee'
Sync.defer(this, @loadLocal)
@change (record, event) ->
Sync.addToQueue(event, record)
console.log '%c> Calling change: ' + event, 'background: #eee'
Sync.defer(this, @saveLocal)
Sync.timeoutSync()
Sync.connect()
saveLocal: ->
result = JSON.stringify(@)
# Save to IndexedDB
trans = Sync.db.transaction(["meta"], "readwrite")
store = trans.objectStore "meta"
request = store.put(result, @className)
loadLocal: (options = {}) ->
console.log "%c> Fetching #{@className}s", 'color: blue'
options.clear = true unless options.hasOwnProperty('clear')
# Load from IndexedDB
trans = Sync.db.transaction(["meta"], "readwrite")
store = trans.objectStore "meta"
request = store.get(@className)
request.onsuccess = (e) =>
result = e.target.result
@refresh(result or [], options)
Spine.trigger "#{@className}:ready"
saveNote: (content, callback) ->
trans = Sync.db.transaction(["notes"], "readwrite")
store = trans.objectStore "notes"
request = store.put(content, @id)
request.onsuccess = (e) =>
Sync.timeoutSync()
callback() if callback
loadNote: (callback) ->
trans = Sync.db.transaction(["notes"], "readwrite")
store = trans.objectStore "notes"
request = store.get(@id)
request.onsuccess = (e) =>
result = e.target.result
callback(result) if callback
deleteNote: (callback) ->
trans = Sync.db.transaction(["notes"], "readwrite")
store = trans.objectStore "notes"
request = store.delete(@id)
request.onsuccess = (e) =>
callback() if callback
Model.Sync.Methods =
extended: ->
@extend Extend
@include Include
module?.exports = Sync
================================================
FILE: app/controllers/upgrader.coffee
================================================
###############
# If you're reading this code, and you're like
# ERMAGERD, WHY IS THIS IDIOT USING NODE.JS, S`NOT EVEN ASYNC!!11!!
# Well, it's because Spine doesn't work too well when it's async.
# Not sure why, it just loves to run out of memory and crash.
###############
Spine = require 'spine'
marked = require 'marked'
Note = require '../models/note.coffee'
Notebook = require '../models/notebook.coffee'
class window.upgrader extends Spine.Controller
constructor: ->
super
notebookPromise = new $.Deferred()
notePromise = new $.Deferred()
Spine.bind "Notebook:ready", notebookPromise.resolve
Spine.bind "Note:ready", notePromise.resolve
$.when.apply($, [notebookPromise.promise(), notePromise.promise()]).then =>
@upgrade(localStorage.version)
localStorage.version = "2.0"
upgrade: (version) ->
# New install, add default notes
if version is undefined
for notebook in ['Personal', 'Scrap', 'Work']
Notebook.create
name: notebook
attributes: {starred: false}
categories: ["General"]
date: Math.round(new Date()/1000)
# Semi Ajaxed in, rather than hardcoded.
$.getJSON('default.json').done (data) ->
for defaultNote in data
note = Note.create
name: defaultNote.name
excerpt: defaultNote.content.substring(0, 100)
notebook: 'c-2'
category: 'General'
date: Math.round(new Date().getTime()/1000)
note.saveNote defaultNote.content
# For whatever reason, it doesn't reload afterwards?
$("#notebook-all").trigger("click")
else if version is "1.1"
console.log "Upgrading DB"
for note in Note.toJSON()
note = Note.create
name: note.name
starred: false
categories: note.categories
date: note.date
Note.find(note.id).destroy()
$.getJSON('default.json').done (data) ->
for defaultNote in data
note = Note.create
name: defaultNote.name
excerpt: defaultNote.content.substring(0, 100)
notebook: 'c-2'
category: 'General'
date: Math.round(new Date().getTime()/1000)
note.saveNote defaultNote.content
# For whatever reason, it doesn't reload afterwards?
$("#notebook-all").trigger("click")
else if version is "1.0"
path = window.require 'path'
fs = window.require 'fs'
# Make variables. Do checks.
homedir = window.process.env.HOME
# Set up where we're going to store stuff.
if window.process.platform is 'darwin'
storagedir = path.join(homedir, "/Library/Application Support/Springseed/")
else if window.process.platform is 'win32'
storagedir = path.join(process.env.LOCALAPPDATA, "/Springseed/")
else if window.process.platform is 'linux'
storagedir = path.join(homedir, '/.config/Springseed/')
notebookdir = path.join(storagedir, 'Notebooks')
notebooks = {}
files = fs.readdirSync notebookdir
files.forEach (file) =>
if file.substr(16,5) is ".list"
# Read the file, syncronously.
data = fs.readFileSync path.join(notebookdir, file)
console.log data.toString()
# Read the old notebook, create a new spine model
try
oldnotebook = JSON.parse(data)
newNotebook = Notebook.create
name: oldnotebook.name
categories: ["General"]
date: Math.round(new Date()/1000)
# Our mapping array to show the new positions
notebooks[oldnotebook.id] = newNotebook.id
catch error
console.log("there was an error", id)
# Eh, what can we do?
return
# Load the notes from the notebook into the model
files.forEach (file) =>
if file.substr(33,5) is ".note"
raw = fs.readFileSync path.join(notebookdir, file)
try
contents = JSON.parse(raw)
contents.notebook = notebooks[contents.notebook]
catch error
contents = {
name: "Untitled Note"
content: raw.toString()
date: Math.round(new Date()/1000)
notebook: Notebook.all()[0].id
}
note = Note.create
name: contents.name
excerpt: $(marked(contents.content.substring(0,100))).text()
notebook: contents.notebook
category: "General"
date: contents.date
note.saveNote(contents.content)
module.exports = upgrader
================================================
FILE: app/index.coffee
================================================
require './lib/setup.coffee'
Spine = require 'spine'
shell = window.require('shell') if window.require
Analytics = require 'analytics-node'
# Upgrader
Upgrader = require('./controllers/upgrader.coffee')
# Splitter. Not working in setup for whatever reason.
Splitter = require('./lib/splitter.js')
# Modals
Notebook = require './models/notebook.coffee'
Note = require './models/note.coffee'
# Controllers
Panel = require './controllers/panel.coffee'
Sidebar = require './controllers/sidebar.coffee'
Browser = require './controllers/browser.coffee'
Editor = require './controllers/editor.coffee'
Popover = require './controllers/popover.coffee'
Modal = require './controllers/modal.coffee'
Account = require './controllers/account.coffee'
Settings = require './controllers/settings.coffee'
class App extends Spine.Controller
elements:
'#panel': 'panel'
'#sidebar': 'sidebar'
'#browser': 'browser'
'#editor': 'editor'
'.popover-mask': 'popoverMask'
'.modal.preferences': 'settings'
events:
'mousedown': 'checkSel'
'mouseup': 'checkSel'
constructor: ->
super
Account.enableChecks()
Notebook.fetch()
Note.fetch()
anal = new Analytics('u9g1p9otaa')
# We're nosey.
anal.track({
'userId': 'anonymous_user',
'event': 'Open App',
'properties': {
'os': @getOS(),
'country': @getCountry(),
'language': navigator.language,
'version': localStorage.version
}
})
# Init the Splitter so we can see crap.
Splitter.init
parent: $('#parent')[0],
panels:
left:
el: $("#sidebar")[0]
min: 150
width: 200
max: 450
center:
el: $("#browser")[0]
min: 250
width: 300
max: 850
right:
el: $("#editor")[0]
min: 450
width: 550
max: Infinity
Modal.init()
Settings.init()
@settings = Settings.get()
# Init Stuff
new Upgrader()
@panel = new Panel( el: @panel )
@sidebar = new Sidebar( el: @sidebar )
@browser = new Browser( el: @browser )
@editor = new Editor( el: @editor )
@popover = new Popover( el: @popoverMask )
# We'll put the sync conenct here as well.
Spine.trigger 'sync:authorized' if Sync.oauth.service != "undefined"
Sync.anal()
# Stuff for node webkit.
$('a').on 'click', (e) ->
e.preventDefault()
if e.which is 1 or e.which is 2
shell.openExternal $(@).attr("href")
return false
# Going to use this to enable the dev tools, because yolo
# konami_keys = [38, 38, 40, 40, 37, 39, 37, 39, 66, 65]
# konami_index = 0
# $(document).keydown (e) ->
# location.reload() if e.keyCode is 116
# if e.keyCode is konami_keys[konami_index++]
# if konami_index is konami_keys.length
# $(document).unbind "keydown", arguments.callee
# <nw require>.Window.get().showDevTools()
# else
# konami_index = 0
# We're sending an event to the editor here because we need the checksel to be global
checkSel: ->
@editor.trigger("checkSel")
getOS: ->
ua = navigator.userAgent
return "Linux" if ua.indexOf("Linux") > -1
return "Mac OS X 10.9" if ua.indexOf("Mac OS X 10_9") > -1
return "Mac OS X 10.8" if ua.indexOf("Mac OS X 10_8") > -1
return "Mac OS X 10.7" if ua.indexOf("Mac OS X 10_7") > -1
return "Mac OS X 10.6" if ua.indexOf("Mac OS X 10_6") > -1
return "Windows XP" if ua.indexOf("Windows NT 6.2") > -1
return "Windows Vista" if ua.indexOf("Windows NT 6") > -1
return "Windows 7" if ua.indexOf("Windows NT 6.1") > -1
return "Windows 8" if ua.indexOf("Windows NT 6.2") > -1
return "Windows 8.1" if ua.indexOf("Windows NT 6.3") > -1
getCountry: ->
$.getJSON 'http://freegeoip.net/json/', (loc) ->
return loc.country_name
module.exports = App
================================================
FILE: app/init.coffee
================================================
jQuery = require 'jqueryify'
exports = this
jQuery ->
App = require './index.coffee'
exports.app = new App
el: $('body')
================================================
FILE: app/lib/setup.coffee
================================================
require('jqueryify')
require('spine')
require '../controllers/sync.coffee'
================================================
FILE: app/lib/splitter.js
================================================
(function() {
// Variables
var parent;
var panels = {};
var splitters = {};
var parentOffset = 0;
var width = 0;
var active = false;
var offset = {};
var minWidth = 0;
var last = {
pos: 0,
width: 0
};
// Class Panel
var Panel = function(id, options) {
this.id = id;
this.el = options.el;
this.min = options.min || 0;
this.max = options.max || Infinity;
this.width = options.width || 0;
switch (this.id) {
case "left":
this.pos = 0;
break;
case "center":
this.pos = panels.left.width;
break;
case "right":
this.pos = panels.left.width + panels.center.width;
break;
}
if (this.id !== "right") { this.el.style.width = this.width + "px"; }
this.el.style.left = this.pos + "px";
};
Panel.prototype = {
move: function(position, relative) {
if (relative) { this.pos += position; }
else { this.pos = position; }
this.el.style.left = this.pos + "px";
},
resize: function(width, relative) {
if (relative) { this.width += width; }
else { this.width = width; }
if (this.width < this.min) { this.width = this.min; }
else if (this.width > this.max) { this.width = this.max; }
this.el.style.width = this.width + "px";
}
};
// Class Splitter
var Splitter = function(id) {
this.id = id;
this.el = doc.createElement("div")
this.pos = 0;
if (this.id === "left") {
this.left = panels.left;
this.right = panels.center;
}
else if (this.id === "right") {
this.left = panels.center;
this.right = panels.right;
}
this.el.className = "splitter split-" + this.id;
this.el.style.left = this.right.pos + "px";
this.left.el.insertAdjacentElement('afterend', this.el);
this.pos = this.el.offsetLeft;
this.el.obj = this;
this.el.onmousedown = events.mousedown;
};
Splitter.prototype = {
move: Panel.prototype.move,
resize: function(pos) {
var resizeWindow = true;
// Left min
if (pos < this.left.min + offset.left) {
pos = this.left.min + offset.left;
}
// Left max
else if (pos > this.left.max + offset.left) {
pos = this.left.max + offset.left;
}
if (this.id === "right") {
resizeWindow = false;
// Right min
if (offset.right - pos < this.right.min) {
resizeWindow = true;
}
// Right max
else if (offset.right - pos > this.right.max) {
pos = offset.right - this.right.max;
}
}
// Calculate diff
var diff = pos - last.pos;
if (diff === 0) {
this.pos = pos;
return true;
}
// Right Splitter
if (this.id === "right") {
panels.center.resize(pos - offset.left);
panels.right.move(pos);
splitters.right.move(pos);
}
// Left splitter
else if (this.id === "left") {
splitters.left.move(pos);
splitters.right.move(pos + panels.center.width);
panels.left.resize(pos - offset.left);
panels.center.move(pos);
panels.right.move(splitters.right.pos);
minWidth = pos + panels.center.min + panels.right.min;
}
// Resize window frame
if (mode === "node" && resizeWindow) {
width += diff;
offset.right = win.width = width;
}
// Save position
this.pos = last.pos = pos;
}
};
/*
Node Webkit Support
-------------------
global.document = document
global.window = window
*/
var win, doc, mode;
// Think cake is fucking this.
// if (typeof(process) != "undefined") {
// mode = "node";
// console.log("2")
// win = global.gui.Window.get();
// doc = global.document;
// } else {
mode = "browser";
win = window;
doc = document;
// }
// Calculate position of cursor on parent.
var getPos = function(clientX) {
var pos = clientX - parentOffset;
if (pos < 0) {
pos = 0;
}
else if (pos > width) {
pos = width;
}
return pos;
};
// Events
var events = {
mousedown: function(event) {
active = this.obj;
offset = {
left: 0,
right: width
};
if (active.id === "right") {
offset.left = splitters.left.pos;
}
else if (active.id === "left") {
offset.right = splitters.right.pos;
}
last.pos = getPos(event.clientX);
doc.body.className = "resizing";
},
mousemove: function(event) {
if (active) {
var pos = getPos(event.clientX);
active.resize(pos);
}
},
mouseup: function() {
if (mode == "node" && active.id === "left") {
win.setMinimumSize(panels.left.max + panels.center.min + panels.right.min, 0);
}
doc.body.className = "";
active = false;
},
resize: function() {
// Only run if triggered by user
if (!active) {
// Get new width and check if it hase changed
width = parent.offsetWidth;
var diff = width - last.width;
if (diff === 0 || width < minWidth) { return false; }
// If window is shrinking
if (diff < 0) {
// Check right panel for min width
if (panels.right.el.offsetWidth <= panels.right.min) {
panels.center.resize(diff, true);
panels.right.move(diff, true);
splitters.right.move(diff, true);
}
}
last.width = width;
}
}
};
var init = function(options) {
// Get options
parent = options.parent;
// Create Panels
panels.left = new Panel("left", options.panels.left);
panels.center = new Panel("center", options.panels.center);
panels.right = new Panel("right", options.panels.right);
// Create Splitters
splitters.left = new Splitter("left");
splitters.right = new Splitter("right");
// Get width of parent
width = parent.offsetWidth;
minWidth = panels.left.min + panels.center.min + panels.right.min;
// Bind events
doc.onmousemove = events.mousemove;
doc.onmouseup = events.mouseup;
window.onresize = events.resize;
};
var exports = {
init: init,
panels: panels,
splitters: splitters
};
if (typeof(module) !== "undefined") {
module.exports = exports;
}
else {
window.Splitter = exports;
}
}());
================================================
FILE: app/models/.gitkeep
================================================
================================================
FILE: app/models/note.coffee
================================================
Spine = require 'spine'
Notebook = require './notebook.coffee'
class window.Note extends Spine.Model
@configure 'Note',
'name',
'starred',
'excerpt',
'notebook',
'category',
'date'
@extend @Sync
@include @Sync
# Deletes it from IndexedDB
@.bind "beforeDestroy", (note) ->
note.deleteNote()
# since we can't change .find, we can use this to the same effect
@filter: (notebook, category) ->
# returns all the notes if no notebook specified
if notebook && notebook isnt "all"
array = Note.findAllByAttribute("notebook", notebook)
# because 0 evals to false in JS
category = category.toString()
# returns items in a category, unless not specfied
if category && category isnt "all"
# matches the id number to the realname. i wish i had made a better system.
category = Notebook.find(notebook).categories[category]
newArray = []
for note in array
newArray.push(note) if note.category is category
array = newArray
else
array = Note.all()
return array
@search: (string) ->
# Ported from 1.0
results = []
notes = Note.all()
for note in notes
if note.name.match(new RegExp(string, 'i'))# or content.match(new RegExp(string, 'i'))
note.date = note.prettyDate()
results.push(note)
console.log(results)
Notebook.trigger 'changeNotebook', {id: 'all', category: 'all', search: true, result: results}
prettyDate: (time) =>
date = new Date(@date * 1000)
pad = (n) -> (if (n < 10) then ("0" + n) else n)
month = [
"Jan"
"Feb"
"Mar"
"Apr"
"May"
"Jun"
"Jul"
"Aug"
"Sep"
"Oct"
"Nov"
"Dec"
]
now = new Date()
difference = 0
oneDay = 86400000; # 1000*60*60*24 - one day in milliseconds
words = ''
# Find difference between days
difference = Math.ceil((date.getTime() - now.getTime()) / oneDay)
# Show difference nicely
if difference is 0
words = "Today"
words = "Yesterday" if now.getDate() isnt date.getDate()
else if difference is -1
words = "Yesterday"
else if difference > 0
# If the user has a TARDIS
words = "Today"
else if difference > -15
words = Math.abs(difference) + " days ago"
else if difference > -365
words = month[date.getMonth()] + " " + date.getDate()
else
words = pad(date.getFullYear())+"-"+(pad(date.getMonth()+1))+"-"+pad(date.getDate())
words += " " + date.getHours() + ":" + pad(date.getMinutes()) if time
return words
module.exports = Note
================================================
FILE: app/models/notebook.coffee
================================================
Spine = require 'spine'
class window.Notebook extends Spine.Model
@configure 'Notebook',
'name',
'categories',
'date'
@extend @Sync
@.bind "beforeUpdate", (notebook, context) ->
if context isnt "date"
notebook.updateAttributes {
"date": Math.round(new Date()/1000)
}, "date"
@.bind "beforeDestroy", (notebook) ->
for note in Note.filter(notebook.id, "all")
note.destroy()
subcategoryDestroy: (category) ->
for note in Note.filter(Notebook.current.id, category)
note.destroy()
# this is a terrible hack. I will fix it maybe
# in theory, they can't an empty subcategory string
arr = @categories.slice(0)
arr[category] = ""
@updateAttribute 'categories', arr
module.exports = Notebook
================================================
FILE: app/views/.gitkeep
================================================
================================================
FILE: app/views/note.handlebars
================================================
<li id="note-{{id}}" draggable="true" data-notebook="{{notebook}}">
<h2>{{name}}</h2>
<time>{{date}} - </time><span>{{{excerpt}}}</span>
</li>
================================================
FILE: app/views/notebook.handlebars
================================================
<li class="notebook" id="notebook-{{id}}"><span class="name">{{name}}</span><span class="icon"></span>
<ul class="category">
<li data-category="all" class="selected">All Notes</li>
{{#each categories}}
<li data-category="{{@index}}">{{this}}</li>
{{/each}}
</ul>
</li>
================================================
FILE: css/font.scss
================================================
@font-face {
font-family: 'YK Thin';
font-style: normal;
font-weight: 400;
src: url("YanoneKaffeesatz-Thin.ttf") format('ttf');
}
@font-face {
font-family: 'YK Sans';
font-style: normal;
font-weight: 200;
src: url("YanoneKaffeesatz-Thin.ttf") format('ttf');
}
@font-face {
font-family: 'YK Sans';
font-style: normal;
font-weight: 300;
src: url("YanoneKaffeesatz-Light.ttf") format('ttf');
}
@font-face {
font-family: 'PT Sans';
font-style: normal;
font-weight: 400;
src: url("YanoneKaffeesatz-Normal.ttf") format('ttf');
}
@font-face {
font-family: 'PT Sans';
font-style: normal;
font-weight: 700;
src: url("YanoneKaffeesatz-Bold.ttf") format('ttf');
}
================================================
FILE: css/keyframes.scss
================================================
@-webkit-keyframes zoom-in {
0% {
-webkit-transform: scale(0.1) transform3d(0,0,0);
}
100% {
-webkit-transform: scale(1) transform3d(0,0,0);
}
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg) transform3d(0,0,0);
}
100% {
-webkit-transform: rotate(3600deg) transform3d(0,0,0);
}
}
================================================
FILE: css/new.scss
================================================
@import "font";
@import "normalize";
@import "keyframes";
$light-color: #ecf0f1;
$gray-color: #AAAAAA;
$dark-color: #2d3235;
$font: "YK Sans", sans-serif;
$light-font: "YK Thin", sans-serif;
.spin {
-webkit-animation: spin 1s linear;
}
::-webkit-scrollbar {
width: 5px;
}
::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.25);
border-radius: 2.5px;
}
body, input, textarea {
font-family: $font;
overflow: hidden;
-webkit-font-smoothing: antialiased;
}
// Dragging stuff.
#sidebar, #panel, #browser, .drag {
-webkit-app-region: drag;
-webkit-user-select: none;
cursor: default!important;
}
#sidebar, #browser, .nodrag {
-webkit-app-region: no-drag;
}
//
// ABOUT PAGE
//
body.about {
text-align: center;
font-family: $light-font;
background: #F0F0F0;
font-size: 16px;
color: $dark-color;
font-weight: 200;
h1 {
font-weight: 400;
}
p {
font-size: 10px;
}
}
// Panel
#panel {
width: 100%;
height: 15px;
background: #FFFFFF;
border-left: 200px solid $dark-color;
box-shadow: 300px 0 0 0 $light-color inset;
cursor: default;
}
#decor {
display: inline-block;
height: 19px;
margin: 12px 9px;
position: absolute;
top:0;left:0;
img {
padding: 3px;
-webkit-transition: all 0.25s ease;
}
}
#loginbox {
position: absolute;
top: 0; right: 15px;
text-align: right;
padding: 5px;
color: lighten($dark-color, 15%);
.name {
display: block;
text-transform: uppercase;
font-size: 10pt;
font-weight: 600;
}
.gopro {
display: block;
text-transform: lowercase;
font-size: 8pt;
font-weight: 200;
}
}
#parent {
position: absolute;
overflow: hidden;
top: 15px; bottom: 0;
left: 0; right: 0;
}
.panel {
position: absolute;
top: 0; bottom: 0;
overflow: hidden;
box-sizing: border-box;
}
#sidebar {
position: absolute;
left: 0;
background: $dark-color;
padding-bottom: 30px;
.container {
position: absolute;
top: 0;
width: 100%;
bottom: 30px;
overflow-x: hidden;
& .title {
padding: 10px 15px;
color: $light-color;
font-family: $light-font;
font-size: 20pt;
font-weight: 200;
}
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
}
li.notebook {
font-family: $light-font;
font-size: 10pt;
font-weight: 200;
color: $light-color;
padding: 10px 15px;
margin: 5px 0;
&.selected {
background: darken($dark-color, 5%);
}
.name {
font-size: 14pt;
}
}
#settingsbar {
position: absolute;
bottom: 0; left: 0;
color: $light-color;
padding: 5px;
& i {
font-size: 12pt;
cursor: pointer;
}
}
input {
background: $dark-color;
border: none;
font-size: 14pt;
font-weight: 200;
color: $light-color;
padding: 10px 15px;
margin: 5px 0;
}
}
#browser {
position: absolute;
background: $light-color;
overflow-x: hidden;
overflow-y: auto;
& .title {
padding: 10px 15px;
color: $dark-color;
font-family: $light-font;
font-size: 20pt;
font-weight: 200;
}
& #new {
font-size: 18pt;
color: $dark-color;
cursor: pointer;
}
ul {
margin: 0;
padding: 5px 0 0;
list-style-type: none;
}
li {
cursor: default;
padding: 4px 15px 8px;
color: $dark-color;
line-height: 16px;
font-family: $light-font;
font-weight: 200;
&.selected {
background: darken($light-color, 5%);
}
}
h2 {
margin: 8px 0;
font-size: 14pt;
font-weight: 200;
}
span {
font-size: 10pt;
font-weight: 200;
}
time {
display: none;
}
}
#editor {
position: absolute;
background: #ffffff;
right: 0;
&.deselected {
background: #FFFFFF url(img/page.svg) 50% 100px no-repeat;
background-size: 140px;
&::after {
content: "Select a note using the sidebar.";
position: absolute;
top: 230px;
font-weight: 200;
font-size: 16pt;
left: 0;
right: 0;
color: $gray-color;
text-align: center;
}
header, #contentread, #contentwrite {
display: none !important;
}
}
header {
margin: 0;
background: #9b59b6;
position: absolute;
left: 15px; right: 15px;
padding: 10px;
.headerwrap {
-webkit-user-select: none;
.star {
color: $light-color;
font-size: 14pt;
}
.star.starred {
color: #f1c40f;
}
input {
font-weight: 200;
outline: 0;
margin: 0;
color: white;
padding: 5px;
background: transparent;
font-size: 18pt;
border: 0;
box-sizing: border-box;
max-width: 400px;
}
.left {
float: left;
time {
display: none;
}
}
.right {
float: right;
a {
color: $gray-color;
font-size: 14pt;
padding: 10px;
cursor: pointer;
display: inline-block;
}
button {
color: white;
border: none;
background: transparent;
font-size: 14pt;
padding: 8px;
outline: 0;
}
}
}
}
&.edit {
#contentread {
display: none;
}
#contentwrite {
display: block;
}
}
}
#contentscroller {
position: absolute;
top: 60px; bottom: 0;
left: 0; right: 0;
overflow-y: auto;
}
#contentread, #contentwrite {
position: absolute;
top: 0; bottom: 0;
left: 0; right: 0;
background: #FFFFFF;
padding: 0 30px;
color: $dark-color;
font-family: $font;
}
#psuedoinput {
position: fixed;
top: -9999px;
}
#contentwrite {
padding: 35px 25px;
display: none;
> .inner {
width: 100%;
margin: 0;
font-size: 15px;
line-height: 1.1;
padding: 0 0 30px;
outline: none;
white-space: pre-wrap;
}
& > .wc {
position: fixed;
bottom: 5px;
right: 5px;
border-radius: 3px;
font-size: 12px;
padding: 5px 10px;
background: #3f3b3a;
color: white;
display: block-inline;
}
}
#contentread {
padding: 35px 25px;
bottom: auto;
z-index: 4;
line-height: 1.1;
font-size: 15px;
font-family: $light-font;
font-weight: 200;
:first-child {
margin-top: 0;
}
h1, h2, h3 {
margin-bottom: 0.2em;
font-weight: 400;
}
ul {
padding: 0 0 0 20px;
}
p {
margin-top: 0.4em;
}
a {
color: #2d8dd6;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
code, pre {
font-size: 0.95em;
line-height: 1.1;
font-family: monospace;
}
& > .wc {
display: none;
}
}
.modal {
display: none;
font-size: 14px;
color: hsl(0,0,30%);
width: 100%;
height: 100%;
opacity: 0;
z-index: 9999!important;
background: rgba(0,0,0,0.7);
position: absolute;
top: 0; left: 0;
bottom: 0; right: 0;
-webkit-transition: .3s opacity ease;
-webkit-user-select: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
&.show {
opacity: 1;
& > div {
-webkit-transition: .3s zoom-in ease linear;
}
}
& > div {
position: absolute;
top: 15%;
left: 50%;
background: white;
font-size: 16px;
box-shadow: 0 2px 15px rgba(0,0,0,0.2);
}
.delete-container, .error-container {
width: 600px;
margin-left: -328px;
padding: 25px;
text-align: center;
font-size: 18px;
font-weight: 200;
color: $dark-color;
border: 5px solid #E15556;
h1 {
-webkit-user-select: none;
}
span.name {
font-weight: 400;
}
}
.syncmeta-container {
width: 600px;
margin-left: -328px;
padding: 25px;
text-align: center;
font-size: 16px;
font-weight: 200;
color: $dark-color;
border: 5px solid #66BE7F;
.special {
border-color: #E15556;
}
.special.green {
border-color: #66BE7F;
}
}
.newNote-container {
width: 600px;
margin-left: -328px;
padding: 25px;
text-align: center;
font-size: 16px;
font-weight: 200;
color: $dark-color;
border: 5px solid #66BE7F;
}
.rename-container {
width: 600px;
margin-left: -328px;
padding: 25px;
text-align: center;
font-size: 16px;
font-weight: 200;
color: $dark-color;
border: 5px solid $dark-color;
.special {
border-color: #E15556;
}
.special.green {
border-color: #66BE7F;
}
}
.preferences-container {
width: 450px;
margin-left: -227px;
text-align: center;
font-size: 16px;
color: $dark-color;
border: 5px solid #9b59b6;
font-weight: 200;
.tabs {
border-bottom: 2px solid #9b59b6;
ul {
list-style: none;
margin: 0;
padding: 0;
font-size: 0;
}
li {
display: inline-block;
line-height: 30px;
font-size: 15px;
padding: 0 15px;
font-weight: 400;
color: $dark-color;
cursor: default;
&:active {
color: darken(#9b59b6, 15%);
}
&.current {
background: #9b59b6;
color: white;
}
}
}
a {
text-decoration: none;
}
.account {
padding: 10px;
.pro {
background: #9b59b6;
color: white;
padding: 1px 35px 20px;
margin: 10px -35px;
p {
font-weight: normal;
}
}
}
.container {
padding: 10px 25px;
}
.signedin, .signedout {
padding-bottom: 15px;
p {
margin: 0;
}
button {
font-size: 13px;
}
}
.signedin {
display: none;
}
input {
display: block;
text-decoration: none;
border-radius: 0.0001px;
border: 1px solid $light-color;
background: lighten($light-color, 5%);
margin: 10px 0 0;
padding: 10px 15px;
font-size: 10pt;
}
.about, .sync, .account {
display: none;
text-align: left;
h1 {
font-weight: 400;
}
p {
font-weight: 200;
font-size: 10pt;
}
& img {
float: left;
margin-right: 10px;
}
&.current {
display: block;
}
}
}
button.special, a.special {
display: inline-block;
text-decoration: none;
border-radius: 0.0001px;
border: 1px solid $light-color;
background: lighten($light-color, 5%);
margin: 10px 0 0;
padding: 10px 20px;
}
}
#editorcontrols {
display: none;
position: absolute;
z-index: 1000;
background: $dark-color;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
#editorcontrols button {
-webkit-transition: 200ms ease all;
padding: 10px 15px;
float: left;
border: 0;
background: transparent;
}
#editorcontrols button:hover {
background: rgba(0,0,0,0.15);
}
.popover-mask {
display: none;
z-index: 100;
width: 100%;
height: 100%;
position: absolute;
top: 0;
& > div {
display: none;
input {
background: $light-color;
display: block;
font-size: 14pt;
font-weight: 200;
border: 0;
width: 100%;
box-sizing: border-box;
padding: 5px 10px;
outline: 0;
color: $gray-color;
text-align: left;
-webkit-transition: .5s all ease;
}
button {
background: $light-color;
display: block;
font-size: 14pt;
font-weight: 200;
border: 0;
width: 100%;
box-sizing: border-box;
padding: 5px 10px;
outline: 0;
color: $gray-color;
text-align: left;
-webkit-transition: .5s all ease;
}
button:hover {
color: $dark-color;
}
}
}
.delete-popover, .category-popover {
display: inline-block;
width: 300px;
height: 36px;
position: absolute;
.arrow {
display: none;
}
.container {
display: inline-block;
background: $light-color;
width: 150px;
vertical-align: top;
padding: 5px;
box-shadow: 0 0 5px rgba(0,0,0,0.2);
}
input {
width: 100px;
}
}
.category { display: none; }
.expanded .category { display: block; }
.category .selected { font-weight: 400; }
img.emoji {
width: 20px;
height: 20px;
}
================================================
FILE: css/normalize.scss
================================================
/*! normalize.css v1.0.1 | MIT License | git.io/normalize */
/* ==========================================================================
HTML5 display definitions
========================================================================== */
/*
* Corrects `block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
nav,
section,
summary {
display: block;
}
/*
* Corrects `inline-block` display not defined in IE 6/7/8/9 and Firefox 3.
*/
audio,
canvas,
video {
display: inline-block;
*display: inline;
*zoom: 1;
}
/*
* Prevents modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/*
* Addresses styling for `hidden` attribute not present in IE 7/8/9, Firefox 3,
* and Safari 4.
* Known issue: no IE 6 support.
*/
[hidden] {
display: none;
}
/* ==========================================================================
Base
========================================================================== */
/*
* 1. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using
* `em` units.
* 2. Prevents iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-size: 100%; /* 1 */
-webkit-text-size-adjust: 100%; /* 2 */
-ms-text-size-adjust: 100%; /* 2 */
}
/*
* Addresses `font-family` inconsistency between `textarea` and other form
* elements.
*/
html,
button,
input,
select,
textarea {
font-family: sans-serif;
}
/*
* Addresses margins handled incorrectly in IE 6/7.
*/
body {
margin: 0;
}
/* ==========================================================================
Links
========================================================================== */
/*
* Addresses `outline` inconsistency between Chrome and other browsers.
*/
a:focus {
outline: thin dotted;
}
/*
* Improves readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* ==========================================================================
Typography
========================================================================== */
/*
* Addresses font sizes and margins set differently in IE 6/7.
* Addresses font sizes within `section` and `article` in Firefox 4+, Safari 5,
* and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
h2 {
font-size: 1.5em;
margin: 0.83em 0;
}
h3 {
font-size: 1.17em;
margin: 1em 0;
}
h4 {
font-size: 1em;
margin: 1.33em 0;
}
h5 {
font-size: 0.83em;
margin: 1.67em 0;
}
h6 {
font-size: 0.75em;
margin: 2.33em 0;
}
/*
* Addresses styling not present in IE 7/8/9, Safari 5, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/*
* Addresses style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome.
*/
b,
strong {
font-weight: bold;
}
blockquote {
margin: 1em 40px;
}
/*
* Addresses styling not present in Safari 5 and Chrome.
*/
dfn {
font-style: italic;
}
/*
* Addresses styling not present in IE 6/7/8/9.
*/
mark {
background: #ff0;
color: #000;
}
/*
* Addresses margins set differently in IE 6/7.
*/
p,
pre {
margin: 1em 0;
}
/*
* Corrects font family set oddly in IE 6, Safari 4/5, and Chrome.
*/
code,
kbd,
pre,
samp {
font-family: 'Source Code Pro', monospace;
font-size: 1em;
}
/*
* Improves readability of pre-formatted text in all browsers.
*/
pre {
white-space: pre;
white-space: pre-wrap;
word-wrap: break-word;
}
/*
* Addresses CSS quotes not supported in IE 6/7.
*/
q {
quotes: none;
}
/*
* Addresses `quotes` property not supported in Safari 4.
*/
q:before,
q:after {
content: '';
content: none;
}
/*
* Addresses inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/*
* Prevents `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* ==========================================================================
Lists
========================================================================== */
/*
* Addresses margins set differently in IE 6/7.
*/
dl,
menu,
ol,
ul {
margin: 1em 0;
}
dd {
margin: 0 0 0 40px;
}
/*
* Addresses paddings set differently in IE 6/7.
*/
menu,
ol,
ul {
padding: 0 0 0 40px;
}
/*
* Corrects list images handled incorrectly in IE 7.
*/
nav ul,
nav ol {
list-style: none;
list-style-image: none;
}
/* ==========================================================================
Embedded content
========================================================================== */
/*
* 1. Removes border when inside `a` element in IE 6/7/8/9 and Firefox 3.
* 2. Improves image quality when scaled in IE 7.
*/
img {
border: 0; /* 1 */
-ms-interpolation-mode: bicubic; /* 2 */
}
/*
* Corrects overflow displayed oddly in IE 9.
*/
svg:not(:root) {
overflow: hidden;
}
/* ==========================================================================
Figures
========================================================================== */
/*
* Addresses margin not present in IE 6/7/8/9, Safari 5, and Opera 11.
*/
figure {
margin: 0;
}
/* ==========================================================================
Forms
========================================================================== */
/*
* Corrects margin displayed oddly in IE 6/7.
*/
form {
margin: 0;
}
/*
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/*
* 1. Corrects color not being inherited in IE 6/7/8/9.
* 2. Corrects text not wrapping in Firefox 3.
* 3. Corrects alignment displayed oddly in IE 6/7.
*/
legend {
border: 0; /* 1 */
padding: 0;
white-space: normal; /* 2 */
*margin-left: -7px; /* 3 */
}
/*
* 1. Corrects font size not being inherited in all browsers.
* 2. Addresses margins set differently in IE 6/7, Firefox 3+, Safari 5,
* and Chrome.
* 3. Improves appearance and consistency in all browsers.
*/
button,
input,
select,
textarea {
font-size: 100%; /* 1 */
margin: 0; /* 2 */
vertical-align: baseline; /* 3 */
*vertical-align: middle; /* 3 */
}
/*
* Addresses Firefox 3+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
button,
input {
line-height: normal;
}
/*
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Corrects inability to style clickable `input` types in iOS.
* 3. Improves usability and consistency of cursor style between image-type
* `input` and others.
* 4. Removes inner spacing in IE 7 without affecting normal text inputs.
* Known issue: inner spacing remains in IE 6.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
*overflow: visible; /* 4 */
}
/*
* Re-set default cursor for disabled elements.
*/
button[disabled],
input[disabled] {
cursor: default;
}
/*
* 1. Addresses box sizing set to content-box in IE 8/9.
* 2. Removes excess padding in IE 8/9.
* 3. Removes excess padding in IE 7.
* Known issue: excess padding remains in IE 6.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
*height: 13px; /* 3 */
*width: 13px; /* 3 */
}
/*
* 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome.
* 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/*
* Removes inner padding and search cancel button in Safari 5 and Chrome
* on OS X.
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/*
* Removes inner padding and border in Firefox 3+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/*
* 1. Removes default vertical scrollbar in IE 6/7/8/9.
* 2. Improves readability and alignment in all browsers.
*/
textarea {
overflow: auto; /* 1 */
vertical-align: top; /* 2 */
}
/* ==========================================================================
Tables
========================================================================== */
/*
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
================================================
FILE: package.json
================================================
{
"main": "src/main.js",
"name": "Springseed",
"version": "2.0.0",
"repository": {
"type":"git",
"url": "http://github.com/springseednotes/springseed.git"
},
"window": {
"toolbar": true,
"width": 1024,
"height": 600,
"position": "center",
"min_width": 300,
"min_height": 500
},
"dependencies": {
"underscore": "1.4.x",
"jqueryify": "~1.9.1",
"spine": "~1.1.x",
"marked": "0.2.9",
"socket.io-client": "0.9.16",
"highlight.js": "7.3.0",
"open": "0.0.5",
"analytics-node": "1.0.4"
},
"devDependencies": {
"handlebars": "1.0.x",
"coffee-script": "1.7.x",
"browserify": "4.1.x",
"coffeeify": "0.6.x"
}
}
================================================
FILE: public/about.html
================================================
<!DOCTYPE html>
<html>
<head>
<title>About</title>
<link rel="stylesheet" type="text/css" href="application.css"/>
</head>
<body class="about">
<img src="img/icon.svg" height="150">
<h1>Springseed <small>2.0</small></h1>
<p>Copyright © 2012-2014 Springseed.</p>
<p><a href="http://getspringseed.com">http://getspringseed.com/</a></p>
</body>
</html>
================================================
FILE: public/default.json
================================================
[
{
"name": "Springseed!",
"content": "Springseed 2.0 is here and there's a lot going on. We finally have a Twitter and Google+ page, which means that we're going to keep you updated with what is going on with Springseed.\n\nOur Twitter can be found at http://twitter.com/springseednotes whereas our Google+ page can be found at http://plus.google.com/+Getspringseed - we hope that you follow us on these platforms because it's great to let you know what's happening.\n\nIn other news, we're also working on other cool stuff so do try and get involved as much as you can, it means a lot to us!\n\nOh, and before we forgot, we also have IRC! We can be found at irc.freenode.net in #springseed"
}
]
================================================
FILE: public/emojify.js
================================================
/*! emojify.js - v0.9.2 -
* Copyright (c) Hassan Khan 2014
*/!function(a){"use strict";var b=function(){function b(a){return" "===a||" "===a||"\r"===a||"\n"===a||""===a}function c(a,b,c){var d=i.createElement("img");d.setAttribute("title",":"+c+":"),d.setAttribute("alt",":"+c+":"),d.setAttribute("class","emoji"),d.setAttribute("src",q.img_dir+"/"+c+".png"),d.setAttribute("align","absmiddle"),a.splitText(b.index),a.nextSibling.nodeValue=a.nextSibling.nodeValue.substr(b[0].length,a.nextSibling.nodeValue.length),d.appendChild(a.splitText(b.index)),a.parentNode.insertBefore(d,a.nextSibling)}function d(a){if(a[1]&&a[2]){var b=a[2];if(l[b])return b}else for(var c=3;c<a.length-1;c++)if(a[c])return n[c-2][1]}function e(a,b){return"<img title=':"+b+":' alt=':"+b+":' class='emoji' src='"+q.img_dir+"/"+b+".png' align='absmiddle' />"}function f(){this.lastEmojiTerminatedAt=-1}function g(a,b){if(!a)return a;b||(b=e);var c=new f;return a.replace(p,function(){var a=Array.prototype.slice.call(arguments,0,-2),d=arguments[arguments.length-2],e=arguments[arguments.length-1],f=c.validate(a,d,e);return f?b(arguments[0],f):arguments[0]})}function h(a){"undefined"==typeof a&&(a=q.only_crawl_id?i.getElementById(q.only_crawl_id):i.body);for(var b,e=q.ignored_tags,g=i.createTreeWalker(a,NodeFilter.SHOW_TEXT|NodeFilter.SHOW_ELEMENT,function(a){return 1!==a.nodeType?NodeFilter.FILTER_ACCEPT:e[a.tagName]||a.classList.contains("no-emojify")?NodeFilter.FILTER_REJECT:NodeFilter.FILTER_SKIP},!1),h=[];null!==(b=g.nextNode());)h.push(b);h.forEach(function(a){for(var b,e=[],g=new f;null!==(b=p.exec(a.data));)g.validate(b,b.index,b.input)&&e.push(b);for(var h=e.length;h-->0;){var i=d(e[h]);c(a,e[h],i)}})}var i=a.window.document,j="100,109,1234,-1,+1,8ball,abcd,abc,ab,accept,aerial_tramway,airplane,alarm_clock,alien,ambulance,anchor,angel,anger,angry,anguished,ant,a,apple,aquarius,aries,arrow_backward,arrow_double_down,arrow_double_up,arrow_down,arrow_down_small,arrow_forward,arrow_heading_down,arrow_heading_up,arrow_left,arrow_lower_left,arrow_lower_right,arrow_right_hook,arrow_right,arrows_clockwise,arrows_counterclockwise,arrow_up_down,arrow_upper_left,arrow_upper_right,arrow_up,arrow_up_small,articulated_lorry,art,astonished,atm,baby_bottle,baby_chick,baby,baby_symbol,baggage_claim,balloon,ballot_box_with_check,bamboo,banana,bangbang,bank,barber,bar_chart,baseball,basketball,bath,bathtub,battery,bear,bee,beer,beers,beetle,beginner,bell,bento,bicyclist,bike,bikini,bird,birthday,black_circle,black_joker,black_nib,black_square_button,black_square,blossom,blowfish,blue_book,blue_car,blue_heart,blush,boar,boat,bomb,bookmark,bookmark_tabs,book,books,boom,boot,bouquet,bowling,bow,bowtie,boy,b,bread,bride_with_veil,bridge_at_night,briefcase,broken_heart,bug,bulb,bullettrain_front,bullettrain_side,bus,busstop,bust_in_silhouette,busts_in_silhouette,cactus,cake,calendar,calling,camel,camera,cancer,candy,capital_abcd,capricorn,card_index,carousel_horse,car,cat2,cat,cd,chart,chart_with_downwards_trend,chart_with_upwards_trend,checkered_flag,cherries,cherry_blossom,chestnut,chicken,children_crossing,chocolate_bar,christmas_tree,church,cinema,circus_tent,city_sunrise,city_sunset,clapper,clap,clipboard,clock1030,clock10,clock1130,clock11,clock1230,clock12,clock130,clock1,clock230,clock2,clock330,clock3,clock430,clock4,clock530,clock5,clock630,clock6,clock730,clock7,clock830,clock8,clock930,clock9,closed_book,closed_lock_with_key,closed_umbrella,cloud,cl,clubs,cn,cocktail,coffee,cold_sweat,collision,computer,confetti_ball,confounded,confused,congratulations,construction,construction_worker,convenience_store,cookie,cool,cop,copyright,corn,couplekiss,couple,couple_with_heart,cow2,cow,credit_card,crocodile,crossed_flags,crown,crying_cat_face,cry,crystal_ball,cupid,curly_loop,currency_exchange,curry,custard,customs,cyclone,dancer,dancers,dango,dart,dash,date,deciduous_tree,department_store,de,diamond_shape_with_a_dot_inside,diamonds,disappointed,disappointed_relieved,dizzy_face,dizzy,dog2,dog,dollar,dolls,dolphin,do_not_litter,donut,door,doughnut,dragon_face,dragon,dress,dromedary_camel,droplet,dvd,ear_of_rice,ear,earth_africa,earth_americas,earth_asia,eggplant,egg,eight,eight_pointed_black_star,eight_spoked_asterisk,electric_plug,elephant,e-mail,email,end,envelope,es,european_castle,european_post_office,euro,evergreen_tree,exclamation,expressionless,eyeglasses,eyes,facepunch,factory,fallen_leaf,family,fast_forward,fax,fearful,feelsgood,feet,ferris_wheel,file_folder,finnadie,fire_engine,fire,fireworks,first_quarter_moon,first_quarter_moon_with_face,fish_cake,fishing_pole_and_fish,fish,fist,five,flags,flashlight,floppy_disk,flower_playing_cards,flushed,foggy,football,fork_and_knife,fountain,four_leaf_clover,four,free,fried_shrimp,fries,frog,frowning,fr,fuelpump,full_moon,full_moon_with_face,fu,game_die,gb,gemini,gem,ghost,gift_heart,gift,girl,globe_with_meridians,goat,goberserk,godmode,golf,grapes,green_apple,green_book,green_heart,grey_exclamation,grey_question,grimacing,grinning,grin,guardsman,guitar,gun,haircut,hamburger,hammer,hamster,handbag,hand,hankey,hash,hatched_chick,hatching_chick,headphones,hear_no_evil,heartbeat,heart_decoration,heart_eyes_cat,heart_eyes,heart,heartpulse,hearts,heavy_check_mark,heavy_division_sign,heavy_dollar_sign,heavy_exclamation_mark,heavy_minus_sign,heavy_multiplication_x,heavy_plus_sign,helicopter,herb,hibiscus,high_brightness,high_heel,hocho,honeybee,honey_pot,horse,horse_racing,hospital,hotel,hotsprings,hourglass_flowing_sand,hourglass,house,house_with_garden,hurtrealbad,hushed,ice_cream,icecream,ideograph_advantage,id,imp,inbox_tray,incoming_envelope,information_desk_person,information_source,innocent,interrobang,iphone,it,izakaya_lantern,jack_o_lantern,japanese_castle,japanese_goblin,japanese_ogre,japan,jeans,joy_cat,joy,jp,keycap_ten,key,kimono,kissing_cat,kissing_closed_eyes,kissing_face,kissing_heart,kissing,kissing_smiling_eyes,kiss,koala,koko,kr,large_blue_circle,large_blue_diamond,large_orange_diamond,last_quarter_moon,last_quarter_moon_with_face,laughing,leaves,ledger,left_luggage,left_right_arrow,leftwards_arrow_with_hook,lemon,leopard,leo,libra,light_rail,link,lips,lipstick,lock,lock_with_ink_pen,lollipop,loop,loudspeaker,love_hotel,love_letter,low_brightness,mag,mag_right,mahjong,mailbox_closed,mailbox,mailbox_with_mail,mailbox_with_no_mail,man,mans_shoe,man_with_gua_pi_mao,man_with_turban,maple_leaf,mask,massage,meat_on_bone,mega,melon,memo,mens,metal,metro,microphone,microscope,milky_way,minibus,minidisc,mobile_phone_off,moneybag,money_with_wings,monkey_face,monkey,monorail,moon,mortar_board,mountain_bicyclist,mountain_cableway,mountain_railway,mount_fuji,mouse2,mouse,movie_camera,moyai,m,muscle,mushroom,musical_keyboard,musical_note,musical_score,mute,nail_care,name_badge,neckbeard,necktie,negative_squared_cross_mark,neutral_face,new_moon,new_moon_with_face,new,newspaper,ng,nine,no_bell,no_bicycles,no_entry,no_entry_sign,no_good,no_mobile_phones,no_mouth,non-potable_water,no_pedestrians,nose,no_smoking,notebook,notebook_with_decorative_cover,notes,nut_and_bolt,o2,ocean,octocat,octopus,oden,office,ok_hand,ok,ok_woman,older_man,older_woman,oncoming_automobile,oncoming_bus,oncoming_police_car,oncoming_taxi,one,on,open_file_folder,open_hands,open_mouth,ophiuchus,o,orange_book,outbox_tray,ox,page_facing_up,pager,page_with_curl,palm_tree,panda_face,paperclip,parking,part_alternation_mark,partly_sunny,passport_control,paw_prints,peach,pear,pencil2,pencil,penguin,pensive,performing_arts,persevere,person_frowning,person_with_blond_hair,person_with_pouting_face,phone,pig2,pig_nose,pig,pill,pineapple,pisces,pizza,plus1,point_down,point_left,point_right,point_up_2,point_up,police_car,poodle,poop,postal_horn,postbox,post_office,potable_water,pouch,poultry_leg,pound,pouting_cat,pray,princess,punch,purple_heart,purse,pushpin,put_litter_in_its_place,question,rabbit2,rabbit,racehorse,radio_button,radio,rage1,rage2,rage3,rage4,rage,railway_car,rainbow,raised_hand,raised_hands,raising_hand,ramen,ram,rat,recycle,red_car,red_circle,registered,relaxed,relieved,repeat_one,repeat,restroom,revolving_hearts,rewind,ribbon,rice_ball,rice_cracker,rice,rice_scene,ring,rocket,roller_coaster,rooster,rose,rotating_light,round_pushpin,rowboat,rugby_football,runner,running,running_shirt_with_sash,ru,sagittarius,sailboat,sake,sandal,santa,sa,satellite,satisfied,saxophone,school,school_satchel,scissors,scorpius,scream_cat,scream,scroll,seat,secret,seedling,see_no_evil,seven,shaved_ice,sheep,shell,shipit,ship,shirt,shit,shoe,shower,signal_strength,six,six_pointed_star,ski,skull,sleeping,sleepy,slot_machine,small_blue_diamond,small_orange_diamond,small_red_triangle_down,small_red_triangle,smile_cat,smile,smiley_cat,smiley,smiling_imp,smirk_cat,smirk,smoking,snail,snake,snowboarder,snowflake,snowman,sob,soccer,soon,sos,sound,space_invader,spades,spaghetti,sparkler,sparkles,sparkling_heart,speaker,speak_no_evil,speech_balloon,speedboat,squirrel,star2,star,stars,station,statue_of_liberty,steam_locomotive,stew,straight_ruler,strawberry,stuck_out_tongue_closed_eyes,stuck_out_tongue,stuck_out_tongue_winking_eye,sunflower,sunglasses,sunny,sunrise_over_mountains,sunrise,sun_with_face,surfer,sushi,suspect,suspension_railway,sweat_drops,sweat,sweat_smile,sweet_potato,swimmer,symbols,syringe,tada,tanabata_tree,tangerine,taurus,taxi,tea,telephone,telephone_receiver,telescope,tennis,tent,thought_balloon,three,thumbsdown,thumbsup,ticket,tiger2,tiger,tired_face,tm,toilet,tokyo_tower,tomato,tongue,tophat,top,tractor,traffic_light,train2,train,tram,triangular_flag_on_post,triangular_ruler,trident,triumph,trolleybus,trollface,trophy,tropical_drink,tropical_fish,truck,trumpet,tshirt,tulip,turtle,tv,twisted_rightwards_arrows,two_hearts,two_men_holding_hands,two,two_women_holding_hands,u5272,u5408,u55b6,u6307,u6708,u6709,u6e80,u7121,u7533,u7981,u7a7a,uk,umbrella,unamused,underage,unlock,up,us,vertical_traffic_light,vhs,vibration_mode,video_camera,video_game,violin,virgo,volcano,v,vs,walking,waning_crescent_moon,waning_gibbous_moon,warning,watch,water_buffalo,watermelon,wave,wavy_dash,waxing_crescent_moon,waxing_gibbous_moon,wc,weary,wedding,whale2,whale,wheelchair,white_check_mark,white_circle,white_flower,white_large_square,white_square_button,white_square,wind_chime,wine_glass,wink2,wink,wolf,woman,womans_clothes,womans_hat,womens,worried,wrench,x,yellow_heart,yen,yum,zap,zero,zzz",k=j.split(/,/),l=k.reduce(function(a,b){return a[b]=!0,a},{}),m={named:/:([a-z0-9A-Z_-]+):/,blush:/:-?\)/g,scream:/:-o/gi,smirk:/[:;]-?]/g,smiley:/[:;]-?d/gi,stuck_out_tongue_closed_eyes:/x-d/gi,stuck_out_tongue_winking_eye:/[:;]-?p/gi,rage:/:-?[\[@]/g,disappointed:/:-?\(/g,sob:/:['’]-?\(|:'\(/g,kissing_heart:/:-?\*/g,wink:/;-?\)/g,pensive:/:-?\//g,confounded:/:-?s/gi,flushed:/:-?\|/g,relaxed:/:-?\$/g,mask:/:-x/gi,heart:/<3|<3/g,broken_heart:/<\/3|</3/g,thumbsup:/:\+1:/g,thumbsdown:/:\-1:/g},n=Object.keys(m).map(function(a){return[m[a],a]}),o=n.map(function(a){var b=a[0],c=b.source||b;return c=c.replace(/(^|[^\[])\^/g,"$1"),"("+c+")"}).join("|"),p=new RegExp(o,"gi"),q={emojify_tag_type:"div",only_crawl_id:null,img_dir:"images/emoji",ignored_tags:{SCRIPT:1,TEXTAREA:1,A:1,PRE:1,CODE:1}};return f.prototype={validate:function(a,c,e){function f(){return g.lastEmojiTerminatedAt=j+c,h}var g=this,h=d(a);if(h){var i=a[0],j=i.length;if(0===c)return f();if(e.length===i.length+c)return f();var k=this.lastEmojiTerminatedAt===c;if(k)return f();if(b(e.charAt(c-1)))return f();var l=b(e.charAt(i.length+c));return l&&k?f():void 0}}},{defaultConfig:q,emojiNames:k,setConfig:function(a){Object.keys(q).forEach(function(b){b in a&&(q[b]=a[b])})},replace:g,run:h}}();return a.emojify=b,"function"==typeof define&&define.amd&&define([],function(){return b}),b}(this);
================================================
FILE: public/handlebars.runtime.js
================================================
/*
Copyright (C) 2011 by Yehuda Katz
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
// lib/handlebars/browser-prefix.js
var Handlebars = {};
(function(Handlebars, undefined) {
;
// lib/handlebars/base.js
Handlebars.VERSION = "1.0.0";
Handlebars.COMPILER_REVISION = 4;
Handlebars.REVISION_CHANGES = {
1: '<= 1.0.rc.2', // 1.0.rc.2 is actually rev2 but doesn't report it
2: '== 1.0.0-rc.3',
3: '== 1.0.0-rc.4',
4: '>= 1.0.0'
};
Handlebars.helpers = {};
Handlebars.partials = {};
var toString = Object.prototype.toString,
functionType = '[object Function]',
objectType = '[object Object]';
Handlebars.registerHelper = function(name, fn, inverse) {
if (toString.call(name) === objectType) {
if (inverse || fn) { throw new Handlebars.Exception('Arg not supported with multiple helpers'); }
Handlebars.Utils.extend(this.helpers, name);
} else {
if (inverse) { fn.not = inverse; }
this.helpers[name] = fn;
}
};
Handlebars.registerPartial = function(name, str) {
if (toString.call(name) === objectType) {
Handlebars.Utils.extend(this.partials, name);
} else {
this.partials[name] = str;
}
};
Handlebars.registerHelper('helperMissing', function(arg) {
if(arguments.length === 2) {
return undefined;
} else {
throw new Error("Missing helper: '" + arg + "'");
}
});
Handlebars.registerHelper('blockHelperMissing', function(context, options) {
var inverse = options.inverse || function() {}, fn = options.fn;
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
if(context === true) {
return fn(this);
} else if(context === false || context == null) {
return inverse(this);
} else if(type === "[object Array]") {
if(context.length > 0) {
return Handlebars.helpers.each(context, options);
} else {
return inverse(this);
}
} else {
return fn(context);
}
});
Handlebars.K = function() {};
Handlebars.createFrame = Object.create || function(object) {
Handlebars.K.prototype = object;
var obj = new Handlebars.K();
Handlebars.K.prototype = null;
return obj;
};
Handlebars.logger = {
DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
methodMap: {0: 'debug', 1: 'info', 2: 'warn', 3: 'error'},
// can be overridden in the host environment
log: function(level, obj) {
if (Handlebars.logger.level <= level) {
var method = Handlebars.logger.methodMap[level];
if (typeof console !== 'undefined' && console[method]) {
console[method].call(console, obj);
}
}
}
};
Handlebars.log = function(level, obj) { Handlebars.logger.log(level, obj); };
Handlebars.registerHelper('each', function(context, options) {
var fn = options.fn, inverse = options.inverse;
var i = 0, ret = "", data;
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
if (options.data) {
data = Handlebars.createFrame(options.data);
}
if(context && typeof context === 'object') {
if(context instanceof Array){
for(var j = context.length; i<j; i++) {
if (data) { data.index = i; }
ret = ret + fn(context[i], { data: data });
}
} else {
for(var key in context) {
if(context.hasOwnProperty(key)) {
if(data) { data.key = key; }
ret = ret + fn(context[key], {data: data});
i++;
}
}
}
}
if(i === 0){
ret = inverse(this);
}
return ret;
});
Handlebars.registerHelper('if', function(conditional, options) {
var type = toString.call(conditional);
if(type === functionType) { conditional = conditional.call(this); }
if(!conditional || Handlebars.Utils.isEmpty(conditional)) {
return options.inverse(this);
} else {
return options.fn(this);
}
});
Handlebars.registerHelper('unless', function(conditional, options) {
return Handlebars.helpers['if'].call(this, conditional, {fn: options.inverse, inverse: options.fn});
});
Handlebars.registerHelper('with', function(context, options) {
var type = toString.call(context);
if(type === functionType) { context = context.call(this); }
if (!Handlebars.Utils.isEmpty(context)) return options.fn(context);
});
Handlebars.registerHelper('log', function(context, options) {
var level = options.data && options.data.level != null ? parseInt(options.data.level, 10) : 1;
Handlebars.log(level, context);
});
;
// lib/handlebars/utils.js
var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack'];
Handlebars.Exception = function(message) {
var tmp = Error.prototype.constructor.apply(this, arguments);
// Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work.
for (var idx = 0; idx < errorProps.length; idx++) {
this[errorProps[idx]] = tmp[errorProps[idx]];
}
};
Handlebars.Exception.prototype = new Error();
// Build out our basic SafeString type
Handlebars.SafeString = function(string) {
this.string = string;
};
Handlebars.SafeString.prototype.toString = function() {
return this.string.toString();
};
var escape = {
"&": "&",
"<": "<",
">": ">",
'"': """,
"'": "'",
"`": "`"
};
var badChars = /[&<>"'`]/g;
var possible = /[&<>"'`]/;
var escapeChar = function(chr) {
return escape[chr] || "&";
};
Handlebars.Utils = {
extend: function(obj, value) {
for(var key in value) {
if(value.hasOwnProperty(key)) {
obj[key] = value[key];
}
}
},
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
} else if (string == null || string === false) {
return "";
}
// Force a string conversion as this will be done by the append regardless and
// the regex test will do this transparently behind the scenes, causing issues if
// an object's to string has escaped characters in it.
string = string.toString();
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
if (!value && value !== 0) {
return true;
} else if(toString.call(value) === "[object Array]" && value.length === 0) {
return true;
} else {
return false;
}
}
};
;
// lib/handlebars/runtime.js
Handlebars.VM = {
template: function(templateSpec) {
// Just add water
var container = {
escapeExpression: Handlebars.Utils.escapeExpression,
invokePartial: Handlebars.VM.invokePartial,
programs: [],
program: function(i, fn, data) {
var programWrapper = this.programs[i];
if(data) {
programWrapper = Handlebars.VM.program(i, fn, data);
} else if (!programWrapper) {
programWrapper = this.programs[i] = Handlebars.VM.program(i, fn);
}
return programWrapper;
},
merge: function(param, common) {
var ret = param || common;
if (param && common) {
ret = {};
Handlebars.Utils.extend(ret, common);
Handlebars.Utils.extend(ret, param);
}
return ret;
},
programWithDepth: Handlebars.VM.programWithDepth,
noop: Handlebars.VM.noop,
compilerInfo: null
};
return function(context, options) {
options = options || {};
var result = templateSpec.call(container, Handlebars, context, options.helpers, options.partials, options.data);
var compilerInfo = container.compilerInfo || [],
compilerRevision = compilerInfo[0] || 1,
currentRevision = Handlebars.COMPILER_REVISION;
if (compilerRevision !== currentRevision) {
if (compilerRevision < currentRevision) {
var runtimeVersions = Handlebars.REVISION_CHANGES[currentRevision],
compilerVersions = Handlebars.REVISION_CHANGES[compilerRevision];
throw "Template was precompiled with an older version of Handlebars than the current runtime. "+
"Please update your precompiler to a newer version ("+runtimeVersions+") or downgrade your runtime to an older version ("+compilerVersions+").";
} else {
// Use the embedded version info since the runtime doesn't know about this revision yet
throw "Template was precompiled with a newer version of Handlebars than the current runtime. "+
"Please update your runtime to a newer version ("+compilerInfo[1]+").";
}
}
return result;
};
},
programWithDepth: function(i, fn, data /*, $depth */) {
var args = Array.prototype.slice.call(arguments, 3);
var program = function(context, options) {
options = options || {};
return fn.apply(this, [context, options.data || data].concat(args));
};
program.program = i;
program.depth = args.length;
return program;
},
program: function(i, fn, data) {
var program = function(context, options) {
options = options || {};
return fn(context, options.data || data);
};
program.program = i;
program.depth = 0;
return program;
},
noop: function() { return ""; },
invokePartial: function(partial, name, context, helpers, partials, data) {
var options = { helpers: helpers, partials: partials, data: data };
if(partial === undefined) {
throw new Handlebars.Exception("The partial " + name + " could not be found");
} else if(partial instanceof Function) {
return partial(context, options);
} else if (!Handlebars.compile) {
throw new Handlebars.Exception("The partial " + name + " could not be compiled when running in runtime-only mode");
} else {
partials[name] = Handlebars.compile(partial, {data: data !== undefined});
return partials[name](context, options);
}
}
};
Handlebars.template = Handlebars.VM.template;
;
// lib/handlebars/browser-suffix.js
})(Handlebars);
;
================================================
FILE: public/images/emoji/emojify.css
================================================
.emojify{width:1.5em;height:1.5em;display:inline-block;margin-bottom:-0.25em}.emojify.alien{background:url(alien.png) no-repeat;background-size:1.5em}.emojify.angel{background:url(angel.png) no-repeat;background-size:1.5em}.emojify.anger{background:url(anger.png) no-repeat;background-size:1.5em}.emojify.angry{background:url(angry.png) no-repeat;background-size:1.5em}.emojify.anguished{background:url(anguished.png) no-repeat;background-size:1.5em}.emojify.astonished{background:url(astonished.png) no-repeat;background-size:1.5em}.emojify.baby{background:url(baby.png) no-repeat;background-size:1.5em}.emojify.blue_heart{background:url(blue_heart.png) no-repeat;background-size:1.5em}.emojify.blush{background:url(blush.png) no-repeat;background-size:1.5em}.emojify.boom{background:url(boom.png) no-repeat;background-size:1.5em}.emojify.bow{background:url(bow.png) no-repeat;background-size:1.5em}.emojify.bowtie{background:url(bowtie.png) no-repeat;background-size:1.5em}.emojify.boy{background:url(boy.png) no-repeat;background-size:1.5em}.emojify.bride_with_veil{background:url(bride_with_veil.png) no-repeat;background-size:1.5em}.emojify.broken_heart{background:url(broken_heart.png) no-repeat;background-size:1.5em}.emojify.bust_in_silhouette{background:url(bust_in_silhouette.png) no-repeat;background-size:1.5em}.emojify.busts_in_silhouette{background:url(busts_in_silhouette.png) no-repeat;background-size:1.5em}.emojify.clap{background:url(clap.png) no-repeat;background-size:1.5em}.emojify.cold_sweat{background:url(cold_sweat.png) no-repeat;background-size:1.5em}.emojify.collision{background:url(collision.png) no-repeat;background-size:1.5em}.emojify.confounded{background:url(confounded.png) no-repeat;background-size:1.5em}.emojify.confused{background:url(confused.png) no-repeat;background-size:1.5em}.emojify.construction_worker{background:url(construction_worker.png) no-repeat;background-size:1.5em}.emojify.cop{background:url(cop.png) no-repeat;background-size:1.5em}.emojify.couple_with_heart{background:url(couple_with_heart.png) no-repeat;background-size:1.5em}.emojify.couple{background:url(couple.png) no-repeat;background-size:1.5em}.emojify.couplekiss{background:url(couplekiss.png) no-repeat;background-size:1.5em}.emojify.cry{background:url(cry.png) no-repeat;background-size:1.5em}.emojify.crying_cat_face{background:url(crying_cat_face.png) no-repeat;background-size:1.5em}.emojify.cupid{background:url(cupid.png) no-repeat;background-size:1.5em}.emojify.dancer{background:url(dancer.png) no-repeat;background-size:1.5em}.emojify.dancers{background:url(dancers.png) no-repeat;background-size:1.5em}.emojify.dash{background:url(dash.png) no-repeat;background-size:1.5em}.emojify.disappointed{background:url(disappointed.png) no-repeat;background-size:1.5em}.emojify.dizzy_face{background:url(dizzy_face.png) no-repeat;background-size:1.5em}.emojify.dizzy{background:url(dizzy.png) no-repeat;background-size:1.5em}.emojify.droplet{background:url(droplet.png) no-repeat;background-size:1.5em}.emojify.ear{background:url(ear.png) no-repeat;background-size:1.5em}.emojify.exclamation{background:url(exclamation.png) no-repeat;background-size:1.5em}.emojify.expressionless{background:url(expressionless.png) no-repeat;background-size:1.5em}.emojify.eyes{background:url(eyes.png) no-repeat;background-size:1.5em}.emojify.facepunch{background:url(facepunch.png) no-repeat;background-size:1.5em}.emojify.family{background:url(family.png) no-repeat;background-size:1.5em}.emojify.fearful{background:url(fearful.png) no-repeat;background-size:1.5em}.emojify.feelsgood{background:url(feelsgood.png) no-repeat;background-size:1.5em}.emojify.feet{background:url(feet.png) no-repeat;background-size:1.5em}.emojify.finnadie{background:url(finnadie.png) no-repeat;background-size:1.5em}.emojify.fire{background:url(fire.png) no-repeat;background-size:1.5em}.emojify.fist{background:url(fist.png) no-repeat;background-size:1.5em}.emojify.flushed{background:url(flushed.png) no-repeat;background-size:1.5em}.emojify.frowning{background:url(frowning.png) no-repeat;background-size:1.5em}.emojify.girl{background:url(girl.png) no-repeat;background-size:1.5em}.emojify.goberserk{background:url(goberserk.png) no-repeat;background-size:1.5em}.emojify.godmode{background:url(godmode.png) no-repeat;background-size:1.5em}.emojify.green_heart{background:url(green_heart.png) no-repeat;background-size:1.5em}.emojify.grey_exclamation{background:url(grey_exclamation.png) no-repeat;background-size:1.5em}.emojify.grey_question{background:url(grey_question.png) no-repeat;background-size:1.5em}.emojify.grimacing{background:url(grimacing.png) no-repeat;background-size:1.5em}.emojify.grin{background:url(grin.png) no-repeat;background-size:1.5em}.emojify.grinning{background:url(grinning.png) no-repeat;background-size:1.5em}.emojify.guardsman{background:url(guardsman.png) no-repeat;background-size:1.5em}.emojify.haircut{background:url(haircut.png) no-repeat;background-size:1.5em}.emojify.hand{background:url(hand.png) no-repeat;background-size:1.5em}.emojify.hankey{background:url(hankey.png) no-repeat;background-size:1.5em}.emojify.hear_no_evil{background:url(hear_no_evil.png) no-repeat;background-size:1.5em}.emojify.heart_eyes_cat{background:url(heart_eyes_cat.png) no-repeat;background-size:1.5em}.emojify.heart_eyes{background:url(heart_eyes.png) no-repeat;background-size:1.5em}.emojify.heart{background:url(heart.png) no-repeat;background-size:1.5em}.emojify.heartbeat{background:url(heartbeat.png) no-repeat;background-size:1.5em}.emojify.heartpulse{background:url(heartpulse.png) no-repeat;background-size:1.5em}.emojify.hurtrealbad{background:url(hurtrealbad.png) no-repeat;background-size:1.5em}.emojify.hushed{background:url(hushed.png) no-repeat;background-size:1.5em}.emojify.imp{background:url(imp.png) no-repeat;background-size:1.5em}.emojify.information_desk_person{background:url(information_desk_person.png) no-repeat;background-size:1.5em}.emojify.innocent{background:url(innocent.png) no-repeat;background-size:1.5em}.emojify.japanese_goblin{background:url(japanese_goblin.png) no-repeat;background-size:1.5em}.emojify.japanese_ogre{background:url(japanese_ogre.png) no-repeat;background-size:1.5em}.emojify.joy_cat{background:url(joy_cat.png) no-repeat;background-size:1.5em}.emojify.joy{background:url(joy.png) no-repeat;background-size:1.5em}.emojify.kiss{background:url(kiss.png) no-repeat;background-size:1.5em}.emojify.kissing_cat{background:url(kissing_cat.png) no-repeat;background-size:1.5em}.emojify.kissing_closed_eyes{background:url(kissing_closed_eyes.png) no-repeat;background-size:1.5em}.emojify.kissing_heart{background:url(kissing_heart.png) no-repeat;background-size:1.5em}.emojify.kissing_smiling_eyes{background:url(kissing_smiling_eyes.png) no-repeat;background-size:1.5em}.emojify.kissing{background:url(kissing.png) no-repeat;background-size:1.5em}.emojify.laughing{background:url(laughing.png) no-repeat;background-size:1.5em}.emojify.lips{background:url(lips.png) no-repeat;background-size:1.5em}.emojify.love_letter{background:url(love_letter.png) no-repeat;background-size:1.5em}.emojify.man_with_gua_pi_mao{background:url(man_with_gua_pi_mao.png) no-repeat;background-size:1.5em}.emojify.man_with_turban{background:url(man_with_turban.png) no-repeat;background-size:1.5em}.emojify.man{background:url(man.png) no-repeat;background-size:1.5em}.emojify.mask{background:url(mask.png) no-repeat;background-size:1.5em}.emojify.massage{background:url(massage.png) no-repeat;background-size:1.5em}.emojify.metal{background:url(metal.png) no-repeat;background-size:1.5em}.emojify.muscle{background:url(muscle.png) no-repeat;background-size:1.5em}.emojify.musical_note{background:url(musical_note.png) no-repeat;background-size:1.5em}.emojify.nail_care{background:url(nail_care.png) no-repeat;background-size:1.5em}.emojify.neckbeard{background:url(neckbeard.png) no-repeat;background-size:1.5em}.emojify.neutral_face{background:url(neutral_face.png) no-repeat;background-size:1.5em}.emojify.no_good{background:url(no_good.png) no-repeat;background-size:1.5em}.emojify.no_mouth{background:url(no_mouth.png) no-repeat;background-size:1.5em}.emojify.nose{background:url(nose.png) no-repeat;background-size:1.5em}.emojify.notes{background:url(notes.png) no-repeat;background-size:1.5em}.emojify.ok_hand{background:url(ok_hand.png) no-repeat;background-size:1.5em}.emojify.ok_woman{background:url(ok_woman.png) no-repeat;background-size:1.5em}.emojify.older_man{background:url(older_man.png) no-repeat;background-size:1.5em}.emojify.older_woman{background:url(older_woman.png) no-repeat;background-size:1.5em}.emojify.open_hands{background:url(open_hands.png) no-repeat;background-size:1.5em}.emojify.open_mouth{background:url(open_mouth.png) no-repeat;background-size:1.5em}.emojify.pensive{background:url(pensive.png) no-repeat;background-size:1.5em}.emojify.persevere{background:url(persevere.png) no-repeat;background-size:1.5em}.emojify.person_frowning{background:url(person_frowning.png) no-repeat;background-size:1.5em}.emojify.person_with_blond_hair{background:url(person_with_blond_hair.png) no-repeat;background-size:1.5em}.emojify.person_with_pouting_face{background:url(person_with_pouting_face.png) no-repeat;background-size:1.5em}.emojify.point_down{background:url(point_down.png) no-repeat;background-size:1.5em}.emojify.point_left{background:url(point_left.png) no-repeat;background-size:1.5em}.emojify.point_right{background:url(point_right.png) no-repeat;background-size:1.5em}.emojify.point_up_2{background:url(point_up_2.png) no-repeat;background-size:1.5em}.emojify.point_up{background:url(point_up.png) no-repeat;background-size:1.5em}.emojify.poop{background:url(poop.png) no-repeat;background-size:1.5em}.emojify.pouting_cat{background:url(pouting_cat.png) no-repeat;background-size:1.5em}.emojify.pray{background:url(pray.png) no-repeat;background-size:1.5em}.emojify.princess{background:url(princess.png) no-repeat;background-size:1.5em}.emojify.punch{background:url(punch.png) no-repeat;background-size:1.5em}.emojify.purple_heart{background:url(purple_heart.png) no-repeat;background-size:1.5em}.emojify.question{background:url(question.png) no-repeat;background-size:1.5em}.emojify.rage{background:url(rage.png) no-repeat;background-size:1.5em}.emojify.rage1{background:url(rage1.png) no-repeat;background-size:1.5em}.emojify.rage2{background:url(rage2.png) no-repeat;background-size:1.5em}.emojify.rage3{background:url(rage3.png) no-repeat;background-size:1.5em}.emojify.rage4{background:url(rage4.png) no-repeat;background-size:1.5em}.emojify.raised_hand{background:url(raised_hand.png) no-repeat;background-size:1.5em}.emojify.raised_hands{background:url(raised_hands.png) no-repeat;background-size:1.5em}.emojify.relaxed{background:url(relaxed.png) no-repeat;background-size:1.5em}.emojify.relieved{background:url(relieved.png) no-repeat;background-size:1.5em}.emojify.revolving_hearts{background:url(revolving_hearts.png) no-repeat;background-size:1.5em}.emojify.runner{background:url(runner.png) no-repeat;background-size:1.5em}.emojify.running{background:url(running.png) no-repeat;background-size:1.5em}.emojify.satisfied{background:url(satisfied.png) no-repeat;background-size:1.5em}.emojify.scream_cat{background:url(scream_cat.png) no-repeat;background-size:1.5em}.emojify.scream{background:url(scream.png) no-repeat;background-size:1.5em}.emojify.see_no_evil{background:url(see_no_evil.png) no-repeat;background-size:1.5em}.emojify.shit{background:url(shit.png) no-repeat;background-size:1.5em}.emojify.skull{background:url(skull.png) no-repeat;background-size:1.5em}.emojify.sleeping{background:url(sleeping.png) no-repeat;background-size:1.5em}.emojify.sleepy{background:url(sleepy.png) no-repeat;background-size:1.5em}.emojify.smile_cat{background:url(smile_cat.png) no-repeat;background-size:1.5em}.emojify.smile{background:url(smile.png) no-repeat;background-size:1.5em}.emojify.smiley_cat{background:url(smiley_cat.png) no-repeat;background-size:1.5em}.emojify.smiley{background:url(smiley.png) no-repeat;background-size:1.5em}.emojify.smiling_imp{background:url(smiling_imp.png) no-repeat;background-size:1.5em}.emojify.smirk_cat{background:url(smirk_cat.png) no-repeat;background-size:1.5em}.emojify.smirk{background:url(smirk.png) no-repeat;background-size:1.5em}.emojify.sob{background:url(sob.png) no-repeat;background-size:1.5em}.emojify.sparkling_heart{background:url(sparkling_heart.png) no-repeat;background-size:1.5em}.emojify.sparkles{background:url(sparkles.png) no-repeat;background-size:1.5em}.emojify.speak_no_evil{background:url(speak_no_evil.png) no-repeat;background-size:1.5em}.emojify.speech_balloon{background:url(speech_balloon.png) no-repeat;background-size:1.5em}.emojify.star{background:url(star.png) no-repeat;background-size:1.5em}.emojify.star2{background:url(star2.png) no-repeat;background-size:1.5em}.emojify.stuck_out_tongue_closed_eyes{background:url(stuck_out_tongue_closed_eyes.png) no-repeat;background-size:1.5em}.emojify.stuck_out_tongue_winking_eye{background:url(stuck_out_tongue_winking_eye.png) no-repeat;background-size:1.5em}.emojify.stuck_out_tongue{background:url(stuck_out_tongue.png) no-repeat;background-size:1.5em}.emojify.sunglasses{background:url(sunglasses.png) no-repeat;background-size:1.5em}.emojify.suspect{background:url(suspect.png) no-repeat;background-size:1.5em}.emojify.sweat_drops{background:url(sweat_drops.png) no-repeat;background-size:1.5em}.emojify.sweat_smile{background:url(sweat_smile.png) no-repeat;background-size:1.5em}.emojify.sweat{background:url(sweat.png) no-repeat;background-size:1.5em}.emojify.thought_balloon{background:url(thought_balloon.png) no-repeat;background-size:1.5em}.emojify.thumbsdown{background:url(thumbsdown.png) no-repeat;background-size:1.5em}.emojify.thumbsup{background:url(thumbsup.png) no-repeat;background-size:1.5em}.emojify.tired_face{background:url(tired_face.png) no-repeat;background-size:1.5em}.emojify.tongue{background:url(tongue.png) no-repeat;background-size:1.5em}.emojify.triumph{background:url(triumph.png) no-repeat;background-size:1.5em}.emojify.trollface{background:url(trollface.png) no-repeat;background-size:1.5em}.emojify.two_hearts{background:url(two_hearts.png) no-repeat;background-size:1.5em}.emojify.two_men_holding_hands{background:url(two_men_holding_hands.png) no-repeat;background-size:1.5em}.emojify.two_women_holding_hands{background:url(two_women_holding_hands.png) no-repeat;background-size:1.5em}.emojify.unamused{background:url(unamused.png) no-repeat;background-size:1.5em}.emojify.v{background:url(v.png) no-repeat;background-size:1.5em}.emojify.walking{background:url(walking.png) no-repeat;background-size:1.5em}.emojify.wave{background:url(wave.png) no-repeat;background-size:1.5em}.emojify.weary{background:url(weary.png) no-repeat;background-size:1.5em}.emojify.wink2{background:url(wink2.png) no-repeat;background-size:1.5em}.emojify.wink{background:url(wink.png) no-repeat;background-size:1.5em}.emojify.woman{background:url(woman.png) no-repeat;background-size:1.5em}.emojify.worried{background:url(worried.png) no-repeat;background-size:1.5em}.emojify.yellow_heart{background:url(yellow_heart.png) no-repeat;background-size:1.5em}.emojify.yum{background:url(yum.png) no-repeat;background-size:1.5em}.emojify.zzz{background:url(zzz.png) no-repeat;background-size:1.5em}.emojify.sunny{background:url(sunny.png) no-repeat;background-size:1.5em}.emojify.umbrella{background:url(umbrella.png) no-repeat;background-size:1.5em}.emojify.cloud{background:url(cloud.png) no-repeat;background-size:1.5em}.emojify.snowflake{background:url(snowflake.png) no-repeat;background-size:1.5em}.emojify.snowman{background:url(snowman.png) no-repeat;background-size:1.5em}.emojify.zap{background:url(zap.png) no-repeat;background-size:1.5em}.emojify.cyclone{background:url(cyclone.png) no-repeat;background-size:1.5em}.emojify.foggy{background:url(foggy.png) no-repeat;background-size:1.5em}.emojify.ocean{background:url(ocean.png) no-repeat;background-size:1.5em}.emojify.cat{background:url(cat.png) no-repeat;background-size:1.5em}.emojify.dog{background:url(dog.png) no-repeat;background-size:1.5em}.emojify.mouse{background:url(mouse.png) no-repeat;background-size:1.5em}.emojify.hamster{background:url(hamster.png) no-repeat;background-size:1.5em}.emojify.rabbit{background:url(rabbit.png) no-repeat;background-size:1.5em}.emojify.wolf{background:url(wolf.png) no-repeat;background-size:1.5em}.emojify.frog{background:url(frog.png) no-repeat;background-size:1.5em}.emojify.tiger{background:url(tiger.png) no-repeat;background-size:1.5em}.emojify.koala{background:url(koala.png) no-repeat;background-size:1.5em}.emojify.bear{background:url(bear.png) no-repeat;background-size:1.5em}.emojify.pig{background:url(pig.png) no-repeat;background-size:1.5em}.emojify.pig_nose{background:url(pig_nose.png) no-repeat;background-size:1.5em}.emojify.cow{background:url(cow.png) no-repeat;background-size:1.5em}.emojify.boar{background:url(boar.png) no-repeat;background-size:1.5em}.emojify.monkey_face{background:url(monkey_face.png) no-repeat;background-size:1.5em}.emojify.monkey{background:url(monkey.png) no-repeat;background-size:1.5em}.emojify.horse{background:url(horse.png) no-repeat;background-size:1.5em}.emojify.racehorse{background:url(racehorse.png) no-repeat;background-size:1.5em}.emojify.camel{background:url(camel.png) no-repeat;background-size:1.5em}.emojify.sheep{background:url(sheep.png) no-repeat;background-size:1.5em}.emojify.elephant{background:url(elephant.png) no-repeat;background-size:1.5em}.emojify.panda_face{background:url(panda_face.png) no-repeat;background-size:1.5em}.emojify.snake{background:url(snake.png) no-repeat;background-size:1.5em}.emojify.bird{background:url(bird.png) no-repeat;background-size:1.5em}.emojify.baby_chick{background:url(baby_chick.png) no-repeat;background-size:1.5em}.emojify.hatched_chick{background:url(hatched_chick.png) no-repeat;background-size:1.5em}.emojify.hatching_chick{background:url(hatching_chick.png) no-repeat;background-size:1.5em}.emojify.chicken{background:url(chicken.png) no-repeat;background-size:1.5em}.emojify.penguin{background:url(penguin.png) no-repeat;background-size:1.5em}.emojify.turtle{background:url(turtle.png) no-repeat;background-size:1.5em}.emojify.bug{background:url(bug.png) no-repeat;background-size:1.5em}.emojify.honeybee{background:url(honeybee.png) no-repeat;background-size:1.5em}.emojify.ant{background:url(ant.png) no-repeat;background-size:1.5em}.emojify.beetle{background:url(beetle.png) no-repeat;background-size:1.5em}.emojify.snail{background:url(snail.png) no-repeat;background-size:1.5em}.emojify.octopus{background:url(octopus.png) no-repeat;background-size:1.5em}.emojify.tropical_fish{background:url(tropical_fish.png) no-repeat;background-size:1.5em}.emojify.fish{background:url(fish.png) no-repeat;background-size:1.5em}.emojify.whale{background:url(whale.png) no-repeat;background-size:1.5em}.emojify.whale2{background:url(whale2.png) no-repeat;background-size:1.5em}.emojify.dolphin{background:url(dolphin.png) no-repeat;background-size:1.5em}.emojify.cow2{background:url(cow2.png) no-repeat;background-size:1.5em}.emojify.ram{background:url(ram.png) no-repeat;background-size:1.5em}.emojify.rat{background:url(rat.png) no-repeat;background-size:1.5em}.emojify.water_buffalo{background:url(water_buffalo.png) no-repeat;background-size:1.5em}.emojify.tiger2{background:url(tiger2.png) no-repeat;background-size:1.5em}.emojify.rabbit2{background:url(rabbit2.png) no-repeat;background-size:1.5em}.emojify.dragon{background:url(dragon.png) no-repeat;background-size:1.5em}.emojify.goat{background:url(goat.png) no-repeat;background-size:1.5em}.emojify.rooster{background:url(rooster.png) no-repeat;background-size:1.5em}.emojify.dog2{background:url(dog2.png) no-repeat;background-size:1.5em}.emojify.pig2{background:url(pig2.png) no-repeat;background-size:1.5em}.emojify.mouse2{background:url(mouse2.png) no-repeat;background-size:1.5em}.emojify.ox{background:url(ox.png) no-repeat;background-size:1.5em}.emojify.dragon_face{background:url(dragon_face.png) no-repeat;background-size:1.5em}.emojify.blowfish{background:url(blowfish.png) no-repeat;background-size:1.5em}.emojify.crocodile{background:url(crocodile.png) no-repeat;background-size:1.5em}.emojify.dromedary_camel{background:url(dromedary_camel.png) no-repeat;background-size:1.5em}.emojify.leopard{background:url(leopard.png) no-repeat;background-size:1.5em}.emojify.cat2{background:url(cat2.png) no-repeat;background-size:1.5em}.emojify.poodle{background:url(poodle.png) no-repeat;background-size:1.5em}.emojify.paw_prints{background:url(paw_prints.png) no-repeat;background-size:1.5em}.emojify.bouquet{background:url(bouquet.png) no-repeat;background-size:1.5em}.emojify.cherry_blossom{background:url(cherry_blossom.png) no-repeat;background-size:1.5em}.emojify.tulip{background:url(tulip.png) no-repeat;background-size:1.5em}.emojify.four_leaf_clover{background:url(four_leaf_clover.png) no-repeat;background-size:1.5em}.emojify.rose{background:url(rose.png) no-repeat;background-size:1.5em}.emojify.sunflower{background:url(sunflower.png) no-repeat;background-size:1.5em}.emojify.hibiscus{background:url(hibiscus.png) no-repeat;background-size:1.5em}.emojify.maple_leaf{background:url(maple_leaf.png) no-repeat;background-size:1.5em}.emojify.leaves{background:url(leaves.png) no-repeat;background-size:1.5em}.emojify.fallen_leaf{background:url(fallen_leaf.png) no-repeat;background-size:1.5em}.emojify.herb{background:url(herb.png) no-repeat;background-size:1.5em}.emojify.mushroom{background:url(mushroom.png) no-repeat;background-size:1.5em}.emojify.cactus{background:url(cactus.png) no-repeat;background-size:1.5em}.emojify.palm_tree{background:url(palm_tree.png) no-repeat;background-size:1.5em}.emojify.evergreen_tree{background:url(evergreen_tree.png) no-repeat;background-size:1.5em}.emojify.deciduous_tree{background:url(deciduous_tree.png) no-repeat;background-size:1.5em}.emojify.chestnut{background:url(chestnut.png) no-repeat;background-size:1.5em}.emojify.seedling{background:url(seedling.png) no-repeat;background-size:1.5em}.emojify.blossom{background:url(blossom.png) no-repeat;background-size:1.5em}.emojify.ear_of_rice{background:url(ear_of_rice.png) no-repeat;background-size:1.5em}.emojify.shell{background:url(shell.png) no-repeat;background-size:1.5em}.emojify.globe_with_meridians{background:url(globe_with_meridians.png) no-repeat;background-size:1.5em}.emojify.sun_with_face{background:url(sun_with_face.png) no-repeat;background-size:1.5em}.emojify.full_moon_with_face{background:url(full_moon_with_face.png) no-repeat;background-size:1.5em}.emojify.new_moon_with_face{background:url(new_moon_with_face.png) no-repeat;background-size:1.5em}.emojify.new_moon{background:url(new_moon.png) no-repeat;background-size:1.5em}.emojify.waxing_crescent_moon{background:url(waxing_crescent_moon.png) no-repeat;background-size:1.5em}.emojify.first_quarter_moon{background:url(first_quarter_moon.png) no-repeat;background-size:1.5em}.emojify.waxing_gibbous_moon{background:url(waxing_gibbous_moon.png) no-repeat;background-size:1.5em}.emojify.full_moon{background:url(full_moon.png) no-repeat;background-size:1.5em}.emojify.waning_gibbous_moon{background:url(waning_gibbous_moon.png) no-repeat;background-size:1.5em}.emojify.last_quarter_moon{background:url(last_quarter_moon.png) no-repeat;background-size:1.5em}.emojify.waning_crescent_moon{background:url(waning_crescent_moon.png) no-repeat;background-size:1.5em}.emojify.last_quarter_moon_with_face{background:url(last_quarter_moon_with_face.png) no-repeat;background-size:1.5em}.emojify.first_quarter_moon_with_face{background:url(first_quarter_moon_with_face.png) no-repeat;background-size:1.5em}.emojify.moon{background:url(moon.png) no-repeat;background-size:1.5em}.emojify.earth_africa{background:url(earth_africa.png) no-repeat;background-size:1.5em}.emojify.earth_americas{background:url(earth_americas.png) no-repeat;background-size:1.5em}.emojify.earth_asia{background:url(earth_asia.png) no-repeat;background-size:1.5em}.emojify.volcano{background:url(volcano.png) no-repeat;background-size:1.5em}.emojify.milky_way{background:url(milky_way.png) no-repeat;background-size:1.5em}.emojify.partly_sunny{background:url(partly_sunny.png) no-repeat;background-size:1.5em}.emojify.octocat{background:url(octocat.png) no-repeat;background-size:1.5em}.emojify.squirrel{background:url(squirrel.png) no-repeat;background-size:1.5em}.emojify.bamboo{background:url(bamboo.png) no-repeat;background-size:1.5em}.emojify.gift_heart{background:url(gift_heart.png) no-repeat;background-size:1.5em}.emojify.dolls{background:url(dolls.png) no-repeat;background-size:1.5em}.emojify.school_satchel{background:url(school_satchel.png) no-repeat;background-size:1.5em}.emojify.mortar_board{background:url(mortar_board.png) no-repeat;background-size:1.5em}.emojify.flags{background:url(flags.png) no-repeat;background-size:1.5em}.emojify.fireworks{background:url(fireworks.png) no-repeat;background-size:1.5em}.emojify.sparkler{background:url(sparkler.png) no-repeat;background-size:1.5em}.emojify.wind_chime{background:url(wind_chime.png) no-repeat;background-size:1.5em}.emojify.rice_scene{background:url(rice_scene.png) no-repeat;background-size:1.5em}.emojify.jack_o_lantern{background:url(jack_o_lantern.png) no-repeat;background-size:1.5em}.emojify.ghost{background:url(ghost.png) no-repeat;background-size:1.5em}.emojify.santa{background:url(santa.png) no-repeat;background-size:1.5em}.emojify.christmas_tree{background:url(christmas_tree.png) no-repeat;background-size:1.5em}.emojify.gift{background:url(gift.png) no-repeat;background-size:1.5em}.emojify.bell{background:url(bell.png) no-repeat;background-size:1.5em}.emojify.no_bell{background:url(no_bell.png) no-repeat;background-size:1.5em}.emojify.tanabata_tree{background:url(tanabata_tree.png) no-repeat;background-size:1.5em}.emojify.tada{background:url(tada.png) no-repeat;background-size:1.5em}.emojify.confetti_ball{background:url(confetti_ball.png) no-repeat;background-size:1.5em}.emojify.balloon{background:url(balloon.png) no-repeat;background-size:1.5em}.emojify.crystal_ball{background:url(crystal_ball.png) no-repeat;background-size:1.5em}.emojify.cd{background:url(cd.png) no-repeat;background-size:1.5em}.emojify.dvd{background:url(dvd.png) no-repeat;background-size:1.5em}.emojify.floppy_disk{background:url(floppy_disk.png) no-repeat;background-size:1.5em}.emojify.camera{background:url(camera.png) no-repeat;background-size:1.5em}.emojify.video_camera{background:url(video_camera.png) no-repeat;background-size:1.5em}.emojify.movie_camera{background:url(movie_camera.png) no-repeat;background-size:1.5em}.emojify.computer{background:url(computer.png) no-repeat;background-size:1.5em}.emojify.tv{background:url(tv.png) no-repeat;background-size:1.5em}.emojify.iphone{background:url(iphone.png) no-repeat;background-size:1.5em}.emojify.phone{background:url(phone.png) no-repeat;background-size:1.5em}.emojify.telephone{background:url(telephone.png) no-repeat;background-size:1.5em}.emojify.telephone_receiver{background:url(telephone_receiver.png) no-repeat;background-size:1.5em}.emojify.pager{background:url(pager.png) no-repeat;background-size:1.5em}.emojify.fax{background:url(fax.png) no-repeat;background-size:1.5em}.emojify.minidisc{background:url(minidisc.png) no-repeat;background-size:1.5em}.emojify.vhs{background:url(vhs.png) no-repeat;background-size:1.5em}.emojify.sound{background:url(sound.png) no-repeat;background-size:1.5em}.emojify.speaker{background:url(speaker.png) no-repeat;background-size:1.5em}.emojify.mute{background:url(mute.png) no-repeat;background-size:1.5em}.emojify.loudspeaker{background:url(loudspeaker.png) no-repeat;background-size:1.5em}.emojify.mega{background:url(mega.png) no-repeat;background-size:1.5em}.emojify.hourglass{background:url(hourglass.png) no-repeat;background-size:1.5em}.emojify.hourglass_flowing_sand{background:url(hourglass_flowing_sand.png) no-repeat;background-size:1.5em}.emojify.alarm_clock{background:url(alarm_clock.png) no-repeat;background-size:1.5em}.emojify.watch{background:url(watch.png) no-repeat;background-size:1.5em}.emojify.radio{background:url(radio.png) no-repeat;background-size:1.5em}.emojify.satellite{background:url(satellite.png) no-repeat;background-size:1.5em}.emojify.loop{background:url(loop.png) no-repeat;background-size:1.5em}.emojify.mag{background:url(mag.png) no-repeat;background-size:1.5em}.emojify.mag_right{background:url(mag_right.png) no-repeat;background-size:1.5em}.emojify.unlock{background:url(unlock.png) no-repeat;background-size:1.5em}.emojify.lock{background:url(lock.png) no-repeat;background-size:1.5em}.emojify.lock_with_ink_pen{background:url(lock_with_ink_pen.png) no-repeat;background-size:1.5em}.emojify.closed_lock_with_key{background:url(closed_lock_with_key.png) no-repeat;background-size:1.5em}.emojify.key{background:url(key.png) no-repeat;background-size:1.5em}.emojify.bulb{background:url(bulb.png) no-repeat;background-size:1.5em}.emojify.flashlight{background:url(flashlight.png) no-repeat;background-size:1.5em}.emojify.high_brightness{background:url(high_brightness.png) no-repeat;background-size:1.5em}.emojify.low_brightness{background:url(low_brightness.png) no-repeat;background-size:1.5em}.emojify.electric_plug{background:url(electric_plug.png) no-repeat;background-size:1.5em}.emojify.battery{background:url(battery.png) no-repeat;background-size:1.5em}.emojify.calling{background:url(calling.png) no-repeat;background-size:1.5em}.emojify.email{background:url(email.png) no-repeat;background-size:1.5em}.emojify.mailbox{background:url(mailbox.png) no-repeat;background-size:1.5em}.emojify.postbox{background:url(postbox.png) no-repeat;background-size:1.5em}.emojify.bath{background:url(bath.png) no-repeat;background-size:1.5em}.emojify.bathtub{background:url(bathtub.png) no-repeat;background-size:1.5em}.emojify.shower{background:url(shower.png) no-repeat;background-size:1.5em}.emojify.toilet{background:url(toilet.png) no-repeat;background-size:1.5em}.emojify.wrench{background:url(wrench.png) no-repeat;background-size:1.5em}.emojify.nut_and_bolt{background:url(nut_and_bolt.png) no-repeat;background-size:1.5em}.emojify.hammer{background:url(hammer.png) no-repeat;background-size:1.5em}.emojify.seat{background:url(seat.png) no-repeat;background-size:1.5em}.emojify.moneybag{background:url(moneybag.png) no-repeat;background-size:1.5em}.emojify.yen{background:url(yen.png) no-repeat;background-size:1.5em}.emojify.dollar{background:url(dollar.png) no-repeat;background-size:1.5em}.emojify.pound{background:url(pound.png) no-repeat;background-size:1.5em}.emojify.euro{background:url(euro.png) no-repeat;background-size:1.5em}.emojify.credit_card{background:url(credit_card.png) no-repeat;background-size:1.5em}.emojify.money_with_wings{background:url(money_with_wings.png) no-repeat;background-size:1.5em}.emojify.e-mail{background:url(e-mail.png) no-repeat;background-size:1.5em}.emojify.inbox_tray{background:url(inbox_tray.png) no-repeat;background-size:1.5em}.emojify.outbox_tray{background:url(outbox_tray.png) no-repeat;background-size:1.5em}.emojify.envelope{background:url(envelope.png) no-repeat;background-size:1.5em}.emojify.incoming_envelope{background:url(incoming_envelope.png) no-repeat;background-size:1.5em}.emojify.postal_horn{background:url(postal_horn.png) no-repeat;background-size:1.5em}.emojify.mailbox_closed{background:url(mailbox_closed.png) no-repeat;background-size:1.5em}.emojify.mailbox_with_mail{background:url(mailbox_with_mail.png) no-repeat;background-size:1.5em}.emojify.mailbox_with_no_mail{background:url(mailbox_with_no_mail.png) no-repeat;background-size:1.5em}.emojify.door{background:url(door.png) no-repeat;background-size:1.5em}.emojify.smoking{background:url(smoking.png) no-repeat;background-size:1.5em}.emojify.bomb{background:url(bomb.png) no-repeat;background-size:1.5em}.emojify.gun{background:url(gun.png) no-repeat;background-size:1.5em}.emojify.hocho{background:url(hocho.png) no-repeat;background-size:1.5em}.emojify.pill{background:url(pill.png) no-repeat;background-size:1.5em}.emojify.syringe{background:url(syringe.png) no-repeat;background-size:1.5em}.emojify.page_facing_up{background:url(page_facing_up.png) no-repeat;background-size:1.5em}.emojify.page_with_curl{background:url(page_with_curl.png) no-repeat;background-size:1.5em}.emojify.bookmark_tabs{background:url(bookmark_tabs.png) no-repeat;background-size:1.5em}.emojify.bar_chart{background:url(bar_chart.png) no-repeat;background-size:1.5em}.emojify.chart_with_upwards_trend{background:url(chart_with_upwards_trend.png) no-repeat;background-size:1.5em}.emojify.chart_with_downwards_trend{background:url(chart_with_downwards_trend.png) no-repeat;background-size:1.5em}.emojify.scroll{background:url(scroll.png) no-repeat;background-size:1.5em}.emojify.clipboard{background:url(clipboard.png) no-repeat;background-size:1.5em}.emojify.calendar{background:url(calendar.png) no-repeat;background-size:1.5em}.emojify.date{background:url(date.png) no-repeat;background-size:1.5em}.emojify.card_index{background:url(card_index.png) no-repeat;background-size:1.5em}.emojify.file_folder{background:url(file_folder.png) no-repeat;background-size:1.5em}.emojify.open_file_folder{background:url(open_file_folder.png) no-repeat;background-size:1.5em}.emojify.scissors{background:url(scissors.png) no-repeat;background-size:1.5em}.emojify.pushpin{background:url(pushpin.png) no-repeat;background-size:1.5em}.emojify.paperclip{background:url(paperclip.png) no-repeat;background-size:1.5em}.emojify.black_nib{background:url(black_nib.png) no-repeat;background-size:1.5em}.emojify.pencil2{background:url(pencil2.png) no-repeat;background-size:1.5em}.emojify.straight_ruler{background:url(straight_ruler.png) no-repeat;background-size:1.5em}.emojify.triangular_ruler{background:url(triangular_ruler.png) no-repeat;background-size:1.5em}.emojify.closed_book{background:url(closed_book.png) no-repeat;background-size:1.5em}.emojify.green_book{background:url(green_book.png) no-repeat;background-size:1.5em}.emojify.blue_book{background:url(blue_book.png) no-repeat;background-size:1.5em}.emojify.orange_book{background:url(orange_book.png) no-repeat;background-size:1.5em}.emojify.notebook{background:url(notebook.png) no-repeat;background-size:1.5em}.emojify.notebook_with_decorative_cover{background:url(notebook_with_decorative_cover.png) no-repeat;background-size:1.5em}.emojify.ledger{background:url(ledger.png) no-repeat;background-size:1.5em}.emojify.books{background:url(books.png) no-repeat;background-size:1.5em}.emojify.bookmark{background:url(bookmark.png) no-repeat;background-size:1.5em}.emojify.name_badge{background:url(name_badge.png) no-repeat;background-size:1.5em}.emojify.microscope{background:url(microscope.png) no-repeat;background-size:1.5em}.emojify.telescope{background:url(telescope.png) no-repeat;background-size:1.5em}.emojify.newspaper{background:url(newspaper.png) no-repeat;background-size:1.5em}.emojify.football{background:url(football.png) no-repeat;background-size:1.5em}.emojify.basketball{background:url(basketball.png) no-repeat;background-size:1.5em}.emojify.soccer{background:url(soccer.png) no-repeat;background-size:1.5em}.emojify.baseball{background:url(baseball.png) no-repeat;background-size:1.5em}.emojify.tennis{background:url(tennis.png) no-repeat;background-size:1.5em}.emojify.eightball{background:url(eightball.png) no-repeat;background-size:1.5em}.emojify.rugby_football{background:url(rugby_football.png) no-repeat;background-size:1.5em}.emojify.bowling{background:url(bowling.png) no-repeat;background-size:1.5em}.emojify.golf{background:url(golf.png) no-repeat;background-size:1.5em}.emojify.mountain_bicyclist{background:url(mountain_bicyclist.png) no-repeat;background-size:1.5em}.emojify.bicyclist{background:url(bicyclist.png) no-repeat;background-size:1.5em}.emojify.horse_racing{background:url(horse_racing.png) no-repeat;background-size:1.5em}.emojify.snowboarder{background:url(snowboarder.png) no-repeat;background-size:1.5em}.emojify.swimmer{background:url(swimmer.png) no-repeat;background-size:1.5em}.emojify.surfer{background:url(surfer.png) no-repeat;background-size:1.5em}.emojify.ski{background:url(ski.png) no-repeat;background-size:1.5em}.emojify.spades{background:url(spades.png) no-repeat;background-size:1.5em}.emojify.hearts{background:url(hearts.png) no-repeat;background-size:1.5em}.emojify.clubs{background:url(clubs.png) no-repeat;background-size:1.5em}.emojify.diamonds{background:url(diamonds.png) no-repeat;background-size:1.5em}.emojify.gem{background:url(gem.png) no-repeat;background-size:1.5em}.emojify.ring{background:url(ring.png) no-repeat;background-size:1.5em}.emojify.trophy{background:url(trophy.png) no-repeat;background-size:1.5em}.emojify.musical_score{background:url(musical_score.png) no-repeat;background-size:1.5em}.emojify.musical_keyboard{background:url(musical_keyboard.png) no-repeat;background-size:1.5em}.emojify.violin{background:url(violin.png) no-repeat;background-size:1.5em}.emojify.space_invader{background:url(space_invader.png) no-repeat;background-size:1.5em}.emojify.video_game{background:url(video_game.png) no-repeat;background-size:1.5em}.emojify.black_joker{background:url(black_joker.png) no-repeat;background-size:1.5em}.emojify.flower_playing_cards{background:url(flower_playing_cards.png) no-repeat;background-size:1.5em}.emojify.game_die{background:url(game_die.png) no-repeat;background-size:1.5em}.emojify.dart{background:url(dart.png) no-repeat;background-size:1.5em}.emojify.mahjong{background:url(mahjong.png) no-repeat;background-size:1.5em}.emojify.clapper{background:url(clapper.png) no-repeat;background-size:1.5em}.emojify.memo{background:url(memo.png) no-repeat;background-size:1.5em}.emojify.pencil{background:url(pencil.png) no-repeat;background-size:1.5em}.emojify.book{background:url(book.png) no-repeat;background-size:1.5em}.emojify.art{background:url(art.png) no-repeat;background-size:1.5em}.emojify.microphone{background:url(microphone.png) no-repeat;background-size:1.5em}.emojify.headphones{background:url(headphones.png) no-repeat;background-size:1.5em}.emojify.trumpet{background:url(trumpet.png) no-repeat;background-size:1.5em}.emojify.saxophone{background:url(saxophone.png) no-repeat;background-size:1.5em}.emojify.guitar{background:url(guitar.png) no-repeat;background-size:1.5em}.emojify.shoe{background:url(shoe.png) no-repeat;background-size:1.5em}.emojify.sandal{background:url(sandal.png) no-repeat;background-size:1.5em}.emojify.high_heel{background:url(high_heel.png) no-repeat;background-size:1.5em}.emojify.lipstick{background:url(lipstick.png) no-repeat;background-size:1.5em}.emojify.boot{background:url(boot.png) no-repeat;background-size:1.5em}.emojify.shirt{background:url(shirt.png) no-repeat;background-size:1.5em}.emojify.tshirt{background:url(tshirt.png) no-repeat;background-size:1.5em}.emojify.necktie{background:url(necktie.png) no-repeat;background-size:1.5em}.emojify.womans_clothes{background:url(womans_clothes.png) no-repeat;background-size:1.5em}.emojify.dress{background:url(dress.png) no-repeat;background-size:1.5em}.emojify.running_shirt_with_sash{background:url(running_shirt_with_sash.png) no-repeat;background-size:1.5em}.emojify.jeans{background:url(jeans.png) no-repeat;background-size:1.5em}.emojify.kimono{background:url(kimono.png) no-repeat;background-size:1.5em}.emojify.bikini{background:url(bikini.png) no-repeat;background-size:1.5em}.emojify.ribbon{background:url(ribbon.png) no-repeat;background-size:1.5em}.emojify.tophat{background:url(tophat.png) no-repeat;background-size:1.5em}.emojify.crown{background:url(crown.png) no-repeat;background-size:1.5em}.emojify.womans_hat{background:url(womans_hat.png) no-repeat;background-size:1.5em}.emojify.mans_shoe{background:url(mans_shoe.png) no-repeat;background-size:1.5em}.emojify.closed_umbrella{background:url(closed_umbrella.png) no-repeat;background-size:1.5em}.emojify.briefcase{background:url(briefcase.png) no-repeat;background-size:1.5em}.emojify.handbag{background:url(handbag.png) no-repeat;background-size:1.5em}.emojify.pouch{background:url(pouch.png) no-repeat;background-size:1.5em}.emojify.purse{background:url(purse.png) no-repeat;background-size:1.5em}.emojify.eyeglasses{background:url(eyeglasses.png) no-repeat;background-size:1.5em}.emojify.fishing_pole_and_fish{background:url(fishing_pole_and_fish.png) no-repeat;background-size:1.5em}.emojify.coffee{background:url(coffee.png) no-repeat;background-size:1.5em}.emojify.tea{background:url(tea.png) no-repeat;background-size:1.5em}.emojify.sake{background:url(sake.png) no-repeat;background-size:1.5em}.emojify.baby_bottle{background:url(baby_bottle.png) no-repeat;background-size:1.5em}.emojify.beer{background:url(beer.png) no-repeat;background-size:1.5em}.emojify.beers{background:url(beers.png) no-repeat;background-size:1.5em}.emojify.cocktail{background:url(cocktail.png) no-repeat;background-size:1.5em}.emojify.tropical_drink{background:url(tropical_drink.png) no-repeat;background-size:1.5em}.emojify.wine_glass{background:url(wine_glass.png) no-repeat;background-size:1.5em}.emojify.fork_and_knife{background:url(fork_and_knife.png) no-repeat;background-size:1.5em}.emojify.pizza{background:url(pizza.png) no-repeat;background-size:1.5em}.emojify.hamburger{background:url(hamburger.png) no-repeat;background-size:1.5em}.emojify.fries{background:url(fries.png) no-repeat;background-size:1.5em}.emojify.poultry_leg{background:url(poultry_leg.png) no-repeat;background-size:1.5em}.emojify.meat_on_bone{background:url(meat_on_bone.png) no-repeat;background-size:1.5em}.emojify.spaghetti{background:url(spaghetti.png) no-repeat;background-size:1.5em}.emojify.curry{background:url(curry.png) no-repeat;background-size:1.5em}.emojify.fried_shrimp{background:url(fried_shrimp.png) no-repeat;background-size:1.5em}.emojify.bento{background:url(bento.png) no-repeat;background-size:1.5em}.emojify.sushi{background:url(sushi.png) no-repeat;background-size:1.5em}.emojify.fish_cake{background:url(fish_cake.png) no-repeat;background-size:1.5em}.emojify.rice_ball{background:url(rice_ball.png) no-repeat;background-size:1.5em}.emojify.rice_cracker{background:url(rice_cracker.png) no-repeat;background-size:1.5em}.emojify.rice{background:url(rice.png) no-repeat;background-size:1.5em}.emojify.ramen{background:url(ramen.png) no-repeat;background-size:1.5em}.emojify.stew{background:url(stew.png) no-repeat;background-size:1.5em}.emojify.oden{background:url(oden.png) no-repeat;background-size:1.5em}.emojify.dango{background:url(dango.png) no-repeat;background-size:1.5em}.emojify.egg{background:url(egg.png) no-repeat;background-size:1.5em}.emojify.bread{background:url(bread.png) no-repeat;background-size:1.5em}.emojify.doughnut{background:url(doughnut.png) no-repeat;background-size:1.5em}.emojify.custard{background:url(custard.png) no-repeat;background-size:1.5em}.emojify.icecream{background:url(icecream.png) no-repeat;background-size:1.5em}.emojify.ice_cream{background:url(ice_cream.png) no-repeat;background-size:1.5em}.emojify.shaved_ice{background:url(shaved_ice.png) no-repeat;background-size:1.5em}.emojify.birthday{background:url(birthday.png) no-repeat;background-size:1.5em}.emojify.cake{background:url(cake.png) no-repeat;background-size:1.5em}.emojify.cookie{background:url(cookie.png) no-repeat;background-size:1.5em}.emojify.chocolate_bar{background:url(chocolate_bar.png) no-repeat;background-size:1.5em}.emojify.candy{background:url(candy.png) no-repeat;background-size:1.5em}.emojify.lollipop{background:url(lollipop.png) no-repeat;background-size:1.5em}.emojify.honey_pot{background:url(honey_pot.png) no-repeat;background-size:1.5em}.emojify.apple{background:url(apple.png) no-repeat;background-size:1.5em}.emojify.green_apple{background:url(green_apple.png) no-repeat;background-size:1.5em}.emojify.tangerine{background:url(tangerine.png) no-repeat;background-size:1.5em}.emojify.lemon{background:url(lemon.png) no-repeat;background-size:1.5em}.emojify.cherries{background:url(cherries.png) no-repeat;background-size:1.5em}.emojify.grapes{background:url(grapes.png) no-repeat;background-size:1.5em}.emojify.watermelon{background:url(watermelon.png) no-repeat;background-size:1.5em}.emojify.strawberry{background:url(strawberry.png) no-repeat;background-size:1.5em}.emojify.peach{background:url(peach.png) no-repeat;background-size:1.5em}.emojify.melon{background:url(melon.png) no-repeat;background-size:1.5em}.emojify.banana{background:url(banana.png) no-repeat;background-size:1.5em}.emojify.pear{background:url(pear.png) no-repeat;background-size:1.5em}.emojify.pineapple{background:url(pineapple.png) no-repeat;background-size:1.5em}.emojify.sweet_potato{background:url(sweet_potato.png) no-repeat;background-size:1.5em}.emojify.eggplant{background:url(eggplant.png) no-repeat;background-size:1.5em}.emojify.tomato{background:url(tomato.png) no-repeat;background-size:1.5em}.emojify.corn{background:url(corn.png) no-repeat;background-size:1.5em}.emojify.onezeronine{background:url(onezeronine.png) no-repeat;background-size:1.5em}.emojify.house{background:url(house.png) no-repeat;background-size:1.5em}.emojify.house_with_garden{background:url(house_with_garden.png) no-repeat;background-size:1.5em}.emojify.school{background:url(school.png) no-repeat;background-size:1.5em}.emojify.office{background:url(office.png) no-repeat;background-size:1.5em}.emojify.post_office{background:url(post_office.png) no-repeat;background-size:1.5em}.emojify.hospital{background:url(hospital.png) no-repeat;background-size:1.5em}.emojify.bank{background:url(bank.png) no-repeat;background-size:1.5em}.emojify.convenience_store{background:url(convenience_store.png) no-repeat;background-size:1.5em}.emojify.love_hotel{background:url(love_hotel.png) no-repeat;background-size:1.5em}.emojify.hotel{background:url(hotel.png) no-repeat;background-size:1.5em}.emojify.wedding{background:url(wedding.png) no-repeat;background-size:1.5em}.emojify.church{background:url(church.png) no-repeat;background-size:1.5em}.emojify.department_store{background:url(department_store.png) no-repeat;background-size:1.5em}.emojify.european_post_office{background:url(european_post_office.png) no-repeat;background-size:1.5em}.emojify.city_sunrise{background:url(city_sunrise.png) no-repeat;background-size:1.5em}.emojify.city_sunset{background:url(city_sunset.png) no-repeat;background-size:1.5em}.emojify.japanese_castle{background:url(japanese_castle.png) no-repeat;background-size:1.5em}.emojify.european_castle{background:url(european_castle.png) no-repeat;background-size:1.5em}.emojify.tent{background:url(tent.png) no-repeat;background-size:1.5em}.emojify.factory{background:url(factory.png) no-repeat;background-size:1.5em}.emojify.tokyo_tower{background:url(tokyo_tower.png) no-repeat;background-size:1.5em}.emojify.japan{background:url(japan.png) no-repeat;background-size:1.5em}.emojify.mount_fuji{background:url(mount_fuji.png) no-repeat;background-size:1.5em}.emojify.sunrise_over_mountains{background:url(sunrise_over_mountains.png) no-repeat;background-size:1.5em}.emojify.sunrise{background:url(sunrise.png) no-repeat;background-size:1.5em}.emojify.stars{background:url(stars.png) no-repeat;background-size:1.5em}.emojify.statue_of_liberty{background:url(statue_of_liberty.png) no-repeat;background-size:1.5em}.emojify.bridge_at_night{background:url(bridge_at_night.png) no-repeat;background-size:1.5em}.emojify.carousel_horse{background:url(carousel_horse.png) no-repeat;background-size:1.5em}.emojify.rainbow{background:url(rainbow.png) no-repeat;background-size:1.5em}.emojify.ferris_wheel{background:url(ferris_wheel.png) no-repeat;background-size:1.5em}.emojify.fountain{background:url(fountain.png) no-repeat;background-size:1.5em}.emojify.roller_coaster{background:url(roller_coaster.png) no-repeat;background-size:1.5em}.emojify.ship{background:url(ship.png) no-repeat;background-size:1.5em}.emojify.speedboat{background:url(speedboat.png) no-repeat;background-size:1.5em}.emojify.boat{background:url(boat.png) no-repeat;background-size:1.5em}.emojify.sailboat{background:url(sailboat.png) no-repeat;background-size:1.5em}.emojify.rowboat{background:url(rowboat.png) no-repeat;background-size:1.5em}.emojify.anchor{background:url(anchor.png) no-repeat;background-size:1.5em}.emojify.rocket{background:url(rocket.png) no-repeat;background-size:1.5em}.emojify.airplane{background:url(airplane.png) no-repeat;background-size:1.5em}.emojify.helicopter{background:url(helicopter.png) no-repeat;background-size:1.5em}.emojify.steam_locomotive{background:url(steam_locomotive.png) no-repeat;background-size:1.5em}.emojify.tram{background:url(tram.png) no-repeat;background-size:1.5em}.emojify.mountain_railway{background:url(mountain_railway.png) no-repeat;background-size:1.5em}.emojify.bike{background:url(bike.png) no-repeat;background-size:1.5em}.emojify.aerial_tramway{background:url(aerial_tramway.png) no-repeat;background-size:1.5em}.emojify.suspension_railway{background:url(suspension_railway.png) no-repeat;background-size:1.5em}.emojify.mountain_cableway{background:url(mountain_cableway.png) no-repeat;background-size:1.5em}.emojify.tractor{background:url(tractor.png) no-repeat;background-size:1.5em}.emojify.blue_car{background:url(blue_car.png) no-repeat;background-size:1.5em}.emojify.oncoming_automobile{background:url(oncoming_automobile.png) no-repeat;background-size:1.5em}.emojify.car{background:url(car.png) no-repeat;background-size:1.5em}.emojify.red_car{background:url(red_car.png) no-repeat;background-size:1.5em}.emojify.taxi{background:url(taxi.png) no-repeat;background-size:1.5em}.emojify.oncoming_taxi{background:url(oncoming_taxi.png) no-repeat;background-size:1.5em}.emojify.articulated_lorry{background:url(articulated_lorry.png) no-repeat;background-size:1.5em}.emojify.bus{background:url(bus.png) no-repeat;background-size:1.5em}.emojify.oncoming_bus{background:url(oncoming_bus.png) no-repeat;background-size:1.5em}.emojify.rotating_light{background:url(rotating_light.png) no-repeat;background-size:1.5em}.emojify.police_car{background:url(police_car.png) no-repeat;background-size:1.5em}.emojify.oncoming_police_car{background:url(oncoming_police_car.png) no-repeat;background-size:1.5em}.emojify.fire_engine{background:url(fire_engine.png) no-repeat;background-size:1.5em}.emojify.ambulance{background:url(ambulance.png) no-repeat;background-size:1.5em}.emojify.minibus{background:url(minibus.png) no-repeat;background-size:1.5em}.emojify.truck{background:url(truck.png) no-repeat;background-size:1.5em}.emojify.train{background:url(train.png) no-repeat;background-size:1.5em}.emojify.station{background:url(station.png) no-repeat;background-size:1.5em}.emojify.train2{background:url(train2.png) no-repeat;background-size:1.5em}.emojify.bullettrain_front{background:url(bullettrain_front.png) no-repeat;background-size:1.5em}.emojify.bullettrain_side{background:url(bullettrain_side.png) no-repeat;background-size:1.5em}.emojify.light_rail{background:url(light_rail.png) no-repeat;background-size:1.5em}.emojify.monorail{background:url(monorail.png) no-repeat;background-size:1.5em}.emojify.railway_car{background:url(railway_car.png) no-repeat;background-size:1.5em}.emojify.trolleybus{background:url(trolleybus.png) no-repeat;background-size:1.5em}.emojify.ticket{background:url(ticket.png) no-repeat;background-size:1.5em}.emojify.fuelpump{background:url(fuelpump.png) no-repeat;background-size:1.5em}.emojify.vertical_traffic_light{background:url(vertical_traffic_light.png) no-repeat;background-size:1.5em}.emojify.traffic_light{background:url(traffic_light.png) no-repeat;background-size:1.5em}.emojify.warning{background:url(warning.png) no-repeat;background-size:1.5em}.emojify.construction{background:url(construction.png) no-repeat;background-size:1.5em}.emojify.beginner{background:url(beginner.png) no-repeat;background-size:1.5em}.emojify.atm{background:url(atm.png) no-repeat;background-size:1.5em}.emojify.slot_machine{background:url(slot_machine.png) no-repeat;background-size:1.5em}.emojify.busstop{background:url(busstop.png) no-repeat;background-size:1.5em}.emojify.barber{background:url(barber.png) no-repeat;background-size:1.5em}.emojify.hotsprings{background:url(hotsprings.png) no-repeat;background-size:1.5em}.emojify.checkered_flag{background:url(checkered_flag.png) no-repeat;background-size:1.5em}.emojify.crossed_flags{background:url(crossed_flags.png) no-repeat;background-size:1.5em}.emojify.izakaya_lantern{background:url(izakaya_lantern.png) no-repeat;background-size:1.5em}.emojify.moyai{background:url(moyai.png) no-repeat;background-size:1.5em}.emojify.circus_tent{background:url(circus_tent.png) no-repeat;background-size:1.5em}.emojify.performing_arts{background:url(performing_arts.png) no-repeat;background-size:1.5em}.emojify.round_pushpin{background:url(round_pushpin.png) no-repeat;background-size:1.5em}.emojify.triangular_flag_on_post{background:url(triangular_flag_on_post.png) no-repeat;background-size:1.5em}.emojify.jp{background:url(jp.png) no-repeat;background-size:1.5em}.emojify.kr{background:url(kr.png) no-repeat;background-size:1.5em}.emojify.cn{background:url(cn.png) no-repeat;background-size:1.5em}.emojify.us{background:url(us.png) no-repeat;background-size:1.5em}.emojify.fr{background:url(fr.png) no-repeat;background-size:1.5em}.emojify.es{background:url(es.png) no-repeat;background-size:1.5em}.emojify.it{background:url(it.png) no-repeat;background-size:1.5em}.emojify.ru{background:url(ru.png) no-repeat;background-size:1.5em}.emojify.gb{background:url(gb.png) no-repeat;background-size:1.5em}.emojify.uk{background:url(uk.png) no-repeat;background-size:1.5em}.emojify.de{background:url(de.png) no-repeat;background-size:1.5em}.emojify.one{background:url(one.png) no-repeat;background-size:1.5em}.emojify.two{background:url(two.png) no-repeat;background-size:1.5em}.emojify.three{background:url(three.png) no-repeat;background-size:1.5em}.emojify.four{background:url(four.png) no-repeat;background-size:1.5em}.emojify.five{background:url(five.png) no-repeat;background-size:1.5em}.emojify.six{background:url(six.png) no-repeat;background-size:1.5em}.emojify.seven{background:url(seven.png) no-repeat;background-size:1.5em}.emojify.eight{background:url(eight.png) no-repeat;background-size:1.5em}.emojify.nine{background:url(nine.png) no-repeat;background-size:1.5em}.emojify.keycap_ten{background:url(keycap_ten.png) no-repeat;background-size:1.5em}.emojify.onetwothreefour{background:url(onetwothreefour.png) no-repeat;background-size:1.5em}.emojify.zero{background:url(zero.png) no-repeat;background-size:1.5em}.emojify.hash{background:url(hash.png) no-repeat;background-size:1.5em}.emojify.symbols{background:url(symbols.png) no-repeat;background-size:1.5em}.emojify.arrow_backward{background:url(arrow_backward.png) no-repeat;background-size:1.5em}.emojify.arrow_down{background:url(arrow_down.png) no-repeat;background-size:1.5em}.emojify.arrow_forward{background:url(arrow_forward.png) no-repeat;background-size:1.5em}.emojify.arrow_left{background:url(arrow_left.png) no-repeat;background-size:1.5em}.emojify.capital_abcd{background:url(capital_abcd.png) no-repeat;background-size:1.5em}.emojify.abcd{background:url(abcd.png) no-repeat;background-size:1.5em}.emojify.abc{background:url(abc.png) no-repeat;background-size:1.5em}.emojify.arrow_lower_left{background:url(arrow_lower_left.png) no-repeat;background-size:1.5em}.emojify.arrow_lower_right{background:url(arrow_lower_right.png) no-repeat;background-size:1.5em}.emojify.arrow_right{background:url(arrow_right.png) no-repeat;background-size:1.5em}.emojify.arrow_up{background:url(arrow_up.png) no-repeat;background-size:1.5em}.emojify.arrow_upper_left{background:url(arrow_upper_left.png) no-repeat;background-size:1.5em}.emojify.arrow_upper_right{background:url(arrow_upper_right.png) no-repeat;background-size:1.5em}.emojify.arrow_double_down{background:url(arrow_double_down.png) no-repeat;background-size:1.5em}.emojify.arrow_double_up{background:url(arrow_double_up.png) no-repeat;background-size:1.5em}.emojify.arrow_down_small{background:url(arrow_down_small.png) no-repeat;background-size:1.5em}.emojify.arrow_heading_down{background:url(arrow_heading_down.png) no-repeat;background-size:1.5em}.emojify.arrow_heading_up{background:url(arrow_heading_up.png) no-repeat;background-size:1.5em}.emojify.leftwards_arrow_with_hook{background:url(leftwards_arrow_with_hook.png) no-repeat;background-size:1.5em}.emojify.arrow_right_hook{background:url(arrow_right_hook.png) no-repeat;background-size:1.5em}.emojify.left_right_arrow{background:url(left_right_arrow.png) no-repeat;background-size:1.5em}.emojify.arrow_up_down{background:url(arrow_up_down.png) no-repeat;background-size:1.5em}.emojify.arrow_up_small{background:url(arrow_up_small.png) no-repeat;background-size:1.5em}.emojify.arrows_clockwise{background:url(arrows_clockwise.png) no-repeat;background-size:1.5em}.emojify.arrows_counterclockwise{background:url(arrows_counterclockwise.png) no-repeat;background-size:1.5em}.emojify.rewind{background:url(rewind.png) no-repeat;background-size:1.5em}.emojify.fast_forward{background:url(fast_forward.png) no-repeat;background-size:1.5em}.emojify.information_source{background:url(information_source.png) no-repeat;background-size:1.5em}.emojify.ok{background:url(ok.png) no-repeat;background-size:1.5em}.emojify.twisted_rightwards_arrows{background:url(twisted_rightwards_arrows.png) no-repeat;background-size:1.5em}.emojify.repeat{background:url(repeat.png) no-repeat;background-size:1.5em}.emojify.repeat_one{background:url(repeat_one.png) no-repeat;background-size:1.5em}.emojify.new{background:url(new.png) no-repeat;background-size:1.5em}.emojify.top{background:url(top.png) no-repeat;background-size:1.5em}.emojify.up{background:url(up.png) no-repeat;background-size:1.5em}.emojify.cool{background:url(cool.png) no-repeat;background-size:1.5em}.emojify.free{background:url(free.png) no-repeat;background-size:1.5em}.emojify.ng{background:url(ng.png) no-repeat;background-size:1.5em}.emojify.cinema{background:url(cinema.png) no-repeat;background-size:1.5em}.emojify.koko{background:url(koko.png) no-repeat;background-size:1.5em}.emojify.signal_strength{background:url(signal_strength.png) no-repeat;background-size:1.5em}.emojify.u5272{background:url(u5272.png) no-repeat;background-size:1.5em}.emojify.u5408{background:url(u5408.png) no-repeat;background-size:1.5em}.emojify.u55b6{background:url(u55b6.png) no-repeat;background-size:1.5em}.emojify.u6307{background:url(u6307.png) no-repeat;background-size:1.5em}.emojify.u6708{background:url(u6708.png) no-repeat;background-size:1.5em}.emojify.u6709{background:url(u6709.png) no-repeat;background-size:1.5em}.emojify.u6e80{background:url(u6e80.png) no-repeat;background-size:1.5em}.emojify.u7121{background:url(u7121.png) no-repeat;background-size:1.5em}.emojify.u7533{background:url(u7533.png) no-repeat;background-size:1.5em}.emojify.u7a7a{background:url(u7a7a.png) no-repeat;background-size:1.5em}.emojify.u7981{background:url(u7981.png) no-repeat;background-size:1.5em}.emojify.sa{background:url(sa.png) no-repeat;background-size:1.5em}.emojify.restroom{background:url(restroom.png) no-repeat;background-size:1.5em}.emojify.mens{background:url(mens.png) no-repeat;background-size:1.5em}.emojify.womens{background:url(womens.png) no-repeat;background-size:1.5em}.emojify.baby_symbol{background:url(baby_symbol.png) no-repeat;background-size:1.5em}.emojify.no_smoking{background:url(no_smoking.png) no-repeat;background-size:1.5em}.emojify.parking{background:url(parking.png) no-repeat;background-size:1.5em}.emojify.wheelchair{background:url(wheelchair.png) no-repeat;background-size:1.5em}.emojify.metro{background:url(metro.png) no-repeat;background-size:1.5em}.emojify.baggage_claim{background:url(baggage_claim.png) no-repeat;background-size:1.5em}.emojify.accept{background:url(accept.png) no-repeat;background-size:1.5em}.emojify.wc{background:url(wc.png) no-repeat;background-size:1.5em}.emojify.potable_water{background:url(potable_water.png) no-repeat;background-size:1.5em}.emojify.put_litter_in_its_place{background:url(put_litter_in_its_place.png) no-repeat;background-size:1.5em}.emojify.secret{background:url(secret.png) no-repeat;background-size:1.5em}.emojify.congratulations{background:url(congratulations.png) no-repeat;background-size:1.5em}.emojify.m{background:url(m.png) no-repeat;background-size:1.5em}.emojify.passport_control{background:url(passport_control.png) no-repeat;background-size:1.5em}.emojify.left_luggage{background:url(left_luggage.png) no-repeat;background-size:1.5em}.emojify.customs{background:url(customs.png) no-repeat;background-size:1.5em}.emojify.ideograph_advantage{background:url(ideograph_advantage.png) no-repeat;background-size:1.5em}.emojify.cl{background:url(cl.png) no-repeat;background-size:1.5em}.emojify.sos{background:url(sos.png) no-repeat;background-size:1.5em}.emojify.id{background:url(id.png) no-repeat;background-size:1.5em}.emojify.no_entry_sign{background:url(no_entry_sign.png) no-repeat;background-size:1.5em}.emojify.underage{background:url(underage.png) no-repeat;background-size:1.5em}.emojify.no_mobile_phones{background:url(no_mobile_phones.png) no-repeat;background-size:1.5em}.emojify.do_not_litter{background:url(do_not_litter.png) no-repeat;background-size:1.5em}.emojify.non-potable_water{background:url(non-potable_water.png) no-repeat;background-size:1.5em}.emojify.no_bicycles{background:url(no_bicycles.png) no-repeat;background-size:1.5em}.emojify.no_pedestrians{background:url(no_pedestrians.png) no-repeat;background-size:1.5em}.emojify.children_crossing{background:url(children_crossing.png) no-repeat;background-size:1.5em}.emojify.no_entry{background:url(no_entry.png) no-repeat;background-size:1.5em}.emojify.eight_spoked_asterisk{background:url(eight_spoked_asterisk.png) no-repeat;background-size:1.5em}.emojify.eight_pointed_black_star{background:url(eight_pointed_black_star.png) no-repeat;background-size:1.5em}.emojify.heart_decoration{background:url(heart_decoration.png) no-repeat;background-size:1.5em}.emojify.vs{background:url(vs.png) no-repeat;background-size:1.5em}.emojify.vibration_mode{background:url(vibration_mode.png) no-repeat;background-size:1.5em}.emojify.mobile_phone_off{background:url(mobile_phone_off.png) no-repeat;background-size:1.5em}.emojify.chart{background:url(chart.png) no-repeat;background-size:1.5em}.emojify.currency_exchange{background:url(currency_exchange.png) no-repeat;background-size:1.5em}.emojify.aries{background:url(aries.png) no-repeat;background-size:1.5em}.emojify.taurus{background:url(taurus.png) no-repeat;background-size:1.5em}.emojify.gemini{background:url(gemini.png) no-repeat;background-size:1.5em}.emojify.cancer{background:url(cancer.png) no-repeat;background-size:1.5em}.emojify.leo{background:url(leo.png) no-repeat;background-size:1.5em}.emojify.virgo{background:url(virgo.png) no-repeat;background-size:1.5em}.emojify.libra{background:url(libra.png) no-repeat;background-size:1.5em}.emojify.scorpius{background:url(scorpius.png) no-repeat;background-size:1.5em}.emojify.sagittarius{background:url(sagittarius.png) no-repeat;background-size:1.5em}.emojify.capricorn{background:url(capricorn.png) no-repeat;background-size:1.5em}.emojify.aquarius{background:url(aquarius.png) no-repeat;background-size:1.5em}.emojify.pisces{background:url(pisces.png) no-repeat;background-size:1.5em}.emojify.ophiuchus{background:url(ophiuchus.png) no-repeat;background-size:1.5em}.emojify.six_pointed_star{background:url(six_pointed_star.png) no-repeat;background-size:1.5em}.emojify.negative_squared_cross_mark{background:url(negative_squared_cross_mark.png) no-repeat;background-size:1.5em}.emojify.a{background:url(a.png) no-repeat;background-size:1.5em}.emojify.b{background:url(b.png) no-repeat;background-size:1.5em}.emojify.ab{background:url(ab.png) no-repeat;background-size:1.5em}.emojify.o2{background:url(o2.png) no-repeat;background-size:1.5em}.emojify.diamond_shape_with_a_dot_inside{background:url(diamond_shape_with_a_dot_inside.png) no-repeat;background-size:1.5em}.emojify.recycle{background:url(recycle.png) no-repeat;background-size:1.5em}.emojify.end{background:url(end.png) no-repeat;background-size:1.5em}.emojify.on{background:url(on.png) no-repeat;background-size:1.5em}.emojify.soon{background:url(soon.png) no-repeat;background-size:1.5em}.emojify.clock1{background:url(clock1.png) no-repeat;background-size:1.5em}.emojify.clock130{background:url(clock130.png) no-repeat;background-size:1.5em}.emojify.clock10{background:url(clock10.png) no-repeat;background-size:1.5em}.emojify.clock1030{background:url(clock1030.png) no-repeat;background-size:1.5em}.emojify.clock11{background:url(clock11.png) no-repeat;background-size:1.5em}.emojify.clock1130{background:url(clock1130.png) no-repeat;background-size:1.5em}.emojify.clock12{background:url(clock12.png) no-repeat;background-size:1.5em}.emojify.clock1230{background:url(clock1230.png) no-repeat;background-size:1.5em}.emojify.clock2{background:url(clock2.png) no-repeat;background-size:1.5em}.emojify.clock230{background:url(clock230.png) no-repeat;background-size:1.5em}.emojify.clock3{background:url(clock3.png) no-repeat;background-size:1.5em}.emojify.clock330{background:url(clock330.png) no-repeat;background-size:1.5em}.emojify.clock4{background:url(clock4.png) no-repeat;background-size:1.5em}.emojify.clock430{background:url(clock430.png) no-repeat;background-size:1.5em}.emojify.clock5{background:url(clock5.png) no-repeat;background-size:1.5em}.emojify.clock530{background:url(clock530.png) no-repeat;background-size:1.5em}.emojify.clock6{background:url(clock6.png) no-repeat;background-size:1.5em}.emojify.clock630{background:url(clock630.png) no-repeat;background-size:1.5em}.emojify.clock7{background:url(clock7.png) no-repeat;background-size:1.5em}.emojify.clock730{background:url(clock730.png) no-repeat;background-size:1.5em}.emojify.clock8{background:url(clock8.png) no-repeat;background-size:1.5em}.emojify.clock830{background:url(clock830.png) no-repeat;background-size:1.5em}.emojify.clock9{background:url(clock9.png) no-repeat;background-size:1.5em}.emojify.clock930{background:url(clock930.png) no-repeat;background-size:1.5em}.emojify.heavy_dollar_sign{background:url(heavy_dollar_sign.png) no-repeat;background-size:1.5em}.emojify.copyright{background:url(copyright.png) no-repeat;background-size:1.5em}.emojify.registered{background:url(registered.png) no-repeat;background-size:1.5em}.emojify.tm{background:url(tm.png) no-repeat;background-size:1.5em}.emojify.x{background:url(x.png) no-repeat;background-size:1.5em}.emojify.heavy_exclamation_mark{background:url(heavy_exclamation_mark.png) no-repeat;background-size:1.5em}.emojify.bangbang{background:url(bangbang.png) no-repeat;background-size:1.5em}.emojify.interrobang{backgro
gitextract_ku31l5mv/
├── .gitattributes
├── .gitignore
├── .gitmodules
├── Cakefile
├── Gemfile
├── Guardfile
├── LICENSE
├── Makefile
├── Procfile
├── README.md
├── app/
│ ├── controllers/
│ │ ├── account.coffee
│ │ ├── browser.coffee
│ │ ├── editor.coffee
│ │ ├── modal.coffee
│ │ ├── note.item.coffee
│ │ ├── notebook.item.coffee
│ │ ├── panel.coffee
│ │ ├── popover.coffee
│ │ ├── settings.coffee
│ │ ├── sidebar.coffee
│ │ ├── sync.coffee
│ │ └── upgrader.coffee
│ ├── index.coffee
│ ├── init.coffee
│ ├── lib/
│ │ ├── setup.coffee
│ │ └── splitter.js
│ ├── models/
│ │ ├── .gitkeep
│ │ ├── note.coffee
│ │ └── notebook.coffee
│ └── views/
│ ├── .gitkeep
│ ├── note.handlebars
│ └── notebook.handlebars
├── css/
│ ├── font.scss
│ ├── keyframes.scss
│ ├── new.scss
│ └── normalize.scss
├── package.json
├── public/
│ ├── about.html
│ ├── default.json
│ ├── emojify.js
│ ├── handlebars.runtime.js
│ ├── images/
│ │ └── emoji/
│ │ └── emojify.css
│ ├── index.html
│ └── sublime.css
└── src/
├── AboutWindow.coffee
├── Springseed.coffee
└── main.coffee
SYMBOL INDEX (8 symbols across 1 files)
FILE: public/emojify.js
function b (line 3) | function b(a){return" "===a||" "===a||"\r"===a||"\n"===a||""===a}
function c (line 3) | function c(a,b,c){var d=i.createElement("img");d.setAttribute("title",":...
function d (line 3) | function d(a){if(a[1]&&a[2]){var b=a[2];if(l[b])return b}else for(var c=...
function e (line 3) | function e(a,b){return"<img title=':"+b+":' alt=':"+b+":' class='emoji' ...
function f (line 3) | function f(){this.lastEmojiTerminatedAt=-1}
function g (line 3) | function g(a,b){if(!a)return a;b||(b=e);var c=new f;return a.replace(p,f...
function h (line 3) | function h(a){"undefined"==typeof a&&(a=q.only_crawl_id?i.getElementById...
function f (line 3) | function f(){return g.lastEmojiTerminatedAt=j+c,h}
Condensed preview — 47 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (220K chars).
[
{
"path": ".gitattributes",
"chars": 89,
"preview": "* text\n\n# Binary Files.\n*.png binary\n*.jpg binary\n*.gif binary\n*.svg binary\n*.ttf binary\n"
},
{
"path": ".gitignore",
"chars": 273,
"preview": "*~\njavascript/*.js\n.sass-cache\nnode_modules\n*.sublime-project\n*.sublime-workspace\n\ncodekit-config.json\nlibpeerconnection"
},
{
"path": ".gitmodules",
"chars": 130,
"preview": "[submodule \"app/lib/socket.io-client\"]\n\tpath = app/lib/socket.io-client\n\turl = https://github.com/LearnBoost/socket.io-c"
},
{
"path": "Cakefile",
"chars": 2171,
"preview": "\nScrunch = require 'coffee-scrunch'\nuglify = require 'uglify-js'\nserver = require 'node-static'\nhttp = require 'htt"
},
{
"path": "Gemfile",
"chars": 157,
"preview": "source 'https://rubygems.org'\n\n# Automatically run `make` everytime a file is changed, see Guardfile for internals.\ngem "
},
{
"path": "Guardfile",
"chars": 454,
"preview": "# Only used for development!\n# Always run using bundle! (`bundle exec guard`)\n\n\nguard :shell do\n watch(/.*\\.coffee$/) d"
},
{
"path": "LICENSE",
"chars": 1189,
"preview": "The MIT License (MIT)\n\nCopyright (c) 2013-2014 Jono Cooper\nCopyright (c) 2013-2014 Micheal Harker\nCopyright (c) 2013-201"
},
{
"path": "Makefile",
"chars": 694,
"preview": "node_bin = ./node_modules/.bin\n\nspsd = app/init.coffee\natom = $(wildcard src/*.coffee)\ncss = css/new.scss\n\nspsd_out = pu"
},
{
"path": "Procfile",
"chars": 21,
"preview": "web: serveup ./public"
},
{
"path": "README.md",
"chars": 2227,
"preview": "# Springseed\n[](http://waffl"
},
{
"path": "app/controllers/account.coffee",
"chars": 1709,
"preview": "Spine = require 'spine'\n\nclass Account\n constructor: ->\n\n @signin: (username, password) ->\n # Sign in to the Spring"
},
{
"path": "app/controllers/browser.coffee",
"chars": 2715,
"preview": "Spine = require 'spine'\n\n# Models\nNote = require '../models/note.coffee'\nNotebook = require '../models/notebook.coffee'\n"
},
{
"path": "app/controllers/editor.coffee",
"chars": 9680,
"preview": "Spine = require 'spine'\nmarked = require 'marked'\nhljs = require 'highlight.js'\n\n# Models\nNote = require '../models/note"
},
{
"path": "app/controllers/modal.coffee",
"chars": 4462,
"preview": "Spine = require 'spine'\n$ = Spine.$\n\n# Needed.\nNote = require '../models/note.coffee'\nNotebook = require '../models/note"
},
{
"path": "app/controllers/note.item.coffee",
"chars": 1088,
"preview": "Spine = require 'spine'\n\n# Models\nNote = require '../models/note.coffee'\n\nclass NoteItem extends Spine.Controller\n\n ele"
},
{
"path": "app/controllers/notebook.item.coffee",
"chars": 3968,
"preview": "Spine = require 'spine'\n\n# Models\nNotebook = require '../models/notebook.coffee'\n\nclass NotebookItem extends Spine.Contr"
},
{
"path": "app/controllers/panel.coffee",
"chars": 2098,
"preview": "Spine = require 'spine'\n\n# Models\nNote = require '../models/note.coffee'\nNotebook = require '../models/notebook.coffee'\n"
},
{
"path": "app/controllers/popover.coffee",
"chars": 1825,
"preview": "Spine = require 'spine'\n\n# Models\nNotebook = require '../models/notebook.coffee'\n\nModal = require './modal.coffee'\n\nclas"
},
{
"path": "app/controllers/settings.coffee",
"chars": 3248,
"preview": "Spine = require 'spine'\n$ = Spine.$\n\nshell = window.require('shell') if window.require\n\nSync = require './sync.coffee'\nA"
},
{
"path": "app/controllers/sidebar.coffee",
"chars": 3114,
"preview": "Spine = require 'spine'\n\n# Models\nNote = require '../models/note.coffee'\nNotebook = require '../models/notebook.coffee'\n"
},
{
"path": "app/controllers/sync.coffee",
"chars": 21179,
"preview": "Spine = @Spine or require 'spine'\nio = require '../lib/socket.io-client/dist/socket.io.js'\nModel = Spine.Model\n\n# Connec"
},
{
"path": "app/controllers/upgrader.coffee",
"chars": 4666,
"preview": "###############\n# If you're reading this code, and you're like\n# ERMAGERD, WHY IS THIS IDIOT USING NODE.JS, S`NOT EVEN A"
},
{
"path": "app/index.coffee",
"chars": 3937,
"preview": "require './lib/setup.coffee'\nSpine = require 'spine'\n\nshell = window.require('shell') if window.require\nAnalytics = requ"
},
{
"path": "app/init.coffee",
"chars": 130,
"preview": "jQuery = require 'jqueryify'\nexports = this\njQuery ->\n App = require './index.coffee'\n exports.app = new App\n el: "
},
{
"path": "app/lib/setup.coffee",
"chars": 76,
"preview": "require('jqueryify')\n\nrequire('spine')\nrequire '../controllers/sync.coffee'\n"
},
{
"path": "app/lib/splitter.js",
"chars": 6463,
"preview": "(function() {\n\n // Variables\n var parent;\n var panels = {};\n var splitters = {};\n var parentOffset = 0;\n var width"
},
{
"path": "app/models/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "app/models/note.coffee",
"chars": 2648,
"preview": "Spine = require 'spine'\nNotebook = require './notebook.coffee'\n\nclass window.Note extends Spine.Model\n @configure 'Note"
},
{
"path": "app/models/notebook.coffee",
"chars": 775,
"preview": "Spine = require 'spine'\n\nclass window.Notebook extends Spine.Model\n @configure 'Notebook',\n 'name',\n 'categories'"
},
{
"path": "app/views/.gitkeep",
"chars": 0,
"preview": ""
},
{
"path": "app/views/note.handlebars",
"chars": 148,
"preview": "<li id=\"note-{{id}}\" draggable=\"true\" data-notebook=\"{{notebook}}\">\n <h2>{{name}}</h2>\n <time>{{date}} - </time><span"
},
{
"path": "app/views/notebook.handlebars",
"chars": 277,
"preview": "<li class=\"notebook\" id=\"notebook-{{id}}\"><span class=\"name\">{{name}}</span><span class=\"icon\"></span>\n<ul class=\"catego"
},
{
"path": "css/font.scss",
"chars": 692,
"preview": "@font-face {\n font-family: 'YK Thin';\n font-style: normal;\n font-weight: 400;\n src: url(\"YanoneKaffeesatz-Thin.ttf\")"
},
{
"path": "css/keyframes.scss",
"chars": 315,
"preview": "@-webkit-keyframes zoom-in {\n\t0% {\n\t\t-webkit-transform: scale(0.1) transform3d(0,0,0);\n\t}\n\n\t100% {\n\t\t-webkit-transform: "
},
{
"path": "css/new.scss",
"chars": 11122,
"preview": "@import \"font\";\n@import \"normalize\";\n@import \"keyframes\";\n\n$light-color: #ecf0f1;\n$gray-color: #AAAAAA;\n$dark-color: #2d"
},
{
"path": "css/normalize.scss",
"chars": 9035,
"preview": "/*! normalize.css v1.0.1 | MIT License | git.io/normalize */\n\n/* ======================================================="
},
{
"path": "package.json",
"chars": 803,
"preview": "{\n \"main\": \"src/main.js\",\n \"name\": \"Springseed\",\n \"version\": \"2.0.0\",\n \"repository\": {\n \"type\":\"git\","
},
{
"path": "public/about.html",
"chars": 387,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <title>About</title>\n <link rel=\"stylesheet\" type=\"text/css\" href=\"application.cs"
},
{
"path": "public/default.json",
"chars": 703,
"preview": "[\n\t{\n\t\t\"name\": \"Springseed!\",\n\t\t\"content\": \"Springseed 2.0 is here and there's a lot going on. We finally have a Twitter"
},
{
"path": "public/emojify.js",
"chars": 11803,
"preview": "/*! emojify.js - v0.9.2 - \n * Copyright (c) Hassan Khan 2014\n */!function(a){\"use strict\";var b=function(){function b(a)"
},
{
"path": "public/handlebars.runtime.js",
"chars": 10982,
"preview": "/*\n\nCopyright (C) 2011 by Yehuda Katz\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof t"
},
{
"path": "public/images/emoji/emojify.css",
"chars": 70112,
"preview": ".emojify{width:1.5em;height:1.5em;display:inline-block;margin-bottom:-0.25em}.emojify.alien{background:url(alien.png) no"
},
{
"path": "public/index.html",
"chars": 7798,
"preview": "<!DOCTYPE html>\n<html>\n<head>\n <meta charset=utf-8>\n <title>Springseed</title>\n <!-- TODO: Find out how to include th"
},
{
"path": "public/sublime.css",
"chars": 1606,
"preview": "/*\n\nMonokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/\n\n*/\n\npre code {\n display:"
},
{
"path": "src/AboutWindow.coffee",
"chars": 535,
"preview": "# Springseed. Simply awesome note taking.\n# Copyright (c) 2014, Springseed Team\n# All Rights Reserved.\n\napp = require \"a"
},
{
"path": "src/Springseed.coffee",
"chars": 1777,
"preview": "# Springseed. Simply awesome note taking.\n# Copyright (c) 2014, Springseed Team\n# All Rights Reserved.\n\napp = require \"a"
},
{
"path": "src/main.coffee",
"chars": 343,
"preview": "# Springseed. Simply awesome note taking.\n# Copyright (c) 2014, Springseed Team\n# All Rights Reserved.\n\napp = require \"a"
}
]
About this extraction
This page contains the full source code of the byhestia/springseed GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 47 files (204.7 KB), approximately 59.0k tokens, and a symbol index with 8 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.