chocolate
Version:
A full stack Node.js web framework built using Coffeescript
1,077 lines (919 loc) • 174 kB
text/coffeescript
_ = 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/> ' + 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 -