From 4ed8178127b68e7228f8e303bd28dfe9f4103348 Mon Sep 17 00:00:00 2001 From: Alec Perkins Date: Wed, 22 Feb 2012 23:56:35 -0500 Subject: [PATCH] Fix #24 - trap autosuggest matching regex errors to avoid polluting console --- coffeetable-min.js | 32 +- coffeetable.js | 334 ++++++++++++++----- src/Cakefile | 8 +- src/coffeetable.coffee | 38 ++- src/coffeetable.js | 724 ----------------------------------------- src/dev.html | 2 +- 6 files changed, 289 insertions(+), 849 deletions(-) delete mode 100644 src/coffeetable.js diff --git a/coffeetable-min.js b/coffeetable-min.js index 0fa2a79..8e858fe 100644 --- a/coffeetable-min.js +++ b/coffeetable-min.js @@ -1,14 +1,18 @@ -(function(){var p,b,q,H,I,s,J,y,t,j,l,z,o,K,u,A,B,n,C,L,v,M,D,E,F,h,r,w,N,O,P;J={autoload_coffee_script:true,autoload_jquery:true,coffeescript_js:"http://code.alecperkins.net/coffeetable/lib/coffee_script-1.1.1-min.js",jquery_js:"http://code.alecperkins.net/coffeetable/lib/jquery-1.6.2-min.js",local_storage:true,ls_key:"coffee-table",clear_on_load:false,replay_on_load:false,multi_line:false,indent:" ",auto_suggest:true,widget_position:"fixed",widget_top:"5px",widget_right:"5px",widget_id:"CoffeeTable-"+ -(new Date).getTime()};typeof console!=="undefined"&&console!==null||(console={log:function(){},dir:function(){}});if(window.log==null)window.log=function(){return console.log.apply(console,arguments)};if(window.dir==null)window.dir=function(){return console.dir.apply(console,arguments)};o={UP:38,DOWN:40,ENTER:13,TAB:9,BACKSPACE:8,ESCAPE:27};w='
clearreplay?

multiline

      '; -b=h=p=null;r=B=y=q=false;l=0;n={jquery_js:false,coffeescript_js:false};j=[];z=function(){if(n.jquery_js&&n.coffeescript_js){p=window.jQuery;w=w.replace(/__ID__/g,h.widget_id);M();h.local_storage&&K();return B=true}};I=function(a,d){var c,e,f,g,k,i,m;if(d.which===o.ESCAPE||d.which===o.BACKSPACE&&a.length===0&&b.autosuggest_list.html().length!==0)return b.autosuggest_list.html("");else{f=a.split(".");i=[[window,"window"]];m=f.slice(0,f.length-1);g=0;for(k=m.length;g0&&i.push([i[i.length-1][0][e],e])}e=[];g=RegExp("^"+f[f.length-1]);m=i[i.length-1][0];for(c in m){k=m[c];f=g.exec(c);if((f!=null?f.length:void 0)>0)e.push([c,typeof k])}e.sort();return L(i,e)}};L=function(a,d){var c,e,f,g;c="";f=0;for(g=a.length;f"+c+"";f=0;for(g=d.length;f"+e[0]+""}b.autosuggest_list.html(c);return E()};K=function(){var a,d,c,e,f;if(typeof localStorage!=="undefined"&& -localStorage!==null){d=localStorage.getItem(h.ls_key);if(d!=null){d=JSON.parse(d);f=[];c=0;for(e=d.length;c").text(""+m+": "+i);if(k)c.addClass("js-error");else e&&c.addClass("cs-error");c.click(function(){u(false,m);return b.textarea.focus()});c.prependTo(b.history_list); -e=function(){var x,Q,G;G=[];x=0;for(Q=j.length;x": -"type CoffeeScript, press ";return b.instructions.text(a)}};u=function(a,d){if(a==null)a=false;if(d!=null)l=d+1;if(l===-1||l===0)l=j.length-1;else if(a)l+=1;else l-=1;if(j.length>0){b.textarea.val(j[l].source);b.textarea.selectionStart=0;return b.textarea.selectionEnd=0}};N=function(){var a;if(r=!r){a="4em";b.autosuggest_list.hide()}else{a="";h.auto_suggest&&b.autosuggest_list.show()}b.textarea.css("height",a).focus();return v()};E=function(){var a;a=""+(window.innerHeight-140)+"px";b.autosuggest_list.css({"max-height":a, -"max-width":""+(window.innerWidth-255)+"px"});return b.history_list.css("max-height",a)};M=function(){var a;a=p(w);b={widget:a,textarea:a.find("textarea"),autosuggest_list:a.find("ul.autosuggest"),history_list:a.find("ul.history"),button:a.find("button"),div:a.find("div"),clear_history:a.find("span.clear"),replay_history:a.find("span.replay"),a:a.find("a"),toggle_multiline:a.find("input"),p:a.find("p.mode"),instructions:a.find("p.instructions"),li:a.find("li")};b.widget.css({position:""+h.widget_position, -top:""+h.widget_top,right:""+h.widget_right});v();H();return a.appendTo("body")};H=function(){b.button.click(function(){if(q=!q){b.div.show();b.p.show();b.textarea.focus();return b.button.addClass("active")}else{b.div.hide();b.p.hide();return b.button.removeClass("active")}});b.clear_history.click(function(){return s()});b.replay_history.click(function(){b.history_list.empty();return setTimeout(function(){return D()},100)});b.toggle_multiline.click(function(){return N()});b.textarea.bind("keydown", -function(a){var d;d=b.textarea.val();if(this.selectionStart===0)if(a.which===o.UP)u();else a.which===o.DOWN&&u(true);if(a.which===o.ENTER&&(!r||a.shiftKey)){a.preventDefault();if(d!==""){t(d);return b.textarea.val("")}}else if(a.which===o.TAB){a.preventDefault();a=this.selectionStart;this.value=this.value.substring(0,a)+h.indent+this.value.substring(a);this.selectionStart=a+h.indent.length;return this.selectionEnd=a+h.indent.length}});b.textarea.bind("keyup",function(a){var d;d=b.textarea.val();if(!h.multi_line&& -h.auto_suggest)return I(d,a)});h.multi_line&&b.toggle_multiline.click();return p(window).resize(function(){return E()})};O=function(){b.widget.remove();window.CoffeeTable=null;return delete window.CoffeeTable};if((P=window.CoffeeTable)!=null)P.unload();window.CoffeeTable={show:function(){b.widget.show();return this},hide:function(){b.widget.hide();return this},clear:function(){s();return this},replay:function(){D();return this},deferInit:function(){y=true;return this},init:function(a){F(a);C();return this}, -unload:function(){O();return this},active:function(){return q}};F=function(a){var d,c,e;h=J;e=[];for(d in a){c=a[d];e.push(h[d]=c)}return e};A=function(a){var d,c;d=document.getElementsByTagName("head")[0];c=document.createElement("script");c.type="application/javascript";c.src=h[a];c.async=true;c.onload=function(){n[a]=true;return z()};return d.appendChild(c)};C=function(){q=false;n.jquery_js=window.jQuery!=null;n.coffeescript_js=window.CoffeeScript!=null;if(!n.coffeescript_js)if(h.autoload_coffee_script)A("coffeescript_js"); -else throw"CoffeeTable requires coffee_script.js";if(!n.jquery_js)if(h.autoload_jquery)A("jquery_js");else throw"CoffeeTable requires jQuery";return z()};document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,false);if(!y&&!B){F();return C()}},false)}).call(this); +(function(){var l,b,q,G,H,t,I,z,J,A,r,h,k,B,m,K,u,C,v,i,D,L,w,M,E,x,F,d,s,y,N,O,p=Array.prototype.slice;J={autoload_coffee_script:!0,autoload_jquery:!0,coffeescript_js:"http://code.alecperkins.net/coffeetable/lib/coffee_script-1.1.1-min.js",jquery_js:"http://code.alecperkins.net/coffeetable/lib/jquery-1.6.2-min.js",local_storage:!0,ls_key:"coffee-table",clear_on_load:!1,replay_on_load:!1,multi_line:!1,indent:" ",auto_suggest:!0,widget_position:"fixed",widget_top:"5px",widget_right:"5px",widget_id:"CoffeeTable-"+ +(new Date).getTime(),alias_log:!0,alias_dir:!0,adopt_log:!0,adopt_dir:!0};m={UP:38,DOWN:40,ENTER:13,TAB:9,BACKSPACE:8,ESCAPE:27};y='
      \n \n \n clearreplay\n ?\n

      multiline

      \n
      \n \n

      \n
        \n
          \n
          \n
          '; +b=d=l=null;s=v=A=q=!1;k=0;i={jquery_js:!1,coffeescript_js:!1};h=[];B=function(a){a==null&&(a={});if(i.jquery_js&&i.coffeescript_js){l=window.jQuery;if(d.adopt_log){if(typeof console==="undefined"||console===null)console={};console.log=function(){var a;a=1<=arguments.length?p.call(arguments,0):[];return z.apply(null,a)}}if(d.adopt_dir){if(typeof console==="undefined"||console===null)console={};console.dir=function(){1<=arguments.length&&p.call(arguments,0)}}if(d.alias_log&&window.log==null)window.log= +function(){return console.log.apply(console,arguments)};if(d.alias_dir&&window.dir==null)window.dir=function(){return console.dir.apply(console,arguments)};y=y.replace(/__ID__/g,d.widget_id);M();d.local_storage&&K();v=!0}return v};z=function(){var a,b,e,g,f;b=1<=arguments.length?p.call(arguments,0):[];e=function(){var b;b=a.toString().replace(/\'/g,"\\'").replace(/\"/g,'\\"');return r("'log: "+b+"'")};g=0;for(f=b.length;g0&&o.push([o[o.length-1][0][g],g]);g=[];try{d=RegExp("^"+h[h.length-1]);i=o[o.length-1][0];for(e in i)l=i[e],f=d.exec(e),(f!=null?f.length:void 0)>0&&g.push([e,typeof l]);g.sort();return L(o, +g)}catch(p){}}};L=function(a,c){var e,g,f,d;e="";f=0;for(d=a.length;f"+e+"";f=0;for(d=c.length;f"+g[0]+"";b.autosuggest_list.html(e);return x()};K=function(){var a,b,e,g,f;if(typeof localStorage!=="undefined"&&localStorage!==null){if(b=localStorage.getItem(d.ls_key),b!=null){b=JSON.parse(b);f=[];e=0;for(g=b.length;e^');f=n.toString().replace(/&/g,"&").replace(//g,">");j=l("
        • \n "+q+":\n "+f+"\n
        • ").append(i);typeof n==="object"&&(g=function(a,b){return l("
        • \n "+a+":\n "+b+"\n
        • ")}, +e=function(a,b){var d,f,c,h,i,j;b.children("ul").remove();d=l("
            ");b.append(d);c=function(){var b;b=[];for(f in a)b.push(f);return b}();c.sort();j=[];h=0;for(i=c.length;h+-'),c.children("span.value").prepend(b),b.click(function(a){a.stopPropagation();return c.hasClass("open")?(c.removeClass("open"), +c.find("ul").remove()):(c.addClass("open"),d.show(),e(h,c))})}());return j},f=l(''),j.children("span.result").before(f),f.click(function(){return j.hasClass("open")?(j.removeClass("open"),j.find("ul").remove()):(j.addClass("open"),e(n,j))}));o?j.addClass("js-error"):m&&j.addClass("cs-error");i.click(function(){u(!1,q);return b.textarea.focus()});j.prependTo(b.history_list);m=function(){var a,b,c;c=[];a=0;for(b= +h.length;a":"type CoffeeScript, press ", +b.instructions.text(a)};u=function(a,c){a==null&&(a=!1);c!=null&&(k=c+1);k===-1||k===0?k=h.length-1:a?k+=1:k-=1;if(h.length>0)return b.textarea.val(h[k].source),b.textarea.selectionStart=0,b.textarea.selectionEnd=0};N=function(){var a;(s=!s)?(a="4em",b.autosuggest_list.hide()):(a="",d.auto_suggest&&b.autosuggest_list.show());b.textarea.css("height",a).focus();return w()};x=function(){var a;a=""+(window.innerHeight-140)+"px";b.autosuggest_list.css({"max-height":a,"max-width":""+(window.innerWidth- +255)+"px"});return b.history_list.css("max-height",a)};M=function(){var a;a=l(y);b={widget:a,textarea:a.find("textarea"),autosuggest_list:a.find("ul.autosuggest"),history_list:a.find("ul.history"),button:a.find("button"),div:a.find("div"),clear_history:a.find("span.clear"),replay_history:a.find("span.replay"),a:a.find("a"),toggle_multiline:a.find("input"),p:a.find("p.mode"),instructions:a.find("p.instructions"),li:a.find("li")};b.widget.css({position:""+d.widget_position,top:""+d.widget_top,right:""+ +d.widget_right});w();G();a.appendTo("body");return x()};G=function(){b.button.click(function(){return(q=!q)?(b.div.show(),b.p.show(),b.textarea.focus(),b.button.addClass("active")):(b.div.hide(),b.p.hide(),b.button.removeClass("active"))});b.clear_history.click(function(){return t()});b.replay_history.click(function(){b.history_list.empty();return setTimeout(function(){return E()},100)});b.toggle_multiline.click(function(){return N()});b.textarea.bind("keydown",function(a){var c;c=b.textarea.val(); +this.selectionStart===0&&(a.which===m.UP?u():a.which===m.DOWN&&u(!0));if(a.which===m.ENTER&&(!s||a.shiftKey)){if(a.preventDefault(),c!=="")return r(c),b.textarea.val("")}else if(a.which===m.TAB)return a.preventDefault(),a=this.selectionStart,this.value=this.value.substring(0,a)+d.indent+this.value.substring(a),this.selectionStart=a+d.indent.length,this.selectionEnd=a+d.indent.length});b.textarea.bind("keyup",function(a){var c;c=b.textarea.val();if(!d.multi_line&&d.auto_suggest)return H(c,a)});d.multi_line&& +b.toggle_multiline.click();return l(window).resize(function(){return x()})};O=function(){b!=null&&b.widget.remove();window.CoffeeTable=null;return delete window.CoffeeTable};window.CoffeeTable={show:function(){b.widget.show();return this},hide:function(){b.widget.hide();return this},clear:function(){t();return this},replay:function(){E();return this},deferInit:function(){A=!0;return this},init:function(a){var b;(b=window.CoffeeTable)!=null&&b.unload();F(a);D();return this},unload:function(){O();return this}, +active:function(){return q},log:function(){var a;a=1<=arguments.length?p.call(arguments,0):[];return z.apply(null,a)},dir:function(){var a;a=1<=arguments.length?p.call(arguments,0):[];return I.apply(null,a)}};F=function(a){var b,e,g;d=J;g=[];for(b in a)e=a[b],g.push(d[b]=e);return g};C=function(a){var b,e;b=document.getElementsByTagName("head")[0];e=document.createElement("script");e.type="application/javascript";e.src=d[a];e.async=!0;e.onload=function(){i[a]=!0;return B()};return b.appendChild(e)}; +D=function(){q=!1;i.jquery_js=window.jQuery!=null;i.coffeescript_js=window.CoffeeScript!=null;if(!i.coffeescript_js)if(d.autoload_coffee_script)C("coffeescript_js");else throw"CoffeeTable requires coffee_script.js";if(!i.jquery_js)if(d.autoload_jquery)C("jquery_js");else throw"CoffeeTable requires jQuery";return B()};document.addEventListener("DOMContentLoaded",function(){document.removeEventListener("DOMContentLoaded",arguments.callee,!1);if(!A&&!v)return F(),D()},!1)}).call(this); diff --git a/coffeetable.js b/coffeetable.js index 7f1a12e..0f311ac 100644 --- a/coffeetable.js +++ b/coffeetable.js @@ -1,20 +1,24 @@ + +/* + +# CoffeeTable vDEV + +A drop-in, CoffeeScript-fluent console for web pages. + +* [Demo + Instructions](http://code.alecperkins.net/coffeetable) +* [GitHub repo](https://github.com/alecperkins/coffeetable) + +## To use + +Load `coffeetable-min.js` into the page: +`` +...and the widget will automatically initialize. +*/ + (function() { - /* - - # CoffeeTable v0.2.0 - - A drop-in, CoffeeScript-fluent console for web pages. - - * [Demo + Instructions](http://code.alecperkins.net/coffeetable) - * [GitHub repo](https://github.com/alecperkins/coffeetable) - - ## To use - - Load `coffeetable-min.js` into the page: - `` - ...and the widget will automatically initialize. - - */ var $, $els, active, bindEvents, buildAutosuggest, clearHistory, defaults, deferred, execute, history, history_index, init, keycode, loadFromStorage, loadPrevious, loadScript, loaded, loaded_scripts, preInit, renderAutosuggest, renderInstructions, renderWidget, replayHistory, resizeWidget, setSettings, settings, showing_multi_line, template, toggleMultiLine, unloadWidget, _ref, _ref2, _ref3; + var $, $els, active, bindEvents, buildAutosuggest, clearHistory, ctDir, ctLog, defaults, deferred, execute, history, history_index, init, keycode, loadFromStorage, loadPrevious, loadScript, loaded, loaded_scripts, preInit, renderAutosuggest, renderInstructions, renderWidget, replayHistory, resizeWidget, setSettings, settings, showing_multi_line, template, toggleMultiLine, unloadWidget, + __slice = Array.prototype.slice; + defaults = { autoload_coffee_script: true, autoload_jquery: true, @@ -30,30 +34,13 @@ widget_position: 'fixed', widget_top: '5px', widget_right: '5px', - widget_id: "CoffeeTable-" + ((new Date()).getTime()) - }; - if (typeof console !== "undefined" && console !== null) { - console; - } else { - console = { - log: function() {}, - dir: function() {} - }; - }; - if ((_ref = window.log) != null) { - _ref; - } else { - window.log = function() { - return console.log.apply(console, arguments); - }; - }; - if ((_ref2 = window.dir) != null) { - _ref2; - } else { - window.dir = function() { - return console.dir.apply(console, arguments); - }; + widget_id: "CoffeeTable-" + ((new Date()).getTime()), + alias_log: true, + alias_dir: true, + adopt_log: true, + adopt_dir: true }; + keycode = { UP: 38, DOWN: 40, @@ -62,19 +49,30 @@ BACKSPACE: 8, ESCAPE: 27 }; - template = '
            clearreplay?

            multiline

                '; + + template = "
                \n \n \n clearreplay\n ?\n

                multiline

                \n
                \n \n

                \n
                  \n
                    \n
                    \n
                    "; + $ = null; + settings = null; + $els = null; + active = false; + deferred = false; + loaded = false; + showing_multi_line = false; + history_index = 0; + loaded_scripts = { jquery_js: false, coffeescript_js: false }; + /* Track the input history with a list of objects like this... ` @@ -85,62 +83,130 @@ ` ...in order of entry by the user. */ + history = []; + /* Loads settings, and initiates the rendering of the widget and loading of previous history from localStorage. */ + init = function(opts) { - if (opts == null) { - opts = {}; - } + if (opts == null) opts = {}; if (loaded_scripts.jquery_js && loaded_scripts.coffeescript_js) { $ = window.jQuery; + if (settings.adopt_log) { + if (typeof console === "undefined" || console === null) console = {}; + console.log = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return ctLog.apply(null, args); + }; + } + if (settings.adopt_dir) { + if (typeof console === "undefined" || console === null) console = {}; + console.dir = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + }; + } + if (settings.alias_log) { + if (window.log == null) { + window.log = function() { + return console.log.apply(console, arguments); + }; + } + } + if (settings.alias_dir) { + if (window.dir == null) { + window.dir = function() { + return console.dir.apply(console, arguments); + }; + } + } template = template.replace(/__ID__/g, settings.widget_id); renderWidget(); - if (settings.local_storage) { - loadFromStorage(); - } - return loaded = true; + if (settings.local_storage) loadFromStorage(); + loaded = true; + } + return loaded; + }; + + /* + Log one or more values to the CoffeeTable widget. Used as the implementation of + `console.log` if `settings.adopt_log` is `true` and `console.log` isn't already + available. + */ + + ctLog = function() { + var arg, args, _fn, _i, _len; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + _fn = function() { + var output; + output = arg.toString().replace(/\'/g, "\\'").replace(/\"/g, '\\"'); + return execute("'log: " + output + "'"); + }; + for (_i = 0, _len = args.length; _i < _len; _i++) { + arg = args[_i]; + _fn(); } }; + + /* + Dir one or more values to the CoffeeTable widget. Used as the implementation of + `console.dir` if `settings.adopt_dir` is `true` and `console.dir` isn't already + available. + */ + + ctDir = function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + }; + /* Generate the auto-suggest list based on the object represented by the supplied text. Done by iterating over the objects loaded, starting with `window` and progressing through the subsequent properties. */ + buildAutosuggest = function(text, e) { - var attribute, match_list, matches, to_match, token, tokens, value, working_items, _i, _len, _ref3, _ref4; + var attribute, match_list, matches, to_match, token, tokens, value, working_items, _i, _len, _ref, _ref2; if (e.which === keycode.ESCAPE || (e.which === keycode.BACKSPACE && text.length === 0 && $els.autosuggest_list.html().length !== 0)) { return $els.autosuggest_list.html(''); } else { tokens = text.split('.'); working_items = [[window, 'window']]; - _ref3 = tokens.slice(0, tokens.length - 1); - for (_i = 0, _len = _ref3.length; _i < _len; _i++) { - token = _ref3[_i]; + _ref = tokens.slice(0, (tokens.length - 1)); + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + token = _ref[_i]; token = token.replace('(', '').replace(')', ''); if (token.length > 0) { working_items.push([working_items[working_items.length - 1][0][token], token]); } } match_list = []; - to_match = new RegExp('^' + tokens[tokens.length - 1]); - _ref4 = working_items[working_items.length - 1][0]; - for (attribute in _ref4) { - value = _ref4[attribute]; - matches = to_match.exec(attribute); - if ((matches != null ? matches.length : void 0) > 0) { - match_list.push([attribute, typeof value]); + try { + to_match = new RegExp('^' + tokens[tokens.length - 1]); + _ref2 = working_items[working_items.length - 1][0]; + for (attribute in _ref2) { + value = _ref2[attribute]; + matches = to_match.exec(attribute); + if ((matches != null ? matches.length : void 0) > 0) { + match_list.push([attribute, typeof value]); + } } + match_list.sort(); + return renderAutosuggest(working_items, match_list); + } catch (e) { + } - match_list.sort(); - return renderAutosuggest(working_items, match_list); } }; + /* Build and show the ul for the auto-suggest matched items. */ + renderAutosuggest = function(working_items, match_list) { var html, item, list, _i, _j, _len, _len2; list = ''; @@ -156,10 +222,12 @@ $els.autosuggest_list.html(html); return resizeWidget(); }; + /* If localStorage is supported, try loading previous command history. Throws an error if called when localStorage is not supported. */ + loadFromStorage = function() { var entry_source, previous_data, _i, _len, _results; if (!(typeof localStorage !== "undefined" && localStorage !== null)) { @@ -177,9 +245,11 @@ } } }; + /* Clear the display of the history and excute each item in the history. */ + replayHistory = function() { var entries_to_replay, entry, _i, _len, _results; entries_to_replay = (function() { @@ -201,21 +271,19 @@ } return _results; }; + /* Compile and evaluate the specified CoffeeScript source string. Optionally, the execution can be a dry run and the source won't actually be executed. */ + execute = function(source, dry_run) { - var compiled_js, cs_error, entry, entry_sources, error_output, js_error, new_li, result, this_result_index; - if (dry_run == null) { - dry_run = false; - } + var assembleList, buildLI, clean_result, compiled_js, cs_error, entry, entry_sources, error_output, expand_button, expand_button_template, js_error, load_button, new_li, result, this_result_index; + if (dry_run == null) dry_run = false; if (source === 'localStorage.clear()') { return clearHistory(); } else { - if (history.length === 0) { - $els.history_list.empty(); - } + if (history.length === 0) $els.history_list.empty(); history_index = -1; error_output = null; cs_error = false; @@ -238,21 +306,81 @@ } else if (dry_run) { result = compiled_js; } - if (error_output != null) { - result = error_output; - } + if (error_output != null) result = error_output; history.push({ result: result, source: source }); this_result_index = history.length - 1; - new_li = $("
                  • ").text("" + this_result_index + ": " + result); + load_button = $(''); + clean_result = result.toString().replace(/&/g, "&").replace(//g, ">"); + new_li = $("
                  • \n " + this_result_index + ":\n " + clean_result + "\n
                  • ").append(load_button); + if (typeof result === 'object') { + expand_button_template = ''; + buildLI = function(prop, val) { + return $("
                  • \n " + prop + ":\n " + val + "\n
                  • "); + }; + assembleList = function(obj, el) { + var children_ul, prop, prop_list, val, _i, _len, _results; + el.children('ul').remove(); + children_ul = $('
                      '); + el.append(children_ul); + prop_list = (function() { + var _results; + _results = []; + for (prop in obj) { + val = obj[prop]; + _results.push(prop); + } + return _results; + })(); + prop_list.sort(); + _results = []; + for (_i = 0, _len = prop_list.length; _i < _len; _i++) { + prop = prop_list[_i]; + _results.push((function() { + var child_expand_button, child_li, p_name, p_val; + p_name = prop; + p_val = obj[prop]; + child_li = buildLI(p_name, p_val); + children_ul.append(child_li); + if (typeof p_val === 'object') { + child_expand_button = $(expand_button_template); + child_li.children('span.value').prepend(child_expand_button); + return child_expand_button.click(function(e) { + e.stopPropagation(); + if (child_li.hasClass('open')) { + child_li.removeClass('open'); + return child_li.find('ul').remove(); + } else { + child_li.addClass('open'); + children_ul.show(); + return assembleList(p_val, child_li); + } + }); + } + })()); + } + return _results; + }; + expand_button = $(expand_button_template); + new_li.children('span.result').before(expand_button); + expand_button.click(function() { + if (new_li.hasClass('open')) { + new_li.removeClass('open'); + return new_li.find('ul').remove(); + } else { + new_li.addClass('open'); + return assembleList(result, new_li); + } + }); + } if (js_error) { new_li.addClass('js-error'); } else if (cs_error) { new_li.addClass('cs-error'); } - new_li.click(function() { + load_button.click(function() { loadPrevious(false, this_result_index); return $els.textarea.focus(); }); @@ -273,9 +401,11 @@ return $els.replay_history.show(); } }; + /* Empty the history in memory, disk, and page. */ + clearHistory = function() { var autosuggest, autsuggest_query; $els.history_list.empty(); @@ -289,9 +419,11 @@ $els.clear_history.hide(); return $els.replay_history.hide(); }; + /* Show instructions for how to use the widget, depending on multi-line setting. */ + renderInstructions = function() { var instructions; if (history.length === 0) { @@ -303,18 +435,16 @@ return $els.instructions.text(instructions); } }; + /* Given a history index to load, set the current source input to that entry's source. Supports going either direction through the history; pass `true` for `forward` to move forward in the list. */ + loadPrevious = function(forward, target_index) { - if (forward == null) { - forward = false; - } - if (target_index != null) { - history_index = target_index + 1; - } + if (forward == null) forward = false; + if (target_index != null) history_index = target_index + 1; if (history_index === -1 || history_index === 0) { history_index = history.length - 1; } else { @@ -330,10 +460,12 @@ return $els.textarea.selectionEnd = 0; } }; + /* Switch between single-line and multi-line modes, disabling auto-suggest if in multi-line mode. */ + toggleMultiLine = function() { var new_height; showing_multi_line = !showing_multi_line; @@ -342,18 +474,18 @@ $els.autosuggest_list.hide(); } else { new_height = ''; - if (settings.auto_suggest) { - $els.autosuggest_list.show(); - } + if (settings.auto_suggest) $els.autosuggest_list.show(); } $els.textarea.css('height', new_height).focus(); return renderInstructions(); }; + /* Set the max-height and max-width of the widget and auto-suggest list to keep it visible in the window. (Being absolutely positioned, it doesn't affect the scrolling of the overall window.) */ + resizeWidget = function() { var height, width; height = "" + (window.innerHeight - 140) + "px"; @@ -364,9 +496,11 @@ }); return $els.history_list.css('max-height', height); }; + /* Build and display the widget elements. */ + renderWidget = function() { var widget; widget = $(template); @@ -392,11 +526,14 @@ }); renderInstructions(); bindEvents(); - return widget.appendTo('body'); + widget.appendTo('body'); + return resizeWidget(); }; + /* Setup the various events for the control elements. */ + bindEvents = function() { $els.button.click(function() { active = !active; @@ -455,24 +592,22 @@ return buildAutosuggest(entered_source, e); } }); - if (settings.multi_line) { - $els.toggle_multiline.click(); - } + if (settings.multi_line) $els.toggle_multiline.click(); return $(window).resize(function() { return resizeWidget(); }); }; + /* Remove the widget's element from the DOM and clear the `CoffeeTable` object. */ + unloadWidget = function() { - $els.widget.remove(); + if ($els != null) $els.widget.remove(); window.CoffeeTable = null; return delete window.CoffeeTable; }; - if ((_ref3 = window.CoffeeTable) != null) { - _ref3.unload(); - } + window.CoffeeTable = { show: function() { $els.widget.show(); @@ -495,6 +630,8 @@ return this; }, init: function(opts) { + var _ref; + if ((_ref = window.CoffeeTable) != null) _ref.unload(); setSettings(opts); preInit(); return this; @@ -505,11 +642,23 @@ }, active: function() { return active; + }, + log: function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return ctLog.apply(null, args); + }, + dir: function() { + var args; + args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; + return ctDir.apply(null, args); } }; + /* Load the default settings and apply user overrides. */ + setSettings = function(opts) { var key, value, _results; settings = defaults; @@ -520,9 +669,11 @@ } return _results; }; + /* Helper for loading external scripts. */ + loadScript = function(script_name) { var head, script; head = document.getElementsByTagName('head')[0]; @@ -536,9 +687,11 @@ }; return head.appendChild(script); }; + /* Helper for prepping settings and checking if dependencies are loaded. */ + preInit = function() { active = false; loaded_scripts.jquery_js = window.jQuery != null; @@ -559,9 +712,11 @@ } return init(); }; + /* Automatically load dependencies and initialize the widget, unless deferred. */ + document.addEventListener('DOMContentLoaded', function() { document.removeEventListener('DOMContentLoaded', arguments.callee, false); if (!deferred && !loaded) { @@ -569,4 +724,5 @@ return preInit(); } }, false); + }).call(this); diff --git a/src/Cakefile b/src/Cakefile index a5150d6..6e25bea 100644 --- a/src/Cakefile +++ b/src/Cakefile @@ -9,7 +9,7 @@ task 'build', 'compile coffee, minify js, build annotated source', -> throw err if err console.log stdout console.log stderr - exec 'python ~/forks/pycco/pycco/main.py coffeetable.coffee', (err, stdout, stderr) -> - throw err if err - console.log stdout - console.log stderr \ No newline at end of file + # exec 'python ~/forks/pycco/pycco/main.py coffeetable.coffee', (err, stdout, stderr) -> + # throw err if err + # console.log stdout + # console.log stderr \ No newline at end of file diff --git a/src/coffeetable.coffee b/src/coffeetable.coffee index d1012fe..a2f39c0 100644 --- a/src/coffeetable.coffee +++ b/src/coffeetable.coffee @@ -210,24 +210,28 @@ buildAutosuggest = (text, e) -> if token.length > 0 working_items.push([working_items[working_items.length-1][0][token], token]) - # Set up the matching pattern to match the last token entered. + # Set up the matching pattern to match the last token entered. This + # process is wrapped in a try/catch to prevent faulty regex matches + # from polluting the console with (inconsequential) errors. match_list = [] - to_match = new RegExp('^' + tokens[tokens.length-1]) - - # Iterate over the properties of the latest working_item, running the - # matching pattern against the name of each one, building a list of - # the properties that match. - for attribute, value of working_items[working_items.length-1][0] - matches = to_match.exec(attribute) - if matches?.length > 0 - # Push the name and type of property (for color coding) - match_list.push([attribute, typeof value]) - - # Sort the auto-suggest matches in ascending alphabetical order - match_list.sort() - - # Display the list of matches - renderAutosuggest(working_items, match_list) + try + to_match = new RegExp('^' + tokens[tokens.length-1]) + + # Iterate over the properties of the latest working_item, running the + # matching pattern against the name of each one, building a list of + # the properties that match. + for attribute, value of working_items[working_items.length-1][0] + matches = to_match.exec(attribute) + if matches?.length > 0 + # Push the name and type of property (for color coding) + match_list.push([attribute, typeof value]) + + # Sort the auto-suggest matches in ascending alphabetical order + match_list.sort() + + # Display the list of matches + renderAutosuggest(working_items, match_list) + catch e # ### renderAutosuggest diff --git a/src/coffeetable.js b/src/coffeetable.js deleted file mode 100644 index 63b1df2..0000000 --- a/src/coffeetable.js +++ /dev/null @@ -1,724 +0,0 @@ - -/* - -# CoffeeTable vDEV - -A drop-in, CoffeeScript-fluent console for web pages. - -* [Demo + Instructions](http://code.alecperkins.net/coffeetable) -* [GitHub repo](https://github.com/alecperkins/coffeetable) - -## To use - -Load `coffeetable-min.js` into the page: -`` -...and the widget will automatically initialize. -*/ - -(function() { - var $, $els, active, bindEvents, buildAutosuggest, clearHistory, ctDir, ctLog, defaults, deferred, execute, history, history_index, init, keycode, loadFromStorage, loadPrevious, loadScript, loaded, loaded_scripts, preInit, renderAutosuggest, renderInstructions, renderWidget, replayHistory, resizeWidget, setSettings, settings, showing_multi_line, template, toggleMultiLine, unloadWidget, - __slice = Array.prototype.slice; - - defaults = { - autoload_coffee_script: true, - autoload_jquery: true, - coffeescript_js: 'http://code.alecperkins.net/coffeetable/lib/coffee_script-1.1.1-min.js', - jquery_js: 'http://code.alecperkins.net/coffeetable/lib/jquery-1.6.2-min.js', - local_storage: true, - ls_key: 'coffee-table', - clear_on_load: false, - replay_on_load: false, - multi_line: false, - indent: ' ', - auto_suggest: true, - widget_position: 'fixed', - widget_top: '5px', - widget_right: '5px', - widget_id: "CoffeeTable-" + ((new Date()).getTime()), - alias_log: true, - alias_dir: true, - adopt_log: true, - adopt_dir: true - }; - - keycode = { - UP: 38, - DOWN: 40, - ENTER: 13, - TAB: 9, - BACKSPACE: 8, - ESCAPE: 27 - }; - - template = "
                      \n \n \n clearreplay\n ?\n

                      multiline

                      \n
                      \n \n

                      \n
                        \n
                          \n
                          \n
                          "; - - $ = null; - - settings = null; - - $els = null; - - active = false; - - deferred = false; - - loaded = false; - - showing_multi_line = false; - - history_index = 0; - - loaded_scripts = { - jquery_js: false, - coffeescript_js: false - }; - - /* - Track the input history with a list of objects like this... - ` - { - source: 'CoffeeScript code', - result: 'result of eval' - } - ` - ...in order of entry by the user. - */ - - history = []; - - /* - Loads settings, and initiates the rendering of the widget and loading of - previous history from localStorage. - */ - - init = function(opts) { - if (opts == null) opts = {}; - if (loaded_scripts.jquery_js && loaded_scripts.coffeescript_js) { - $ = window.jQuery; - if (settings.adopt_log) { - if (typeof console === "undefined" || console === null) console = {}; - console.log = function() { - var args; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - return ctLog.apply(null, args); - }; - } - if (settings.adopt_dir) { - if (typeof console === "undefined" || console === null) console = {}; - console.dir = function() { - var args; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - }; - } - if (settings.alias_log) { - if (window.log == null) { - window.log = function() { - return console.log.apply(console, arguments); - }; - } - } - if (settings.alias_dir) { - if (window.dir == null) { - window.dir = function() { - return console.dir.apply(console, arguments); - }; - } - } - template = template.replace(/__ID__/g, settings.widget_id); - renderWidget(); - if (settings.local_storage) loadFromStorage(); - loaded = true; - } - return loaded; - }; - - /* - Log one or more values to the CoffeeTable widget. Used as the implementation of - `console.log` if `settings.adopt_log` is `true` and `console.log` isn't already - available. - */ - - ctLog = function() { - var arg, args, _fn, _i, _len; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - _fn = function() { - var output; - output = arg.toString().replace(/\'/g, "\\'").replace(/\"/g, '\\"'); - return execute("'log: " + output + "'"); - }; - for (_i = 0, _len = args.length; _i < _len; _i++) { - arg = args[_i]; - _fn(); - } - }; - - /* - Dir one or more values to the CoffeeTable widget. Used as the implementation of - `console.dir` if `settings.adopt_dir` is `true` and `console.dir` isn't already - available. - */ - - ctDir = function() { - var args; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - }; - - /* - Generate the auto-suggest list based on the object represented by the - supplied text. Done by iterating over the objects loaded, starting with - `window` and progressing through the subsequent properties. - */ - - buildAutosuggest = function(text, e) { - var attribute, match_list, matches, to_match, token, tokens, value, working_items, _i, _len, _ref, _ref2; - if (e.which === keycode.ESCAPE || (e.which === keycode.BACKSPACE && text.length === 0 && $els.autosuggest_list.html().length !== 0)) { - return $els.autosuggest_list.html(''); - } else { - tokens = text.split('.'); - working_items = [[window, 'window']]; - _ref = tokens.slice(0, (tokens.length - 1)); - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - token = _ref[_i]; - token = token.replace('(', '').replace(')', ''); - if (token.length > 0) { - working_items.push([working_items[working_items.length - 1][0][token], token]); - } - } - match_list = []; - to_match = new RegExp('^' + tokens[tokens.length - 1]); - _ref2 = working_items[working_items.length - 1][0]; - for (attribute in _ref2) { - value = _ref2[attribute]; - matches = to_match.exec(attribute); - if ((matches != null ? matches.length : void 0) > 0) { - match_list.push([attribute, typeof value]); - } - } - match_list.sort(); - return renderAutosuggest(working_items, match_list); - } - }; - - /* - Build and show the ul for the auto-suggest matched items. - */ - - renderAutosuggest = function(working_items, match_list) { - var html, item, list, _i, _j, _len, _len2; - list = ''; - for (_i = 0, _len = working_items.length; _i < _len; _i++) { - item = working_items[_i]; - list += item[1] + '.'; - } - html = "
                        • " + list + "
                        • "; - for (_j = 0, _len2 = match_list.length; _j < _len2; _j++) { - item = match_list[_j]; - html += "
                        • " + item[0] + "
                        • "; - } - $els.autosuggest_list.html(html); - return resizeWidget(); - }; - - /* - If localStorage is supported, try loading previous command history. - Throws an error if called when localStorage is not supported. - */ - - loadFromStorage = function() { - var entry_source, previous_data, _i, _len, _results; - if (!(typeof localStorage !== "undefined" && localStorage !== null)) { - throw 'localStorage not supported by this browser. History will not be persisted.'; - } else { - previous_data = localStorage.getItem(settings.ls_key); - if (previous_data != null) { - previous_data = JSON.parse(previous_data); - _results = []; - for (_i = 0, _len = previous_data.length; _i < _len; _i++) { - entry_source = previous_data[_i]; - _results.push(execute(entry_source, !settings.replay_on_load)); - } - return _results; - } - } - }; - - /* - Clear the display of the history and excute each item in the history. - */ - - replayHistory = function() { - var entries_to_replay, entry, _i, _len, _results; - entries_to_replay = (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = history.length; _i < _len; _i++) { - entry = history[_i]; - _results.push(entry.source); - } - return _results; - })(); - history = []; - $els.history_list.empty(); - history_index = 0; - _results = []; - for (_i = 0, _len = entries_to_replay.length; _i < _len; _i++) { - entry = entries_to_replay[_i]; - _results.push(execute(entry)); - } - return _results; - }; - - /* - Compile and evaluate the specified CoffeeScript source string. Optionally, - the execution can be a dry run and the source won't actually be executed. - */ - - execute = function(source, dry_run) { - var assembleList, buildLI, clean_result, compiled_js, cs_error, entry, entry_sources, error_output, expand_button, expand_button_template, js_error, load_button, new_li, result, this_result_index; - if (dry_run == null) dry_run = false; - if (source === 'localStorage.clear()') { - return clearHistory(); - } else { - if (history.length === 0) $els.history_list.empty(); - history_index = -1; - error_output = null; - cs_error = false; - js_error = false; - try { - compiled_js = CoffeeScript.compile(source, { - bare: true - }); - } catch (error) { - cs_error = true; - error_output = error; - } - if (!(error_output != null) && !dry_run) { - try { - result = eval.call(window, compiled_js); - } catch (eval_error) { - js_error = true; - error_output = eval_error; - } - } else if (dry_run) { - result = compiled_js; - } - if (error_output != null) result = error_output; - history.push({ - result: result, - source: source - }); - this_result_index = history.length - 1; - load_button = $(''); - clean_result = result.toString().replace(/&/g, "&").replace(//g, ">"); - new_li = $("
                        • \n " + this_result_index + ":\n " + clean_result + "\n
                        • ").append(load_button); - if (typeof result === 'object') { - expand_button_template = ''; - buildLI = function(prop, val) { - return $("
                        • \n " + prop + ":\n " + val + "\n
                        • "); - }; - assembleList = function(obj, el) { - var children_ul, prop, prop_list, val, _i, _len, _results; - el.children('ul').remove(); - children_ul = $('
                            '); - el.append(children_ul); - prop_list = (function() { - var _results; - _results = []; - for (prop in obj) { - val = obj[prop]; - _results.push(prop); - } - return _results; - })(); - prop_list.sort(); - _results = []; - for (_i = 0, _len = prop_list.length; _i < _len; _i++) { - prop = prop_list[_i]; - _results.push((function() { - var child_expand_button, child_li, p_name, p_val; - p_name = prop; - p_val = obj[prop]; - child_li = buildLI(p_name, p_val); - children_ul.append(child_li); - if (typeof p_val === 'object') { - child_expand_button = $(expand_button_template); - child_li.children('span.value').prepend(child_expand_button); - return child_expand_button.click(function(e) { - e.stopPropagation(); - if (child_li.hasClass('open')) { - child_li.removeClass('open'); - return child_li.find('ul').remove(); - } else { - child_li.addClass('open'); - children_ul.show(); - return assembleList(p_val, child_li); - } - }); - } - })()); - } - return _results; - }; - expand_button = $(expand_button_template); - new_li.children('span.result').before(expand_button); - expand_button.click(function() { - if (new_li.hasClass('open')) { - new_li.removeClass('open'); - return new_li.find('ul').remove(); - } else { - new_li.addClass('open'); - return assembleList(result, new_li); - } - }); - } - if (js_error) { - new_li.addClass('js-error'); - } else if (cs_error) { - new_li.addClass('cs-error'); - } - load_button.click(function() { - loadPrevious(false, this_result_index); - return $els.textarea.focus(); - }); - new_li.prependTo($els.history_list); - entry_sources = (function() { - var _i, _len, _results; - _results = []; - for (_i = 0, _len = history.length; _i < _len; _i++) { - entry = history[_i]; - _results.push(entry.source); - } - return _results; - })(); - if (typeof localStorage !== "undefined" && localStorage !== null) { - localStorage.setItem(settings.ls_key, JSON.stringify(entry_sources)); - } - $els.clear_history.show(); - return $els.replay_history.show(); - } - }; - - /* - Empty the history in memory, disk, and page. - */ - - clearHistory = function() { - var autosuggest, autsuggest_query; - $els.history_list.empty(); - history = []; - if (typeof localStorage !== "undefined" && localStorage !== null) { - localStorage.removeItem(settings.ls_key); - } - renderInstructions(); - autosuggest = [[window, 'window']]; - autsuggest_query = ''; - $els.clear_history.hide(); - return $els.replay_history.hide(); - }; - - /* - Show instructions for how to use the widget, depending on multi-line setting. - */ - - renderInstructions = function() { - var instructions; - if (history.length === 0) { - if (showing_multi_line) { - instructions = 'type CoffeeScript, press '; - } else { - instructions = 'type CoffeeScript, press '; - } - return $els.instructions.text(instructions); - } - }; - - /* - Given a history index to load, set the current source input to that entry's - source. Supports going either direction through the history; pass `true` for - `forward` to move forward in the list. - */ - - loadPrevious = function(forward, target_index) { - if (forward == null) forward = false; - if (target_index != null) history_index = target_index + 1; - if (history_index === -1 || history_index === 0) { - history_index = history.length - 1; - } else { - if (forward) { - history_index += 1; - } else { - history_index -= 1; - } - } - if (history.length > 0) { - $els.textarea.val(history[history_index].source); - $els.textarea.selectionStart = 0; - return $els.textarea.selectionEnd = 0; - } - }; - - /* - Switch between single-line and multi-line modes, disabling auto-suggest if in - multi-line mode. - */ - - toggleMultiLine = function() { - var new_height; - showing_multi_line = !showing_multi_line; - if (showing_multi_line) { - new_height = '4em'; - $els.autosuggest_list.hide(); - } else { - new_height = ''; - if (settings.auto_suggest) $els.autosuggest_list.show(); - } - $els.textarea.css('height', new_height).focus(); - return renderInstructions(); - }; - - /* - Set the max-height and max-width of the widget and auto-suggest list to keep - it visible in the window. (Being absolutely positioned, it doesn't affect the - scrolling of the overall window.) - */ - - resizeWidget = function() { - var height, width; - height = "" + (window.innerHeight - 140) + "px"; - width = "" + (window.innerWidth - 255) + "px"; - $els.autosuggest_list.css({ - 'max-height': height, - 'max-width': width - }); - return $els.history_list.css('max-height', height); - }; - - /* - Build and display the widget elements. - */ - - renderWidget = function() { - var widget; - widget = $(template); - $els = { - widget: widget, - textarea: widget.find('textarea'), - autosuggest_list: widget.find('ul.autosuggest'), - history_list: widget.find('ul.history'), - button: widget.find('button'), - div: widget.find('div'), - clear_history: widget.find('span.clear'), - replay_history: widget.find('span.replay'), - a: widget.find('a'), - toggle_multiline: widget.find('input'), - p: widget.find('p.mode'), - instructions: widget.find('p.instructions'), - li: widget.find('li') - }; - $els.widget.css({ - 'position': "" + settings.widget_position, - 'top': "" + settings.widget_top, - 'right': "" + settings.widget_right - }); - renderInstructions(); - bindEvents(); - widget.appendTo('body'); - return resizeWidget(); - }; - - /* - Setup the various events for the control elements. - */ - - bindEvents = function() { - $els.button.click(function() { - active = !active; - if (active) { - $els.div.show(); - $els.p.show(); - $els.textarea.focus(); - return $els.button.addClass('active'); - } else { - $els.div.hide(); - $els.p.hide(); - return $els.button.removeClass('active'); - } - }); - $els.clear_history.click(function() { - return clearHistory(); - }); - $els.replay_history.click(function() { - $els.history_list.empty(); - return setTimeout(function() { - return replayHistory(); - }, 100); - }); - $els.toggle_multiline.click(function() { - return toggleMultiLine(); - }); - $els.textarea.bind('keydown', function(e) { - var end, entered_source, start; - entered_source = $els.textarea.val(); - if (this.selectionStart === 0) { - if (e.which === keycode.UP) { - loadPrevious(); - } else if (e.which === keycode.DOWN) { - loadPrevious(true); - } - } - if (e.which === keycode.ENTER && (!showing_multi_line || e.shiftKey)) { - e.preventDefault(); - if (entered_source !== '') { - execute(entered_source); - return $els.textarea.val(''); - } - } else if (e.which === keycode.TAB) { - e.preventDefault(); - start = this.selectionStart; - end = this.selectionEnd; - this.value = this.value.substring(0, start) + settings.indent + this.value.substring(start); - this.selectionStart = start + settings.indent.length; - return this.selectionEnd = start + settings.indent.length; - } - }); - $els.textarea.bind('keyup', function(e) { - var entered_source; - entered_source = $els.textarea.val(); - if (!settings.multi_line && settings.auto_suggest) { - return buildAutosuggest(entered_source, e); - } - }); - if (settings.multi_line) $els.toggle_multiline.click(); - return $(window).resize(function() { - return resizeWidget(); - }); - }; - - /* - Remove the widget's element from the DOM and clear the `CoffeeTable` object. - */ - - unloadWidget = function() { - if ($els != null) $els.widget.remove(); - window.CoffeeTable = null; - return delete window.CoffeeTable; - }; - - window.CoffeeTable = { - show: function() { - $els.widget.show(); - return this; - }, - hide: function() { - $els.widget.hide(); - return this; - }, - clear: function() { - clearHistory(); - return this; - }, - replay: function() { - replayHistory(); - return this; - }, - deferInit: function() { - deferred = true; - return this; - }, - init: function(opts) { - var _ref; - if ((_ref = window.CoffeeTable) != null) _ref.unload(); - setSettings(opts); - preInit(); - return this; - }, - unload: function() { - unloadWidget(); - return this; - }, - active: function() { - return active; - }, - log: function() { - var args; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - return ctLog.apply(null, args); - }, - dir: function() { - var args; - args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; - return ctDir.apply(null, args); - } - }; - - /* - Load the default settings and apply user overrides. - */ - - setSettings = function(opts) { - var key, value, _results; - settings = defaults; - _results = []; - for (key in opts) { - value = opts[key]; - _results.push(settings[key] = value); - } - return _results; - }; - - /* - Helper for loading external scripts. - */ - - loadScript = function(script_name) { - var head, script; - head = document.getElementsByTagName('head')[0]; - script = document.createElement('script'); - script.type = 'application/javascript'; - script.src = settings[script_name]; - script.async = true; - script.onload = function() { - loaded_scripts[script_name] = true; - return init(); - }; - return head.appendChild(script); - }; - - /* - Helper for prepping settings and checking if dependencies are loaded. - */ - - preInit = function() { - active = false; - loaded_scripts.jquery_js = window.jQuery != null; - loaded_scripts.coffeescript_js = window.CoffeeScript != null; - if (!loaded_scripts.coffeescript_js) { - if (!settings.autoload_coffee_script) { - throw 'CoffeeTable requires coffee_script.js'; - } else { - loadScript('coffeescript_js'); - } - } - if (!loaded_scripts.jquery_js) { - if (!settings.autoload_jquery) { - throw 'CoffeeTable requires jQuery'; - } else { - loadScript('jquery_js'); - } - } - return init(); - }; - - /* - Automatically load dependencies and initialize the widget, unless deferred. - */ - - document.addEventListener('DOMContentLoaded', function() { - document.removeEventListener('DOMContentLoaded', arguments.callee, false); - if (!deferred && !loaded) { - setSettings(); - return preInit(); - } - }, false); - -}).call(this); diff --git a/src/dev.html b/src/dev.html index 92d29ab..40af50c 100644 --- a/src/dev.html +++ b/src/dev.html @@ -1,6 +1,6 @@ - + CoffeeTable, go!