cache = {} 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) } trigger_callback = (control_name,event)-> $.ajax data: control_name: control_name event: event states: JSON.stringify(window.states) cache: no .done (new_states)-> #Update all classes for name,state of new_states controls[name]?.update(state) return 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) 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)-> return attach_events: ()-> return update: (state)-> return #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 @ controls = {} class WSF_BUTTON_CONTROL extends WSF_CONTROL attach_events: ()-> self = @ @$el.click (e)-> e.preventDefault() self.click() click: ()-> if window.states[@control_name]['callback_click'] trigger_callback(@control_name, 'click') update: (state) -> if state.text? window.states[@control_name]['text'] = state.text @$el.text(state.text) class WSF_INPUT_CONTROL extends WSF_CONTROL attach_events: ()-> 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') @trigger('change') value:()-> return @$el.val() update: (state) -> if state.text? window.states[@control_name]['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'] engine: Mini remote: url:"" replace: (url, uriEncodedQuery) -> window.states[self.control_name]['text'] = self.$el.val() '?' + $.param control_name: self.control_name event: 'autocomplete' states: JSON.stringify(window.states) filter: (parsedResponse) -> parsedResponse[self.control_name]['suggestions'] }) class WSF_CHECKBOX_CONTROL extends WSF_CONTROL attach_events: ()-> self = @ @checked_value = window.states[@control_name]['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') @trigger('change') value:()-> return @$el.is(':checked') update: (state) -> if state.text? window.states[@control_name]['checked'] = state.checked @$el.prop('checked',state.checked) class WSF_FORM_ELEMENT_CONTROL extends WSF_CONTROL attach_events: ()-> self = @ @value_control = controls[window.states[@control_name]['value_control']] 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 #Use serverside validator if no js implementation @serverside_validator = true return #value_control changed run validators change: ()-> for validator in @validators if not validator.validate() @showerror(validator.error) return @showerror("") #If there is validator which is not implemented in js ask server to validate if @serverside_validator trigger_callback(@control_name, 'validate') return showerror: (message)-> @$el.removeClass("has-error") @$el.find(".validation").remove() if message.length>0 @$el.addClass("has-error") errordiv = $("
").addClass('help-block').addClass('validation').text(message) @$el.find(".col-lg-10").append(errordiv) update: (state) -> if state.error? @showerror(state.error) value: ()-> @value_control.value() class WSF_HTML_CONTROL extends WSF_CONTROL value:()-> return @$el.html() update: (state) -> if state.html? window.states[@control_name]['html'] = state.html @$el.html(state.html) class WSF_CHECKBOX_LIST_CONTROL extends WSF_CONTROL attach_events: ()-> self = @ @subcontrols = [] #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,@) return change:()-> @trigger("change") value:()-> result = [] for subc in @subcontrols if subc.value() result.push(subc.checked_value) return result class WSF_PROGRESS_CONTROL extends WSF_CONTROL update: (state)-> if state.progress? window.states[@control_name]['progress'] = state.progress $('#' + @control_name).children('.progress-bar').attr('aria-valuenow', state.progress).width(state.progress + '%') #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 #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()