UNPKG

chocolate

Version:

A full stack Node.js web framework built using Coffeescript

1,077 lines (919 loc) 174 kB
_ = require './chocodash' Chocokup = require './chocokup' Highlight = require './highlight' Chocodown = require './chocodown' Newnotes = class @kup: -> id = Newnotes.panels.length @params.taste ?= '' style """ #newnotes-#{id}-note-panel > div { line-height:1.1em; } #newnotes-#{id}-note-panel code { display: inline-block; margin-left: 24px; } #newnotes-#{id}-note-panel ul { margin-left: 0; padding-left: 1em; } #newnotes-#{id}-filter-list-body { margin: 10px; font-size: 0.8em; line-height: 1.4em; } #newnotes-#{id}-note-panel li.note { list-style: none; line-height: 1.5em; } #newnotes-#{id}-note-panel li { list-style: disc; list-style-position: inside; line-height: 1.15em; } #newnotes-#{id}-note-panel li, #newnotes-#{id}-filter-list-body div { cursor: pointer; padding: 0.4em 0; } #newnotes-#{id}-note-panel li.compacted { overflow: hidden; } #newnotes-#{id}-note-panel li.compacted p, #newnotes-#{id}-note-panel li.compacted p + * { display:none; } #newnotes-#{id}-note-panel > .fullscreen-narrow { font-size:1.4em; } #newnotes-#{id}-list-panel { margin: 0; padding: 1em; } #newnotes-#{id}-note-panel select { font-size: 0.8em; } #newnotes-#{id}-filter-input { width: 60%; padding: 4px 10px 4px; font-style: italic; border: none; font-size: 12px; -moz-border-radius: 12px; -webkit-border-radius: 12px; -moz-box-shadow: inset 1px 1px 2px #bbb; -webkit-box-shadow: inset 1px 1px 2px #bbb; box-shadow: inset 1px 1px 2px #bbb; color: #999; } #{unless @params.inherit_selected_class then '#newnotes-' + id + '-note-panel li.selected {background-color:rgba(0,0,0,0.1);}' else ''} .newnotes-priority-Now { color:red; } .newnotes-priority-Tomorrow { color:gold; } .newnotes-priority-Later { color:skyblue; } .newnotes-priority-Done { color:lawngreen; } .newnotes-filtered { background-color: gold; color: black; } #newnotes-#{id}-note-panel .leather, #newnotes-#{id}-note-panel .leather a, #newnotes-#{id}-note-panel .leather select { background-color: #262626; color: rgb(165, 164, 151); text-shadow: 0 1px 3px rgba(171, 128, 112, 0.5) , 0 -2px 4px rgb(0, 0, 0); font-size: 14pt; font-family: Comic Sans MS; } #newnotes-#{id}-note-panel .leather select { font-size: 0.8em; } #newnotes-#{id}-note-panel .leather a { border: 1px solid rgb(26, 23, 23); border-radius: 8px; padding: 0px 10px 0px 8px; box-shadow: inset 2px 2px 12px rgb(19, 17, 17); text-decoration:none; } #newnotes-#{id}-note-panel .leather > .sizer { border: 1px dashed rgba(60, 66, 53, 1); outline: 1px dashed rgba(171, 128, 112, 0.7); margin: 6px; } #newnotes-#{id}-note-panel .stitch > .sizer { margin: 6px; } #newnotes-#{id}-note-panel .paper { background-color: rgb(245, 242, 209); color: rgb(85, 78, 75); text-shadow: none; border-right: 6px double rgb(202, 195, 181); border-top-right-radius: 10px; border-bottom-right-radius: 10px; border-left: 2px solid rgb(119, 118, 112); font-size: 12pt; border-bottom: 1px solid black; background: -webkit-linear-gradient(top, rgb(228, 228, 228) 0%, rgb(245, 242, 209) 8%) 0 57px; background: -moz-linear-gradient(top, rgb(228, 228, 228) 0%, rgb(245, 242, 209) 8%) 0 57px; background: linear-gradient(top, rgb(228, 228, 228) 0%, rgb(245, 242, 209) 8%) 0 57px; -webkit-background-size: 100% 30px; -moz-background-size: 100% 30px; -ms-background-size: 100% 30px; background-size: 100% 30px; background-position-y: 12px; background-attachment: local; } #newnotes-#{id}-note-panel .paper.disconnected { background: rgb(219, 219, 219); } #newnotes-#{id}-note-panel .white-paper { background-color: rgb(247, 246, 232); border-radius: 14px; padding: 10px; color: rgb(85, 78, 75); text-shadow: none; font-size: 12pt; background: -webkit-linear-gradient(top, rgb(228, 228, 228) 0%, rgb(247, 246, 232) 8%) 0 57px; background: -moz-linear-gradient(top, rgb(228, 228, 228) 0%, rgb(247, 246, 232) 8%) 0 57px; background: linear-gradient(top, rgb(228, 228, 228) 0%, rgb(247, 246, 232) 8%) 0 57px; -webkit-background-size: 100% 30px; -moz-background-size: 100% 30px; -ms-background-size: 100% 30px; background-size: 100% 30px; background-position-y: 12px; background-attachment: local; }""" style @params.code_css css_class = @params.box_css_class ? '' css_class = '.' + css_class if css_class isnt '' newnotes_list = -> body "#newnotes-#{id}-list-main", -> box '#.' + css_class, -> body "#newnotes-#{id}-list-body.paper", -> ul "#newnotes-#{id}-list-panel", '' newnotes_filter = -> div -> a href:'#', onclick:"Newnotes.panels[#{id}].stop_filter();", 'Find' text ' : ' input "#newnotes-#{id}-filter-input", onkeyup:"Newnotes.panels[#{id}].on_key_filter(this);", onfocus:"Newnotes.panels[#{id}].on_filter_focus(this);" a href:'#', onclick:"Newnotes.panels[#{id}].init_filter();", 'Clear' newnotes_filter_list = (options) -> hidden = if options?.hidden then '.hidden' else '' body "#newnotes-#{id}-filter-list-main" + hidden, -> box '#.' + css_class, -> body "#newnotes-#{id}-filter-list-body.white-paper", -> newnotes_search = -> add_selector = (name) => select "#newnotes-#{id}-search-#{name.toLowerCase()}-select" + css_class, onchange:"Newnotes.panels[#{id}].on_search_option_selected('#{name}', this);", '' span '#', -> text 'Pin' ; select "#newnotes-#{id}-pin-mode" + css_class, onchange:"Newnotes.panels[#{id}].on_pin_mode_changed(this);", -> option 'on' ; option 'off' span ': ' span "#newnotes-#{id}-pin-list", -> 'None' a href:'#', onclick:"Newnotes.panels[#{id}].pin();", '+' add_selector name for own name of Newnotes.Dimensions newnotes_note_action = -> div -> a href:'#', onclick:"Newnotes.panels[#{id}].move(-1);", '▲' a href:'#', onclick:"Newnotes.panels[#{id}].dent('out');", '◄' a href:'#', onclick:"Newnotes.panels[#{id}].home();", 'Home' a href:'#', onclick:"Newnotes.panels[#{id}].new();", 'New' a href:'#', onclick:"Newnotes.panels[#{id}].sort();", 'Sort' a href:'#', onclick:"Newnotes.panels[#{id}].dent('in');", '►' a href:'#', onclick:"Newnotes.panels[#{id}].move(1);", '▼' div -> a href:'#', onclick:"Newnotes.panels[#{id}].open();", 'Develop' a href:'#', onclick:"Newnotes.panels[#{id}].close();", 'Open' a href:'#', onclick:"Newnotes.panels[#{id}].toggle();", 'Toggle' a href:'#', onclick:"Newnotes.panels[#{id}].compact();", 'Compact' a href:'#', onclick:"Newnotes.panels[#{id}].expand();", 'Expand' newnotes_note_attributes = -> add_selector = (name) => select "#newnotes-#{id}-#{name.toLowerCase()}-select" + css_class, onchange:"Newnotes.panels[#{id}].on_option_selected('#{name}', this);", '' add_selector name for own name of Newnotes.Dimensions newnotes_note_input = -> span -> (if @params.use_ace then panel else textarea)("#newnotes-#{id}-note-text-input", style:'background-color:white;width:0;height:0;', 'data-ace-theme':@params.ace_theme, '') newnotes_note_history = -> div "#newnotes-#{id}-history-body", -> newnotes_note_memory = -> div "#newnotes-#{id}-note-memory", -> text ' ' newnotes_note_message = -> body "#newnotes-#{id}-note-message", -> text ' ' newnotes_log_buttons = (options) -> div style:(if options?.float is yes then "float:right;" else ""), -> input "#newnotes-#{id}-note-logoff.hidden", type:'button', onclick:"Newnotes.panels[#{id}].logoff();", value:'Logoff', style:"background-color:green; color:white;" input "#newnotes-#{id}-note-logon.hidden", type:'button', onclick:"Newnotes.panels[#{id}].switch_login(true);", value:'Logon', style:"background-color:red; color:white;" panel "#newnotes-#{id}-login-panel.hidden", -> form -> text 'Enter your key: ' input "#newnotes-#{id}-login-key", type:'password', '' input type:'submit', onclick:"Newnotes.panels[#{id}].register_key(); return false;", value:'Enter' panel "#newnotes-#{id}-note-panel", -> body "#.#{@params.taste}", -> switch @params.taste when 'fullscreen-wide' @params.in_list = yes box "#.leather", -> box "#.stitch", -> panel proportion:'served', -> panel -> header by:5, -> newnotes_filter() newnotes_search() newnotes_note_memory() body -> newnotes_filter_list() box -> panel -> newnotes_list() panel -> header by:10, -> newnotes_log_buttons float:yes newnotes_note_action() newnotes_note_attributes() body -> panel proportion:'half-served', orientation:'vertical', -> panel -> newnotes_note_history() panel -> newnotes_note_message() newnotes_note_input() when 'fullscreen-narrow' @params.in_list = yes panel '#.hidden', -> newnotes_search() newnotes_note_memory() box "#.leather", -> box "#.stitch", -> panel -> header -> newnotes_filter() body -> panel proportion:'half-served', orientation:'vertical', -> box -> panel -> newnotes_filter_list hidden:yes newnotes_list() panel -> header by:2, -> newnotes_note_action() footer -> newnotes_log_buttons() panel '#.hidden', -> newnotes_note_attributes() newnotes_note_input() else @params.in_list = no panel orientation:'vertical', proportion:'half-served', -> panel -> header by:3, -> body -> newnotes_filter() newnotes_search() newnotes_note_memory() newnotes_filter_list hidden:yes newnotes_list() panel -> header by:3, -> box -> body -> newnotes_note_action() newnotes_note_attributes() body -> box '#.' + css_class, -> body -> newnotes_note_input(yes) script '_id = ' + id + ';' + '_url = ' + JSON.stringify(@params.url) + ';' + '_in_list = ' + @params.in_list + ';' + '_has_keyboard = ' + @params.has_keyboard + ';' + '_use_ace = ' + @params.use_ace + ';' + '_standalone = ' + @params.standalone + ';' coffeescript -> Newnotes.panels.push new Newnotes.HtmlPanel new Newnotes, _id, _url, _in_list, _has_keyboard, _use_ace, _standalone @Dimensions: Priority: values: ['Now', 'Tomorrow', 'Later', 'Done'] Scope: values: ['Me', 'Household', 'Family', 'Friends', 'Community', 'Business', 'State', 'Public'] node: 'free' Action: values: ['Explore', 'Memorize', 'Learn', 'Teach', 'Discuss', 'Do', 'Control', 'Delegate', 'Relax'] node: 'free' Intention: values: ['Know', 'Use', 'Make', 'Manage'] node: 'free' Wish: values: ['No wish', 'Identity', 'Hobbies', 'Education', 'Religion', 'Leisure'] node: 'free' Need: values: ['No need', 'Food', 'Health', 'Clothing', 'Equipment', 'Housing'] node: 'free' Share: values: ['No share', 'Communication', 'Partnership', 'Work', 'Finance', 'Insurance'] node: 'free' @HtmlPanel: class constructor: (@newnotes, @id, @url, @in_list, @has_keyboard, @use_ace, @standalone) -> @editor = new class get: -> if @use_ace then @_.getSession().getValue() else @_.get 'value' set: (value) -> if @use_ace then @_.getSession().setValue(value) else @_.set 'value', value focus: -> @_.focus() if @use_ace then @_.selectAll() else @_.select() clear: -> @current_id = undefined @tree_states = {} @tree_navigate = {} current_id: undefined tree_states: {} tree_navigate : {} exporting: no modified: no waiting: no editing: no DOMEvent.defineKeys '36': 'home' '35': 'end' if @use_ace @editor.use_ace = yes @editor._ = ace.edit "newnotes-#{@id}-note-text-input" @editor._.setShowPrintMargin false @editor._.renderer.setShowGutter false input_box = document.id("newnotes-#{@id}-note-text-input") ace_theme = input_box.attributes['data-ace-theme']?.value @editor._.setTheme ace_theme if ace_theme? Mode = require('ace/mode/markdown').Mode @editor._.getSession().setMode new Mode() @editor._.getSession().on 'change', (e) => return unless @editor.editing @[if @editor.current_id? then 'save' else 'add'] not @in_list @editor._.on 'focus', (e) => @editor.editing = yes if @editor._.container.getStyle('width') isnt '0px' @editor._.on 'blur', (e) => @editor.editing = no @editor._.getSession().setUseWrapMode yes @editor._.getSession().setWrapLimitRange null, null commands = @editor._.commands commands.addCommand name: "save_or_add" bindKey: win: "Return", mac: "Return" exec: (editor) => document = editor.getSession().getDocument() last_row = document.getLength() - 1 last_col = document.getLine(last_row).length range = editor.getSelectionRange() if not @editor.editing or range.isEnd last_row, last_col then @new() else @editor._.insert '\n' commands.addCommand name: "save_or_add_prev" bindKey: win: "Alt-Return", mac: "Alt-Return" exec: => @new(before:on) commands.addCommand name: "export" bindKey: win: "Ctrl-S", mac: "Command-S" exec: => @export() commands.addCommand name: "filter" bindKey: win: "Ctrl-F", mac: "Command-F" exec: => @focus_filter() commands.addCommand name: "indent" bindKey: win: "Tab", mac: "Tab" exec: (editor) => range = editor.getSelectionRange() if range.isStart 0,0 then @dent 'in'; editor.focus() else editor.indent() multiSelectAction: "forEach" commands.addCommand name: "outdent" bindKey: win: "Shift-Tab", mac: "Shift-Tab" exec: (editor) => range = editor.getSelectionRange() if range.isStart 0,0 then @dent 'out'; editor.focus() else editor.blockOutdent() multiSelectAction: "forEach" commands.addCommand name: "home" bindKey: win: "Home", mac: "Home" exec: (editor, args) => @home(); editor.focus() commands.addCommand name: "gotoleft" bindKey: win: "Left", mac: "Left|Ctrl-B" exec: (editor, args) => if not @editor.editing then @modify() ; @editor.focus() else editor.navigateLeft args.times multiSelectAction: "forEach" readOnly: yes commands.addCommand name: "gotoright" bindKey: win: "Right", mac: "Right|Ctrl-F" exec: (editor, args) => if not @editor.editing then @modify() ; @editor.focus() else editor.navigateRight args.times multiSelectAction: "forEach" readOnly: yes commands.addCommand name: "golineup" bindKey: win: "Up", mac: "Up|Ctrl-P" exec: (editor, args) => range = editor.getSelectionRange() if range.isStart 0,0 then @navigate(-1) else editor.navigateUp args.times multiSelectAction: "forEach" readOnly: yes commands.addCommand name: "golinedown" bindKey: win: "Down", mac: "Down|Ctrl-N" exec: (editor, args) => document = editor.getSession().getDocument() last_row = document.getLength() - 1 last_col = document.getLine(last_row).length range = editor.getSelectionRange() if range.isEnd last_row, last_col then @navigate(1) else editor.navigateDown args.times multiSelectAction: "forEach" readOnly: yes commands.addCommand name: "movelineup" bindKey: win: "Ctrl-Up", mac: "Ctrl-Up" exec: (editor, args) => @move(-1); editor.focus(); yes commands.addCommand name: "movelinedown" bindKey: win: "Ctrl-Down", mac: "Ctrl-Down" exec: (editor, args) => @move(1); editor.focus(); yes commands.addCommand name: "compact" bindKey: win: "Ctrl-Shift-Up", mac: "Ctrl-Shift-Up" exec: (editor, args) => @open(); editor.focus(); yes commands.addCommand name: "expand" bindKey: win: "Ctrl-Shift-Down", mac: "Ctrl-Shift-Down" exec: (editor, args) => @close(); editor.focus(); yes commands.addCommand name: "clear_filter" bindKey: win: "Esc", mac: "Esc" exec: (editor, args) => @clear_filter(); editor.focus() commands.addCommand name: "togglepriority_or_split_node_or_newline" bindKey: win: "Ctrl-Return", mac: "Ctrl-Return" exec: (editor, args) => if not @editor.editing then @toggle_priority(); editor.focus() else document = editor.getSession().getDocument() last_row = document.getLength() - 1 last_col = document.getLine(last_row).length range = editor.getSelectionRange() if not range.isEnd(last_row, last_col) then @split(); editor.focus() else @editor._.insert '\n' commands.addCommand name: "toggle_or_newline" bindKey: win: "Shift-Return", mac: "Shift-Return" exec: (editor, args) => document = editor.getSession().getDocument() last_row = document.getLength() - 1 last_col = document.getLine(last_row).length range = editor.getSelectionRange() if range.isStart(0,0) and range.isEnd(last_row, last_col) @toggle() @list() editor.focus() yes else @editor._.insert '\n' commands.addCommand name: "pin" bindKey: win: "Ctrl-Shift-Return", mac: "Ctrl-Shift-Return" exec: (editor, args) => if not @editor.editing then @pin(); editor.focus() else @editor.use_ace = no @editor._ = document.id("newnotes-#{@id}-note-text-input") @editor._.addEvent 'keyup', (e) => return unless @editor.editing @[if @editor.current_id? then 'save' else 'add'] no @editor._.addEvent 'focus', (e) => @editor.editing = yes if @editor._.container.getStyle('width') isnt '0px' @editor._.addEvent 'blur', (e) => @editor.editing = no @list() @editor._.container = @editor._ @editor._.container.dispose() start = => @check_connected() setInterval ( => @check_connected() ), 1 * 60 * 1000 @reload() #Check if a new cache is available on page load. cache = window.applicationCache if @standalone window.addEventListener 'load', (e) -> cache.addEventListener 'updateready', (e) -> if cache.status is cache.UPDATEREADY # Browser downloaded a new app cache. # Swap it in and reload the page to get the new hotness. cache.swapCache() window.location.reload() else # Manifest didn't changed. Nothing new to server. , no cache.addEventListener 'cached', ((e) -> start()), no cache.addEventListener 'noupdate', ((e) -> start()), no cache.addEventListener 'error', ((e) -> start()), no , no else start() current_get : -> @newnotes.data.by.id[@editor.current_id] current_set : (id) -> @editor.current_id = id @newnotes.path = [] if id? note = @newnotes.data.by.id[id] @newnotes.path.push id while (note = note.parent)? then @newnotes.path.unshift note.uuid register_key : -> if @url?.register_key? new Request data: key:document.id("newnotes-#{@id}-login-key").value noCache: yes url: @url.register_key onSuccess: (data) => @switch_login off @check_connected() onFailure: (xhr) -> .post() logoff : -> if @url?.forget_keys? new Request url: @url.forget_keys noCache: yes onSuccess: (data) => @switch_login on @check_connected() onFailure: (xhr) -> .post() switch_login: (switched) => if document.id("newnotes-#{@id}-#{if switched is on then 'login' else 'note'}-panel").hasClass 'hidden' document.id("newnotes-#{@id}-note-panel")["#{if switched is on then 'add' else 'remove'}Class"] 'hidden' document.id("newnotes-#{@id}-login-panel")["#{if switched is on then 'remove' else 'add'}Class"] 'hidden' @reload() unless switched data_modified_date: undefined get_data_modified_date: -> if @is_connected new Request url: @url.modified_date noCache: yes onSuccess: (responseText) => if responseText isnt '' value = parseInt responseText @data_modified_date ?= value if value > @data_modified_date @data_modified_date = value window.location.reload() onFailure: (xhr) -> .get() is_connected: false check_connected: -> switch_log = (switched) => @is_connected = not switched document.id("newnotes-#{@id}-note-log#{if switched is on then 'on' else 'off'}").removeClass 'hidden' document.id("newnotes-#{@id}-note-log#{if switched is on then 'off' else 'on'}").addClass 'hidden' if @is_connected then setTimeout (=> @get_data_modified_date.call @), 100 document.id("newnotes-#{@id}-list-body")[(if switched then 'add' else 'remove') + 'Class'] 'disconnected' if @url?.connected? new Request url: @url.connected noCache: yes onSuccess: (data) -> if data is "connected" then switch_log off else switch_log on onFailure: (xhr) -> switch_log on .get() 'new': (option) -> @remove_if_empty() note = @newnotes.add '', (if option?.at_end then undefined else @current_get()), (if option?.before then 0 else 1) @on_tagging_change note @current_set note.uuid @editor.set '' @on_note_modified() @modify() add: -> note = @newnotes.add @editor.get() @on_tagging_change note @current_set note.uuid @list() @on_note_modified() split: -> document = @editor._.getSession().getDocument() last_row = document.getLength() - 1 last_col = document.getLine(last_row).length pos = @editor._.getCursorPosition() Range = require('ace/range').Range del_range = new Range pos.row, pos.column, last_row, last_col del_text = @editor._.getSession().getTextRange del_range this.editor._.getSession().remove del_range @new() this.editor._.setValue del_text sort: -> note = @current_get() @newnotes.sort note @list() @on_note_modified() dent: (direction) -> note = @current_get() parent = note.parent @newnotes[direction + 'dent'] note result = [] result.push @on_tagging_change(note) result.push @on_tagging_change(parent, yes) if parent? unless yes in result @list() @on_note_modified() move: (step) -> @newnotes.move @current_get(), step @list() @on_note_modified() remove_if_empty: -> note = @current_get() if note?.title is '' and note.subs.length is 0 @newnotes.remove note @on_tagging_change note @newnotes.path = [] @on_note_modified() yes else no set_dimension: (option, value, note, dolist, propagate) -> dolist ?= yes propagate ?= yes if note? @save no, option, value, note @on_tagging_change note, undefined, no if propagate @on_note_modified() @list() if dolist @newnotes.dimensions[option].value = value toggle_priority: (item, index) -> id = if item? then item.attributes['data-uuid'].value else @current_get().uuid note = @newnotes.data.by.id[id] if note?.subs.length > 0 then return dimension = 'Priority' index ?= Newnotes.Dimensions.Priority.values.indexOf(note.dimensions[dimension]) + 1 if index >= Newnotes.Dimensions[dimension].values.length then index = 0 value = Newnotes.Dimensions[dimension].values[index] @set_dimension dimension, value, note document.id("newnotes-#{@id}-#{dimension.toLowerCase()}-select").value = value @on_note_modified() @list() @editor.focus() save: (dolist, option, value, note) -> note ?= @current_get() unless option? @newnotes.modify note, @editor.get() else @newnotes.modify note, option, value @list() if dolist isnt no @on_note_modified() unless window.event.type is 'click' load: (callback) -> if @url?.load? new Request.JSON url: @url.load onSuccess: (data) => if data? for own uuid, note of data.by.id data.by.id[uuid] = Newnotes.Note.create note for own uuid, note of data.by.id Newnotes.Note.awake note, data data.by.root[i] = data.by.id[uuid] for uuid, i in data.by.root data.by[name][key][i] = data.by.id[uuid] for uuid, i in data.by[name][key] for own key of data.by[name] for own name of Newnotes.Dimensions @newnotes.data = data if data.by.root.length is 0 then @newnotes.add 'new Note !' callback?() onFailure: (xhr) -> .get() reload: -> @load => @list() @fill_search_selector() for own name of Newnotes.Dimensions document.id("newnotes-#{@id}-#{name.toLowerCase()}-select").set 'html', @html_options "#{name}" @editor._.resize() @editor.focus() export: -> if @remove_if_empty() then @list() data = Newnotes.new_data() for own uuid, note of @newnotes.data.by.id data.by.id[uuid] = note.export() data.by.root.push item.uuid for item in @newnotes.data.by.root (data.by[name][key] ?= []).push item.uuid for item in @newnotes.data.by[name][key] for own key of @newnotes.data.by[name] for own name of Newnotes.Dimensions if @url?.save? @editor.exporting = yes new Request.JSON url: @url.save onSuccess: => @editor.exporting = no @data_modified_date = undefined setTimeout (=> @get_data_modified_date.call @), 100 onFailure: (xhr) -> .post JSON.stringify data display_message: (message) -> new Element('div').set('html', ('<p>' + new Date().toString 'dd/MM/yyyy HH:mm:ss') + '<br/>&nbsp;&nbsp;' + message + '</p>').inject document.id("newnotes-#{@id}-note-message"), 'top' list: -> kup = -> nav = @params.editor.tree_navigate nav.previous = nav.current = nav.next = undefined kup = (selection) -> selection ?= @params.selection if @params.filter.text? if selection.filtered.first? if @params.filter.last_filter isnt @params.filter.text @params.editor.current_id = @params.filter.first = selection.filtered.first @params.filter.last_filter = @params.filter.text else if @params.filter.last_filter? then @params.filter.last_filter = @params.filter.first = undefined regexp = new RegExp(@params.filter.text, 'ig') if @params.filter.text? for item, index in selection if @params.editor.current_id is undefined then @params.editor.current_id = item.note.uuid @params.editor.tree_states[item.note.uuid] ?= node:no, closed:(if item.note.subs.length > 0 then yes else no), compacted:(if item.filtered then no else yes) selected = @params.editor.current_id is item.note.uuid state = @params.editor.tree_states[item.note.uuid] if state.new_closed_state? @params.editor.new_closed_subs_state = state.new_closed_state if state.new_compacted_state? @params.editor.new_compacted_subs_state = state.new_compacted_state if @params.editor.new_closed_subs_state? state.closed = if state.new_closed_state? then no else @params.editor.new_closed_subs_state if @params.editor.new_compacted_subs_state? state.compacted = @params.editor.new_compacted_subs_state is_compacted = no str_title = item.note.title str_content = undefined if state.compacted compacted_title = Newnotes.Note.compact_title item.note, with_content:yes str_title = compacted_title.title str_content = compacted_title.content is_compacted = compacted_title.is_compacted str_title = new Chocodown.converter().makeHtml str_title if str_title[0..2] is '<p>' then str_title = str_title[3..] if str_title[-4..] is '</p>' then str_title = str_title[...-4] if str_content? str_content = new Chocodown.converter().makeHtml str_content if str_content[0..2] is '<p>' then str_content = str_content[3..] if str_content[-4..] is '</p>' then str_content = str_content[...-4] if item.filtered then str_title = str_title.replace regexp, "<span class='newnotes-filtered'>$&</span>" state.filtered = item.filtered if item.subs.filtered.exists then state.closed = false state.node = (item.note.subs.length > 0) if item.filtered or not @params.filter.text? nav.next = item.note.uuid unless nav.next? or not nav.current? nav.current = item.note.uuid if selected nav.previous = item.note.uuid unless nav.current? bullet = if state.node then (if state.closed then '+' else '-') else '>' bullet_char = if item.subs.length > 0 then '•' else '●' priority = if item.note.dimensions.Priority? then "<span class='newnotes-priority-#{item.note.dimensions.Priority}'>#{bullet_char}</span>" else '' classes = ['note'] classes.push 'selected' if selected classes.push 'compacted' if is_compacted li 'class': classes.join(' '), 'data-uuid':item.note.uuid, onclick:"Newnotes.panels[#{@params.id}].modify(this);", -> span 'data-uuid':item.note.uuid, onclick:"Newnotes.panels[#{@params.id}].toggle(this);Newnotes.panels[#{@params.id}].modify(this, {no_focus:true});window.event.stopPropagation();", bullet span 'data-uuid':item.note.uuid, onclick:"Newnotes.panels[#{@params.id}].toggle_priority(this);Newnotes.panels[#{@params.id}].modify(this, {no_focus:true});window.event.stopPropagation();", (' ' + priority) text ' ' + str_title if is_compacted then a href:'#', 'data-uuid':item.note.uuid, onclick:"Newnotes.panels[#{@params.id}].expand(this);", '...' p ' ' + str_content if str_content? if item.subs.length > 0 and not state.closed then ul -> kup.call @, item.subs if state.new_closed_state? state.new_closed_state = @params.editor.new_closed_subs_state = undefined if state.new_compacted_state? state.new_compacted_state = @params.editor.new_compacted_subs_state = undefined kup.call @ is_editing = @editor.editing list_body = document.id("newnotes-#{@id}-list-body") list_panel = document.id("newnotes-#{@id}-list-panel") list_panel.setStyle 'display', '' list_panel.set 'html', new Chocokup.Panel({editor:@editor, id:@id, selection:@newnotes.select(undefined, @current_get()), filter:@newnotes.filter}, kup).render() @editor.editing = is_editing new_element = list_body.getElement "li[data-uuid=#{@editor.current_id}]" if new_element? while new_element.getPosition(list_body).y + new_element.getSize().y - 1 > list_body.getSize().y / 2 + new_element.getSize().y / 2 scroll_pos = list_body.getScroll().y list_body.scrollTo 0, list_body.getScroll().y + (new_element.getPosition(list_body).y + new_element.getSize().y - list_body.getSize().y / 2 - new_element.getSize().y / 2) if list_body.getScroll().y is scroll_pos then break while new_element.getPosition(list_body).y + 1 < list_body.getSize().y / 2 - new_element.getSize().y / 2 scroll_pos = list_body.getScroll().y list_body.scrollTo 0, list_body.getScroll().y - (list_body.getSize().y / 2 - new_element.getSize().y / 2 - new_element.getPosition(list_body).y) if list_body.getScroll().y is scroll_pos then break if @in_list is no @editor._.container.setStyle 'width', '100%' @editor._.container.setStyle 'height', '100%' else if is_editing if @has_keyboard @editor._.container.setStyle 'position', 'relative' @editor._.container.setStyle 'left', '24px' @editor._.container.setStyle 'top', '-14px' @editor._.container.setStyle 'margin-bottom', '-19px' @editor._.container.setStyle 'width', new_element.getSize().x - 30 line_count = 1 + ((@current_get().title.match /\n/g)?.length ? 0) line_height = parseFloat(new_element.getComputedStyle('line-height')) new_height = Math.max line_count * line_height , new_element.getSize().y @editor._.container.setStyle 'height', "#{new_height}px" new_element.setStyle 'height', "#{new_height}px" @editor._.container.inject new_element, 'after' else list_panel.setStyle 'display', 'none' @editor._.container.setStyle 'position', 'absolute' @editor._.container.setStyle 'left', '0px' @editor._.container.setStyle 'top', '0px' @editor._.container.setStyle 'width', '100%' @editor._.container.setStyle 'height', '100%' @editor._.container.setStyle 'font-size', '1em' @editor._.container.inject list_body, 'after' new_element.setStyle 'overflow', 'hidden' new_element.setStyle 'width', '20px' new_element.setStyle 'height', '7px' @editor._.resize() if @use_ace else if @use_ace @editor._.container.setStyle 'position', 'absolute' @editor._.container.setStyle 'left', 0 @editor._.container.setStyle 'top', 0 @editor._.container.setStyle 'width', 0 @editor._.container.setStyle 'height', 0 @editor._.container.inject new_element, 'after' else @editor._.container.dispose() modify: (item, options) -> id = if item? then item.attributes['data-uuid'].value else @editor.current_id if id? and id is @editor.current_id or not @in_list @editor.editing = yes unless options?.no_focus else @remove_if_empty() @current_set id note = @current_get() @editor.set note.title for own name of Newnotes.Dimensions value = note.dimensions[name] document.id("newnotes-#{@id}-#{name.toLowerCase()}-select").value = value @newnotes.dimensions[name].value = value @list() unless options?.no_focus then @editor.focus() open: (item) -> id = if item? then item.attributes['data-uuid'].value else @current_get()?.uuid if id? @editor.tree_states[id].new_closed_state = no @editor.tree_states[id].new_compacted_state = no @list() close: (item) -> id = if item? then item.attributes['data-uuid'].value else @current_get()?.uuid if id? note = @newnotes.data.by.id[id] @editor.tree_states[id].new_closed_state = yes @editor.tree_states[id].new_compacted_state = if note.subs.length > 0 then yes else no @list() expand: (item) -> id = if item? then item.attributes['data-uuid'].value else @current_get()?.uuid if id? @editor.tree_states[id].new_compacted_state = no @list() compact: (item) -> id = if item? then item.attributes['data-uuid'].value else @current_get()?.uuid if id? @editor.tree_states[id].new_compacted_state = yes @list() home: -> @switch_filter_list off @clear_filter() @clear_pin() @editor.clear() @list() @editor.focus() navigate: (step) -> dest_id = @editor.tree_navigate[if step > 0 then 'next' else 'previous'] item = document.id("newnotes-#{@id}-list-body").getElement "li[data-uuid=#{dest_id}]" if dest_id? ( @editor.editing = no ; @modify item; ) if item? toggle: (item) -> id = if item? then item.attributes['data-uuid'].value else @current_get().uuid note = @newnotes.data.by.id[id] if @editor.tree_states[id]?.node @editor.tree_states[id].closed = not @editor.tree_states[id].closed else @editor.tree_states[id].compacted = not @editor.tree_states[id].compacted @list() focus_filter: -> item = document.id "newnotes-#{@id}-filter-input" item.focus() clear_filter: -> item = document.id "newnotes-#{@id}-filter-input" item.value = '' @filter item @list() init_filter: -> @clear_filter() @focus_filter() stop_filter: -> @newnotes.filter.text = undefined @list() start_filter: -> item = document.id "newnotes-#{@id}-filter-input" if item.value isnt @newnotes.filter.text and item.value isnt "" @newnotes.filter.text = item.value @list() filter: (item) -> @newnotes.filter.set if item.value isnt '' then item.value else undefined @current_set undefined if item.value isnt '' @filter_list() @list() filter_list: -> kup = -> kup = (selection) -> selection ?= @params.selection for item, index in selection if item.filtered str = item.note.title regexp = new RegExp( @params.filter.text, "ig" ) while match = regexp.exec str i = match.index len = @params.filter.text.length close_size = 40 trail_left = if i -