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 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 [![Stories in Ready](https://badge.waffle.io/byhestia/springseed.svg?label=ready&title=Ready)](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 `/electron ` where `` 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 Copyright © 2013-2014 [Caffeinated Code][3]
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 = '
  • All Notes
  • ' for category, i in @notebook.categories str += "
  • #{category}
  • " @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 # .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 ================================================
  • {{name}}

    {{{excerpt}}}
  • ================================================ FILE: app/views/notebook.handlebars ================================================
  • {{name}}
    • All Notes
    • {{#each categories}}
    • {{this}}
    • {{/each}}
  • ================================================ 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 ================================================ About

    Springseed 2.0

    Copyright © 2012-2014 Springseed.

    http://getspringseed.com/

    ================================================ 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"}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": ">", '"': """, "'": "'", "`": "`" }; 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{background:url(interrobang.png) no-repeat;background-size:1.5em}.emojify.o{background:url(o.png) no-repeat;background-size:1.5em}.emojify.heavy_multiplication_x{background:url(heavy_multiplication_x.png) no-repeat;background-size:1.5em}.emojify.heavy_plus_sign{background:url(heavy_plus_sign.png) no-repeat;background-size:1.5em}.emojify.heavy_minus_sign{background:url(heavy_minus_sign.png) no-repeat;background-size:1.5em}.emojify.heavy_division_sign{background:url(heavy_division_sign.png) no-repeat;background-size:1.5em}.emojify.white_flower{background:url(white_flower.png) no-repeat;background-size:1.5em}.emojify.onehundred{background:url(onehundred.png) no-repeat;background-size:1.5em}.emojify.heavy_check_mark{background:url(heavy_check_mark.png) no-repeat;background-size:1.5em}.emojify.ballot_box_with_check{background:url(ballot_box_with_check.png) no-repeat;background-size:1.5em}.emojify.radio_button{background:url(radio_button.png) no-repeat;background-size:1.5em}.emojify.link{background:url(link.png) no-repeat;background-size:1.5em}.emojify.curly_loop{background:url(curly_loop.png) no-repeat;background-size:1.5em}.emojify.wavy_dash{background:url(wavy_dash.png) no-repeat;background-size:1.5em}.emojify.part_alternation_mark{background:url(part_alternation_mark.png) no-repeat;background-size:1.5em}.emojify.trident{background:url(trident.png) no-repeat;background-size:1.5em}.emojify.black_square{background:url(black_square.png) no-repeat;background-size:1.5em}.emojify.white_square{background:url(white_square.png) no-repeat;background-size:1.5em}.emojify.white_check_mark{background:url(white_check_mark.png) no-repeat;background-size:1.5em}.emojify.black_square_button{background:url(black_square_button.png) no-repeat;background-size:1.5em}.emojify.white_square_button{background:url(white_square_button.png) no-repeat;background-size:1.5em}.emojify.black_circle{background:url(black_circle.png) no-repeat;background-size:1.5em}.emojify.white_circle{background:url(white_circle.png) no-repeat;background-size:1.5em}.emojify.red_circle{background:url(red_circle.png) no-repeat;background-size:1.5em}.emojify.large_blue_circle{background:url(large_blue_circle.png) no-repeat;background-size:1.5em}.emojify.large_blue_diamond{background:url(large_blue_diamond.png) no-repeat;background-size:1.5em}.emojify.large_orange_diamond{background:url(large_orange_diamond.png) no-repeat;background-size:1.5em}.emojify.small_blue_diamond{background:url(small_blue_diamond.png) no-repeat;background-size:1.5em}.emojify.small_orange_diamond{background:url(small_orange_diamond.png) no-repeat;background-size:1.5em}.emojify.small_red_triangle{background:url(small_red_triangle.png) no-repeat;background-size:1.5em}.emojify.small_red_triangle_down{background:url(small_red_triangle_down.png) no-repeat;background-size:1.5em}.emojify.shipit{background:url(shipit.png) no-repeat;background-size:1.5em} ================================================ FILE: public/index.html ================================================ Springseed
    notes
      0
      ================================================ FILE: public/sublime.css ================================================ /* Monokai Sublime style. Derived from Monokai by noformnocontent http://nn.mit-license.org/ */ pre code { display: block; padding: 0.5em; background: #23241f; color: #f8f8f2; } pre .tag, pre code { color: #f8f8f2; } pre .keyword, pre .function, pre .literal, pre .change, pre .winutils, pre .flow, pre .lisp .title, pre .clojure .built_in, pre .nginx .title, pre .tex .special { color: #66d9ef; } pre .variable, pre .params { color: #fd9720; } pre .constant { color: #66d9ef; } pre .title, pre .class .title, pre .css .class { color: #a6e22e; } pre .attribute, pre .symbol, pre .symbol .string, pre .tag .title, pre .value, pre .css .tag { color: #f92672; } pre .number, pre .preprocessor, pre .regexp { color: #ae81ff; } pre .tag .value, pre .string, pre .css .id, pre .subst, pre .haskell .type, pre .ruby .class .parent, pre .built_in, pre .sql .aggregate, pre .django .template_tag, pre .django .variable, pre .smalltalk .class, pre .django .filter .argument, pre .smalltalk .localvars, pre .smalltalk .array, pre .attr_selector, pre .pseudo, pre .addition, pre .stream, pre .envvar, pre .apache .tag, pre .apache .cbracket, pre .tex .command, pre .prompt { color: #e6db74; } pre .comment, pre .javadoc, pre .java .annotation, pre .python .decorator, pre .template_comment, pre .pi, pre .doctype, pre .deletion, pre .shebang, pre .apache .sqbracket, pre .tex .formula { color: #75715e; } pre .coffeescript .javascript, pre .javascript .xml, pre .tex .formula { opacity: 0.5; } pre .xml .javascript, pre .xml .vbscript, pre .xml .css, pre .xml .cdata { opacity: 0.5; } ================================================ FILE: src/AboutWindow.coffee ================================================ # Springseed. Simply awesome note taking. # Copyright (c) 2014, Springseed Team # All Rights Reserved. app = require "app" BrowserWindow = require "browser-window" class AboutWindow constructor: (devtools) -> @window = new BrowserWindow 'width': 400 'height': 300 'center': true 'resizable': false 'title': "About Springseed" @window.loadUrl "file://#{__dirname}/../public/about.html" @window.on "closed", -> @window = null # Dereference the window. module.exports = AboutWindow ================================================ FILE: src/Springseed.coffee ================================================ # Springseed. Simply awesome note taking. # Copyright (c) 2014, Springseed Team # All Rights Reserved. app = require "app" Menu = require "menu" BrowserWindow = require "browser-window" AboutWindow = require "./AboutWindow" # We need a global reference of the window because Node.js may GC if if we don't. window = null class SpringseedWindow constructor: -> window = new BrowserWindow 'width': 1024 'height': 600 'min-width': 500 'min-height': 300 'center': true 'title': "Springseed" console.log __dirname window.loadUrl "file://"+__dirname+"/../public/index.html" window.on "closed", -> window = null # Dereference the window. if process.platform is "darwin" @osxMenus() else @linuxMenus() osxMenus: -> tmpl = [{ label: "Springseed", submenu: [{ label: "Developer Tools", accelerator: "Control+Alt+I" click: => window.openDevTools() }, { label: "About Springseed", click: => new AboutWindow() }, { label: "Quit", accelerator: "Command+Q", click: => app.quit() }] }] linuxMenus: -> tmpl = [{ label: "Springseed", submenu: [{ label: "Developer Tools", accelerator: "Control+Alt+I" click: => window.openDevTools() }, { label: "About Springseed", click: => new AboutWindow() }, { label: "Quit", accelerator: "Ctrl+Q", click: => app.quit() }] }] menu = Menu.buildFromTemplate tmpl Menu.setApplicationMenu menu module.exports = SpringseedWindow ================================================ FILE: src/main.coffee ================================================ # Springseed. Simply awesome note taking. # Copyright (c) 2014, Springseed Team # All Rights Reserved. app = require "app" SpringseedWindow = require './Springseed' app.on 'ready', -> window = new SpringseedWindow() app.on 'window-all-closed', -> app.quit() app.on 'activate-with-no-open-windows', -> window = new SpringseedWindow()