UNPKG

chocolate

Version:

A full stack Node.js web framework built using Coffeescript

1,061 lines (954 loc) 110 kB
# **Immediate** programing environment Chocokup = require '../general/chocokup' Doccolate = require '../general/doccolate' exports.interface = (__) -> exports.enter(__) exports.enter = (__) -> new Chocokup.App 'Chocolate Studio', with_coffee:yes, doccolate_style:Doccolate.options.default_style, snippets:__.config.get('snippets'), -> style type:"text/css" media:"screen" """ .choco_title { font-family: Times new roman; font-size: 17px; font-style: italic; } body { font-family:Lucida Console,monospace; font-size:9pt; line-height: 12pt; } a.selected { border-bottom: 1px double; } li.selected { background-color:rgba(0,0,0,0.1); } .blurred { -webkit-filter: blur(3px); -moz-filter: blur(3px); -o-filter: blur(3px); -ms-filter: blur(3px); filter: blur(3px); } .chocoblack code, .chocomilk code { font-size: 12px; padding: 0 0.2em; border: 1px solid #262836; background: rgb(248, 248, 248); display: inline-block; -webkit-box-shadow: 0 0 4px rgb(92, 75, 68); -moz-box-shadow: 0 0 4px rgb(92, 75, 68); -o-box-shadow: 0 0 4px rgb(92, 75, 68); box-shadow: 0 0 4px rgb(92, 75, 68); padding: 2px 4px; border-bottom-left-radius: 7px; border-top-right-radius: 8px; } .panel.frame > .sizer { outline: inherit; } .white { background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 0.6); } .white .sizer { outline: none; } .chocomilk { background-color:rgb(245, 242, 232); border: 2px solid rgba(255, 204, 0, 0.3); color: rgb(92, 75, 68); } .chocomilk a, .chocomilk select { color: rgb(92, 75, 68); } .white-background { background-color: white; } .transparent-background { background-color: transparent; } .round.frame > .sizer { font-size: 9pt; line-height: 12pt; } .padded { padding: 8px; } .darkTheme .ace_editor.dimmed .ace_scroller { background-color: dimgrey; } .lightTheme .ace_editor.dimmed .ace_scroller { background-color: lightgrey; } .bite_style { height: 7px; width: 7px; border-right: 1px solid white; border-bottom: 1px solid white; } .ace_editor { height: 100%; } #editor .ace_text-layer, #editor .ace_gutter { font-size: 9pt; } #specolate-panel-editor .ace_text-layer, #specolate-panel-editor .ace_gutter { font-size: 9pt; } #experiment-script-panel-editor .ace_text-layer, #experiment-markup-panel-editor .ace_text-layer, #notes-panel .ace_text-layer { font-size: 9pt; line-height: 12pt; font-weight: normal; } #help-panel .body { padding: 12px; } #help-panel h1 { font-size: 1.9em; } #help-panel h2 { font-size: 1.1em; } #help-panel h3 { font-size: 1.05em; } #notes-panel .panel .paper { font-size: 11pt; } #source_select { border: none; vertical-align: middle; } #source_close, #source_exec { border: 1px solid; padding: 0px 3px; cursor:pointer; } #toggle-login { color: red; } #toggle-login.logged { color: green; } #toggle-login.checking, #toggle-debug.checking { color: yellow; animation: blink-animation 1s steps(5, start) infinite; -webkit-animation: blink-animation 1s steps(5, start) infinite; } @keyframes blink-animation { to { visibility: hidden; } } @-webkit-keyframes blink-animation { to { visibility: hidden; } } #toggle-debug { color: green; } #toggle-debug.debugging { color: red; } #input-login { line-height: 2em; } /* darkTheme */ body.darkTheme { background-color:#1a1c24; color:#eaeaea; } .darkTheme #header > .panel { border-bottom: 1px solid #171717; } .darkTheme .header, .darkTheme .footer { background-color: #323244; color: #eaeaea; border-top: 1px solid #080808; } .darkTheme a, .darkTheme select { color:#eaeaea; font-weight: normal; } .darkTheme .sizer { line-height: 2em; border-left: 2px solid #262836; margin: 1px; outline: 1px solid #262836; border-bottom-left-radius: 8px; border-top-right-radius: 9px; } .darkTheme .chocoblack { background-color: #1a1c24; } .darkTheme .round.frame { padding: 6px 6px; border-radius: 6px; margin: 3px 3px; } .darkTheme .round.frame > .sizer { width: 98%; height: 98%; top: 1%; left: 1%; border-style:none; } .darkTheme .frame.white.round, .darkTheme .frame.chocomilk.round { -webkit-box-shadow: inset 0 0 20px #080706; -moz-box-shadow: inset 0 0 20px #080706; -o-box-shadow: inset 0 0 20px #080706; box-shadow: inset 0 0 20px #080706; } .darkTheme .white { border: 2px solid rgba(119, 110, 73, 0.3); } .darkTheme .grey { background-color: #191919; border: 2px solid #202020; } .darkTheme #source_select { background-color: #4c433e; color: #eaeaea; } .darkTheme #source_close, .darkTheme #source_exec { border-color: #eaeaea; color: #eaeaea; } /* lightTheme */ body.lightTheme { background-color: #FBFBFB; color: #0E0E0E; } .lightTheme .header, .lightTheme .footer { background-color: #DAE1EC; color: #46423D; border-top: 1px solid; } .lightTheme a, .lightTheme select { color: #312E29; font-weight: normal; } .lightTheme .sizer { border-right: 1px solid #C0C0C0; } .lightTheme .round.frame > .sizer > .panel { padding: 4px; } .lightTheme .grey { background-color: #F3F3F3; } .lightTheme #documentation-panel, .lightTheme #experiment-panel { border-top: 1px solid #C0C0C0; } .lightTheme #source_select { background-color: #FBFBFB; color: #0E0E0E; } .lightTheme #source_close, .lightTheme #source_exec { border-color: #424242; color: #424242; } """ style -> text @params.doccolate_style snippets = coffee : """ # Require Locco Interface snippet locco_require_interface \tInterface = require 'chocolate/general/locco/interface'\n # Locco full Interface snippet locco_interface_full \tnew Interface${1:.Web.Html | .Web.App | .Web.Document | .Remote} \t\tdefaults: -> ${2} \t\tlocks: -> ${3} \t\tcheck: (${4}) -> \t\t\t${5:# body...} \t\tsteps: (${6:$2}) -> \t\t\t${7:# body...} \t\trender: (${8:$2}) -> \t\t\t${9:# body...} # Locco full Interface.Web.Html snippet locco_interface_html_full \tnew Interface.Web.Html \t\tdefaults: -> ${1} \t\tlocks: -> ${2} \t\tcheck: (${3}) -> \t\t\t${4:# body...} \t\tsteps: (${5}) -> \t\t\t${6:# body...} \t\trender: (${7}) -> \t\t\t${8:# body...} # Locco standard Interface.Web.Html snippet locco_interface_html_standard \tnew Interface.Web.Html (-> ${1}), ${2}-> \t\t${3:# body...} # Locco minimal Interface.Web.Html snippet locco_interface_html_minimal \tnew Interface.Web.Html -> \t\t${1:# body...} # Chocodash require snippet require_chocodash \t_ = require 'chocolate/general/chocodash'\n # Chocodash async snippet chocodash_async_or_flow \t_.flow (run) -> \t\trun (end) -> \t\t\t${1:end() | end.later} """ text "<script>_ = Chocodash; _ide = {}; _sofkey = #{if backdoor_key isnt '' then '"' + backdoor_key + '"' else 'null'};</script>\n" text "<script>_ide.snippets = {studio:{applied:{}, modes:#{JSON.stringify(snippets)}}, user:{applied:{}, modes:#{if @params.snippets then JSON.stringify @params.snippets else '{}'}}}</script>" script src:"/static/vendor/ace/ace.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/ext-searchbox.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/ext-language_tools.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/ext-whitespace.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/snippets/coffee.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/snippets/javascript.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/snippets/json.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/snippets/css.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/snippets/text.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/snippets/html.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/snippets/markdown.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/mode-coffee.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/mode-javascript.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/mode-json.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/mode-css.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/mode-scss.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/mode-text.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/mode-html.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/mode-markdown.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/theme-coffee.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/ace/theme-coffee_white.js", type:"text/javascript", charset:"utf-8" script src:"/static/lib/doccolate.js", type:"text/javascript", charset:"utf-8" script src:"/static/lib/js2coffee.js", type:"text/javascript", charset:"utf-8" script src:"/static/lib/htmlkup.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/datejs/date.js", type:"text/javascript", charset:"utf-8" script src:"/static/vendor/mootools/mootools-core-uncompressed.js", type:"text/javascript", charset:"utf-8" script src:"/static/lib/newnotes.js", type:"text/javascript", charset:"utf-8" body '.lightTheme', -> panel -> header '#header', -> panel proportion:'served', -> panel -> span '#.choco_title.float-margin-left', 'Chocolate.js' panel -> select '#source_select.hidden', onchange:'_ide.open_file()', '' span '.float-right', -> span '#source_exec.hidden', onclick:'_ide.exec_file()', '↗' text '&nbsp;&nbsp;' span '#source_close.hidden', onclick:'_ide.close_file()', 'x' a '#switch-invisible.float-margin-right', title:'Switch invisible caracters display', style:'font-size: 9pt;text-decoration: none;', href:"#", onclick:"_ide.switchInvisible();", -> '...' a '#switch-wrap.float-margin-right', title:'Switch wrap mode', style:'font-size: 9pt;text-decoration: none;', href:"#", onclick:"_ide.switchWrap();", -> '↩' a '#switch-unfold.float-margin-right', title:'Switch unfold', style:'font-size: 9pt;text-decoration: none;', href:"#", onclick:"_ide.switchFoldAll(false);", -> '⤓' a '#switch-foldall.float-margin-right', title:'Switch fold all', style:'font-size: 9pt;text-decoration: none;', href:"#", onclick:"_ide.switchFoldAll(true);", -> '⤒' a '#show-find.float-margin-right', title:'Show find dialog', style:'font-size: 9pt;text-decoration: none;', href:"#", onclick:"_ide.showFindDialog();", -> '🔎' panel -> input '#input-login.float-margin-right.hidden', type:'password', onchange:'_ide.register_key()', '' a '#toggle-login.float-margin-right', title:'Login/Logoff', style:'font-size: 15pt;text-decoration: none;', href:"#", onclick:"_ide.toggleLogin();", -> '•' a '#toggle-debug.float-margin-right', title:'Debug/Run', style:'font-size: 13pt;text-decoration: none;', href:"#", onclick:"_ide.toggleDebug();", -> '▣' a '#toggle-fullscreen.float-margin-right', title:'Switch fullscreen', style:'font-size: 15pt;text-decoration: none;', href:"#", onclick:"_ide.toggleFullscreen();", -> '«»' a '#switch-panel.float-margin-right', title:'Switch side by side display', style:'font-size: 9pt;text-decoration: none;', href:"#", onclick:"_ide.switchScreens();", -> '|||' a '#switch-theme.float-margin-right', title:'Switch theme', style:'font-size: 9pt;text-decoration: none;', href:"#", onclick:"_ide.switchTheme();", -> '□' body -> panel '#main-panel', proportion:'served', -> panel -> footer -> panel proportion:'third', -> box -> a '#do-file-create', href:"#", onclick:"_ide.create_file();", -> 'Create' box -> a '#do-git-commit', href:"#", onclick:"_ide.commit_to_git();", -> 'Commit' box -> a '#do-file-upload', href:"#", onclick:"_ide.upload_file('ask');", -> text 'Upload' form "#form-file-upload", action:"", method:"post", enctype:"multipart/form-data", target:"frame-file-upload", -> input "#input-file-upload", name:"input-file-upload", type:'file', onchange:"_ide.upload_file('send');" iframe "#frame-file-upload", name:"frame-file-upload", -> body -> panel proportion:'third', orientation:'vertical', -> panel -> header -> box 'Opened files' body -> box '#.chocoblack.round', -> body '#open_files_list', '' panel -> header -> panel proportion:"half", orientation:"horizontal", -> box -> a '#toggle-search', href:"#", onclick:"_ide.toggleNavigatePanel('search');", -> 'Search' box -> a '#toggle-browse.selected', href:"#", onclick:"_ide.toggleNavigatePanel('browse');", -> 'Browse' body -> panel '#studio-search-panel.hidden', -> panel -> header -> input "#search_in_file_input", size:"70%", onchange:"_ide.search_in_files(this.value);", -> body -> box '#.chocoblack.round', -> body '#studio-search', -> panel '#studio-browse-panel', -> box '#.chocoblack.round', -> body -> div -> text '/' span '#current_dir', -> div '#dir_content', style:'margin-left:4px;', -> panel -> header -> box 'Log' footer -> panel proportion:'third', -> box -> a '#do-file-rename', href:"#", onclick:"_ide.rename_file();", -> 'Rename' box -> a '#do-file-move', href:"#", onclick:"_ide.move_file();", -> 'Move' box -> a '#do-file-delete', href:"#", onclick:"_ide.delete_file();", -> 'Delete' body -> panel '#studio-messages-panel', -> box '#.chocoblack.round', -> body '#studio-messages', -> panel -> footer -> panel proportion:'third', -> box -> a '#toggle-source.selected', href:"#", onclick:"_ide.toggleMainPanel('source');", -> 'Code' panel proportion:'third', -> box -> a '#toggle-shared', href:"#", onclick:"_ide.toggleMainPanel('shared');", -> 'Code & Lab' box -> a '#toggle-specolate', href:"#", onclick:"_ide.toggleMainPanel('specolate');", -> 'Spec' box -> a '#toggle-doccolate', href:"#", onclick:"_ide.toggleMainPanel('doccolate');", -> 'Doc' box -> a '#toggle-experiment', href:"#", onclick:"_ide.toggleMainPanel('experiment');", -> 'Lab' body -> panel '#main-display', proportion:'half', orientation:'vertical', -> panel '#main-editor.expand', -> panel '#editor', '' panel '#main-tabs.shrink', -> panel '#experiment-panel', proportion:'half-served', orientation:'vertical', -> panel proportion:'half', -> panel -> footer -> panel proportion:'half', -> box -> a '#toggle-coffeescript.selected', href:"#", onclick:"_ide.toggleExperimentPanel('coffeescript');", -> 'Coffeescript' text ' / ' a '#toggle-javascript', href:"#", onclick:"_ide.toggleExperimentPanel('javascript');", -> 'Javascript' box -> a '#toggle-chocodown', href:"#", onclick:"_ide.toggleExperimentPanel('chocodown');", -> 'Chocodown' text ' / ' a '#toggle-html', href:"#", onclick:"_ide.toggleExperimentPanel('html');", -> 'Html' body -> box '#experiment-script-panel-main.chocoblack.round', -> panel '#experiment-script-panel-editor', '' box '#experiment-markup-panel-main.chocoblack.round.hidden', -> panel '#experiment-markup-panel-editor', '' panel -> panel '#experiment-script-transpiled', -> footer -> panel proportion:'half', -> box -> a '#toggle-script-output.selected', href:"#", onclick:"_ide.toggleExperimentPanel('script-output');", -> span '#label-javascript-output', 'Javascript' span '#label-coffeescript-output.hidden', 'Coffeescript' box -> panel "#toggle-debug-panel", proportion:'half-served', -> panel -> a '#toggle-debug', href:"#", onclick:"_ide.toggleExperimentPanel('debug');", -> 'Debug' panel proportion:'half', -> panel -> a href:"#", title:"Reduce column width", onclick:"_ide.changeDebugColWidth(-1);", -> '-' panel -> a href:"#", title:"Increase column width", onclick:"_ide.changeDebugColWidth(1);", -> '+' body -> panel '#experiment-script-output-panel-main', -> box '#.white.round', -> body '#experiment-script-output-panel.source-code.dl-s-default', -> panel '#experiment-debug-panel-main.hidden', -> box '#.chocoblack.round', -> panel '#experiment-debug-panel.source-code', '' panel '#experiment-markup-transpiled.hidden', -> footer -> panel proportion:'half', -> box -> panel proportion:'half-served', -> panel -> a '#toggle-markup-output.selected', href:"#", onclick:"_ide.toggleExperimentPanel('markup-output');", -> span '#label-chocodown-output', 'Html' span '#label-html-output.hidden', 'Chocokup' panel '#experiment-markup-options', proportion:'half', -> panel -> a href:"#", title:"Indented Chocokup", onclick:"_ide.toggleFormatChocokup(true);", -> '↘' panel -> a href:"#", title:"Raw Chocokup", onclick:"_ide.toggleFormatChocokup(false);", -> '→' box -> a '#toggle-dom', href:"#", onclick:"_ide.toggleExperimentPanel('dom');", -> 'Dom' body -> panel '#experiment-markup-output-panel-main', -> box '#.white.round', -> body '#experiment-markup-output-panel.source-code', -> panel '#experiment-dom-panel-main.hidden', -> box '#.white.round', -> panel '#.padded', -> iframe '#experiment-dom-panel.expand.fullscreen.white-background', frameborder:'0', -> panel -> box '#.grey.round', -> body '#experiment-run-panel.source-code', '' panel '#documentation-panel.hidden', -> box '#.white.round', -> iframe '#documentation-doccolate-panel.expand.fullscreen.transparent-background', frameborder:'0', -> panel '#specolate-panel.hidden', proportion:'half-served', orientation:'vertical', -> panel -> header -> panel 'Spec' box -> a '#run-specolate.float-margin-right', href:"#", onclick:"_ide.run_specolate();", -> 'Run' body -> box '#specolate-panel-main.chocoblack.round', -> panel '#specolate-panel-editor', '' panel -> box '#.grey.round', -> body '#specolate-result-panel.source-code', '' iframe '#specolate-run-panel.hidden', src:'/-/server/studio?iframe_body', -> panel '#help-display.hidden', proportion:'full', -> header -> panel '#help-title', -> '' panel '#doclosehelpdisplay', -> a '#close-help-display.float-margin-right', style:'text-decoration: none;', href:"#", onclick:"_ide.close_help_panel();", -> 'x' body -> panel '#help-display-body', -> panel -> footer -> panel proportion:'third', -> box -> a '#toggle-git.selected', href:"#", onclick:"_ide.toggleServicesPanel('git');", -> 'Git' box -> a '#toggle-notes', href:"#", onclick:"_ide.toggleServicesPanel('notes');", -> 'Notes' box -> a '#toggle-help', href:"#", onclick:"_ide.toggleServicesPanel('help');", -> 'Help' body -> panel '#git-panel', -> body -> panel '#git-panel-history', -> panel proportion:'half', orientation:'vertical', -> panel '#git-code.expand', -> box '#.chocoblack.round', -> body '#code-git-history', -> panel '#git-spec.shrink', -> header -> box -> 'Spec' body -> box '#.chocoblack.round', -> body '#spec-git-history', -> panel '#git-panel-diff.hidden', -> panel proportion:'half', orientation:'vertical', -> panel '#git-diff-code.overflow.expand', -> box '#.chocoblack.round', -> body '#code-git-diff', -> panel '#git-diff-spec.overflow.shrink', -> header -> box -> 'Spec' body -> box '#.chocoblack.round', -> body '#spec-git-diff', -> panel '#git-panel-status.hidden.overflow', -> footer -> panel proportion:'third', -> box -> a '#toggle-git-history.selected', href:"#", onclick:"_ide.toggleServicesPanel('git', 'history');", -> 'History' box -> a '#toggle-git-diff', href:"#", onclick:"_ide.toggleServicesPanel('git', 'diff');", -> 'Diff' box -> a '#toggle-git-status', href:"#", onclick:"_ide.toggleServicesPanel('git', 'status');", -> 'Status' panel '#notes-panel.hidden', '' panel '#help-panel.hidden', -> panel -> header '#.inline', -> panel proportion:'third', -> box -> a '#toggle-help-chocolate.selected', href:"#", onclick:"_ide.toggleHelpPanel('chocolate');", -> 'Chocolate' box -> a '#toggle-help-coffeescript', href:"#", onclick:"_ide.toggleHelpPanel('coffeescript');", -> 'Coffeescript' box -> a '#toggle-help-chocokup', href:"#", onclick:"_ide.toggleHelpPanel('chocokup');", -> 'Chocokup' header '#.inline', -> panel proportion:'third', -> box -> a '#toggle-help-specolate', href:"#", onclick:"_ide.toggleHelpPanel('specolate');", -> 'Specolate' box -> a '#toggle-help-doccolate', href:"#", onclick:"_ide.toggleHelpPanel('doccolate');", -> 'Doccolate' box -> a '#toggle-help-newnotes', href:"#", onclick:"_ide.toggleHelpPanel('newnotes');", -> 'Newnotes' header '#.inline', -> panel proportion:'third', -> box -> a '#toggle-help-chocodown', href:"#", onclick:"_ide.toggleHelpPanel('chocodown');", -> 'Chocodown' box -> a '#toggle-help-litejq', href:"#", onclick:"_ide.toggleHelpPanel('litejq');", -> 'litejQ' box -> a '#toggle-help-node', href:"#", onclick:"_ide.toggleHelpPanel('node');", -> 'Node' body '#.with-3-headers', -> footer -> panel '#help-selector-panel.hidden', -> select '#help-selector', onchange:"_ide.select_help_panel(true);", -> body -> box '#.chocomilk.round', -> panel -> iframe '#help-frame-panel.expand fullscreen', frameborder: '0', -> coffeescript -> sofkey = _sofkey editor = lab_markup_editor = debug_editor = lab_script_editor = specolate_editor = null ace_theme = if $(document.body).hasClass('lightTheme') then 'ace/theme/coffee_white' else 'ace/theme/coffee' debug_experiment_sync = null sources = opened : [] # codes : { path, doc, from_history , modified, modifiedDate } codes : {} specs : {} current : '' available : {} searched: {} isOpening : false frames = help : {} codeMode = 'opened' debugColumnWidth = 7 formatChocokup = off console_log = console.log console.log = (text) -> _ide.display_message text console_log.apply console, arguments translate = (text) -> text # Display opened source files list _ide.get_extension = (path) -> if (path = path.substr(1 + path.indexOf '/')).indexOf('.') is -1 then '' else '.' + path.split('.')[-1..][0] _ide.get_basename = (path) -> path.split('/')[-1..][0] _ide.is_spec_file = (path) -> suffix = '.spec' index = path.lastIndexOf '.' index = path.length if index < 0 result = path.substr(index - suffix.length, suffix.length) is '.spec' result = path.substr(index) is '.spec' unless result result _ide.get_spec_filename = (path) -> return '' if path is '' return path.replace ext, '.spec' + ext if (ext = _ide.get_extension path) isnt '' return path + '.spec' _ide.has_spec_file = (path) -> item = sources.codes[path] return item.has_spec_file if item? false _ide.get_current_dir = -> if (dir = document.id('current_dir').get 'text') is '' then '.' else dir _ide.iframe_infos = (iframe) -> doc = (iframe.contentDocument || iframe.contentWindow.document) doc_element = doc.documentElement || doc.body.parentNode || doc.body win = iframe.contentWindow scroll = if win.pageXOffset isnt undefined doc:win, attr: x:'pageXOffset', y:'pageYOffset' else doc:doc_element, attr: x:'scrollLeft', y:'scrollTop' scrollX = scroll.doc[scroll.attr.x] scrollY = scroll.doc[scroll.attr.y] {win, doc, doc_element, scrollX, scrollY} _ide.iframe_write = (iframe, text, scroll) -> {doc, win, scrollX, scrollY} = _ide.iframe_infos iframe iframeDoc = doc iframeDoc.open() iframeDoc.write text iframeDoc.close() unless scroll? win.scrollTo scrollX, scrollY else _ide.iframe_scrollTo iframe, scroll.left , scroll.top, scroll.elemW, scroll.elemH _ide.iframe_scrollTo = (iframe, x, y, elemW, elemH) -> {doc, doc_element, scrollX, scrollY} = _ide.iframe_infos iframe container = doc.getElementById('docco_container') ? doc_element ratioX = (container.clientWidth - doc.body.clientWidth) / elemW ratioY = (container.clientHeight - doc.body.clientHeight) / elemH new_scrollX = if x < 0 or elemW < 0 then scrollX else x * ratioX new_scrollY = if y < 0 or elemH < 0 then scrollY else y * ratioY iframe.contentWindow.scrollTo new_scrollX, new_scrollY _ide.display_message = (message) -> new Element('div').set('html', ('<p>' + new Date().toString translate 'dd/MM/yyyy HH:mm:ss') + '<br/>&nbsp;&nbsp;' + message + '</p>').inject document.id('studio-messages'), 'top' _ide.display_help_panel = (name, path) -> path = '' if path is '-' _ide.toggleMainDisplay 'help' if path isnt '' selected_frame = frames.help[path]?.frame for own help_path, help of frames.help if help.frame isnt selected_frame then help.frame.addClass 'hidden' else help.frame.removeClass 'hidden' document.id('help-title').set 'text', path document.id('help-display-body').adopt (frames.help[path] = name: name, frame: new Element('iframe', src:path, frameborder:0, class:'expand fullscreen white-background')).frame unless selected_frame or path is '' help_selector = document.id('help-selector') help_selector.empty() (help_selector.adopt new Element 'option', selected:(if path is help_path then on else off), value: help_path, text: help.name) for own help_path, help of frames.help document.id('help-selector-panel')[(if help_selector.length <= 1 then 'add' else 'remove') + 'Class'] 'hidden' _ide.select_help_panel = (is_on_click) -> help_selector = document.id('help-selector') _ide.display_help_panel help_selector.getSelected().get('text')[0], help_selector.getSelected().get('value')[0] _ide.close_help_panel = -> path = document.id('help-title').get 'text' frames.help[path].frame.dispose() delete frames.help[path] new_help = {name: '', path: ''} for own path, help of frames.help then new_help = name: help.name, path: path _ide.display_help_panel new_help.name, new_help.path _ide.toggleMainDisplay 'main' if document.id('help-selector').length is 0 _ide.display_opened_files_list = -> codes = [] codes.push item for own item of sources.codes codes.sort() content = [] current_steps = [] for item in codes steps = item.split '/' for step, i in steps when i < steps.length-1 if step isnt current_steps[i] current_steps = current_steps[0...i] for j in [i...steps.length-1] current_steps[j] = steps[j] content.push "<div style='margin-top:4px;margin-left:#{j*8}px;'><a href='#' onclick=\"javascript:_ide.goto_dir('#{current_steps.join('/')}');\">#{steps[j]}</a></div>" line = """<div style='margin-left:#{(steps.length-1)*8}px;'>""" line += """<a href="#" onclick="javascript:_ide.close_file('#{item}');">x</a>&nbsp;""" line += """<a href="#" onclick="javascript:_ide.save_file('#{item}');">*</a>&nbsp;""" if sources.codes[item].modified line += """<a href="#" onclick="javascript:_ide.display_code_file('#{item}');">#{steps[steps.length-1]}</a>&nbsp;""" line += """<a href="#" onclick="javascript:_ide.display_synchro_conflict_message('#{item}');">!</a>&nbsp;""" if sources.codes[item].has_synchro_conflict if _ide.has_spec_file item spec_item = _ide.get_spec_filename item line += '[' line += """<a href="#" onclick="javascript:_ide.save_file('#{spec_item}');">*</a>&nbsp;""" if sources.specs[spec_item].modified line += """<a href="#" onclick="javascript:_ide.display_code_file('#{item}'); _ide.toggleMainPanel('specolate')">spec</a>""" line += ']&nbsp;' line += "</div>" content.push line document.id('open_files_list').set 'html', content _ide.display_opened_files_select = -> codes = [] codes.push item for own item of sources.codes codes.sort() content = [] for item in codes if_selected = if sources.current is item then ' selected="selected"' else '' content.push "<option value='#{item}'#{if_selected}>/#{item + if sources.codes[item].modified then '*' else ''}</option>" selector = document.id('source_select').set 'html', content selector[(if content.length > 0 then 'remove' else 'add') + 'Class'] 'hidden' $$("#source_select > option[value='#{sources.current}']").setProperty 'selected', 'selected' _ide.has_modified_file = -> for path in [sources.codes, sources.specs] for own k,v of path then if v.modified then return yes no _ide.switch_login = (switched) -> if not switched? document.id("toggle-login").removeClass('logged').addClass 'checking' document.id("toggle-debug").addClass 'checking' else document.id("toggle-login").removeClass 'checking' document.id("toggle-debug").removeClass 'checking' document.id("toggle-login")["#{if switched is on then 'add' else 'remove'}Class"] 'logged' document.id("main-panel")["#{if switched is off then 'add' else 'remove'}Class"] 'blurred' _ide.sync_exec_windows = (count) -> for path, window_ of _ide.exec_file.paths when window_? if count > 0 window_.document.body.innerHTML = """ <table style="font-size:120pt; width:100%; height:100%; text-align:center; vertical-align:middle"> <tr> <td>#{count}</td> </tr> </table> """ else if window_.closed then delete _ide.exec_file.paths[path] else _ide.exec_file path _ide.check_connected = (timeout = 0) -> try_reconnect = not _ide.check_online.connected if timeout > 0 then _ide.switch_login() start = Date.now() new Request url: (if sofkey? then '/!/' + sofkey else '') + '/-/server/studio?connected&how=raw' noCache: yes timeout: timeout onSuccess: (data) -> _ide.switch_login (_ide.check_online.connected = if data is "connected" then on else off) _ide.sync_exec_windows() if try_reconnect and _ide.check_online.connected onFailure: (xhr) -> unless timeout > 0 _ide.switch_login (_ide.check_online.connected = off) onTimeout: () -> unless timeout > 0 _ide.switch_login (_ide.check_online.connected = off) .get() _ide.check_online = (timeout = 30000, delay = 1500) -> count = 0 index = 0 _ide.check_online.connected = off timer = setInterval ( -> index++ count += delay if count < timeout and (index <= 1 or _ide.check_online.connected isnt yes) _ide.sync_exec_windows(index) ; _ide.check_connected delay else clearInterval timer ), delay _ide.register_key = -> new Request data: key:document.id("input-login").value noCache: yes url: (if sofkey? then '/!/' + sofkey else '') + '/-/server/interface?register_key&how=raw' onSuccess: (data) => _ide.check_connected() document.id("input-login").addClass('hidden').set 'value', '' onFailure: (xhr) -> .post() _ide.logoff = -> new Request noCache: yes url: (if sofkey? then '/!/' + sofkey else '') + '/-/server/interface?forget_keys&how=raw' onSuccess: (data) => _ide.check_connected() onFailure: (xhr) -> .post() _ide.debug = (onoff) -> new Request.JSON noCache: yes url: (if sofkey? then '/!/' + sofkey else '') + '/-/server/config?debug&' + onoff onSuccess: ({protocol, port}) -> if onoff document.id("toggle-debug").addClass 'debugging' else document.id("toggle-debug").removeClass 'debugging' _ide.check_online() if onoff _ide.debug.window = window.open() _ide.debug.window.document.body.innerHTML = """ <div>establish ssh connection: ssh -L 9229:localhost:9229 user@remote.example.com</div> <div>then open chrome://inspect#devices</div> """ else _ide.debug.window.close() onFailure: (xhr) -> .get() _ide.goto_dir = (new_dir, callback) -> parent_dir = if (tmp_ = new_dir.split('/')[0...-1].join('/')) is '' then '.' else tmp_ new Request.JSON url: (if sofkey? then '/!/' + sofkey else '') + '/-/server/file?getDirContent&' + new_dir + '&how=raw' onSuccess: (directory) -> return if directory.error? rel_new_dir = if new_dir is '.' then '' else new_dir + '/' sources.available = {} content = ["""<div><a href="#" onclick="javascript:_ide.goto_dir('#{parent_dir}');">..</a></div>""" if new_dir isnt '.'] for item in directory when item.name[0] isnt '.' # and (item.isDir or item.extension in ['.coffee', '.litcoffee', '.css', '.js', '.json', '.html', '.txt', '.markdown', '.md']) unless _ide.is_spec_file item.name content.push ('<div>' + """<a href="#" onclick="javascript:#{if item.isDir then '_ide.goto_dir' else '_ide.open_file'}('#{(if new_dir isnt '.' then (new_dir + '/') else '') + item.name}');">#{item.name}</a>""" + '</div>') else sources.available[rel_new_dir + item.name.replace '.spec', '']?.has_spec_file = true sources.available[rel_new_dir + item.name] = { name:item.name, extension:item.extension, modifiedDate:item.modifiedDate, has_spec_file:false } if item.isFile document.id('dir_content').set 'html', content document.id('current_dir').set 'text', if new_dir isnt '.' then new_dir else '' _ide.get_git_status() callback?() onFailure: (xhr) -> _ide.display_message "Error with _ide.goto_dir(#{new_dir}) : #{xhr.status}" .get() _ide.load_file = (path, file, from_history) -> is_spec = _ide.is_spec_file path if not is_spec and not sources.codes[path]? # Create or recycle an editor to display source file if sources.opened.length >= 10 delete sources.codes[sources.opened[0]] delete sources.opened[0] sources.opened = sources.opened[1..] sources.opened.push path doc = _ide.create_session file, path item = sources.available[path] ? name:_ide.get_basename(path), extension:_ide.get_extension(path), modifiedDate:sources.searched[path]?.modifiedDate ? 0, has_spec_file:false sources[if is_spec then 'specs' else 'codes'][path] = { path, doc, from_history:from_history ? false, modified:from_history ? false, modifiedDate:item.modifiedDate, has_spec_file:item.has_spec_file } _ide.save_sources_opened() doc _ide.display_code_file = (path, overwrite) -> _ide.toggleMainDisplay 'main' editor.setSession if path is '' then _ide.create_session '' else sources.codes[path].doc document.id('source_exec')[(if path is '' then 'add' else 'remove') + 'Class'] 'hidden' document.id('source_close')[(if path is '' then 'add' else 'remove') + 'Class'] 'hidden' sources.current = path # Switch to Source display if current display is Experiment _ide.toggleMainPanel 'source' if document.id('toggle-experiment').hasClass 'selected' editor.focus() # Refresh file History panel if display file is not from History _ide.get_file_git_history path unless overwrite _ide.get_file_diff path _ide.on_file_status_changed() _ide.on_content_changed init:yes _ide.display_spec_file _ide.get_spec_filename path if path != '' then $$("#source_select > option[value='#{path}']").setProperty 'selected', 'selected' editor.getSession().on 'changeScrollTop', (scroll) -> unless document.id('documentation-panel').hasClass 'hidden' iframe = document.id('documentation-doccolate-panel') _ide.iframe_scrollTo iframe, -1, scroll, -1, editor.getSession().getScreenLength() * editor.renderer.lineHeight - editor.renderer.scroller.clientHeight _ide.display_spec_file = (path, overwrite) -> # Load spec file in editor if present item = sources.specs[path] unless item? then path = '' specolate_editor.setSession if path is '' then _ide.create_session '' else item.doc document.id('specolate-result-panel').set 'text', if path is '' or item.spec_run_result is undefined then '' else item.spec_run_result.join '\n' _ide.on_file_status_changed yes _ide.on_content_changed is_spec:yes, init:yes _ide.create_session = (source, path) -> EditSession = require("ace/edit_session").EditSession UndoManager = require("ace/undomanager").UndoManager mode = switch _ide.get_extension path ? '' when '.coffee', '.chocokup', '.ck' then 'coffee' when '.js' then 'javascript' when '.json' then 'json' when '.css' then 'css' when '.scss' then 'scss' when '.html' then 'html' when '.txt' then 'text' when '.markdown', '.md', '.chocodown', '.cd', '.litcoffee' then 'markdown' else 'text' Mode = require("ace/mode/#{mode}").Mode doc = new EditSession source doc.setUndoManager new UndoManager() doc.setMode new Mode() snippetManager = require("ace/snippets").snippetManager snippetMode = require("ace/snippets/#{mode}") for type in ['studio', 'user'] if _ide.snippets[type].modes[mode]? and _ide.snippets[type].applied[mode] isnt on snippetMode.snippetText ?= "" snippetMode.snippetText += "\n#{_ide.snippets[type].modes[mode]}" snippetManager.unregister _ide.current_snippet _ide.current_snippet = snippetManager.parseSnippetFile(snippetMode.snippetText ? "") snippetManager.register _ide.current_snippet doc.setUseSoftTabs true if path? and path isnt '' unless _ide.is_spec_file path doc.on 'change', -> source = sources.codes[sources.current] if source? if not source.modified source.modified = true sources.opened = (item for item in sources.opened when item isnt sources.current) _ide.on_file_status_changed() _ide.on_content_changed init:yes true else doc.on 'change', -> sources.codes[sources.current].has_spec_file = yes source = sources.specs[_ide.get_spec_filename sources.current] if source? if not source.modified source.modified = true _ide.on_file_status_changed yes _ide.on_content_changed is_spec:yes true doc _ide.create_file = -> cur_dir = _ide.get_current_dir() if filename = prompt translate("Current directory is /#{cur_dir}") + '\n\n' + translate "Enter a filename" if _ide.get_extension(filename) is '' then filename += '.coffee' new Request url: '/' + (if sofkey? then '!/' + sofkey else '') + "#{cur_dir}/#{filename}?so=move&how=raw" onSuccess: (responseText) -> _ide.goto_dir cur_dir, -> _ide.open_file "#{cur_dir}/#{filename}" _ide.display_message translate "File #{cur_dir}/#{filename} was created" onFailure: (xhr) -> _ide.display_message "Error with _ide.create_file() : #{xhr.status}" .get() _ide.check_online() return _ide.upload_file = (step) -> cur_dir = _ide.get_current_dir() form = document.getElementById('form-file-upload') input = document.getElementById('input-file-upload') iframe = document.getElementById('frame-file-upload') on_iframe_load = -> _ide.upload_file 'done' filename = _ide.get_basename input.value.replace /\\/g, '/' switch step when 'ask' input.click() when 'send' if input.value isnt '' iframe.addEvent 'load', on_iframe_load form.action = '/' + (if sofkey? then '!/' + sofkey else '') + "#{cur_dir}/#{filename}?so=move&how=raw" form.submit() when 'done' iframe.removeEvents 'load' form.action = '' input.value = '' _ide.goto_dir cur_dir, -> _ide.open_file "#{cur_dir}/#{filename}" _ide.display_message translate "File /#{cur_dir}/#{filename} was uploaded" _ide.move_file = -> cur_dir = _ide.get_current_dir() filename = _ide.get_basename sources.current if sources.current is "#{cur_dir}/#{filename}" alert translate "This file is already in /#{cur_dir} directory" return if filename is '' or not _ide.will_close_file sources.current then return move_one = (source, dest, callback) -> new Request url: '/' + (if sofkey? then '!/' + sofkey else '') + "#{dest}?so=move&what=#{source}&how=raw" onSuccess: (responseText) -> callback?() onFailure: (xhr) -> _ide.display_message "Error with _ide.move_file.move_one() : #{xhr.status}" .get() _ide.check_online() return if confirm translate("Current file is /#{sources.current}") + '\n\n' + translate "Confirm that you want to move this file to /#{cur_dir}" source = sources.current dest = cur_dir + '/' + filename if _ide.has_spec_file source then move_one _ide.get_spec_filename(source), _ide.get_spec_filename(dest) move_one source, dest, -> _ide.close_file sources.current, yes _ide.goto_dir cur_dir, -> _ide.open_file "#{cur_dir}/#{filename}" _ide.dis