From 3a9ede6e8c65d62949c39561c5f61ca9ff63cad2 Mon Sep 17 00:00:00 2001 From: YNH Webdev Date: Sun, 22 Sep 2013 18:30:48 +0200 Subject: [PATCH] Restructure callbacks --- examples/widgetapp/application.e | 2 +- examples/widgetapp/base_page.e | 8 +- examples/widgetapp/typeahead.min.js | 1143 ++++++++++++++- examples/widgetapp/widget.coffee | 205 +-- examples/widgetapp/widget.js | 1280 +++++++++-------- .../input/wsf_checkbox_list_control.e | 2 +- .../wsf_js_widget/webcontrol/wsf_control.e | 11 +- .../webcontrol/wsf_form_element_control.e | 17 +- .../webcontrol/wsf_multi_control.e | 28 +- .../webcontrol/wsf_page_control.e | 49 +- 10 files changed, 2023 insertions(+), 722 deletions(-) diff --git a/examples/widgetapp/application.e b/examples/widgetapp/application.e index de75b387..09095d28 100644 --- a/examples/widgetapp/application.e +++ b/examples/widgetapp/application.e @@ -135,7 +135,7 @@ feature -- Execution local f: WSF_FILE_RESPONSE do - create f.make_html (name) + create f.make (name) response.send (f) end diff --git a/examples/widgetapp/base_page.e b/examples/widgetapp/base_page.e index 083d95cb..14ee2efb 100644 --- a/examples/widgetapp/base_page.e +++ b/examples/widgetapp/base_page.e @@ -20,10 +20,10 @@ feature create container.make_multi_control ("container") container.add_class ("container") create navbar.make_navbar ("Sample Page") - navbar.add_element (create {WSF_BASIC_CONTROL}.make_with_body ("a", "href=%"/%"", "Home")) - navbar.add_element (create {WSF_BASIC_CONTROL}.make_with_body ("a", "href=%"/grid%"", "Grid")) - navbar.add_element (create {WSF_BASIC_CONTROL}.make_with_body ("a", "href=%"/repeater%"", "Repeater")) - navbar.add_element_right (create {WSF_BASIC_CONTROL}.make_with_body ("a", "href=%"#%"", "About")) + navbar.nav.add_control (create {WSF_BASIC_CONTROL}.make_with_body ("a", "href=%"/%"", "Home")) + navbar.nav.add_control (create {WSF_BASIC_CONTROL}.make_with_body ("a", "href=%"/grid%"", "Grid")) + navbar.nav.add_control (create {WSF_BASIC_CONTROL}.make_with_body ("a", "href=%"/repeater%"", "Repeater")) + navbar.nav_right.add_control (create {WSF_BASIC_CONTROL}.make_with_body ("a", "href=%"#%"", "About")) container.add_control (navbar) control := container end diff --git a/examples/widgetapp/typeahead.min.js b/examples/widgetapp/typeahead.min.js index c085f8a8..8cb76c02 100644 --- a/examples/widgetapp/typeahead.min.js +++ b/examples/widgetapp/typeahead.min.js @@ -1,7 +1,1146 @@ /*! - * typeahead.js 0.9.3 + * typeahead.js 0.9.3 (MODIFIED BY YNH) * https://github.com/twitter/typeahead * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT */ -!function(a){var b="0.9.3",c={isMsie:function(){var a=/(msie) ([\w.]+)/i.exec(navigator.userAgent);return a?parseInt(a[2],10):!1},isBlankString:function(a){return!a||/^\s*$/.test(a)},escapeRegExChars:function(a){return a.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,"\\$&")},isString:function(a){return"string"==typeof a},isNumber:function(a){return"number"==typeof a},isArray:a.isArray,isFunction:a.isFunction,isObject:a.isPlainObject,isUndefined:function(a){return"undefined"==typeof a},bind:a.proxy,bindAll:function(b){var c;for(var d in b)a.isFunction(c=b[d])&&(b[d]=a.proxy(c,b))},indexOf:function(a,b){for(var c=0;c=j?(clearTimeout(e),e=null,g=i,f=a.apply(c,d)):e||(e=setTimeout(h,j)),f}},tokenizeQuery:function(b){return a.trim(b).toLowerCase().split(/[\s]+/)},tokenizeText:function(b){return a.trim(b).toLowerCase().split(/[\s\-_]+/)},getProtocol:function(){return location.protocol},noop:function(){}},d=function(){var a=/\s+/;return{on:function(b,c){var d;if(!c)return this;for(this._callbacks=this._callbacks||{},b=b.split(a);d=b.shift();)this._callbacks[d]=this._callbacks[d]||[],this._callbacks[d].push(c);return this},trigger:function(b,c){var d,e;if(!this._callbacks)return this;for(b=b.split(a);d=b.shift();)if(e=this._callbacks[d])for(var f=0;fa;a++)(b=f.key(a)).match(this.keyMatcher)&&c.push(b.replace(this.keyMatcher,""));for(a=c.length;a--;)this.remove(c[a]);return this},isExpired:function(a){var d=e(f.getItem(this._ttlKey(a)));return c.isNumber(d)&&b()>d?!0:!1}}:{get:c.noop,set:c.noop,remove:c.noop,clear:c.noop,isExpired:c.noop},c.mixin(a.prototype,g),a}(),g=function(){function a(a){c.bindAll(this),a=a||{},this.sizeLimit=a.sizeLimit||10,this.cache={},this.cachedKeysByAge=[]}return c.mixin(a.prototype,{get:function(a){return this.cache[a]},set:function(a,b){var c;this.cachedKeysByAge.length===this.sizeLimit&&(c=this.cachedKeysByAge.shift(),delete this.cache[c]),this.cache[a]=b,this.cachedKeysByAge.push(a)}}),a}(),h=function(){function b(a){c.bindAll(this),a=c.isString(a)?{url:a}:a,i=i||new g,h=c.isNumber(a.maxParallelRequests)?a.maxParallelRequests:h||6,this.url=a.url,this.wildcard=a.wildcard||"%QUERY",this.filter=a.filter,this.replace=a.replace,this.ajaxSettings={type:"get",cache:a.cache,timeout:a.timeout,dataType:a.dataType||"json",beforeSend:a.beforeSend},this._get=(/^throttle$/i.test(a.rateLimitFn)?c.throttle:c.debounce)(this._get,a.rateLimitWait||300)}function d(){j++}function e(){j--}function f(){return h>j}var h,i,j=0,k={};return c.mixin(b.prototype,{_get:function(a,b){function c(c){var e=d.filter?d.filter(c):c;b&&b(e),i.set(a,c)}var d=this;f()?this._sendRequest(a).done(c):this.onDeckRequestArgs=[].slice.call(arguments,0)},_sendRequest:function(b){function c(){e(),k[b]=null,f.onDeckRequestArgs&&(f._get.apply(f,f.onDeckRequestArgs),f.onDeckRequestArgs=null)}var f=this,g=k[b];return g||(d(),g=k[b]=a.ajax(b,this.ajaxSettings).always(c)),g},get:function(a,b){var d,e,f=this,g=encodeURIComponent(a||"");return b=b||c.noop,d=this.replace?this.replace(this.url,g):this.url.replace(this.wildcard,g),(e=i.get(d))?c.defer(function(){b(f.filter?f.filter(e):e)}):this._get(d,b),!!e}}),b}(),i=function(){function d(b){c.bindAll(this),c.isString(b.template)&&!b.engine&&a.error("no template engine specified"),b.local||b.prefetch||b.remote||a.error("one of local, prefetch, or remote is required"),this.name=b.name||c.getUniqueId(),this.limit=b.limit||5,this.minLength=b.minLength||1,this.header=b.header,this.footer=b.footer,this.valueKey=b.valueKey||"value",this.template=e(b.template,b.engine,this.valueKey),this.local=b.local,this.prefetch=b.prefetch,this.remote=b.remote,this.itemHash={},this.adjacencyList={},this.storage=b.name?new f(b.name):null}function e(a,b,d){var e,f;return c.isFunction(a)?e=a:c.isString(a)?(f=b.compile(a),e=c.bind(f.render,f)):e=function(a){return"

"+a[d]+"

"},e}var g={thumbprint:"thumbprint",protocol:"protocol",itemHash:"itemHash",adjacencyList:"adjacencyList"};return c.mixin(d.prototype,{_processLocalData:function(a){this._mergeProcessedData(this._processData(a))},_loadPrefetchData:function(d){function e(a){var b=d.filter?d.filter(a):a,e=m._processData(b),f=e.itemHash,h=e.adjacencyList;m.storage&&(m.storage.set(g.itemHash,f,d.ttl),m.storage.set(g.adjacencyList,h,d.ttl),m.storage.set(g.thumbprint,n,d.ttl),m.storage.set(g.protocol,c.getProtocol(),d.ttl)),m._mergeProcessedData(e)}var f,h,i,j,k,l,m=this,n=b+(d.thumbprint||"");return this.storage&&(f=this.storage.get(g.thumbprint),h=this.storage.get(g.protocol),i=this.storage.get(g.itemHash),j=this.storage.get(g.adjacencyList)),k=f!==n||h!==c.getProtocol(),d=c.isString(d)?{url:d}:d,d.ttl=c.isNumber(d.ttl)?d.ttl:864e5,i&&j&&!k?(this._mergeProcessedData({itemHash:i,adjacencyList:j}),l=a.Deferred().resolve()):l=a.getJSON(d.url).done(e),l},_transformDatum:function(a){var b=c.isString(a)?a:a[this.valueKey],d=a.tokens||c.tokenizeText(b),e={value:b,tokens:d};return c.isString(a)?(e.datum={},e.datum[this.valueKey]=a):e.datum=a,e.tokens=c.filter(e.tokens,function(a){return!c.isBlankString(a)}),e.tokens=c.map(e.tokens,function(a){return a.toLowerCase()}),e},_processData:function(a){var b=this,d={},e={};return c.each(a,function(a,f){var g=b._transformDatum(f),h=c.getUniqueId(g.value);d[h]=g,c.each(g.tokens,function(a,b){var d=b.charAt(0),f=e[d]||(e[d]=[h]);!~c.indexOf(f,h)&&f.push(h)})}),{itemHash:d,adjacencyList:e}},_mergeProcessedData:function(a){var b=this;c.mixin(this.itemHash,a.itemHash),c.each(a.adjacencyList,function(a,c){var d=b.adjacencyList[a];b.adjacencyList[a]=d?d.concat(c):c})},_getLocalSuggestions:function(a){var b,d=this,e=[],f=[],g=[];return c.each(a,function(a,b){var d=b.charAt(0);!~c.indexOf(e,d)&&e.push(d)}),c.each(e,function(a,c){var e=d.adjacencyList[c];return e?(f.push(e),(!b||e.length").css({position:"absolute",left:"-9999px",visibility:"hidden",whiteSpace:"nowrap",fontFamily:b.css("font-family"),fontSize:b.css("font-size"),fontStyle:b.css("font-style"),fontVariant:b.css("font-variant"),fontWeight:b.css("font-weight"),wordSpacing:b.css("word-spacing"),letterSpacing:b.css("letter-spacing"),textIndent:b.css("text-indent"),textRendering:b.css("text-rendering"),textTransform:b.css("text-transform")}).insertAfter(b)}function f(a,b){return a=(a||"").replace(/^\s*/g,"").replace(/\s{2,}/g," "),b=(b||"").replace(/^\s*/g,"").replace(/\s{2,}/g," "),a===b}return c.mixin(b.prototype,d,{_handleFocus:function(){this.trigger("focused")},_handleBlur:function(){this.trigger("blured")},_handleSpecialKeyEvent:function(a){var b=this.specialKeyCodeMap[a.which||a.keyCode];b&&this.trigger(b+"Keyed",a)},_compareQueryToInputValue:function(){var a=this.getInputValue(),b=f(this.query,a),c=b?this.query.length!==a.length:!1;c?this.trigger("whitespaceChanged",{value:this.query}):b||this.trigger("queryChanged",{value:this.query=a})},destroy:function(){this.$hint.off(".tt"),this.$input.off(".tt"),this.$hint=this.$input=this.$overflowHelper=null},focus:function(){this.$input.focus()},blur:function(){this.$input.blur()},getQuery:function(){return this.query},setQuery:function(a){this.query=a},getInputValue:function(){return this.$input.val()},setInputValue:function(a,b){this.$input.val(a),!b&&this._compareQueryToInputValue()},getHintValue:function(){return this.$hint.val()},setHintValue:function(a){this.$hint.val(a)},getLanguageDirection:function(){return(this.$input.css("direction")||"ltr").toLowerCase()},isOverflow:function(){return this.$overflowHelper.text(this.getInputValue()),this.$overflowHelper.width()>this.$input.width()},isCursorAtEnd:function(){var a,b=this.$input.val().length,d=this.$input[0].selectionStart;return c.isNumber(d)?d===b:document.selection?(a=document.selection.createRange(),a.moveStart("character",-b),b===a.text.length):!0}}),b}(),k=function(){function b(b){c.bindAll(this),this.isOpen=!1,this.isEmpty=!0,this.isMouseOverDropdown=!1,this.$menu=a(b.menu).on("mouseenter.tt",this._handleMouseenter).on("mouseleave.tt",this._handleMouseleave).on("click.tt",".tt-suggestion",this._handleSelection).on("mouseover.tt",".tt-suggestion",this._handleMouseover)}function e(a){return a.data("suggestion")}var f={suggestionsList:''},g={suggestionsList:{display:"block"},suggestion:{whiteSpace:"nowrap",cursor:"pointer"},suggestionChild:{whiteSpace:"normal"}};return c.mixin(b.prototype,d,{_handleMouseenter:function(){this.isMouseOverDropdown=!0},_handleMouseleave:function(){this.isMouseOverDropdown=!1},_handleMouseover:function(b){var c=a(b.currentTarget);this._getSuggestions().removeClass("tt-is-under-cursor"),c.addClass("tt-is-under-cursor")},_handleSelection:function(b){var c=a(b.currentTarget);this.trigger("suggestionSelected",e(c))},_show:function(){this.$menu.css("display","block")},_hide:function(){this.$menu.hide()},_moveCursor:function(a){var b,c,d,f;if(this.isVisible()){if(b=this._getSuggestions(),c=b.filter(".tt-is-under-cursor"),c.removeClass("tt-is-under-cursor"),d=b.index(c)+a,d=(d+1)%(b.length+1)-1,-1===d)return this.trigger("cursorRemoved"),void 0;-1>d&&(d=b.length-1),f=b.eq(d).addClass("tt-is-under-cursor"),this._ensureVisibility(f),this.trigger("cursorMoved",e(f))}},_getSuggestions:function(){return this.$menu.find(".tt-suggestions > .tt-suggestion")},_ensureVisibility:function(a){var b=this.$menu.height()+parseInt(this.$menu.css("paddingTop"),10)+parseInt(this.$menu.css("paddingBottom"),10),c=this.$menu.scrollTop(),d=a.position().top,e=d+a.outerHeight(!0);0>d?this.$menu.scrollTop(c+d):e>b&&this.$menu.scrollTop(c+(e-b))},destroy:function(){this.$menu.off(".tt"),this.$menu=null},isVisible:function(){return this.isOpen&&!this.isEmpty},closeUnlessMouseIsOverDropdown:function(){this.isMouseOverDropdown||this.close()},close:function(){this.isOpen&&(this.isOpen=!1,this.isMouseOverDropdown=!1,this._hide(),this.$menu.find(".tt-suggestions > .tt-suggestion").removeClass("tt-is-under-cursor"),this.trigger("closed"))},open:function(){this.isOpen||(this.isOpen=!0,!this.isEmpty&&this._show(),this.trigger("opened"))},setLanguageDirection:function(a){var b={left:"0",right:"auto"},c={left:"auto",right:" 0"};"ltr"===a?this.$menu.css(b):this.$menu.css(c)},moveCursorUp:function(){this._moveCursor(-1)},moveCursorDown:function(){this._moveCursor(1)},getSuggestionUnderCursor:function(){var a=this._getSuggestions().filter(".tt-is-under-cursor").first();return a.length>0?e(a):null},getFirstSuggestion:function(){var a=this._getSuggestions().first();return a.length>0?e(a):null},renderSuggestions:function(b,d){var e,h,i,j,k,l="tt-dataset-"+b.name,m='
%body
',n=this.$menu.find("."+l);0===n.length&&(h=a(f.suggestionsList).css(g.suggestionsList),n=a("
").addClass(l).append(b.header).append(h).append(b.footer).appendTo(this.$menu)),d.length>0?(this.isEmpty=!1,this.isOpen&&this._show(),i=document.createElement("div"),j=document.createDocumentFragment(),c.each(d,function(c,d){d.dataset=b.name,e=b.template(d.datum),i.innerHTML=m.replace("%body",e),k=a(i.firstChild).css(g.suggestion).data("suggestion",d),k.children().each(function(){a(this).css(g.suggestionChild)}),j.appendChild(k[0])}),n.show().find(".tt-suggestions").html(j)):this.clearSuggestions(b.name),this.trigger("suggestionsRendered")},clearSuggestions:function(a){var b=a?this.$menu.find(".tt-dataset-"+a):this.$menu.find('[class^="tt-dataset-"]'),c=b.find(".tt-suggestions");b.hide(),c.empty(),0===this._getSuggestions().length&&(this.isEmpty=!0,this._hide())}}),b}(),l=function(){function b(a){var b,d,f;c.bindAll(this),this.$node=e(a.input),this.datasets=a.datasets,this.dir=null,this.eventBus=a.eventBus,b=this.$node.find(".tt-dropdown-menu"),d=this.$node.find(".tt-query"),f=this.$node.find(".tt-hint"),this.dropdownView=new k({menu:b}).on("suggestionSelected",this._handleSelection).on("cursorMoved",this._clearHint).on("cursorMoved",this._setInputValueToSuggestionUnderCursor).on("cursorRemoved",this._setInputValueToQuery).on("cursorRemoved",this._updateHint).on("suggestionsRendered",this._updateHint).on("opened",this._updateHint).on("closed",this._clearHint).on("opened closed",this._propagateEvent),this.inputView=new j({input:d,hint:f}).on("focused",this._openDropdown).on("blured",this._closeDropdown).on("blured",this._setInputValueToQuery).on("enterKeyed tabKeyed",this._handleSelection).on("queryChanged",this._clearHint).on("queryChanged",this._clearSuggestions).on("queryChanged",this._getSuggestions).on("whitespaceChanged",this._updateHint).on("queryChanged whitespaceChanged",this._openDropdown).on("queryChanged whitespaceChanged",this._setLanguageDirection).on("escKeyed",this._closeDropdown).on("escKeyed",this._setInputValueToQuery).on("tabKeyed upKeyed downKeyed",this._managePreventDefault).on("upKeyed downKeyed",this._moveDropdownCursor).on("upKeyed downKeyed",this._openDropdown).on("tabKeyed leftKeyed rightKeyed",this._autocomplete)}function e(b){var c=a(g.wrapper),d=a(g.dropdown),e=a(b),f=a(g.hint);c=c.css(h.wrapper),d=d.css(h.dropdown),f.css(h.hint).css({backgroundAttachment:e.css("background-attachment"),backgroundClip:e.css("background-clip"),backgroundColor:e.css("background-color"),backgroundImage:e.css("background-image"),backgroundOrigin:e.css("background-origin"),backgroundPosition:e.css("background-position"),backgroundRepeat:e.css("background-repeat"),backgroundSize:e.css("background-size")}),e.data("ttAttrs",{dir:e.attr("dir"),autocomplete:e.attr("autocomplete"),spellcheck:e.attr("spellcheck"),style:e.attr("style")}),e.addClass("tt-query").attr({autocomplete:"off",spellcheck:!1}).css(h.query);try{!e.attr("dir")&&e.attr("dir","auto")}catch(i){}return e.wrap(c).parent().prepend(f).append(d)}function f(a){var b=a.find(".tt-query");c.each(b.data("ttAttrs"),function(a,d){c.isUndefined(d)?b.removeAttr(a):b.attr(a,d)}),b.detach().removeData("ttAttrs").removeClass("tt-query").insertAfter(a),a.remove()}var g={wrapper:'',hint:'',dropdown:''},h={wrapper:{position:"relative",display:"inline-block"},hint:{position:"absolute",top:"0",left:"0",borderColor:"transparent",boxShadow:"none"},query:{position:"relative",verticalAlign:"top",backgroundColor:"transparent"},dropdown:{position:"absolute",top:"100%",left:"0",zIndex:"100",display:"none"}};return c.isMsie()&&c.mixin(h.query,{backgroundImage:"url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)"}),c.isMsie()&&c.isMsie()<=7&&(c.mixin(h.wrapper,{display:"inline",zoom:"1"}),c.mixin(h.query,{marginTop:"-1px"})),c.mixin(b.prototype,d,{_managePreventDefault:function(a){var b,c,d=a.data,e=!1;switch(a.type){case"tabKeyed":b=this.inputView.getHintValue(),c=this.inputView.getInputValue(),e=b&&b!==c;break;case"upKeyed":case"downKeyed":e=!d.shiftKey&&!d.ctrlKey&&!d.metaKey}e&&d.preventDefault()},_setLanguageDirection:function(){var a=this.inputView.getLanguageDirection();a!==this.dir&&(this.dir=a,this.$node.css("direction",a),this.dropdownView.setLanguageDirection(a))},_updateHint:function(){var a,b,d,e,f,g=this.dropdownView.getFirstSuggestion(),h=g?g.value:null,i=this.dropdownView.isVisible(),j=this.inputView.isOverflow();h&&i&&!j&&(a=this.inputView.getInputValue(),b=a.replace(/\s{2,}/g," ").replace(/^\s+/g,""),d=c.escapeRegExChars(b),e=new RegExp("^(?:"+d+")(.*$)","i"),f=e.exec(h),this.inputView.setHintValue(a+(f?f[1]:"")))},_clearHint:function(){this.inputView.setHintValue("")},_clearSuggestions:function(){this.dropdownView.clearSuggestions()},_setInputValueToQuery:function(){this.inputView.setInputValue(this.inputView.getQuery())},_setInputValueToSuggestionUnderCursor:function(a){var b=a.data;this.inputView.setInputValue(b.value,!0)},_openDropdown:function(){this.dropdownView.open()},_closeDropdown:function(a){this.dropdownView["blured"===a.type?"closeUnlessMouseIsOverDropdown":"close"]()},_moveDropdownCursor:function(a){var b=a.data;b.shiftKey||b.ctrlKey||b.metaKey||this.dropdownView["upKeyed"===a.type?"moveCursorUp":"moveCursorDown"]()},_handleSelection:function(a){var b="suggestionSelected"===a.type,d=b?a.data:this.dropdownView.getSuggestionUnderCursor();d&&(this.inputView.setInputValue(d.value),b?this.inputView.focus():a.data.preventDefault(),b&&c.isMsie()?c.defer(this.dropdownView.close):this.dropdownView.close(),this.eventBus.trigger("selected",d.datum,d.dataset))},_getSuggestions:function(){var a=this,b=this.inputView.getQuery();c.isBlankString(b)||c.each(this.datasets,function(c,d){d.getSuggestions(b,function(c){b===a.inputView.getQuery()&&a.dropdownView.renderSuggestions(d,c)})})},_autocomplete:function(a){var b,c,d,e,f;("rightKeyed"!==a.type&&"leftKeyed"!==a.type||(b=this.inputView.isCursorAtEnd(),c="ltr"===this.inputView.getLanguageDirection()?"leftKeyed"===a.type:"rightKeyed"===a.type,b&&!c))&&(d=this.inputView.getQuery(),e=this.inputView.getHintValue(),""!==e&&d!==e&&(f=this.dropdownView.getFirstSuggestion(),this.inputView.setInputValue(f.value),this.eventBus.trigger("autocompleted",f.datum,f.dataset)))},_propagateEvent:function(a){this.eventBus.trigger(a.type)},destroy:function(){this.inputView.destroy(),this.dropdownView.destroy(),f(this.$node),this.$node=null},setQuery:function(a){this.inputView.setQuery(a),this.inputView.setInputValue(a),this._clearHint(),this._clearSuggestions(),this._getSuggestions()}}),b}();!function(){var b,d={},f="ttView";b={initialize:function(b){function g(){var b,d=a(this),g=new e({el:d});b=c.map(h,function(a){return a.initialize()}),d.data(f,new l({input:d,eventBus:g=new e({el:d}),datasets:h})),a.when.apply(a,b).always(function(){c.defer(function(){g.trigger("initialized")})})}var h;return b=c.isArray(b)?b:[b],0===b.length&&a.error("no datasets provided"),h=c.map(b,function(a){var b=d[a.name]?d[a.name]:new i(a);return a.name&&(d[a.name]=b),b}),this.each(g)},destroy:function(){function b(){var b=a(this),c=b.data(f);c&&(c.destroy(),b.removeData(f))}return this.each(b)},setQuery:function(b){function c(){var c=a(this).data(f);c&&c.setQuery(b)}return this.each(c)}},jQuery.fn.typeahead=function(a){return b[a]?b[a].apply(this,[].slice.call(arguments,1)):b.initialize.apply(this,arguments)}}()}(window.jQuery); \ No newline at end of file +(function($) { + var VERSION = "0.9.3"; + var utils = { + isMsie: function() { + var match = /(msie) ([\w.]+)/i.exec(navigator.userAgent); + return match ? parseInt(match[2], 10) : false; + }, + isBlankString: function(str) { + return !str || /^\s*$/.test(str); + }, + escapeRegExChars: function(str) { + return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&"); + }, + isString: function(obj) { + return typeof obj === "string"; + }, + isNumber: function(obj) { + return typeof obj === "number"; + }, + isArray: $.isArray, + isFunction: $.isFunction, + isObject: $.isPlainObject, + isUndefined: function(obj) { + return typeof obj === "undefined"; + }, + bind: $.proxy, + bindAll: function(obj) { + var val; + for (var key in obj) { + $.isFunction(val = obj[key]) && (obj[key] = $.proxy(val, obj)); + } + }, + indexOf: function(haystack, needle) { + for (var i = 0; i < haystack.length; i++) { + if (haystack[i] === needle) { + return i; + } + } + return -1; + }, + each: $.each, + map: $.map, + filter: $.grep, + every: function(obj, test) { + var result = true; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (!(result = test.call(null, val, key, obj))) { + return false; + } + }); + return !!result; + }, + some: function(obj, test) { + var result = false; + if (!obj) { + return result; + } + $.each(obj, function(key, val) { + if (result = test.call(null, val, key, obj)) { + return false; + } + }); + return !!result; + }, + mixin: $.extend, + getUniqueId: function() { + var counter = 0; + return function() { + return counter++; + }; + }(), + defer: function(fn) { + setTimeout(fn, 0); + }, + debounce: function(func, wait, immediate) { + var timeout, result; + return function() { + var context = this, args = arguments, later, callNow; + later = function() { + timeout = null; + if (!immediate) { + result = func.apply(context, args); + } + }; + callNow = immediate && !timeout; + clearTimeout(timeout); + timeout = setTimeout(later, wait); + if (callNow) { + result = func.apply(context, args); + } + return result; + }; + }, + throttle: function(func, wait) { + var context, args, timeout, result, previous, later; + previous = 0; + later = function() { + previous = new Date(); + timeout = null; + result = func.apply(context, args); + }; + return function() { + var now = new Date(), remaining = wait - (now - previous); + context = this; + args = arguments; + if (remaining <= 0) { + clearTimeout(timeout); + timeout = null; + previous = now; + result = func.apply(context, args); + } else if (!timeout) { + timeout = setTimeout(later, remaining); + } + return result; + }; + }, + tokenizeQuery: function(str) { + return $.trim(str).toLowerCase().split(/[\s]+/); + }, + tokenizeText: function(str) { + return $.trim(str).toLowerCase().split(/[\s\-_]+/); + }, + getProtocol: function() { + return location.protocol; + }, + noop: function() {} + }; + var EventTarget = function() { + var eventSplitter = /\s+/; + return { + on: function(events, callback) { + var event; + if (!callback) { + return this; + } + this._callbacks = this._callbacks || {}; + events = events.split(eventSplitter); + while (event = events.shift()) { + this._callbacks[event] = this._callbacks[event] || []; + this._callbacks[event].push(callback); + } + return this; + }, + trigger: function(events, data) { + var event, callbacks; + if (!this._callbacks) { + return this; + } + events = events.split(eventSplitter); + while (event = events.shift()) { + if (callbacks = this._callbacks[event]) { + for (var i = 0; i < callbacks.length; i += 1) { + callbacks[i].call(this, { + type: event, + data: data + }); + } + } + } + return this; + } + }; + }(); + var EventBus = function() { + var namespace = "typeahead:"; + function EventBus(o) { + if (!o || !o.el) { + $.error("EventBus initialized without el"); + } + this.$el = $(o.el); + } + utils.mixin(EventBus.prototype, { + trigger: function(type) { + var args = [].slice.call(arguments, 1); + this.$el.trigger(namespace + type, args); + } + }); + return EventBus; + }(); + var PersistentStorage = function() { + var ls, methods; + try { + ls = window.localStorage; + ls.setItem("~~~", "!"); + ls.removeItem("~~~"); + } catch (err) { + ls = null; + } + function PersistentStorage(namespace) { + this.prefix = [ "__", namespace, "__" ].join(""); + this.ttlKey = "__ttl__"; + this.keyMatcher = new RegExp("^" + this.prefix); + } + if (ls && window.JSON) { + methods = { + _prefix: function(key) { + return this.prefix + key; + }, + _ttlKey: function(key) { + return this._prefix(key) + this.ttlKey; + }, + get: function(key) { + if (this.isExpired(key)) { + this.remove(key); + } + return decode(ls.getItem(this._prefix(key))); + }, + set: function(key, val, ttl) { + if (utils.isNumber(ttl)) { + ls.setItem(this._ttlKey(key), encode(now() + ttl)); + } else { + ls.removeItem(this._ttlKey(key)); + } + return ls.setItem(this._prefix(key), encode(val)); + }, + remove: function(key) { + ls.removeItem(this._ttlKey(key)); + ls.removeItem(this._prefix(key)); + return this; + }, + clear: function() { + var i, key, keys = [], len = ls.length; + for (i = 0; i < len; i++) { + if ((key = ls.key(i)).match(this.keyMatcher)) { + keys.push(key.replace(this.keyMatcher, "")); + } + } + for (i = keys.length; i--; ) { + this.remove(keys[i]); + } + return this; + }, + isExpired: function(key) { + var ttl = decode(ls.getItem(this._ttlKey(key))); + return utils.isNumber(ttl) && now() > ttl ? true : false; + } + }; + } else { + methods = { + get: utils.noop, + set: utils.noop, + remove: utils.noop, + clear: utils.noop, + isExpired: utils.noop + }; + } + utils.mixin(PersistentStorage.prototype, methods); + return PersistentStorage; + function now() { + return new Date().getTime(); + } + function encode(val) { + return JSON.stringify(utils.isUndefined(val) ? null : val); + } + function decode(val) { + return JSON.parse(val); + } + }(); + var RequestCache = function() { + function RequestCache(o) { + utils.bindAll(this); + o = o || {}; + this.sizeLimit = o.sizeLimit || 10; + this.cache = {}; + this.cachedKeysByAge = []; + } + utils.mixin(RequestCache.prototype, { + get: function(url) { + return this.cache[url]; + }, + set: function(url, resp) { + var requestToEvict; + if (this.cachedKeysByAge.length === this.sizeLimit) { + requestToEvict = this.cachedKeysByAge.shift(); + delete this.cache[requestToEvict]; + } + this.cache[url] = resp; + this.cachedKeysByAge.push(url); + } + }); + return RequestCache; + }(); + var Transport = function() { + var pendingRequestsCount = 0, pendingRequests = {}, maxPendingRequests, requestCache; + function Transport(o) { + utils.bindAll(this); + o = utils.isString(o) ? { + url: o + } : o; + requestCache = requestCache || new RequestCache(); + maxPendingRequests = utils.isNumber(o.maxParallelRequests) ? o.maxParallelRequests : maxPendingRequests || 6; + this.url = o.url; + this.wildcard = o.wildcard || "%QUERY"; + this.filter = o.filter; + this.replace = o.replace; + this.ajaxSettings = { + type: "get", + cache: o.cache, + timeout: o.timeout, + dataType: o.dataType || "json", + beforeSend: o.beforeSend + }; + this.fn = o.fn + this._get = (/^throttle$/i.test(o.rateLimitFn) ? utils.throttle : utils.debounce)(this._get, o.rateLimitWait || 300); + } + utils.mixin(Transport.prototype, { + _get: function(url, cb) { + var that = this; + if (belowPendingRequestsThreshold()) { + this._sendRequest(url).done(done); + } else { + this.onDeckRequestArgs = [].slice.call(arguments, 0); + } + function done(resp) { + var data = that.filter ? that.filter(resp) : resp; + cb && cb(data); + requestCache.set(url, resp); + } + }, + _sendRequest: function(url) { + var that = this, jqXhr = pendingRequests[url]; + if (!jqXhr) { + incrementPendingRequests(); + var call = null; + if (this.fn){ + call = this.fn() + }else{ + call = $.ajax(url, this.ajaxSettings) + } + jqXhr = pendingRequests[url] = call.always(always); + } + return jqXhr; + function always() { + decrementPendingRequests(); + pendingRequests[url] = null; + if (that.onDeckRequestArgs) { + that._get.apply(that, that.onDeckRequestArgs); + that.onDeckRequestArgs = null; + } + } + }, + get: function(query, cb) { + var that = this, encodedQuery = encodeURIComponent(query || ""), url, resp; + cb = cb || utils.noop; + url = this.replace ? this.replace(this.url, encodedQuery) : this.url.replace(this.wildcard, encodedQuery); + if (resp = requestCache.get(url)) { + utils.defer(function() { + cb(that.filter ? that.filter(resp) : resp); + }); + } else { + this._get(url, cb); + } + return !!resp; + } + }); + return Transport; + function incrementPendingRequests() { + pendingRequestsCount++; + } + function decrementPendingRequests() { + pendingRequestsCount--; + } + function belowPendingRequestsThreshold() { + return pendingRequestsCount < maxPendingRequests; + } + }(); + var Dataset = function() { + var keys = { + thumbprint: "thumbprint", + protocol: "protocol", + itemHash: "itemHash", + adjacencyList: "adjacencyList" + }; + function Dataset(o) { + utils.bindAll(this); + if (utils.isString(o.template) && !o.engine) { + $.error("no template engine specified"); + } + if (!o.local && !o.prefetch && !o.remote) { + $.error("one of local, prefetch, or remote is required"); + } + this.name = o.name || utils.getUniqueId(); + this.limit = o.limit || 5; + this.minLength = o.minLength || 1; + this.header = o.header; + this.footer = o.footer; + this.valueKey = o.valueKey || "value"; + this.template = compileTemplate(o.template, o.engine, this.valueKey); + this.local = o.local; + this.prefetch = o.prefetch; + this.remote = o.remote; + this.itemHash = {}; + this.adjacencyList = {}; + this.storage = o.name ? new PersistentStorage(o.name) : null; + } + utils.mixin(Dataset.prototype, { + _processLocalData: function(data) { + this._mergeProcessedData(this._processData(data)); + }, + _loadPrefetchData: function(o) { + var that = this, thumbprint = VERSION + (o.thumbprint || ""), storedThumbprint, storedProtocol, storedItemHash, storedAdjacencyList, isExpired, deferred; + if (this.storage) { + storedThumbprint = this.storage.get(keys.thumbprint); + storedProtocol = this.storage.get(keys.protocol); + storedItemHash = this.storage.get(keys.itemHash); + storedAdjacencyList = this.storage.get(keys.adjacencyList); + } + isExpired = storedThumbprint !== thumbprint || storedProtocol !== utils.getProtocol(); + o = utils.isString(o) ? { + url: o + } : o; + o.ttl = utils.isNumber(o.ttl) ? o.ttl : 24 * 60 * 60 * 1e3; + if (storedItemHash && storedAdjacencyList && !isExpired) { + this._mergeProcessedData({ + itemHash: storedItemHash, + adjacencyList: storedAdjacencyList + }); + deferred = $.Deferred().resolve(); + } else { + deferred = $.getJSON(o.url).done(processPrefetchData); + } + return deferred; + function processPrefetchData(data) { + var filteredData = o.filter ? o.filter(data) : data, processedData = that._processData(filteredData), itemHash = processedData.itemHash, adjacencyList = processedData.adjacencyList; + if (that.storage) { + that.storage.set(keys.itemHash, itemHash, o.ttl); + that.storage.set(keys.adjacencyList, adjacencyList, o.ttl); + that.storage.set(keys.thumbprint, thumbprint, o.ttl); + that.storage.set(keys.protocol, utils.getProtocol(), o.ttl); + } + that._mergeProcessedData(processedData); + } + }, + _transformDatum: function(datum) { + var value = utils.isString(datum) ? datum : datum[this.valueKey], tokens = datum.tokens || utils.tokenizeText(value), item = { + value: value, + tokens: tokens + }; + if (utils.isString(datum)) { + item.datum = {}; + item.datum[this.valueKey] = datum; + } else { + item.datum = datum; + } + item.tokens = utils.filter(item.tokens, function(token) { + return !utils.isBlankString(token); + }); + item.tokens = utils.map(item.tokens, function(token) { + return token.toLowerCase(); + }); + return item; + }, + _processData: function(data) { + var that = this, itemHash = {}, adjacencyList = {}; + utils.each(data, function(i, datum) { + var item = that._transformDatum(datum), id = utils.getUniqueId(item.value); + itemHash[id] = item; + utils.each(item.tokens, function(i, token) { + var character = token.charAt(0), adjacency = adjacencyList[character] || (adjacencyList[character] = [ id ]); + !~utils.indexOf(adjacency, id) && adjacency.push(id); + }); + }); + return { + itemHash: itemHash, + adjacencyList: adjacencyList + }; + }, + _mergeProcessedData: function(processedData) { + var that = this; + utils.mixin(this.itemHash, processedData.itemHash); + utils.each(processedData.adjacencyList, function(character, adjacency) { + var masterAdjacency = that.adjacencyList[character]; + that.adjacencyList[character] = masterAdjacency ? masterAdjacency.concat(adjacency) : adjacency; + }); + }, + _getLocalSuggestions: function(terms) { + var that = this, firstChars = [], lists = [], shortestList, suggestions = []; + utils.each(terms, function(i, term) { + var firstChar = term.charAt(0); + !~utils.indexOf(firstChars, firstChar) && firstChars.push(firstChar); + }); + utils.each(firstChars, function(i, firstChar) { + var list = that.adjacencyList[firstChar]; + if (!list) { + return false; + } + lists.push(list); + if (!shortestList || list.length < shortestList.length) { + shortestList = list; + } + }); + if (lists.length < firstChars.length) { + return []; + } + utils.each(shortestList, function(i, id) { + var item = that.itemHash[id], isCandidate, isMatch; + isCandidate = utils.every(lists, function(list) { + return ~utils.indexOf(list, id); + }); + isMatch = isCandidate && utils.every(terms, function(term) { + return utils.some(item.tokens, function(token) { + return token.indexOf(term) === 0; + }); + }); + isMatch && suggestions.push(item); + }); + return suggestions; + }, + initialize: function() { + var deferred; + this.local && this._processLocalData(this.local); + this.transport = this.remote ? new Transport(this.remote) : null; + deferred = this.prefetch ? this._loadPrefetchData(this.prefetch) : $.Deferred().resolve(); + this.local = this.prefetch = this.remote = null; + this.initialize = function() { + return deferred; + }; + return deferred; + }, + getSuggestions: function(query, cb) { + var that = this, terms, suggestions, cacheHit = false; + if (query.length < this.minLength) { + return; + } + terms = utils.tokenizeQuery(query); + suggestions = this._getLocalSuggestions(terms).slice(0, this.limit); + if (suggestions.length < this.limit && this.transport) { + cacheHit = this.transport.get(query, processRemoteData); + } + !cacheHit && cb && cb(suggestions); + function processRemoteData(data) { + suggestions = suggestions.slice(0); + utils.each(data, function(i, datum) { + var item = that._transformDatum(datum), isDuplicate; + isDuplicate = utils.some(suggestions, function(suggestion) { + return item.value === suggestion.value; + }); + !isDuplicate && suggestions.push(item); + return suggestions.length < that.limit; + }); + cb && cb(suggestions); + } + } + }); + return Dataset; + function compileTemplate(template, engine, valueKey) { + var renderFn, compiledTemplate; + if (utils.isFunction(template)) { + renderFn = template; + } else if (utils.isString(template)) { + compiledTemplate = engine.compile(template); + renderFn = utils.bind(compiledTemplate.render, compiledTemplate); + } else { + renderFn = function(context) { + return "

" + context[valueKey] + "

"; + }; + } + return renderFn; + } + }(); + var InputView = function() { + function InputView(o) { + var that = this; + utils.bindAll(this); + this.specialKeyCodeMap = { + 9: "tab", + 27: "esc", + 37: "left", + 39: "right", + 13: "enter", + 38: "up", + 40: "down" + }; + this.$hint = $(o.hint); + this.$input = $(o.input).on("blur.tt", this._handleBlur).on("focus.tt", this._handleFocus).on("keydown.tt", this._handleSpecialKeyEvent); + if (!utils.isMsie()) { + this.$input.on("input.tt", this._compareQueryToInputValue); + } else { + this.$input.on("keydown.tt keypress.tt cut.tt paste.tt", function($e) { + if (that.specialKeyCodeMap[$e.which || $e.keyCode]) { + return; + } + utils.defer(that._compareQueryToInputValue); + }); + } + this.query = this.$input.val(); + this.$overflowHelper = buildOverflowHelper(this.$input); + } + utils.mixin(InputView.prototype, EventTarget, { + _handleFocus: function() { + this.trigger("focused"); + }, + _handleBlur: function() { + this.trigger("blured"); + }, + _handleSpecialKeyEvent: function($e) { + var keyName = this.specialKeyCodeMap[$e.which || $e.keyCode]; + keyName && this.trigger(keyName + "Keyed", $e); + }, + _compareQueryToInputValue: function() { + var inputValue = this.getInputValue(), isSameQuery = compareQueries(this.query, inputValue), isSameQueryExceptWhitespace = isSameQuery ? this.query.length !== inputValue.length : false; + if (isSameQueryExceptWhitespace) { + this.trigger("whitespaceChanged", { + value: this.query + }); + } else if (!isSameQuery) { + this.trigger("queryChanged", { + value: this.query = inputValue + }); + } + }, + destroy: function() { + this.$hint.off(".tt"); + this.$input.off(".tt"); + this.$hint = this.$input = this.$overflowHelper = null; + }, + focus: function() { + this.$input.focus(); + }, + blur: function() { + this.$input.blur(); + }, + getQuery: function() { + return this.query; + }, + setQuery: function(query) { + this.query = query; + }, + getInputValue: function() { + return this.$input.val(); + }, + setInputValue: function(value, silent) { + this.$input.val(value); + !silent && this._compareQueryToInputValue(); + }, + getHintValue: function() { + return this.$hint.val(); + }, + setHintValue: function(value) { + this.$hint.val(value); + }, + getLanguageDirection: function() { + return (this.$input.css("direction") || "ltr").toLowerCase(); + }, + isOverflow: function() { + this.$overflowHelper.text(this.getInputValue()); + return this.$overflowHelper.width() > this.$input.width(); + }, + isCursorAtEnd: function() { + var valueLength = this.$input.val().length, selectionStart = this.$input[0].selectionStart, range; + if (utils.isNumber(selectionStart)) { + return selectionStart === valueLength; + } else if (document.selection) { + range = document.selection.createRange(); + range.moveStart("character", -valueLength); + return valueLength === range.text.length; + } + return true; + } + }); + return InputView; + function buildOverflowHelper($input) { + return $("").css({ + position: "absolute", + left: "-9999px", + visibility: "hidden", + whiteSpace: "nowrap", + fontFamily: $input.css("font-family"), + fontSize: $input.css("font-size"), + fontStyle: $input.css("font-style"), + fontVariant: $input.css("font-variant"), + fontWeight: $input.css("font-weight"), + wordSpacing: $input.css("word-spacing"), + letterSpacing: $input.css("letter-spacing"), + textIndent: $input.css("text-indent"), + textRendering: $input.css("text-rendering"), + textTransform: $input.css("text-transform") + }).insertAfter($input); + } + function compareQueries(a, b) { + a = (a || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + b = (b || "").replace(/^\s*/g, "").replace(/\s{2,}/g, " "); + return a === b; + } + }(); + var DropdownView = function() { + var html = { + suggestionsList: '' + }, css = { + suggestionsList: { + display: "block" + }, + suggestion: { + whiteSpace: "nowrap", + cursor: "pointer" + }, + suggestionChild: { + whiteSpace: "normal" + } + }; + function DropdownView(o) { + utils.bindAll(this); + this.isOpen = false; + this.isEmpty = true; + this.isMouseOverDropdown = false; + this.$menu = $(o.menu).on("mouseenter.tt", this._handleMouseenter).on("mouseleave.tt", this._handleMouseleave).on("click.tt", ".tt-suggestion", this._handleSelection).on("mouseover.tt", ".tt-suggestion", this._handleMouseover); + } + utils.mixin(DropdownView.prototype, EventTarget, { + _handleMouseenter: function() { + this.isMouseOverDropdown = true; + }, + _handleMouseleave: function() { + this.isMouseOverDropdown = false; + }, + _handleMouseover: function($e) { + var $suggestion = $($e.currentTarget); + this._getSuggestions().removeClass("tt-is-under-cursor"); + $suggestion.addClass("tt-is-under-cursor"); + }, + _handleSelection: function($e) { + var $suggestion = $($e.currentTarget); + this.trigger("suggestionSelected", extractSuggestion($suggestion)); + }, + _show: function() { + this.$menu.css("display", "block"); + }, + _hide: function() { + this.$menu.hide(); + }, + _moveCursor: function(increment) { + var $suggestions, $cur, nextIndex, $underCursor; + if (!this.isVisible()) { + return; + } + $suggestions = this._getSuggestions(); + $cur = $suggestions.filter(".tt-is-under-cursor"); + $cur.removeClass("tt-is-under-cursor"); + nextIndex = $suggestions.index($cur) + increment; + nextIndex = (nextIndex + 1) % ($suggestions.length + 1) - 1; + if (nextIndex === -1) { + this.trigger("cursorRemoved"); + return; + } else if (nextIndex < -1) { + nextIndex = $suggestions.length - 1; + } + $underCursor = $suggestions.eq(nextIndex).addClass("tt-is-under-cursor"); + this._ensureVisibility($underCursor); + this.trigger("cursorMoved", extractSuggestion($underCursor)); + }, + _getSuggestions: function() { + return this.$menu.find(".tt-suggestions > .tt-suggestion"); + }, + _ensureVisibility: function($el) { + var menuHeight = this.$menu.height() + parseInt(this.$menu.css("paddingTop"), 10) + parseInt(this.$menu.css("paddingBottom"), 10), menuScrollTop = this.$menu.scrollTop(), elTop = $el.position().top, elBottom = elTop + $el.outerHeight(true); + if (elTop < 0) { + this.$menu.scrollTop(menuScrollTop + elTop); + } else if (menuHeight < elBottom) { + this.$menu.scrollTop(menuScrollTop + (elBottom - menuHeight)); + } + }, + destroy: function() { + this.$menu.off(".tt"); + this.$menu = null; + }, + isVisible: function() { + return this.isOpen && !this.isEmpty; + }, + closeUnlessMouseIsOverDropdown: function() { + if (!this.isMouseOverDropdown) { + this.close(); + } + }, + close: function() { + if (this.isOpen) { + this.isOpen = false; + this.isMouseOverDropdown = false; + this._hide(); + this.$menu.find(".tt-suggestions > .tt-suggestion").removeClass("tt-is-under-cursor"); + this.trigger("closed"); + } + }, + open: function() { + if (!this.isOpen) { + this.isOpen = true; + !this.isEmpty && this._show(); + this.trigger("opened"); + } + }, + setLanguageDirection: function(dir) { + var ltrCss = { + left: "0", + right: "auto" + }, rtlCss = { + left: "auto", + right: " 0" + }; + dir === "ltr" ? this.$menu.css(ltrCss) : this.$menu.css(rtlCss); + }, + moveCursorUp: function() { + this._moveCursor(-1); + }, + moveCursorDown: function() { + this._moveCursor(+1); + }, + getSuggestionUnderCursor: function() { + var $suggestion = this._getSuggestions().filter(".tt-is-under-cursor").first(); + return $suggestion.length > 0 ? extractSuggestion($suggestion) : null; + }, + getFirstSuggestion: function() { + var $suggestion = this._getSuggestions().first(); + return $suggestion.length > 0 ? extractSuggestion($suggestion) : null; + }, + renderSuggestions: function(dataset, suggestions) { + var datasetClassName = "tt-dataset-" + dataset.name, wrapper = '
%body
', compiledHtml, $suggestionsList, $dataset = this.$menu.find("." + datasetClassName), elBuilder, fragment, $el; + if ($dataset.length === 0) { + $suggestionsList = $(html.suggestionsList).css(css.suggestionsList); + $dataset = $("
").addClass(datasetClassName).append(dataset.header).append($suggestionsList).append(dataset.footer).appendTo(this.$menu); + } + if (suggestions.length > 0) { + this.isEmpty = false; + this.isOpen && this._show(); + elBuilder = document.createElement("div"); + fragment = document.createDocumentFragment(); + utils.each(suggestions, function(i, suggestion) { + suggestion.dataset = dataset.name; + compiledHtml = dataset.template(suggestion.datum); + elBuilder.innerHTML = wrapper.replace("%body", compiledHtml); + $el = $(elBuilder.firstChild).css(css.suggestion).data("suggestion", suggestion); + $el.children().each(function() { + $(this).css(css.suggestionChild); + }); + fragment.appendChild($el[0]); + }); + $dataset.show().find(".tt-suggestions").html(fragment); + } else { + this.clearSuggestions(dataset.name); + } + this.trigger("suggestionsRendered"); + }, + clearSuggestions: function(datasetName) { + var $datasets = datasetName ? this.$menu.find(".tt-dataset-" + datasetName) : this.$menu.find('[class^="tt-dataset-"]'), $suggestions = $datasets.find(".tt-suggestions"); + $datasets.hide(); + $suggestions.empty(); + if (this._getSuggestions().length === 0) { + this.isEmpty = true; + this._hide(); + } + } + }); + return DropdownView; + function extractSuggestion($el) { + return $el.data("suggestion"); + } + }(); + var TypeaheadView = function() { + var html = { + wrapper: '', + hint: '', + dropdown: '' + }, css = { + wrapper: { + position: "relative", + display: "inline-block" + }, + hint: { + position: "absolute", + top: "0", + left: "0", + borderColor: "transparent", + boxShadow: "none" + }, + query: { + position: "relative", + verticalAlign: "top", + backgroundColor: "transparent" + }, + dropdown: { + position: "absolute", + top: "100%", + left: "0", + zIndex: "100", + display: "none" + } + }; + if (utils.isMsie()) { + utils.mixin(css.query, { + backgroundImage: "url(data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7)" + }); + } + if (utils.isMsie() && utils.isMsie() <= 7) { + utils.mixin(css.wrapper, { + display: "inline", + zoom: "1" + }); + utils.mixin(css.query, { + marginTop: "-1px" + }); + } + function TypeaheadView(o) { + var $menu, $input, $hint; + utils.bindAll(this); + this.$node = buildDomStructure(o.input); + this.datasets = o.datasets; + this.dir = null; + this.eventBus = o.eventBus; + $menu = this.$node.find(".tt-dropdown-menu"); + $input = this.$node.find(".tt-query"); + $hint = this.$node.find(".tt-hint"); + this.dropdownView = new DropdownView({ + menu: $menu + }).on("suggestionSelected", this._handleSelection).on("cursorMoved", this._clearHint).on("cursorMoved", this._setInputValueToSuggestionUnderCursor).on("cursorRemoved", this._setInputValueToQuery).on("cursorRemoved", this._updateHint).on("suggestionsRendered", this._updateHint).on("opened", this._updateHint).on("closed", this._clearHint).on("opened closed", this._propagateEvent); + this.inputView = new InputView({ + input: $input, + hint: $hint + }).on("focused", this._openDropdown).on("blured", this._closeDropdown).on("blured", this._setInputValueToQuery).on("enterKeyed tabKeyed", this._handleSelection).on("queryChanged", this._clearHint).on("queryChanged", this._clearSuggestions).on("queryChanged", this._getSuggestions).on("whitespaceChanged", this._updateHint).on("queryChanged whitespaceChanged", this._openDropdown).on("queryChanged whitespaceChanged", this._setLanguageDirection).on("escKeyed", this._closeDropdown).on("escKeyed", this._setInputValueToQuery).on("tabKeyed upKeyed downKeyed", this._managePreventDefault).on("upKeyed downKeyed", this._moveDropdownCursor).on("upKeyed downKeyed", this._openDropdown).on("tabKeyed leftKeyed rightKeyed", this._autocomplete); + } + utils.mixin(TypeaheadView.prototype, EventTarget, { + _managePreventDefault: function(e) { + var $e = e.data, hint, inputValue, preventDefault = false; + switch (e.type) { + case "tabKeyed": + hint = this.inputView.getHintValue(); + inputValue = this.inputView.getInputValue(); + preventDefault = hint && hint !== inputValue; + break; + + case "upKeyed": + case "downKeyed": + preventDefault = !$e.shiftKey && !$e.ctrlKey && !$e.metaKey; + break; + } + preventDefault && $e.preventDefault(); + }, + _setLanguageDirection: function() { + var dir = this.inputView.getLanguageDirection(); + if (dir !== this.dir) { + this.dir = dir; + this.$node.css("direction", dir); + this.dropdownView.setLanguageDirection(dir); + } + }, + _updateHint: function() { + var suggestion = this.dropdownView.getFirstSuggestion(), hint = suggestion ? suggestion.value : null, dropdownIsVisible = this.dropdownView.isVisible(), inputHasOverflow = this.inputView.isOverflow(), inputValue, query, escapedQuery, beginsWithQuery, match; + if (hint && dropdownIsVisible && !inputHasOverflow) { + inputValue = this.inputView.getInputValue(); + query = inputValue.replace(/\s{2,}/g, " ").replace(/^\s+/g, ""); + escapedQuery = utils.escapeRegExChars(query); + beginsWithQuery = new RegExp("^(?:" + escapedQuery + ")(.*$)", "i"); + match = beginsWithQuery.exec(hint); + this.inputView.setHintValue(inputValue + (match ? match[1] : "")); + } + }, + _clearHint: function() { + this.inputView.setHintValue(""); + }, + _clearSuggestions: function() { + this.dropdownView.clearSuggestions(); + }, + _setInputValueToQuery: function() { + this.inputView.setInputValue(this.inputView.getQuery()); + }, + _setInputValueToSuggestionUnderCursor: function(e) { + var suggestion = e.data; + this.inputView.setInputValue(suggestion.value, true); + }, + _openDropdown: function() { + this.dropdownView.open(); + }, + _closeDropdown: function(e) { + this.dropdownView[e.type === "blured" ? "closeUnlessMouseIsOverDropdown" : "close"](); + }, + _moveDropdownCursor: function(e) { + var $e = e.data; + if (!$e.shiftKey && !$e.ctrlKey && !$e.metaKey) { + this.dropdownView[e.type === "upKeyed" ? "moveCursorUp" : "moveCursorDown"](); + } + }, + _handleSelection: function(e) { + var byClick = e.type === "suggestionSelected", suggestion = byClick ? e.data : this.dropdownView.getSuggestionUnderCursor(); + if (suggestion) { + this.inputView.setInputValue(suggestion.value); + byClick ? this.inputView.focus() : e.data.preventDefault(); + byClick && utils.isMsie() ? utils.defer(this.dropdownView.close) : this.dropdownView.close(); + this.eventBus.trigger("selected", suggestion.datum, suggestion.dataset); + } + }, + _getSuggestions: function() { + var that = this, query = this.inputView.getQuery(); + if (utils.isBlankString(query)) { + return; + } + utils.each(this.datasets, function(i, dataset) { + dataset.getSuggestions(query, function(suggestions) { + if (query === that.inputView.getQuery()) { + that.dropdownView.renderSuggestions(dataset, suggestions); + } + }); + }); + }, + _autocomplete: function(e) { + var isCursorAtEnd, ignoreEvent, query, hint, suggestion; + if (e.type === "rightKeyed" || e.type === "leftKeyed") { + isCursorAtEnd = this.inputView.isCursorAtEnd(); + ignoreEvent = this.inputView.getLanguageDirection() === "ltr" ? e.type === "leftKeyed" : e.type === "rightKeyed"; + if (!isCursorAtEnd || ignoreEvent) { + return; + } + } + query = this.inputView.getQuery(); + hint = this.inputView.getHintValue(); + if (hint !== "" && query !== hint) { + suggestion = this.dropdownView.getFirstSuggestion(); + this.inputView.setInputValue(suggestion.value); + this.eventBus.trigger("autocompleted", suggestion.datum, suggestion.dataset); + } + }, + _propagateEvent: function(e) { + this.eventBus.trigger(e.type); + }, + destroy: function() { + this.inputView.destroy(); + this.dropdownView.destroy(); + destroyDomStructure(this.$node); + this.$node = null; + }, + setQuery: function(query) { + this.inputView.setQuery(query); + this.inputView.setInputValue(query); + this._clearHint(); + this._clearSuggestions(); + this._getSuggestions(); + } + }); + return TypeaheadView; + function buildDomStructure(input) { + var $wrapper = $(html.wrapper), $dropdown = $(html.dropdown), $input = $(input), $hint = $(html.hint); + $wrapper = $wrapper.css(css.wrapper); + $dropdown = $dropdown.css(css.dropdown); + $hint.css(css.hint).css({ + backgroundAttachment: $input.css("background-attachment"), + backgroundClip: $input.css("background-clip"), + backgroundColor: $input.css("background-color"), + backgroundImage: $input.css("background-image"), + backgroundOrigin: $input.css("background-origin"), + backgroundPosition: $input.css("background-position"), + backgroundRepeat: $input.css("background-repeat"), + backgroundSize: $input.css("background-size") + }); + $input.data("ttAttrs", { + dir: $input.attr("dir"), + autocomplete: $input.attr("autocomplete"), + spellcheck: $input.attr("spellcheck"), + style: $input.attr("style") + }); + $input.addClass("tt-query").attr({ + autocomplete: "off", + spellcheck: false + }).css(css.query); + try { + !$input.attr("dir") && $input.attr("dir", "auto"); + } catch (e) {} + return $input.wrap($wrapper).parent().prepend($hint).append($dropdown); + } + function destroyDomStructure($node) { + var $input = $node.find(".tt-query"); + utils.each($input.data("ttAttrs"), function(key, val) { + utils.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val); + }); + $input.detach().removeData("ttAttrs").removeClass("tt-query").insertAfter($node); + $node.remove(); + } + }(); + (function() { + var cache = {}, viewKey = "ttView", methods; + methods = { + initialize: function(datasetDefs) { + var datasets; + datasetDefs = utils.isArray(datasetDefs) ? datasetDefs : [ datasetDefs ]; + if (datasetDefs.length === 0) { + $.error("no datasets provided"); + } + datasets = utils.map(datasetDefs, function(o) { + var dataset = cache[o.name] ? cache[o.name] : new Dataset(o); + if (o.name) { + cache[o.name] = dataset; + } + return dataset; + }); + return this.each(initialize); + function initialize() { + var $input = $(this), deferreds, eventBus = new EventBus({ + el: $input + }); + deferreds = utils.map(datasets, function(dataset) { + return dataset.initialize(); + }); + $input.data(viewKey, new TypeaheadView({ + input: $input, + eventBus: eventBus = new EventBus({ + el: $input + }), + datasets: datasets + })); + $.when.apply($, deferreds).always(function() { + utils.defer(function() { + eventBus.trigger("initialized"); + }); + }); + } + }, + destroy: function() { + return this.each(destroy); + function destroy() { + var $this = $(this), view = $this.data(viewKey); + if (view) { + view.destroy(); + $this.removeData(viewKey); + } + } + }, + setQuery: function(query) { + return this.each(setQuery); + function setQuery() { + var view = $(this).data(viewKey); + view && view.setQuery(query); + } + } + }; + jQuery.fn.typeahead = function(method) { + if (methods[method]) { + return methods[method].apply(this, [].slice.call(arguments, 1)); + } else { + return methods.initialize.apply(this, arguments); + } + }; + })(); +})(window.jQuery); \ No newline at end of file diff --git a/examples/widgetapp/widget.coffee b/examples/widgetapp/widget.coffee index 27693f56..db600614 100644 --- a/examples/widgetapp/widget.coffee +++ b/examples/widgetapp/widget.coffee @@ -1,3 +1,4 @@ +#IMPORTANT PLEASE COMPILE WITH:: coffee -cbw widget.coffee cache = {} template = tmpl = (str, data) -> # Simple JavaScript Templating @@ -10,20 +11,20 @@ Mini = { render:template(t) } - -trigger_callback = (control_name,event,event_parameter)-> - $.ajax - data: - control_name: control_name - event: event - event_parameter: event_parameter - states: JSON.stringify(window.states) - cache: no - .done (new_states)-> - #Update all classes - for name,state of new_states - controls[name]?.update(state) - return + +build_control = (control_name, state, control)-> + $el = control.$el.find('[data-name='+control_name+']') + #get control type + type = $el.data('type') + #create class + typeclass = null + try + typeclass = eval(type) + catch e + typeclass = WSF_CONTROL + if type? and typeclass? + return new typeclass(control, $el, control_name, state) + return null class WSF_VALIDATOR constructor: (@parent_control, @settings)-> @@ -55,21 +56,71 @@ class WSF_MAX_VALIDATOR extends WSF_VALIDATOR val = @parent_control.value() return (val.length<=@settings.max) -validatormap = - "WSF_REGEXP_VALIDATOR":WSF_REGEXP_VALIDATOR - "WSF_MIN_VALIDATOR":WSF_MIN_VALIDATOR - "WSF_MAX_VALIDATOR":WSF_MAX_VALIDATOR class WSF_CONTROL - constructor: (@control_name, @$el)-> + constructor: (@parent_control, @$el, @control_name, @fullstate)-> + @state = @fullstate.state + @load_subcontrols() return + load_subcontrols: ()-> + if @fullstate.controls? + @controls=(build_control(control_name, state, @) for control_name, state of @fullstate.controls) + else + @controls = [] + + attach_events: ()-> + console.log "Attached #{@control_name}" + for control in @controls + if control? + control.attach_events() return update: (state)-> return + get_state: ()-> + @state + get_control_states:()-> + result = {} + for control in @controls + if control? + result[control.control_name]=control.get_full_state() + result + + get_full_state: ()-> + {"state":@get_state(),"controls":@get_control_states()} + + process_update: (new_states)-> + if new_states[@control_name]? + @update(new_states[@control_name]) + for control in @controls + if control? + control.process_update(new_states) + + get_context_state : ()-> + if @parent_control? + return @parent_control.get_context_state() + return @get_full_state() + + trigger_callback: (control_name,event,event_parameter)-> + if @parent_control? + return @parent_control.trigger_callback(control_name,event,event_parameter) + self = @ + $.ajax + type: 'POST', + url: '?' + $.param + control_name: control_name + event: event + data: + JSON.stringify(@get_full_state()) + processData: false, + contentType: 'application/json', + cache: no + .done (new_states)-> + #Update all classes + self.process_update(new_states) #Simple event listener #subscribe to an event @@ -89,36 +140,45 @@ class WSF_CONTROL ev.callback.call(ev.context) return @ +class WSF_PAGE_CONTROL extends WSF_CONTROL + constructor: (@fullstate)-> + @state = @fullstate.state + @parent_control=null + @$el = $('[data-name='+@state.id+']') + @control_name = @state.id + @load_subcontrols() controls = {} class WSF_BUTTON_CONTROL extends WSF_CONTROL attach_events: ()-> + super self = @ @$el.click (e)-> e.preventDefault() self.click() click: ()-> - if window.states[@control_name]['callback_click'] - trigger_callback(@control_name, 'click') + if @state['callback_click'] + @trigger_callback(@control_name, 'click') update: (state) -> if state.text? - window.states[@control_name]['text'] = state.text + @state['text'] = state.text @$el.text(state.text) class WSF_INPUT_CONTROL extends WSF_CONTROL attach_events: ()-> + super self = @ @$el.change ()-> self.change() change: ()-> #update local state - window.states[@control_name]['text'] = @$el.val() - if window.states[@control_name]['callback_change'] - trigger_callback(@control_name, 'change') + @state['text'] = @$el.val() + if @state['callback_change'] + @trigger_callback(@control_name, 'change') @trigger('change') value:()-> @@ -126,28 +186,31 @@ class WSF_INPUT_CONTROL extends WSF_CONTROL update: (state) -> if state.text? - window.states[@control_name]['text'] = state.text + @state['text'] = state.text @$el.val(state.text) class WSF_TEXTAREA_CONTROL extends WSF_INPUT_CONTROL class WSF_AUTOCOMPLETE_CONTROL extends WSF_INPUT_CONTROL attach_events: () -> + super self = @ @$el.typeahead({ name: @control_name - template: window.states[@control_name]['template'] + template: @state['template'] engine: Mini remote: url:"" replace: (url, uriEncodedQuery) -> - window.states[self.control_name]['text'] = self.$el.val() + self.state['text'] = self.$el.val() '?' + $.param control_name: self.control_name event: 'autocomplete' - states: JSON.stringify(window.states) + states: JSON.stringify(self.get_context_state()) filter: (parsedResponse) -> parsedResponse[self.control_name]['suggestions'] + fn: ()-> + self.trigger_callback(self.control_name, 'autocomplete') }) @$el.on 'typeahead:closed',()-> self.change() @@ -156,16 +219,17 @@ class WSF_AUTOCOMPLETE_CONTROL extends WSF_INPUT_CONTROL class WSF_CHECKBOX_CONTROL extends WSF_CONTROL attach_events: ()-> + super self = @ - @checked_value = window.states[@control_name]['checked_value'] + @checked_value = @state['checked_value'] @$el.change ()-> self.change() change: ()-> #update local state - window.states[@control_name]['checked'] = @$el.is(':checked') - if window.states[@control_name]['callback_change'] - trigger_callback(@control_name, 'change') + @state['checked'] = @$el.is(':checked') + if @state['callback_change'] + @trigger_callback(@control_name, 'change') @trigger('change') value:()-> @@ -173,23 +237,25 @@ class WSF_CHECKBOX_CONTROL extends WSF_CONTROL update: (state) -> if state.text? - window.states[@control_name]['checked'] = state.checked + @state['checked'] = state.checked @$el.prop('checked',state.checked) class WSF_FORM_ELEMENT_CONTROL extends WSF_CONTROL attach_events: ()-> + super self = @ - @value_control = controls[window.states[@control_name]['value_control']] + @value_control = @controls[0] if @value_control? #subscribe to change event on value_control @value_control.on('change',@change,@) @serverside_validator = false #Initialize validators @validators = [] - for validator in window.states[@control_name]['validators'] - if validatormap[validator.name]? - @validators.push new validatormap[validator.name](@,validator) - else + for validator in @state['validators'] + try + validatorclass = eval(validator.name) + @validators.push new validatorclass(@,validator) + catch e #Use serverside validator if no js implementation @serverside_validator = true return @@ -203,7 +269,7 @@ class WSF_FORM_ELEMENT_CONTROL extends WSF_CONTROL @showerror("") #If there is validator which is not implemented in js ask server to validate if @serverside_validator - trigger_callback(@control_name, 'validate') + @trigger_callback(@control_name, 'validate') return showerror: (message)-> @@ -228,19 +294,16 @@ class WSF_HTML_CONTROL extends WSF_CONTROL update: (state) -> if state.html? - window.states[@control_name]['html'] = state.html + @state['html'] = state.html @$el.html(state.html) class WSF_CHECKBOX_LIST_CONTROL extends WSF_CONTROL attach_events: ()-> - self = @ - @subcontrols = [] + super #Listen to events of subelements and forward them - for name,control of controls - if @$el.has(control.$el).length > 0 - @subcontrols.push(control) - control.on('change',@change,@) + for control in @controls + control.on('change',@change,@) return change:()-> @@ -248,7 +311,7 @@ class WSF_CHECKBOX_LIST_CONTROL extends WSF_CONTROL value:()-> result = [] - for subc in @subcontrols + for subc in @controls if subc.value() result.push(subc.checked_value) return result @@ -256,20 +319,22 @@ class WSF_CHECKBOX_LIST_CONTROL extends WSF_CONTROL class WSF_PROGRESS_CONTROL extends WSF_CONTROL attach_events:() -> + super self = @ runfetch= ()-> self.fetch() setInterval(runfetch, 5000) fetch: ()-> - trigger_callback(@control_name, 'progress_fetch') + @trigger_callback(@control_name, 'progress_fetch') update: (state)-> if state.progress? - window.states[@control_name]['progress'] = state.progress + @state['progress'] = state.progress @$el.children('.progress-bar').attr('aria-valuenow', state.progress).width(state.progress + '%') class WSF_PAGINATION_CONTROL extends WSF_CONTROL + attach_events: ()-> self = @ @$el.on 'click', 'a', (e)-> @@ -279,11 +344,11 @@ class WSF_PAGINATION_CONTROL extends WSF_CONTROL click: (e)-> nr = $(e.target).data('nr') if nr == "next" - trigger_callback(@control_name, "next") + @trigger_callback(@control_name, "next") else if nr == "prev" - trigger_callback(@control_name, "prev") + @trigger_callback(@control_name, "prev") else - trigger_callback(@control_name, "goto", nr) + @trigger_callback(@control_name, "goto", nr) update: (state) -> if state._html? @@ -291,49 +356,25 @@ class WSF_PAGINATION_CONTROL extends WSF_CONTROL class WSF_GRID_CONTROL extends WSF_CONTROL attach_events: ()-> + super self = @ update: (state) -> if state.datasource? - window.states[@control_name]['datasource'] = state.datasource + @state['datasource'] = state.datasource if state._body? @$el.find('tbody').html(state._body) class WSF_REPEATER_CONTROL extends WSF_CONTROL attach_events: ()-> + super self = @ update: (state) -> if state.datasource? - window.states[@control_name]['datasource'] = state.datasource + @state['datasource'] = state.datasource if state._body? @$el.find('.repeater_content').html(state._body) console.log state._body -#map class name to effective class -typemap = - "WSF_BUTTON_CONTROL": WSF_BUTTON_CONTROL - "WSF_INPUT_CONTROL": WSF_INPUT_CONTROL - "WSF_TEXTAREA_CONTROL": WSF_TEXTAREA_CONTROL - "WSF_AUTOCOMPLETE_CONTROL": WSF_AUTOCOMPLETE_CONTROL - "WSF_CHECKBOX_CONTROL": WSF_CHECKBOX_CONTROL - "WSF_FORM_ELEMENT_CONTROL": WSF_FORM_ELEMENT_CONTROL - "WSF_HTML_CONTROL": WSF_HTML_CONTROL - "WSF_CHECKBOX_LIST_CONTROL": WSF_CHECKBOX_LIST_CONTROL - "WSF_PROGRESS_CONTROL": WSF_PROGRESS_CONTROL - "WSF_PAGINATION_CONTROL": WSF_PAGINATION_CONTROL - "WSF_GRID_CONTROL": WSF_GRID_CONTROL - "WSF_REPEATER_CONTROL":WSF_REPEATER_CONTROL - -#create a js class for each control -for name,state of window.states - #find control DOM element - $el = $('[data-name='+name+']') - #get control type - type = $el.data('type') - #create class - if type? and typemap[type]? - controls[name]=new typemap[type](name,$el) -for name,state of window.states - controls[name]?.attach_events() - + \ No newline at end of file diff --git a/examples/widgetapp/widget.js b/examples/widgetapp/widget.js index 7daacf62..6e0b4922 100644 --- a/examples/widgetapp/widget.js +++ b/examples/widgetapp/widget.js @@ -1,629 +1,711 @@ // Generated by CoffeeScript 1.6.1 -(function() { - var $el, Mini, WSF_AUTOCOMPLETE_CONTROL, WSF_BUTTON_CONTROL, WSF_CHECKBOX_CONTROL, WSF_CHECKBOX_LIST_CONTROL, WSF_CONTROL, WSF_FORM_ELEMENT_CONTROL, WSF_GRID_CONTROL, WSF_HTML_CONTROL, WSF_INPUT_CONTROL, WSF_MAX_VALIDATOR, WSF_MIN_VALIDATOR, WSF_PAGINATION_CONTROL, WSF_PROGRESS_CONTROL, WSF_REGEXP_VALIDATOR, WSF_REPEATER_CONTROL, WSF_TEXTAREA_CONTROL, WSF_VALIDATOR, cache, controls, name, state, template, tmpl, trigger_callback, type, typemap, validatormap, _ref, _ref1, _ref2, - __hasProp = {}.hasOwnProperty, - __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; +var Mini, WSF_AUTOCOMPLETE_CONTROL, WSF_BUTTON_CONTROL, WSF_CHECKBOX_CONTROL, WSF_CHECKBOX_LIST_CONTROL, WSF_CONTROL, WSF_FORM_ELEMENT_CONTROL, WSF_GRID_CONTROL, WSF_HTML_CONTROL, WSF_INPUT_CONTROL, WSF_MAX_VALIDATOR, WSF_MIN_VALIDATOR, WSF_PAGE_CONTROL, WSF_PAGINATION_CONTROL, WSF_PROGRESS_CONTROL, WSF_REGEXP_VALIDATOR, WSF_REPEATER_CONTROL, WSF_TEXTAREA_CONTROL, WSF_VALIDATOR, build_control, cache, controls, template, tmpl, + __hasProp = {}.hasOwnProperty, + __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; - cache = {}; +cache = {}; - template = tmpl = function(str, data) { - var fn; - fn = (!/\W/.test(str) ? cache[str] = cache[str] || tmpl(str) : new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + "with(obj){p.push('" + str.replace(/[\r\t\n]/g, " ").split("{{").join("\t").replace(/((^|}})[^\t]*)'/g, "$1\r").replace(/\t=(.*?)}}/g, "',$1,'").split("\t").join("');").split("}}").join("p.push('").split("\r").join("\\'") + "');}return p.join('');")); - if (data) { - return fn(data); +template = tmpl = function(str, data) { + var fn; + fn = (!/\W/.test(str) ? cache[str] = cache[str] || tmpl(str) : new Function("obj", "var p=[],print=function(){p.push.apply(p,arguments);};" + "with(obj){p.push('" + str.replace(/[\r\t\n]/g, " ").split("{{").join("\t").replace(/((^|}})[^\t]*)'/g, "$1\r").replace(/\t=(.*?)}}/g, "',$1,'").split("\t").join("');").split("}}").join("p.push('").split("\r").join("\\'") + "');}return p.join('');")); + if (data) { + return fn(data); + } else { + return fn; + } +}; + +Mini = { + compile: function(t) { + return { + render: template(t) + }; + } +}; + +build_control = function(control_name, state, control) { + var $el, type, typeclass; + $el = control.$el.find('[data-name=' + control_name + ']'); + type = $el.data('type'); + typeclass = null; + try { + typeclass = eval(type); + } catch (e) { + typeclass = WSF_CONTROL; + } + if ((type != null) && (typeclass != null)) { + return new typeclass(control, $el, control_name, state); + } + return null; +}; + +WSF_VALIDATOR = (function() { + + function WSF_VALIDATOR(parent_control, settings) { + this.parent_control = parent_control; + this.settings = settings; + this.error = this.settings.error; + return; + } + + WSF_VALIDATOR.prototype.validate = function() { + return true; + }; + + return WSF_VALIDATOR; + +})(); + +WSF_REGEXP_VALIDATOR = (function(_super) { + + __extends(WSF_REGEXP_VALIDATOR, _super); + + function WSF_REGEXP_VALIDATOR() { + WSF_REGEXP_VALIDATOR.__super__.constructor.apply(this, arguments); + this.pattern = new RegExp(this.settings.expression, 'g'); + } + + WSF_REGEXP_VALIDATOR.prototype.validate = function() { + var res, val; + val = this.parent_control.value(); + res = val.match(this.pattern); + return res !== null; + }; + + return WSF_REGEXP_VALIDATOR; + +})(WSF_VALIDATOR); + +WSF_MIN_VALIDATOR = (function(_super) { + + __extends(WSF_MIN_VALIDATOR, _super); + + function WSF_MIN_VALIDATOR() { + return WSF_MIN_VALIDATOR.__super__.constructor.apply(this, arguments); + } + + WSF_MIN_VALIDATOR.prototype.validate = function() { + var val; + val = this.parent_control.value(); + return val.length >= this.settings.min; + }; + + return WSF_MIN_VALIDATOR; + +})(WSF_VALIDATOR); + +WSF_MAX_VALIDATOR = (function(_super) { + + __extends(WSF_MAX_VALIDATOR, _super); + + function WSF_MAX_VALIDATOR() { + return WSF_MAX_VALIDATOR.__super__.constructor.apply(this, arguments); + } + + WSF_MAX_VALIDATOR.prototype.validate = function() { + var val; + val = this.parent_control.value(); + return val.length <= this.settings.max; + }; + + return WSF_MAX_VALIDATOR; + +})(WSF_VALIDATOR); + +WSF_CONTROL = (function() { + + function WSF_CONTROL(parent_control, $el, control_name, fullstate) { + this.parent_control = parent_control; + this.$el = $el; + this.control_name = control_name; + this.fullstate = fullstate; + this.state = this.fullstate.state; + this.load_subcontrols(); + return; + } + + WSF_CONTROL.prototype.load_subcontrols = function() { + var control_name, state; + if (this.fullstate.controls != null) { + return this.controls = (function() { + var _ref, _results; + _ref = this.fullstate.controls; + _results = []; + for (control_name in _ref) { + state = _ref[control_name]; + _results.push(build_control(control_name, state, this)); + } + return _results; + }).call(this); } else { - return fn; + return this.controls = []; } }; - Mini = { - compile: function(t) { - return { - render: template(t) - }; + WSF_CONTROL.prototype.attach_events = function() { + var control, _i, _len, _ref; + console.log("Attached " + this.control_name); + _ref = this.controls; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + control = _ref[_i]; + if (control != null) { + control.attach_events(); + } } }; - trigger_callback = function(control_name, event, event_parameter) { + WSF_CONTROL.prototype.update = function(state) {}; + + WSF_CONTROL.prototype.get_state = function() { + return this.state; + }; + + WSF_CONTROL.prototype.get_control_states = function() { + var control, result, _i, _len, _ref; + result = {}; + _ref = this.controls; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + control = _ref[_i]; + if (control != null) { + result[control.control_name] = control.get_full_state(); + } + } + return result; + }; + + WSF_CONTROL.prototype.get_full_state = function() { + return { + "state": this.get_state(), + "controls": this.get_control_states() + }; + }; + + WSF_CONTROL.prototype.process_update = function(new_states) { + var control, _i, _len, _ref, _results; + if (new_states[this.control_name] != null) { + this.update(new_states[this.control_name]); + } + _ref = this.controls; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + control = _ref[_i]; + if (control != null) { + _results.push(control.process_update(new_states)); + } else { + _results.push(void 0); + } + } + return _results; + }; + + WSF_CONTROL.prototype.get_context_state = function() { + if (this.parent_control != null) { + return this.parent_control.get_context_state(); + } + return this.get_full_state(); + }; + + WSF_CONTROL.prototype.trigger_callback = function(control_name, event, event_parameter) { + var self; + if (this.parent_control != null) { + return this.parent_control.trigger_callback(control_name, event, event_parameter); + } + self = this; return $.ajax({ - data: { + type: 'POST', + url: '?' + $.param({ control_name: control_name, - event: event, - event_parameter: event_parameter, - states: JSON.stringify(window.states) - }, + event: event + }), + data: JSON.stringify(this.get_full_state()), + processData: false, + contentType: 'application/json', cache: false }).done(function(new_states) { - var name, state, _ref; - for (name in new_states) { - state = new_states[name]; - if ((_ref = controls[name]) != null) { - _ref.update(state); - } - } + return self.process_update(new_states); }); }; - WSF_VALIDATOR = (function() { - - function WSF_VALIDATOR(parent_control, settings) { - this.parent_control = parent_control; - this.settings = settings; - this.error = this.settings.error; - return; + WSF_CONTROL.prototype.on = function(name, callback, context) { + if (this._events == null) { + this._events = {}; } - - WSF_VALIDATOR.prototype.validate = function() { - return true; - }; - - return WSF_VALIDATOR; - - })(); - - WSF_REGEXP_VALIDATOR = (function(_super) { - - __extends(WSF_REGEXP_VALIDATOR, _super); - - function WSF_REGEXP_VALIDATOR() { - WSF_REGEXP_VALIDATOR.__super__.constructor.apply(this, arguments); - this.pattern = new RegExp(this.settings.expression, 'g'); + if (this._events[name] == null) { + this._events[name] = []; } - - WSF_REGEXP_VALIDATOR.prototype.validate = function() { - var res, val; - val = this.parent_control.value(); - res = val.match(this.pattern); - return res !== null; - }; - - return WSF_REGEXP_VALIDATOR; - - })(WSF_VALIDATOR); - - WSF_MIN_VALIDATOR = (function(_super) { - - __extends(WSF_MIN_VALIDATOR, _super); - - function WSF_MIN_VALIDATOR() { - return WSF_MIN_VALIDATOR.__super__.constructor.apply(this, arguments); - } - - WSF_MIN_VALIDATOR.prototype.validate = function() { - var val; - val = this.parent_control.value(); - return val.length >= this.settings.min; - }; - - return WSF_MIN_VALIDATOR; - - })(WSF_VALIDATOR); - - WSF_MAX_VALIDATOR = (function(_super) { - - __extends(WSF_MAX_VALIDATOR, _super); - - function WSF_MAX_VALIDATOR() { - return WSF_MAX_VALIDATOR.__super__.constructor.apply(this, arguments); - } - - WSF_MAX_VALIDATOR.prototype.validate = function() { - var val; - val = this.parent_control.value(); - return val.length <= this.settings.max; - }; - - return WSF_MAX_VALIDATOR; - - })(WSF_VALIDATOR); - - validatormap = { - "WSF_REGEXP_VALIDATOR": WSF_REGEXP_VALIDATOR, - "WSF_MIN_VALIDATOR": WSF_MIN_VALIDATOR, - "WSF_MAX_VALIDATOR": WSF_MAX_VALIDATOR + this._events[name].push({ + callback: callback, + context: context + }); + return this; }; - WSF_CONTROL = (function() { - - function WSF_CONTROL(control_name, $el) { - this.control_name = control_name; - this.$el = $el; - return; - } - - WSF_CONTROL.prototype.attach_events = function() {}; - - WSF_CONTROL.prototype.update = function(state) {}; - - WSF_CONTROL.prototype.on = function(name, callback, context) { - if (this._events == null) { - this._events = {}; - } - if (this._events[name] == null) { - this._events[name] = []; - } - this._events[name].push({ - callback: callback, - context: context - }); + WSF_CONTROL.prototype.trigger = function(name) { + var ev, _i, _len, _ref, _ref1; + if (((_ref = this._events) != null ? _ref[name] : void 0) == null) { return this; - }; - - WSF_CONTROL.prototype.trigger = function(name) { - var ev, _i, _len, _ref, _ref1; - if (((_ref = this._events) != null ? _ref[name] : void 0) == null) { - return this; - } - _ref1 = this._events[name]; - for (_i = 0, _len = _ref1.length; _i < _len; _i++) { - ev = _ref1[_i]; - ev.callback.call(ev.context); - } - return this; - }; - - return WSF_CONTROL; - - })(); - - controls = {}; - - WSF_BUTTON_CONTROL = (function(_super) { - - __extends(WSF_BUTTON_CONTROL, _super); - - function WSF_BUTTON_CONTROL() { - return WSF_BUTTON_CONTROL.__super__.constructor.apply(this, arguments); } - - WSF_BUTTON_CONTROL.prototype.attach_events = function() { - var self; - self = this; - return this.$el.click(function(e) { - e.preventDefault(); - return self.click(); - }); - }; - - WSF_BUTTON_CONTROL.prototype.click = function() { - if (window.states[this.control_name]['callback_click']) { - return trigger_callback(this.control_name, 'click'); - } - }; - - WSF_BUTTON_CONTROL.prototype.update = function(state) { - if (state.text != null) { - window.states[this.control_name]['text'] = state.text; - return this.$el.text(state.text); - } - }; - - return WSF_BUTTON_CONTROL; - - })(WSF_CONTROL); - - WSF_INPUT_CONTROL = (function(_super) { - - __extends(WSF_INPUT_CONTROL, _super); - - function WSF_INPUT_CONTROL() { - return WSF_INPUT_CONTROL.__super__.constructor.apply(this, arguments); + _ref1 = this._events[name]; + for (_i = 0, _len = _ref1.length; _i < _len; _i++) { + ev = _ref1[_i]; + ev.callback.call(ev.context); } - - WSF_INPUT_CONTROL.prototype.attach_events = function() { - var self; - self = this; - return this.$el.change(function() { - return self.change(); - }); - }; - - WSF_INPUT_CONTROL.prototype.change = function() { - window.states[this.control_name]['text'] = this.$el.val(); - if (window.states[this.control_name]['callback_change']) { - trigger_callback(this.control_name, 'change'); - } - return this.trigger('change'); - }; - - WSF_INPUT_CONTROL.prototype.value = function() { - return this.$el.val(); - }; - - WSF_INPUT_CONTROL.prototype.update = function(state) { - if (state.text != null) { - window.states[this.control_name]['text'] = state.text; - return this.$el.val(state.text); - } - }; - - return WSF_INPUT_CONTROL; - - })(WSF_CONTROL); - - WSF_TEXTAREA_CONTROL = (function(_super) { - - __extends(WSF_TEXTAREA_CONTROL, _super); - - function WSF_TEXTAREA_CONTROL() { - return WSF_TEXTAREA_CONTROL.__super__.constructor.apply(this, arguments); - } - - return WSF_TEXTAREA_CONTROL; - - })(WSF_INPUT_CONTROL); - - WSF_AUTOCOMPLETE_CONTROL = (function(_super) { - - __extends(WSF_AUTOCOMPLETE_CONTROL, _super); - - function WSF_AUTOCOMPLETE_CONTROL() { - return WSF_AUTOCOMPLETE_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_AUTOCOMPLETE_CONTROL.prototype.attach_events = function() { - var self; - self = this; - this.$el.typeahead({ - name: this.control_name, - template: window.states[this.control_name]['template'], - engine: Mini, - remote: { - url: "", - replace: function(url, uriEncodedQuery) { - window.states[self.control_name]['text'] = self.$el.val(); - return '?' + $.param({ - control_name: self.control_name, - event: 'autocomplete', - states: JSON.stringify(window.states) - }); - }, - filter: function(parsedResponse) { - return parsedResponse[self.control_name]['suggestions']; - } - } - }); - this.$el.on('typeahead:closed', function() { - return self.change(); - }); - return this.$el.on('typeahead:blured', function() { - return self.change(); - }); - }; - - return WSF_AUTOCOMPLETE_CONTROL; - - })(WSF_INPUT_CONTROL); - - WSF_CHECKBOX_CONTROL = (function(_super) { - - __extends(WSF_CHECKBOX_CONTROL, _super); - - function WSF_CHECKBOX_CONTROL() { - return WSF_CHECKBOX_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_CHECKBOX_CONTROL.prototype.attach_events = function() { - var self; - self = this; - this.checked_value = window.states[this.control_name]['checked_value']; - return this.$el.change(function() { - return self.change(); - }); - }; - - WSF_CHECKBOX_CONTROL.prototype.change = function() { - window.states[this.control_name]['checked'] = this.$el.is(':checked'); - if (window.states[this.control_name]['callback_change']) { - trigger_callback(this.control_name, 'change'); - } - return this.trigger('change'); - }; - - WSF_CHECKBOX_CONTROL.prototype.value = function() { - return this.$el.is(':checked'); - }; - - WSF_CHECKBOX_CONTROL.prototype.update = function(state) { - if (state.text != null) { - window.states[this.control_name]['checked'] = state.checked; - return this.$el.prop('checked', state.checked); - } - }; - - return WSF_CHECKBOX_CONTROL; - - })(WSF_CONTROL); - - WSF_FORM_ELEMENT_CONTROL = (function(_super) { - - __extends(WSF_FORM_ELEMENT_CONTROL, _super); - - function WSF_FORM_ELEMENT_CONTROL() { - return WSF_FORM_ELEMENT_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_FORM_ELEMENT_CONTROL.prototype.attach_events = function() { - var self, validator, _i, _len, _ref; - self = this; - this.value_control = controls[window.states[this.control_name]['value_control']]; - if (this.value_control != null) { - this.value_control.on('change', this.change, this); - } - this.serverside_validator = false; - this.validators = []; - _ref = window.states[this.control_name]['validators']; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - validator = _ref[_i]; - if (validatormap[validator.name] != null) { - this.validators.push(new validatormap[validator.name](this, validator)); - } else { - this.serverside_validator = true; - } - } - }; - - WSF_FORM_ELEMENT_CONTROL.prototype.change = function() { - var validator, _i, _len, _ref; - _ref = this.validators; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - validator = _ref[_i]; - if (!validator.validate()) { - this.showerror(validator.error); - return; - } - } - this.showerror(""); - if (this.serverside_validator) { - trigger_callback(this.control_name, 'validate'); - } - }; - - WSF_FORM_ELEMENT_CONTROL.prototype.showerror = function(message) { - var errordiv; - this.$el.removeClass("has-error"); - this.$el.find(".validation").remove(); - if (message.length > 0) { - this.$el.addClass("has-error"); - errordiv = $("
").addClass('help-block').addClass('validation').text(message); - return this.$el.find(".col-lg-10").append(errordiv); - } - }; - - WSF_FORM_ELEMENT_CONTROL.prototype.update = function(state) { - if (state.error != null) { - return this.showerror(state.error); - } - }; - - WSF_FORM_ELEMENT_CONTROL.prototype.value = function() { - return this.value_control.value(); - }; - - return WSF_FORM_ELEMENT_CONTROL; - - })(WSF_CONTROL); - - WSF_HTML_CONTROL = (function(_super) { - - __extends(WSF_HTML_CONTROL, _super); - - function WSF_HTML_CONTROL() { - return WSF_HTML_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_HTML_CONTROL.prototype.value = function() { - return this.$el.html(); - }; - - WSF_HTML_CONTROL.prototype.update = function(state) { - if (state.html != null) { - window.states[this.control_name]['html'] = state.html; - return this.$el.html(state.html); - } - }; - - return WSF_HTML_CONTROL; - - })(WSF_CONTROL); - - WSF_CHECKBOX_LIST_CONTROL = (function(_super) { - - __extends(WSF_CHECKBOX_LIST_CONTROL, _super); - - function WSF_CHECKBOX_LIST_CONTROL() { - return WSF_CHECKBOX_LIST_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_CHECKBOX_LIST_CONTROL.prototype.attach_events = function() { - var control, name, self; - self = this; - this.subcontrols = []; - for (name in controls) { - control = controls[name]; - if (this.$el.has(control.$el).length > 0) { - this.subcontrols.push(control); - control.on('change', this.change, this); - } - } - }; - - WSF_CHECKBOX_LIST_CONTROL.prototype.change = function() { - return this.trigger("change"); - }; - - WSF_CHECKBOX_LIST_CONTROL.prototype.value = function() { - var result, subc, _i, _len, _ref; - result = []; - _ref = this.subcontrols; - for (_i = 0, _len = _ref.length; _i < _len; _i++) { - subc = _ref[_i]; - if (subc.value()) { - result.push(subc.checked_value); - } - } - return result; - }; - - return WSF_CHECKBOX_LIST_CONTROL; - - })(WSF_CONTROL); - - WSF_PROGRESS_CONTROL = (function(_super) { - - __extends(WSF_PROGRESS_CONTROL, _super); - - function WSF_PROGRESS_CONTROL() { - return WSF_PROGRESS_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_PROGRESS_CONTROL.prototype.attach_events = function() { - var runfetch, self; - self = this; - runfetch = function() { - return self.fetch(); - }; - return setInterval(runfetch, 5000); - }; - - WSF_PROGRESS_CONTROL.prototype.fetch = function() { - return trigger_callback(this.control_name, 'progress_fetch'); - }; - - WSF_PROGRESS_CONTROL.prototype.update = function(state) { - if (state.progress != null) { - window.states[this.control_name]['progress'] = state.progress; - return this.$el.children('.progress-bar').attr('aria-valuenow', state.progress).width(state.progress + '%'); - } - }; - - return WSF_PROGRESS_CONTROL; - - })(WSF_CONTROL); - - WSF_PAGINATION_CONTROL = (function(_super) { - - __extends(WSF_PAGINATION_CONTROL, _super); - - function WSF_PAGINATION_CONTROL() { - return WSF_PAGINATION_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_PAGINATION_CONTROL.prototype.attach_events = function() { - var self; - self = this; - return this.$el.on('click', 'a', function(e) { - e.preventDefault(); - return self.click(e); - }); - }; - - WSF_PAGINATION_CONTROL.prototype.click = function(e) { - var nr; - nr = $(e.target).data('nr'); - if (nr === "next") { - return trigger_callback(this.control_name, "next"); - } else if (nr === "prev") { - return trigger_callback(this.control_name, "prev"); - } else { - return trigger_callback(this.control_name, "goto", nr); - } - }; - - WSF_PAGINATION_CONTROL.prototype.update = function(state) { - if (state._html != null) { - return this.$el.html($(state._html).html()); - } - }; - - return WSF_PAGINATION_CONTROL; - - })(WSF_CONTROL); - - WSF_GRID_CONTROL = (function(_super) { - - __extends(WSF_GRID_CONTROL, _super); - - function WSF_GRID_CONTROL() { - return WSF_GRID_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_GRID_CONTROL.prototype.attach_events = function() { - var self; - return self = this; - }; - - WSF_GRID_CONTROL.prototype.update = function(state) { - if (state.datasource != null) { - window.states[this.control_name]['datasource'] = state.datasource; - } - if (state._body != null) { - return this.$el.find('tbody').html(state._body); - } - }; - - return WSF_GRID_CONTROL; - - })(WSF_CONTROL); - - WSF_REPEATER_CONTROL = (function(_super) { - - __extends(WSF_REPEATER_CONTROL, _super); - - function WSF_REPEATER_CONTROL() { - return WSF_REPEATER_CONTROL.__super__.constructor.apply(this, arguments); - } - - WSF_REPEATER_CONTROL.prototype.attach_events = function() { - var self; - return self = this; - }; - - WSF_REPEATER_CONTROL.prototype.update = function(state) { - if (state.datasource != null) { - window.states[this.control_name]['datasource'] = state.datasource; - } - if (state._body != null) { - this.$el.find('.repeater_content').html(state._body); - return console.log(state._body); - } - }; - - return WSF_REPEATER_CONTROL; - - })(WSF_CONTROL); - - typemap = { - "WSF_BUTTON_CONTROL": WSF_BUTTON_CONTROL, - "WSF_INPUT_CONTROL": WSF_INPUT_CONTROL, - "WSF_TEXTAREA_CONTROL": WSF_TEXTAREA_CONTROL, - "WSF_AUTOCOMPLETE_CONTROL": WSF_AUTOCOMPLETE_CONTROL, - "WSF_CHECKBOX_CONTROL": WSF_CHECKBOX_CONTROL, - "WSF_FORM_ELEMENT_CONTROL": WSF_FORM_ELEMENT_CONTROL, - "WSF_HTML_CONTROL": WSF_HTML_CONTROL, - "WSF_CHECKBOX_LIST_CONTROL": WSF_CHECKBOX_LIST_CONTROL, - "WSF_PROGRESS_CONTROL": WSF_PROGRESS_CONTROL, - "WSF_PAGINATION_CONTROL": WSF_PAGINATION_CONTROL, - "WSF_GRID_CONTROL": WSF_GRID_CONTROL, - "WSF_REPEATER_CONTROL": WSF_REPEATER_CONTROL + return this; }; - _ref = window.states; - for (name in _ref) { - state = _ref[name]; - $el = $('[data-name=' + name + ']'); - type = $el.data('type'); - if ((type != null) && (typemap[type] != null)) { - controls[name] = new typemap[type](name, $el); - } + return WSF_CONTROL; + +})(); + +WSF_PAGE_CONTROL = (function(_super) { + + __extends(WSF_PAGE_CONTROL, _super); + + function WSF_PAGE_CONTROL(fullstate) { + this.fullstate = fullstate; + this.state = this.fullstate.state; + this.parent_control = null; + this.$el = $('[data-name=' + this.state.id + ']'); + this.control_name = this.state.id; + this.load_subcontrols(); } - _ref1 = window.states; - for (name in _ref1) { - state = _ref1[name]; - if ((_ref2 = controls[name]) != null) { - _ref2.attach_events(); - } + return WSF_PAGE_CONTROL; + +})(WSF_CONTROL); + +controls = {}; + +WSF_BUTTON_CONTROL = (function(_super) { + + __extends(WSF_BUTTON_CONTROL, _super); + + function WSF_BUTTON_CONTROL() { + return WSF_BUTTON_CONTROL.__super__.constructor.apply(this, arguments); } -}).call(this); + WSF_BUTTON_CONTROL.prototype.attach_events = function() { + var self; + WSF_BUTTON_CONTROL.__super__.attach_events.apply(this, arguments); + self = this; + return this.$el.click(function(e) { + e.preventDefault(); + return self.click(); + }); + }; + + WSF_BUTTON_CONTROL.prototype.click = function() { + if (this.state['callback_click']) { + return this.trigger_callback(this.control_name, 'click'); + } + }; + + WSF_BUTTON_CONTROL.prototype.update = function(state) { + if (state.text != null) { + this.state['text'] = state.text; + return this.$el.text(state.text); + } + }; + + return WSF_BUTTON_CONTROL; + +})(WSF_CONTROL); + +WSF_INPUT_CONTROL = (function(_super) { + + __extends(WSF_INPUT_CONTROL, _super); + + function WSF_INPUT_CONTROL() { + return WSF_INPUT_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_INPUT_CONTROL.prototype.attach_events = function() { + var self; + WSF_INPUT_CONTROL.__super__.attach_events.apply(this, arguments); + self = this; + return this.$el.change(function() { + return self.change(); + }); + }; + + WSF_INPUT_CONTROL.prototype.change = function() { + this.state['text'] = this.$el.val(); + if (this.state['callback_change']) { + this.trigger_callback(this.control_name, 'change'); + } + return this.trigger('change'); + }; + + WSF_INPUT_CONTROL.prototype.value = function() { + return this.$el.val(); + }; + + WSF_INPUT_CONTROL.prototype.update = function(state) { + if (state.text != null) { + this.state['text'] = state.text; + return this.$el.val(state.text); + } + }; + + return WSF_INPUT_CONTROL; + +})(WSF_CONTROL); + +WSF_TEXTAREA_CONTROL = (function(_super) { + + __extends(WSF_TEXTAREA_CONTROL, _super); + + function WSF_TEXTAREA_CONTROL() { + return WSF_TEXTAREA_CONTROL.__super__.constructor.apply(this, arguments); + } + + return WSF_TEXTAREA_CONTROL; + +})(WSF_INPUT_CONTROL); + +WSF_AUTOCOMPLETE_CONTROL = (function(_super) { + + __extends(WSF_AUTOCOMPLETE_CONTROL, _super); + + function WSF_AUTOCOMPLETE_CONTROL() { + return WSF_AUTOCOMPLETE_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_AUTOCOMPLETE_CONTROL.prototype.attach_events = function() { + var self; + WSF_AUTOCOMPLETE_CONTROL.__super__.attach_events.apply(this, arguments); + self = this; + this.$el.typeahead({ + name: this.control_name, + template: this.state['template'], + engine: Mini, + remote: { + url: "", + replace: function(url, uriEncodedQuery) { + self.state['text'] = self.$el.val(); + return '?' + $.param({ + control_name: self.control_name, + event: 'autocomplete', + states: JSON.stringify(self.get_context_state()) + }); + }, + filter: function(parsedResponse) { + return parsedResponse[self.control_name]['suggestions']; + }, + fn: function() { + return self.trigger_callback(self.control_name, 'autocomplete'); + } + } + }); + this.$el.on('typeahead:closed', function() { + return self.change(); + }); + return this.$el.on('typeahead:blured', function() { + return self.change(); + }); + }; + + return WSF_AUTOCOMPLETE_CONTROL; + +})(WSF_INPUT_CONTROL); + +WSF_CHECKBOX_CONTROL = (function(_super) { + + __extends(WSF_CHECKBOX_CONTROL, _super); + + function WSF_CHECKBOX_CONTROL() { + return WSF_CHECKBOX_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_CHECKBOX_CONTROL.prototype.attach_events = function() { + var self; + WSF_CHECKBOX_CONTROL.__super__.attach_events.apply(this, arguments); + self = this; + this.checked_value = this.state['checked_value']; + return this.$el.change(function() { + return self.change(); + }); + }; + + WSF_CHECKBOX_CONTROL.prototype.change = function() { + this.state['checked'] = this.$el.is(':checked'); + if (this.state['callback_change']) { + this.trigger_callback(this.control_name, 'change'); + } + return this.trigger('change'); + }; + + WSF_CHECKBOX_CONTROL.prototype.value = function() { + return this.$el.is(':checked'); + }; + + WSF_CHECKBOX_CONTROL.prototype.update = function(state) { + if (state.text != null) { + this.state['checked'] = state.checked; + return this.$el.prop('checked', state.checked); + } + }; + + return WSF_CHECKBOX_CONTROL; + +})(WSF_CONTROL); + +WSF_FORM_ELEMENT_CONTROL = (function(_super) { + + __extends(WSF_FORM_ELEMENT_CONTROL, _super); + + function WSF_FORM_ELEMENT_CONTROL() { + return WSF_FORM_ELEMENT_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_FORM_ELEMENT_CONTROL.prototype.attach_events = function() { + var self, validator, validatorclass, _i, _len, _ref; + WSF_FORM_ELEMENT_CONTROL.__super__.attach_events.apply(this, arguments); + self = this; + this.value_control = this.controls[0]; + if (this.value_control != null) { + this.value_control.on('change', this.change, this); + } + this.serverside_validator = false; + this.validators = []; + _ref = this.state['validators']; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + validator = _ref[_i]; + try { + validatorclass = eval(validator.name); + this.validators.push(new validatorclass(this, validator)); + } catch (e) { + this.serverside_validator = true; + } + } + }; + + WSF_FORM_ELEMENT_CONTROL.prototype.change = function() { + var validator, _i, _len, _ref; + _ref = this.validators; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + validator = _ref[_i]; + if (!validator.validate()) { + this.showerror(validator.error); + return; + } + } + this.showerror(""); + if (this.serverside_validator) { + this.trigger_callback(this.control_name, 'validate'); + } + }; + + WSF_FORM_ELEMENT_CONTROL.prototype.showerror = function(message) { + var errordiv; + this.$el.removeClass("has-error"); + this.$el.find(".validation").remove(); + if (message.length > 0) { + this.$el.addClass("has-error"); + errordiv = $("
").addClass('help-block').addClass('validation').text(message); + return this.$el.find(".col-lg-10").append(errordiv); + } + }; + + WSF_FORM_ELEMENT_CONTROL.prototype.update = function(state) { + if (state.error != null) { + return this.showerror(state.error); + } + }; + + WSF_FORM_ELEMENT_CONTROL.prototype.value = function() { + return this.value_control.value(); + }; + + return WSF_FORM_ELEMENT_CONTROL; + +})(WSF_CONTROL); + +WSF_HTML_CONTROL = (function(_super) { + + __extends(WSF_HTML_CONTROL, _super); + + function WSF_HTML_CONTROL() { + return WSF_HTML_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_HTML_CONTROL.prototype.value = function() { + return this.$el.html(); + }; + + WSF_HTML_CONTROL.prototype.update = function(state) { + if (state.html != null) { + this.state['html'] = state.html; + return this.$el.html(state.html); + } + }; + + return WSF_HTML_CONTROL; + +})(WSF_CONTROL); + +WSF_CHECKBOX_LIST_CONTROL = (function(_super) { + + __extends(WSF_CHECKBOX_LIST_CONTROL, _super); + + function WSF_CHECKBOX_LIST_CONTROL() { + return WSF_CHECKBOX_LIST_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_CHECKBOX_LIST_CONTROL.prototype.attach_events = function() { + var control, _i, _len, _ref; + WSF_CHECKBOX_LIST_CONTROL.__super__.attach_events.apply(this, arguments); + _ref = this.controls; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + control = _ref[_i]; + control.on('change', this.change, this); + } + }; + + WSF_CHECKBOX_LIST_CONTROL.prototype.change = function() { + return this.trigger("change"); + }; + + WSF_CHECKBOX_LIST_CONTROL.prototype.value = function() { + var result, subc, _i, _len, _ref; + result = []; + _ref = this.controls; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + subc = _ref[_i]; + if (subc.value()) { + result.push(subc.checked_value); + } + } + return result; + }; + + return WSF_CHECKBOX_LIST_CONTROL; + +})(WSF_CONTROL); + +WSF_PROGRESS_CONTROL = (function(_super) { + + __extends(WSF_PROGRESS_CONTROL, _super); + + function WSF_PROGRESS_CONTROL() { + return WSF_PROGRESS_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_PROGRESS_CONTROL.prototype.attach_events = function() { + var runfetch, self; + WSF_PROGRESS_CONTROL.__super__.attach_events.apply(this, arguments); + self = this; + runfetch = function() { + return self.fetch(); + }; + return setInterval(runfetch, 5000); + }; + + WSF_PROGRESS_CONTROL.prototype.fetch = function() { + return this.trigger_callback(this.control_name, 'progress_fetch'); + }; + + WSF_PROGRESS_CONTROL.prototype.update = function(state) { + if (state.progress != null) { + this.state['progress'] = state.progress; + return this.$el.children('.progress-bar').attr('aria-valuenow', state.progress).width(state.progress + '%'); + } + }; + + return WSF_PROGRESS_CONTROL; + +})(WSF_CONTROL); + +WSF_PAGINATION_CONTROL = (function(_super) { + + __extends(WSF_PAGINATION_CONTROL, _super); + + function WSF_PAGINATION_CONTROL() { + return WSF_PAGINATION_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_PAGINATION_CONTROL.prototype.attach_events = function() { + var self; + self = this; + return this.$el.on('click', 'a', function(e) { + e.preventDefault(); + return self.click(e); + }); + }; + + WSF_PAGINATION_CONTROL.prototype.click = function(e) { + var nr; + nr = $(e.target).data('nr'); + if (nr === "next") { + return this.trigger_callback(this.control_name, "next"); + } else if (nr === "prev") { + return this.trigger_callback(this.control_name, "prev"); + } else { + return this.trigger_callback(this.control_name, "goto", nr); + } + }; + + WSF_PAGINATION_CONTROL.prototype.update = function(state) { + if (state._html != null) { + return this.$el.html($(state._html).html()); + } + }; + + return WSF_PAGINATION_CONTROL; + +})(WSF_CONTROL); + +WSF_GRID_CONTROL = (function(_super) { + + __extends(WSF_GRID_CONTROL, _super); + + function WSF_GRID_CONTROL() { + return WSF_GRID_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_GRID_CONTROL.prototype.attach_events = function() { + var self; + WSF_GRID_CONTROL.__super__.attach_events.apply(this, arguments); + return self = this; + }; + + WSF_GRID_CONTROL.prototype.update = function(state) { + if (state.datasource != null) { + this.state['datasource'] = state.datasource; + } + if (state._body != null) { + return this.$el.find('tbody').html(state._body); + } + }; + + return WSF_GRID_CONTROL; + +})(WSF_CONTROL); + +WSF_REPEATER_CONTROL = (function(_super) { + + __extends(WSF_REPEATER_CONTROL, _super); + + function WSF_REPEATER_CONTROL() { + return WSF_REPEATER_CONTROL.__super__.constructor.apply(this, arguments); + } + + WSF_REPEATER_CONTROL.prototype.attach_events = function() { + var self; + WSF_REPEATER_CONTROL.__super__.attach_events.apply(this, arguments); + return self = this; + }; + + WSF_REPEATER_CONTROL.prototype.update = function(state) { + if (state.datasource != null) { + this.state['datasource'] = state.datasource; + } + if (state._body != null) { + this.$el.find('.repeater_content').html(state._body); + return console.log(state._body); + } + }; + + return WSF_REPEATER_CONTROL; + +})(WSF_CONTROL); diff --git a/library/server/wsf_js_widget/input/wsf_checkbox_list_control.e b/library/server/wsf_js_widget/input/wsf_checkbox_list_control.e index e7d883b6..3db18435 100644 --- a/library/server/wsf_js_widget/input/wsf_checkbox_list_control.e +++ b/library/server/wsf_js_widget/input/wsf_checkbox_list_control.e @@ -12,7 +12,7 @@ inherit WSF_VALUE_CONTROL [LIST [STRING]] undefine load_state, - read_state, + full_state, read_state_changes end diff --git a/library/server/wsf_js_widget/webcontrol/wsf_control.e b/library/server/wsf_js_widget/webcontrol/wsf_control.e index cdb05cba..71184040 100644 --- a/library/server/wsf_js_widget/webcontrol/wsf_control.e +++ b/library/server/wsf_js_widget/webcontrol/wsf_control.e @@ -35,7 +35,7 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- STATE MANAGEMENT load_state (new_states: JSON_OBJECT) -- Select state stored with `control_name` as key do - if attached {JSON_OBJECT} new_states.item (control_name) as new_state_obj then + if attached {JSON_OBJECT} new_states.item ("state") as new_state_obj then set_state (new_state_obj) end end @@ -45,10 +45,11 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- STATE MANAGEMENT deferred end - read_state (states: JSON_OBJECT) - -- Add a new entry in the `states` JSON object with the `control_name` as key and the `state` as value + full_state: JSON_OBJECT + -- Return state of object do - states.put (state, control_name) + create Result.make + Result.put (state, "state") end read_state_changes (states: JSON_OBJECT) @@ -62,6 +63,8 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- STATE MANAGEMENT state: JSON_OBJECT -- Returns the current state of the Control as JSON. This state will be transfered to the client. deferred + ensure + controls_not_defined: not (attached Result.item ("controls")) end state_changes: JSON_OBJECT diff --git a/library/server/wsf_js_widget/webcontrol/wsf_form_element_control.e b/library/server/wsf_js_widget/webcontrol/wsf_form_element_control.e index 4145dee7..bc1adac7 100644 --- a/library/server/wsf_js_widget/webcontrol/wsf_form_element_control.e +++ b/library/server/wsf_js_widget/webcontrol/wsf_form_element_control.e @@ -13,7 +13,7 @@ inherit redefine read_state_changes, load_state, - read_state + full_state end WSF_VALIDATABLE @@ -52,7 +52,9 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management -- Pass new_states to subcontrols do Precursor (new_states) - value_control.load_state (new_states) + if attached {JSON_OBJECT} new_states.item ("controls") as ct and then attached {JSON_OBJECT} ct.item (value_control.control_name) as value_state then + value_control.load_state (value_state) + end end set_state (new_state: JSON_OBJECT) @@ -61,11 +63,14 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management value_control.set_state (new_state) end - read_state (states: JSON_OBJECT) - -- Read states in subcontrols + full_state: JSON_OBJECT + local + controls_state: JSON_OBJECT do - Precursor (states) - value_control.read_state (states) + Result := Precursor + create controls_state.make + controls_state.put (value_control.full_state, value_control.control_name) + Result.put (controls_state, "controls") end read_state_changes (states: JSON_OBJECT) diff --git a/library/server/wsf_js_widget/webcontrol/wsf_multi_control.e b/library/server/wsf_js_widget/webcontrol/wsf_multi_control.e index 4bcc54c1..6c281c0b 100644 --- a/library/server/wsf_js_widget/webcontrol/wsf_multi_control.e +++ b/library/server/wsf_js_widget/webcontrol/wsf_multi_control.e @@ -11,7 +11,7 @@ inherit WSF_CONTROL redefine - read_state, + full_state, read_state_changes, load_state end @@ -31,7 +31,7 @@ feature {NONE} -- Initialization -- Initialize with specified control name and tag do make_control (n, t) - controls := create {ARRAYED_LIST [G]}.make(5); + controls := create {ARRAYED_LIST [G]}.make (5); end feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management @@ -40,11 +40,15 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management -- Pass new_states to subcontrols do Precursor (new_states) - across - controls as c - loop - if attached {WSF_CONTROL} c.item as cont then - cont.load_state (new_states) + if attached {JSON_OBJECT} new_states.item ("controls") as ct then + across + controls as c + loop + if attached {WSF_CONTROL} c.item as cont then + if attached {JSON_OBJECT} ct.item (cont.control_name) as value_state then + cont.load_state (value_state) + end + end end end end @@ -54,17 +58,21 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management do end - read_state (states: JSON_OBJECT) + full_state: JSON_OBJECT -- Read states in subcontrols + local + controls_state: JSON_OBJECT do - Precursor (states) + Result := Precursor + create controls_state.make across controls as c loop if attached {WSF_CONTROL} c.item as cont then - cont.read_state (states) + controls_state.put (cont.full_state, cont.control_name) end end + Result.put (controls_state, "controls") end read_state_changes (states: JSON_OBJECT) diff --git a/library/server/wsf_js_widget/webcontrol/wsf_page_control.e b/library/server/wsf_js_widget/webcontrol/wsf_page_control.e index b5e10d69..1df19cdb 100644 --- a/library/server/wsf_js_widget/webcontrol/wsf_page_control.e +++ b/library/server/wsf_js_widget/webcontrol/wsf_page_control.e @@ -47,21 +47,24 @@ feature -- Implementation local event: detachable STRING event_parameter: detachable STRING - control_name: detachable STRING - states: detachable STRING + event_control_name: detachable STRING + states: STRING states_changes: JSON_OBJECT json_parser: JSON_PARSER do - control_name := get_parameter ("control_name") + event_control_name := get_parameter ("control_name") event := get_parameter ("event") event_parameter := get_parameter ("event_parameter") - states := get_parameter ("states") - if attached event and attached control_name and attached control and attached states then + if attached event and attached event_control_name and attached control then + create states.make_empty + request.read_input_data_into (states) create json_parser.make_parser (states) if attached {JSON_OBJECT} json_parser.parse_json as sp then - control.load_state (sp) + if attached {JSON_OBJECT} sp.item ("controls") as ct and then attached {JSON_OBJECT} ct.item (control.control_name) as value_state then + control.load_state (value_state) + end end - control.handle_callback (control_name, event, event_parameter) + control.handle_callback (event_control_name, event, event_parameter) create states_changes.make control.read_state_changes (states_changes) response.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "application/json; charset=ISO-8859-1"]>>) @@ -82,16 +85,14 @@ feature -- Implementation data := "" data.append ("") data.append ("") - data.append ("") + data.append ("") data.append (control.render) - data.append ("") data.append ("") data.append ("") data.append ("") + data.append ("") data.append ("") create page.make page.put_header ({HTTP_STATUS_CODE}.ok, <<["Content-Type", "text/html; charset=ISO-8859-1"]>>) @@ -99,6 +100,28 @@ feature -- Implementation response.send (page) end + control_name: STRING + do + Result := request.request_time_stamp.out + end + + state: JSON_OBJECT + do + create Result.make + Result.put (create {JSON_STRING}.make_json (control_name), "id") + end + + full_state: JSON_OBJECT + local + controls_state: JSON_OBJECT + do + create Result.make + create controls_state.make + controls_state.put (control.full_state, control.control_name) + Result.put (controls_state, "controls") + Result.put (state, "state") + end + get_parameter (key: STRING): detachable STRING -- Read query parameter as string local