#IMPORTANT PLEASE COMPILE WITH:: coffee -cbw widget.coffee cache = {} jQuery.cachedAsset = (url, options) -> if /\.css$/.test(url) $("", rel: "stylesheet" type: "text/css" href: url ).appendTo("head") return { done:(fn)-> fn() } else success = [] head = document.head or document.getElementsByTagName('head')[0] or document.documentElement script = document.createElement 'script' script.async = 'async' script.src = url successful = false onload = (_, aborted = false) -> return unless (aborted or not script.readyState or script.readyState is 'complete') clearTimeout timeoutHandle script.onload = script.onreadystatechange = script.onerror = null head.removeChild(script) if head and script.parentNode script = undefined if success and not aborted successful = true for s in success s() success = [] script.onload = script.onreadystatechange = onload script.onerror = -> onload null, true timeoutHandle = setTimeout script.onerror, 7500 head.insertBefore script, head.firstChild return { done:(fn)-> if not successful success.push(fn) else fn() return } jQuery.unparam = (value) -> params = {} pieces = value.split("&") pair = undefined i = undefined l = undefined i = 0 l = pieces.length while i < l pair = pieces[i].split("=", 2) params[decodeURIComponent(pair[0])] = ((if pair.length is 2 then decodeURIComponent(pair[1].replace(/\+/g, " ")) else true)) i++ params template = tmpl = (str, data) -> # Simple JavaScript Templating # John Resig - http://ejohn.org/ - MIT Licensed fn = (if not /\W/.test(str) then cache[str] = cache[str] or tmpl(str) else 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 then fn(data) else fn) Mini = compile:(t)-> { render:template(t) } parseSuggestions = (data)-> for a of data if a == 'suggestions' return data[a] else d = parseSuggestions(data[a]) if d? return d return null loaded = {} lazy_load = (requirements,fn,that)-> if requirements.length == 0 return ()-> a = arguments fn.apply(that,a) if not that? that = window return ()-> a = arguments if not args? args = [] counter = requirements.length + 1 self = @ done = ()-> counter = counter - 1 if counter == 0 fn.apply(that,a) return for r in requirements if not loaded[r]? loaded[r]=$.cachedAsset(r) loaded[r].done(done) done() build_control = (control_name, state, control)-> $el = control.$el.find('[data-name='+control_name+']').first() #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)-> @error = @settings.error return validate: ()-> return true class WSF_REGEXP_VALIDATOR extends WSF_VALIDATOR constructor: ()-> super @pattern = new RegExp(@settings.expression,'g') validate: ()-> val = @parent_control.value() res = val.match(@pattern) return (res!=null) class WSF_MIN_VALIDATOR extends WSF_VALIDATOR validate: ()-> val = @parent_control.value() return (val.length>=@settings.min) class WSF_MAX_VALIDATOR extends WSF_VALIDATOR validate: ()-> val = @parent_control.value() return (val.length<=@settings.max) class WSF_CONTROL requirements: [] constructor: (@parent_control, @$el, @control_name, @fullstate)-> @state = @fullstate.state @load_subcontrols() @isolation = (""+@$el.data('isolation')=="1") @$el.data('control',@) @initialize = lazy_load @requirements, @attach_events, @ return load_subcontrols: ()-> if @fullstate.controls? @controls=(build_control(control_name, state, @) for control_name, state of @fullstate.controls) else @controls = [] return attach_events: ()-> console.log "Attached #{@control_name}" for control in @controls if control? control.initialize() return update: (state)-> return process_actions: (actions)-> for action in actions try fn = null #Check if action exists in class then check global if @[action.type]? fn = @[action.type] fn.call(@, action) else fn = eval(action.type) fn(action) catch e console.log "Failed preforming action #{action.type}" return process_update: (new_states)-> try if new_states.actions? @process_actions(new_states.actions) if new_states[@control_name]? @update(new_states[@control_name]) for control in @controls if control? control.process_update(new_states[this.control_name]['controls']) catch e return return get_context_state : ()-> if @parent_control? and not @isolation return @parent_control.get_context_state() return @wrap(@control_name,@fullstate) get_full_control_name: ()-> if @parent_control? val = @parent_control.get_full_control_name() if val != "" val = val + "-" return val+@control_name return @control_name wrap : (cname,state)-> ctrs = {} ctrs[cname] = state state = {"controls":ctrs} if @parent_control? return @parent_control.wrap(@parent_control.control_name,state) return state callback_url: (params)-> if @parent_control? return @parent_control.callback_url(params) $.extend params, @url_params @url + '?' + $.param(params) trigger_callback: (control_name,event,event_parameter)-> @run_trigger_callback(@get_full_control_name(),event,event_parameter) get_page:()-> if @parent_control? return @parent_control.get_page() return @ run_trigger_callback: (control_name,event,event_parameter)-> if @parent_control? and not @isolation return @parent_control.run_trigger_callback(control_name,event,event_parameter) self = @ return $.ajax type: 'POST', url: @callback_url control_name: control_name event: event event_parameter: event_parameter data: JSON.stringify(@get_context_state()) processData: false, contentType: 'application/json', cache: no .done (new_states)-> #Update all classes self.get_page().process_update(new_states) #Simple event listener #subscribe to an event on: (name, callback, context)-> if not @_events? @_events = {} if not @_events[name]? @_events[name] = [] @_events[name].push({callback:callback,context:context}) return @ #trigger an event trigger: (name)-> if not @_events?[name]? return @ for ev in @_events[name] ev.callback.call(ev.context) return @ remove:()-> console.log "Removed #{@control_name}" @$el.remove() class WSF_PAGE_CONTROL extends WSF_CONTROL constructor: (@fullstate)-> @state = @fullstate.state @parent_control=null @$el = $('[data-name='+@state.id+']') @control_name = @state.id @url = @state['url'] @url_params = jQuery.unparam(@state['url_params']) @$el.data('control',@) @initialize = lazy_load @requirements, @attach_events, @ @load_subcontrols() process_update: (new_states)-> for control in @controls if control? control.process_update(new_states) return get_full_control_name: ()-> "" wrap : (cname,state)-> state remove:()-> console.log "Removed #{@control_name}" @$el.remove() class WSF_SLIDER_CONTROL extends WSF_CONTROL requirements: ['/assets/bootstrap.min.js'] attach_events: ()-> super id = "slider"+Math.round(Math.random()*10000) @$el.attr("id",id) @$el.find("ol li").attr("data-target","#"+id) @$el.find(".carousel-control").attr("href","#"+id) class WSF_DROPDOWN_CONTROL extends WSF_CONTROL requirements: ['/assets/bootstrap.min.js'] controls = {} class WSF_BUTTON_CONTROL extends WSF_CONTROL attach_events: ()-> super self = @ @$el.click (e)-> e.preventDefault() self.click() click: ()-> if @state['callback_click'] @trigger_callback(@control_name, 'click') update: (state) -> if state.disabled != undefined @state['disabled'] = state.disabled @$el.prop('disabled', state.disabled) if 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 @state['text'] = @$el.val() if @state['callback_change'] @trigger_callback(@control_name, 'change') @trigger('change') value:()-> return @$el.val() update: (state) -> if state.disabled != undefined @state['disabled'] = state.disabled @$el.prop('disabled', state.disabled) if state.text? @state['text'] = state.text @$el.val(state.text) class WSF_FILE_CONTROL extends WSF_CONTROL constructor: ()-> super @uploading = false start_upload: ()-> if @$el[0].files.length==0 return if @uploading return @uploading = true @$el.hide() @progressbar = $ """