diff --git a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_dynamic_multi_control.e b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_dynamic_multi_control.e index 5050d7f8..09adfa56 100644 --- a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_dynamic_multi_control.e +++ b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_dynamic_multi_control.e @@ -26,6 +26,7 @@ feature {NONE} -- Initialization do Precursor (tag) create items.make_array + create pending_removes.make (1) end feature {WSF_DYNAMIC_MULTI_CONTROL} -- Iternal functions @@ -36,11 +37,51 @@ feature {WSF_DYNAMIC_MULTI_CONTROL} -- Iternal functions controls.extend (c) if attached {WSF_CONTROL} c as d then d.control_id := id - max_id := id.max (max_id) end + max_id := id.max (max_id) items_changed := True end + execute_pending_removes + local + found: BOOLEAN + fitem: detachable G + frow: detachable JSON_OBJECT + do + across + pending_removes as id + loop + across + controls as c + until + found + loop + if c.item.control_id = id.item then + fitem := c.item + found := True + end + end + if attached fitem as i then + controls.prune (i) + end + found := False + across + items.array_representation as c + until + found + loop + if attached {JSON_OBJECT} c.item as row and then attached {JSON_NUMBER} row.item ("id") as rid and then rid.item.to_integer_32 = id.item then + frow := row + found := True + end + end + if attached frow as r then + items.array_representation.prune (r) + end + items_changed := True + end + end + feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management set_state (new_state: JSON_OBJECT) @@ -89,12 +130,19 @@ feature end end + remove_control_by_id (id: INTEGER) + --Add removes to pending removes list + do + pending_removes.extend (id) + end + read_state_changes (states: WSF_JSON_OBJECT) local new_state: WSF_JSON_OBJECT sub_state: WSF_JSON_OBJECT do Precursor (states) + execute_pending_removes if items_changed then new_state := state create sub_state.make @@ -114,8 +162,13 @@ feature items: JSON_ARRAY + pending_removes: ARRAYED_LIST [INTEGER] + items_changed: BOOLEAN max_id: INTEGER +invariant + all_items_exist: items.count = controls.count + end diff --git a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_stateless_multi_control.e b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_stateless_multi_control.e index 11821cb3..2dfe1ee9 100644 --- a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_stateless_multi_control.e +++ b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_stateless_multi_control.e @@ -14,7 +14,8 @@ inherit add_control, set_control_name_prefix, handle_callback, - set_control_id + set_control_id, + render_tag end create @@ -65,6 +66,20 @@ feature end end + render_tag (body: STRING; attrs: detachable STRING): STRING + -- Generate HTML of this control with the specified body and attributes + local + css_classes_string: STRING + do + create css_classes_string.make_empty + across + css_classes as c + loop + css_classes_string.append (" " + c.item) + end + Result := render_tag_with_tagname (tag_name, body, attrs, css_classes_string) + end + feature -- Event handling handle_callback (cname: LIST [STRING]; event: STRING; event_parameter: detachable ANY) diff --git a/examples/widgetapp/assets/widget.coffee b/examples/widgetapp/assets/widget.coffee index a62f7d48..d33db0fa 100644 --- a/examples/widgetapp/assets/widget.coffee +++ b/examples/widgetapp/assets/widget.coffee @@ -79,7 +79,12 @@ parseSuggestions = (data)-> return d return null loaded = {} - +once = (f)-> + executed = false + () -> + if not executed + executed = true + f.apply(this, arguments) lazy_load = (requirements,fn,that)-> if requirements.length == 0 return ()-> @@ -105,9 +110,23 @@ lazy_load = (requirements,fn,that)-> loaded[r].done(done) done() +find_control = (root, name) -> + children = root.children() + if children.length + matching = children.filter('[data-name='+name+']') + if matching.length + matching.first() + else + find_control children.filter(':not([data-name])'), name + else + null build_control = (control_name, state, control)-> - $el = control.$el.find('[data-name='+control_name+']').first() + $el = find_control(control.$el, control_name) + if $el == null + return null + if $el.data('control')? + return $el.data('control') #get control type type = $el.data('type') #create class @@ -159,6 +178,7 @@ class WSF_CONTROL @isolation = (""+@$el.data('isolation')=="1") @$el.data('control',@) @initialize = lazy_load @requirements, @attach_events, @ + @initialize = once @initialize return load_subcontrols: ()-> @@ -195,14 +215,15 @@ class WSF_CONTROL process_update: (new_states)-> try - if new_states.actions? + if new_states?.actions? @process_actions(new_states.actions) - if new_states[@control_name]? + 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 + console.error(e) return return @@ -281,7 +302,7 @@ class WSF_CONTROL remove:()-> for control in @controls - control.remove() + control?.remove() console.log "Removed #{@control_name}" @$el.remove() @@ -296,6 +317,7 @@ class WSF_PAGE_CONTROL extends WSF_CONTROL @url_params = jQuery.unparam(@state['url_params']) @$el.data('control',@) @initialize = lazy_load @requirements, @attach_events, @ + @initialize = once @initialize @load_subcontrols() process_update: (new_states)-> @@ -313,7 +335,7 @@ class WSF_PAGE_CONTROL extends WSF_CONTROL remove:()-> for control in @controls - control.remove() + control?.remove() console.log "Removed #{@control_name}" @$el.remove() @@ -507,15 +529,20 @@ class WSF_DYNAMIC_MULTI_CONTROL extends WSF_CONTROL update: (state)-> console.log state - if state.items? and state.render? and state.newstate? + if state.items? and state.render? and state.newstate? @state['items'] = state.items for control in @controls - control.remove() - @$el.html($(state.render).html()) - @fullstate.controls = state.newstate + if not (control?.control_name of state.newstate) + control?.remove() + for el in $(state.render).children() + if @$el.children('[data-name='+$(el).data('name')+']').length == 0 + @$el.append(el) + for k,v of state.newstate + if not (k of @fullstate.controls) + @fullstate.controls[k] = v @load_subcontrols() for control in @controls - control.initialize() + control?.initialize() return class WSF_PASSWORD_CONTROL extends WSF_INPUT_CONTROL diff --git a/examples/widgetapp/assets/widget.js b/examples/widgetapp/assets/widget.js index 359d0f76..cb341ce5 100644 --- a/examples/widgetapp/assets/widget.js +++ b/examples/widgetapp/assets/widget.js @@ -1,5 +1,5 @@ // Generated by CoffeeScript 1.6.1 -var Mini, WSF_AUTOCOMPLETE_CONTROL, WSF_BUTTON_CONTROL, WSF_CHECKBOX_CONTROL, WSF_CHECKBOX_LIST_CONTROL, WSF_CODEVIEW_CONTROL, WSF_CONTROL, WSF_DATE_PICKER_CONTROL, WSF_DROPDOWN_CONTROL, WSF_DYNAMIC_MULTI_CONTROL, WSF_FILE_CONTROL, WSF_FORM_ELEMENT_CONTROL, WSF_GRID_CONTROL, WSF_HTML_CONTROL, WSF_INPUT_CONTROL, WSF_MAX_VALIDATOR, WSF_MIN_VALIDATOR, WSF_NAVLIST_ITEM_CONTROL, WSF_PAGE_CONTROL, WSF_PAGINATION_CONTROL, WSF_PASSWORD_CONTROL, WSF_PROGRESS_CONTROL, WSF_REGEXP_VALIDATOR, WSF_REPEATER_CONTROL, WSF_SLIDER_CONTROL, WSF_TEXTAREA_CONTROL, WSF_VALIDATOR, build_control, cache, controls, lazy_load, loaded, parseSuggestions, redirect, show_alert, start_modal, start_modal_big, template, tmpl, +var Mini, WSF_AUTOCOMPLETE_CONTROL, WSF_BUTTON_CONTROL, WSF_CHECKBOX_CONTROL, WSF_CHECKBOX_LIST_CONTROL, WSF_CODEVIEW_CONTROL, WSF_CONTROL, WSF_DATE_PICKER_CONTROL, WSF_DROPDOWN_CONTROL, WSF_DYNAMIC_MULTI_CONTROL, WSF_FILE_CONTROL, WSF_FORM_ELEMENT_CONTROL, WSF_GRID_CONTROL, WSF_HTML_CONTROL, WSF_INPUT_CONTROL, WSF_MAX_VALIDATOR, WSF_MIN_VALIDATOR, WSF_NAVLIST_ITEM_CONTROL, WSF_PAGE_CONTROL, WSF_PAGINATION_CONTROL, WSF_PASSWORD_CONTROL, WSF_PROGRESS_CONTROL, WSF_REGEXP_VALIDATOR, WSF_REPEATER_CONTROL, WSF_SLIDER_CONTROL, WSF_TEXTAREA_CONTROL, WSF_VALIDATOR, build_control, cache, controls, find_control, lazy_load, loaded, once, parseSuggestions, redirect, show_alert, start_modal, start_modal_big, 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; }; @@ -118,6 +118,17 @@ parseSuggestions = function(data) { loaded = {}; +once = function(f) { + var executed; + executed = false; + return function() { + if (!executed) { + executed = true; + return f.apply(this, arguments); + } + }; +}; + lazy_load = function(requirements, fn, that) { if (requirements.length === 0) { return function() { @@ -154,9 +165,30 @@ lazy_load = function(requirements, fn, that) { }; }; +find_control = function(root, name) { + var children, matching; + children = root.children(); + if (children.length) { + matching = children.filter('[data-name=' + name + ']'); + if (matching.length) { + return matching.first(); + } else { + return find_control(children.filter(':not([data-name])'), name); + } + } else { + return null; + } +}; + build_control = function(control_name, state, control) { var $el, type, typeclass; - $el = control.$el.find('[data-name=' + control_name + ']').first(); + $el = find_control(control.$el, control_name); + if ($el === null) { + return null; + } + if ($el.data('control') != null) { + return $el.data('control'); + } type = $el.data('type'); typeclass = null; try { @@ -257,6 +289,7 @@ WSF_CONTROL = (function() { this.isolation = "" + this.$el.data('isolation') === "1"; this.$el.data('control', this); this.initialize = lazy_load(this.requirements, this.attach_events, this); + this.initialize = once(this.initialize); return; } @@ -314,10 +347,10 @@ WSF_CONTROL = (function() { WSF_CONTROL.prototype.process_update = function(new_states) { var control, _i, _len, _ref; try { - if (new_states.actions != null) { + if ((new_states != null ? new_states.actions : void 0) != null) { this.process_actions(new_states.actions); } - if (new_states[this.control_name] != null) { + if ((new_states != null ? new_states[this.control_name] : void 0) != null) { this.update(new_states[this.control_name]); _ref = this.controls; for (_i = 0, _len = _ref.length; _i < _len; _i++) { @@ -328,6 +361,7 @@ WSF_CONTROL = (function() { } } } catch (e) { + console.error(e); return; } }; @@ -437,7 +471,9 @@ WSF_CONTROL = (function() { _ref = this.controls; for (_i = 0, _len = _ref.length; _i < _len; _i++) { control = _ref[_i]; - control.remove(); + if (control != null) { + control.remove(); + } } console.log("Removed " + this.control_name); return this.$el.remove(); @@ -461,6 +497,7 @@ WSF_PAGE_CONTROL = (function(_super) { this.url_params = jQuery.unparam(this.state['url_params']); this.$el.data('control', this); this.initialize = lazy_load(this.requirements, this.attach_events, this); + this.initialize = once(this.initialize); this.load_subcontrols(); } @@ -488,7 +525,9 @@ WSF_PAGE_CONTROL = (function(_super) { _ref = this.controls; for (_i = 0, _len = _ref.length; _i < _len; _i++) { control = _ref[_i]; - control.remove(); + if (control != null) { + control.remove(); + } } console.log("Removed " + this.control_name); return this.$el.remove(); @@ -803,22 +842,40 @@ WSF_DYNAMIC_MULTI_CONTROL = (function(_super) { } WSF_DYNAMIC_MULTI_CONTROL.prototype.update = function(state) { - var control, _i, _j, _len, _len1, _ref, _ref1; + var control, el, k, v, _i, _j, _k, _len, _len1, _len2, _ref, _ref1, _ref2, _ref3; console.log(state); if ((state.items != null) && (state.render != null) && (state.newstate != null)) { this.state['items'] = state.items; _ref = this.controls; for (_i = 0, _len = _ref.length; _i < _len; _i++) { control = _ref[_i]; - control.remove(); + if (!((control != null ? control.control_name : void 0) in state.newstate)) { + if (control != null) { + control.remove(); + } + } } - this.$el.html($(state.render).html()); - this.fullstate.controls = state.newstate; - this.load_subcontrols(); - _ref1 = this.controls; + _ref1 = $(state.render).children(); for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) { - control = _ref1[_j]; - control.initialize(); + el = _ref1[_j]; + if (this.$el.children('[data-name=' + $(el).data('name') + ']').length === 0) { + this.$el.append(el); + } + } + _ref2 = state.newstate; + for (k in _ref2) { + v = _ref2[k]; + if (!(k in this.fullstate.controls)) { + this.fullstate.controls[k] = v; + } + } + this.load_subcontrols(); + _ref3 = this.controls; + for (_k = 0, _len2 = _ref3.length; _k < _len2; _k++) { + control = _ref3[_k]; + if (control != null) { + control.initialize(); + } } } };