diff --git a/draft/library/wsf_js_widget/kernel/input/wsf_file.e b/draft/library/wsf_js_widget/kernel/input/wsf_file.e new file mode 100644 index 00000000..9f6fd77b --- /dev/null +++ b/draft/library/wsf_js_widget/kernel/input/wsf_file.e @@ -0,0 +1,49 @@ +note + description: "Summary description for {WSF_FILE}." + author: "" + date: "$Date$" + revision: "$Revision$" + +class + WSF_FILE + +create + make + +feature {NONE} + + make (a_name, a_type: STRING; a_size: INTEGER; a_id: detachable STRING) + do + name := a_name + type := a_type + size := a_size + id := a_id + end + +feature + + set_id (a_id: detachable STRING) + do + id := a_id + end + +feature --Properties + + is_uploaded: BOOLEAN + do + Result := attached id + end + + name: STRING + -- File name + + type: STRING + -- File mime type + + size: INTEGER + -- File size + + id: detachable STRING + -- Server side file id (e.g. S3 filename) + +end diff --git a/draft/library/wsf_js_widget/kernel/input/wsf_file_control.e b/draft/library/wsf_js_widget/kernel/input/wsf_file_control.e index 8b2f57a5..4de584b4 100644 --- a/draft/library/wsf_js_widget/kernel/input/wsf_file_control.e +++ b/draft/library/wsf_js_widget/kernel/input/wsf_file_control.e @@ -9,7 +9,7 @@ class inherit - WSF_VALUE_CONTROL [detachable WSF_PENDING_FILE] + WSF_VALUE_CONTROL [detachable WSF_FILE] rename make as make_value_control end @@ -28,12 +28,14 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management set_state (new_state: JSON_OBJECT) -- Restore text from json + local + id: detachable STRING do - if attached {JSON_STRING} new_state.item ("file") as new_name and attached {JSON_STRING} new_state.item ("type") as new_type and attached {JSON_NUMBER} new_state.item ("size") as new_size then - create file.make (new_name.unescaped_string_32, new_type.unescaped_string_32, new_size.item.to_integer_32); - end - if attached {JSON_STRING} new_state.item ("upload_file") as f then - upload_file:=f.unescaped_string_32; + if attached {JSON_STRING} new_state.item ("file_name") as new_name and attached {JSON_STRING} new_state.item ("file_type") as new_type and attached {JSON_NUMBER} new_state.item ("file_size") as new_size then + if attached {JSON_STRING} new_state.item ("file_id") as a_id then + id := a_id.unescaped_string_32 + end + create file.make (new_name.unescaped_string_32, new_type.unescaped_string_32, new_size.item.to_integer_32, id); end end @@ -42,41 +44,44 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management do create Result.make Result.put_boolean (attached change_event, "callback_change") - end - -feature -- Uploaded Files - - set_uploaded_file (p: detachable STRING) - -- Store link to uploaded file in control state. In order to make it availabe for future callbacks - do - if attached p as a_p then - upload_file := a_p - state_changes.put_string (a_p, "upload_file") + Result.put_boolean (attached upload_done_event, "callback_uploaddone") + if attached file as f then + Result.put_string (f.name, "file_name") + Result.put_string (f.type, "file_type") + Result.put_integer (f.size, "file_size") + Result.put_string (f.id, "file_id") end + Result.put_boolean (disabled, "disabled") end feature -- Event handling - set_change_event (e: attached like change_event) - -- Set text change event handle - do - change_event := e - end - - set_upload_function (e: attached like upload_function) - -- Set button click event handle - do - upload_function := e - end - handle_callback (cname: LIST [STRING]; event: STRING; event_parameter: detachable ANY) + local + f_name: detachable STRING + f_type: detachable STRING + f_size: detachable INTEGER + f_id: detachable STRING do if Current.control_name.same_string (cname [1]) then - if attached change_event as cevent and event.same_string ("change") then + if attached change_event as cevent and event.same_string ("change") then cevent.call (Void) - elseif attached upload_function as ufunction and event.same_string ("uploadfile") and attached {ITERABLE[WSF_UPLOADED_FILE]}event_parameter as files then - - set_uploaded_file(ufunction.item ([files])) + elseif attached upload_done_event as udevent and event.same_string ("uploaddone") then + udevent.call (Void) + elseif event.same_string ("uploadfile") and attached {ITERABLE [WSF_UPLOADED_FILE]} event_parameter as files then + if attached file as f then + if attached upload_function as ufunction then + f.set_id (ufunction.item ([files])) + end + f_name := f.name + f_type := f.type + f_size := f.size + f_id := f.id + end + state_changes.replace_with_string (f_name, "file_name") + state_changes.replace_with_string (f_type, "file_type") + state_changes.replace_with_integer (f_size, "file_size") + state_changes.replace_with_string (f_id, "file_id") end end end @@ -94,28 +99,68 @@ feature -- Upload feature -- Implementation - value: detachable WSF_PENDING_FILE + value: detachable WSF_FILE do Result := file end render: STRING + local + attr: STRING do - Result := render_tag ("", "type=%"file%" ") + attr := "type=%"file%" " + if attached attributes as a then + attr.append (a) + end + if disabled then + attr.append ("disabled=%"disabled%" ") + end + Result := render_tag ("", attr) + end + +feature -- Change + + set_change_event (e: attached like change_event) + -- Set text change event handle + do + change_event := e + end + + set_upload_done_event (e: attached like upload_done_event) + -- Set text change event handle + do + upload_done_event := e + end + + set_upload_function (e: attached like upload_function) + -- Set button click event handle + do + upload_function := e + end + + set_disabled (b: BOOLEAN) + do + if disabled /= b then + disabled := b + state_changes.replace_with_boolean (disabled, "disabled") + end end feature -- Properties - file: detachable WSF_PENDING_FILE + disabled: BOOLEAN + -- Defines if the a file is selectable and if a file can be removed once it is uploaded + + file: detachable WSF_FILE -- Text to be displayed change_event: detachable PROCEDURE [ANY, TUPLE] -- Procedure to be execued on change - upload_function: detachable FUNCTION [ANY, TUPLE[ITERABLE[WSF_UPLOADED_FILE]],detachable STRING] - -- Procedure to be execued on change + upload_done_event: detachable PROCEDURE [ANY, TUPLE] + -- Procedure to be execued when upload was successful - upload_file: detachable STRING - -- Link to uploaded file + upload_function: detachable FUNCTION [ANY, TUPLE [ITERABLE [WSF_UPLOADED_FILE]], detachable STRING] + -- Store uploaded file and return server side file id end diff --git a/draft/library/wsf_js_widget/kernel/input/wsf_input_control.e b/draft/library/wsf_js_widget/kernel/input/wsf_input_control.e index d243bd7d..546aaa42 100644 --- a/draft/library/wsf_js_widget/kernel/input/wsf_input_control.e +++ b/draft/library/wsf_js_widget/kernel/input/wsf_input_control.e @@ -22,7 +22,7 @@ feature {NONE} -- Initialization make (v: STRING) -- Initialize with specified name and value do - make_value_control ( "input") + make_value_control ("input") type := "text" text := v end @@ -42,6 +42,7 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management do create Result.make Result.put_string (text, "text") + Result.put_boolean (disabled, "disabled") Result.put_boolean (attached change_event, "callback_change") end @@ -53,9 +54,9 @@ feature --Event handling change_event := e end - handle_callback (cname: LIST[STRING]; event: STRING; event_parameter: detachable ANY) - do - if Current.control_name.same_string (cname[1]) and attached change_event as cevent then + handle_callback (cname: LIST [STRING]; event: STRING; event_parameter: detachable ANY) + do + if Current.control_name.same_string (cname [1]) and attached change_event as cevent then if event.same_string ("change") then cevent.call (Void) end @@ -65,8 +66,17 @@ feature --Event handling feature -- Rendering render: STRING + local + attr: STRING do - Result := render_tag ("", "type=%"" + type + "%" value=%"" + text + "%"") + attr := "type=%"" + type + "%" value=%"" + text + "%" " + if attached attributes as a then + attr.append (a) + end + if disabled then + attr.append ("disabled=%"disabled%" ") + end + Result := render_tag ("", attr) end feature -- Change @@ -80,6 +90,14 @@ feature -- Change end end + set_disabled (b: BOOLEAN) + do + if disabled /= b then + disabled := b + state_changes.replace_with_boolean (disabled, "disabled") + end + end + feature -- Implementation value: STRING @@ -89,6 +107,9 @@ feature -- Implementation feature -- Properties + disabled: BOOLEAN + -- Defines if the input field is editable + text: STRING -- Text to be displayed diff --git a/draft/library/wsf_js_widget/kernel/input/wsf_pending_file.e b/draft/library/wsf_js_widget/kernel/input/wsf_pending_file.e deleted file mode 100644 index 759d39f3..00000000 --- a/draft/library/wsf_js_widget/kernel/input/wsf_pending_file.e +++ /dev/null @@ -1,30 +0,0 @@ -note - description: "Summary description for {WSF_PENDING_FILE}." - author: "" - date: "$Date$" - revision: "$Revision$" - -class - WSF_PENDING_FILE - -create - make - -feature {NONE} - - make (a_name, a_type: STRING; a_size: INTEGER) - do - name := a_name - type := a_type - size := a_size - end - -feature --Properties - - name: STRING - - type: STRING - - size: INTEGER - -end diff --git a/draft/library/wsf_js_widget/kernel/validator/wsf_filesize_validator.e b/draft/library/wsf_js_widget/kernel/validator/wsf_filesize_validator.e index 517decdf..c0158b41 100644 --- a/draft/library/wsf_js_widget/kernel/validator/wsf_filesize_validator.e +++ b/draft/library/wsf_js_widget/kernel/validator/wsf_filesize_validator.e @@ -9,7 +9,7 @@ class inherit - WSF_VALIDATOR [detachable WSF_PENDING_FILE] + WSF_VALIDATOR [detachable WSF_FILE] rename make as make_validator redefine @@ -30,7 +30,7 @@ feature {NONE} -- Initialization feature -- Implementation - is_valid (input: detachable WSF_PENDING_FILE): BOOLEAN + is_valid (input: detachable WSF_FILE): BOOLEAN do Result := True if attached input as a_input then diff --git a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_button_control.e b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_button_control.e index 3b49767e..5692d9ad 100644 --- a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_button_control.e +++ b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_button_control.e @@ -43,6 +43,7 @@ feature {WSF_PAGE_CONTROL, WSF_CONTROL} -- State management do create Result.make Result.put_string (text, "text") + Result.put_boolean (disabled, "disabled") Result.put_boolean (attached click_event, "callback_click") end @@ -54,9 +55,9 @@ feature --Event handling click_event := e end - handle_callback (cname: LIST[STRING]; event: STRING; event_parameter: detachable ANY) + handle_callback (cname: LIST [STRING]; event: STRING; event_parameter: detachable ANY) do - if Current.control_name.same_string (cname[1]) and attached click_event as cevent then + if Current.control_name.same_string (cname [1]) and attached click_event as cevent then cevent.call (Void) end end @@ -65,8 +66,17 @@ feature -- Rendering render: STRING -- HTML representation of this control + local + attr: STRING do - Result := render_tag (text, attributes) + create attr.make_empty + if attached attributes as a then + attr.append (a) + end + if disabled then + attr.append ("disabled=%"disabled%" ") + end + Result := render_tag (text, attr) end feature -- Change @@ -76,12 +86,23 @@ feature -- Change do if not t.same_string (text) then text := t - state_changes.replace (create {JSON_STRING}.make_json (text), "text") + state_changes.replace_with_string (text, "text") + end + end + + set_disabled (b: BOOLEAN) + do + if disabled /= b then + disabled := b + state_changes.replace_with_boolean (disabled, "disabled") end end feature -- Properties + disabled: BOOLEAN + -- Defines if the button is editable + text: STRING -- The text currently displayed on this button diff --git a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_form_control.e b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_form_control.e index 81fc8796..8bc55a65 100644 --- a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_form_control.e +++ b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_form_control.e @@ -34,6 +34,7 @@ feature {NONE} -- Initialization make_multi_control tag_name := "form" label_width := w + add_class ("form-horizontal") end feature diff --git a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_form_element_control.e b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_form_element_control.e index 1c759e94..1494a207 100644 --- a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_form_element_control.e +++ b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_form_element_control.e @@ -36,12 +36,13 @@ feature {NONE} -- Initialization do make_control ("div") add_class ("form-group") - if attached {WSF_HTML_CONTROL} c then - c.add_class ("form-control-static") - elseif not attached {WSF_VALUE_CONTROL [LIST[ANY]]} c then + if not attached {WSF_VALUE_CONTROL [LIST[ANY]]} c then c.add_class ("form-control") + else + c.add_class ("form-control-static") end + label_width := 2 value_control := c validators := v diff --git a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_json_object.e b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_json_object.e index a42e114e..c125181c 100644 --- a/draft/library/wsf_js_widget/kernel/webcontrol/wsf_json_object.e +++ b/draft/library/wsf_js_widget/kernel/webcontrol/wsf_json_object.e @@ -16,114 +16,133 @@ create feature - put_string (value: READABLE_STRING_GENERAL; key: JSON_STRING) + put_string (value: detachable READABLE_STRING_GENERAL; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. require key_not_present: not has_key (key) local - l_value: JSON_STRING + l_value: detachable JSON_STRING do - create l_value.make_json_from_string_32 (value.as_string_32) + if attached value as a_value then + create l_value.make_json_from_string_32 (a_value.as_string_32) + end put (l_value, key) end - put_integer (value: INTEGER_64; key: JSON_STRING) + put_integer (value: detachable INTEGER_64; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. require key_not_present: not has_key (key) local - l_value: JSON_NUMBER + l_value: detachable JSON_NUMBER do - create l_value.make_integer (value) + if attached value as a_value then + create l_value.make_integer (a_value) + end put (l_value, key) end - put_natural (value: NATURAL_64; key: JSON_STRING) + put_natural (value: detachable NATURAL_64; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. require key_not_present: not has_key (key) local - l_value: JSON_NUMBER + l_value: detachable JSON_NUMBER do - create l_value.make_natural (value) + if attached value as a_value then + create l_value.make_natural (a_value) + end put (l_value, key) end - put_real (value: DOUBLE; key: JSON_STRING) + put_real (value: detachable DOUBLE; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. require key_not_present: not has_key (key) local - l_value: JSON_NUMBER + l_value: detachable JSON_NUMBER do - create l_value.make_real (value) + if attached value as a_value then + create l_value.make_real (a_value) + end put (l_value, key) end - put_boolean (value: BOOLEAN; key: JSON_STRING) + put_boolean (value: detachable BOOLEAN; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. require key_not_present: not has_key (key) local - l_value: JSON_BOOLEAN + l_value: detachable JSON_BOOLEAN do - create l_value.make_boolean (value) + if attached value as a_value then + create l_value.make_boolean (a_value) + end put (l_value, key) end - replace_with_string (value: READABLE_STRING_GENERAL; key: JSON_STRING) + replace_with_string (value: detachable READABLE_STRING_GENERAL; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. local - l_value: JSON_STRING + l_value: detachable JSON_STRING do - create l_value.make_json_from_string_32 (value.as_string_32) + if attached value as a_value then + create l_value.make_json_from_string_32 (value.as_string_32) + end replace (l_value, key) end - replace_with_integer (value: INTEGER_64; key: JSON_STRING) + replace_with_integer (value: detachable INTEGER_64; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. local - l_value: JSON_NUMBER + l_value: detachable JSON_NUMBER do - create l_value.make_integer (value) + if attached value as a_value then + create l_value.make_integer (a_value) + end replace (l_value, key) end - replace_with_with_natural (value: NATURAL_64; key: JSON_STRING) + replace_with_with_natural (value: detachable NATURAL_64; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. local - l_value: JSON_NUMBER + l_value: detachable JSON_NUMBER do - create l_value.make_natural (value) + if attached value as a_value then + create l_value.make_natural (a_value) + end replace (l_value, key) end - replace_with_real (value: DOUBLE; key: JSON_STRING) + replace_with_real (value: detachable DOUBLE; key: JSON_STRING) -- Assuming there is no item of key `key', - -- insert `value' with `key'. + -- insert `value' with `key' local - l_value: JSON_NUMBER + l_value: detachable JSON_NUMBER do - create l_value.make_real (value) + if attached value as a_value then + create l_value.make_real (a_value) + end replace (l_value, key) end - - replace_with_boolean (value: BOOLEAN; key: JSON_STRING) + replace_with_boolean (value: detachable BOOLEAN; key: JSON_STRING) -- Assuming there is no item of key `key', -- insert `value' with `key'. local - l_value: JSON_BOOLEAN + l_value: detachable JSON_BOOLEAN do - create l_value.make_boolean (value) + if attached value as a_value then + create l_value.make_boolean (a_value) + end replace (l_value, key) end diff --git a/examples/widgetapp/application.e b/examples/widgetapp/application.e index 8e250c49..d1dbc466 100644 --- a/examples/widgetapp/application.e +++ b/examples/widgetapp/application.e @@ -81,6 +81,7 @@ feature -- Router and Filter map_agent_uri ("/grid", agent grid_demo, Void) map_agent_uri ("/repeater", agent repeater_demo, Void) map_agent_uri ("/slider", agent slider_demo, Void) + map_agent_uri ("/upload", agent upload_demo, Void) map_agent_uri ("/codeview", agent codeview, Void) -- NOTE: you could put all those files in a specific folder, and use WSF_FILE_SYSTEM_HANDLER with "/" @@ -137,6 +138,16 @@ feature -- Execution page.execute end + upload_demo (request: WSF_REQUEST; response: WSF_RESPONSE) + local + page: UPLOAD_PAGE + do + -- To send a response we need to setup, the status code and + -- the response headers. + create page.make (request, response) + page.execute + end + codeview (request: WSF_REQUEST; response: WSF_RESPONSE) local page: CODEVIEW_PAGE diff --git a/examples/widgetapp/assets/widget.coffee b/examples/widgetapp/assets/widget.coffee index 0e7f7cc0..40d18030 100644 --- a/examples/widgetapp/assets/widget.coffee +++ b/examples/widgetapp/assets/widget.coffee @@ -166,6 +166,7 @@ class WSF_CONTROL @controls=(build_control(control_name, state, @) for control_name, state of @fullstate.controls) else @controls = [] + return attach_events: ()-> console.log "Attached #{@control_name}" @@ -190,6 +191,7 @@ class WSF_CONTROL fn(action) catch e console.log "Failed preforming action #{action.type}" + return process_update: (new_states)-> try @@ -204,8 +206,6 @@ class WSF_CONTROL return return - - get_context_state : ()-> if @parent_control? and not @isolation return @parent_control.get_context_state() @@ -340,6 +340,9 @@ class WSF_BUTTON_CONTROL extends WSF_CONTROL @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) @@ -362,6 +365,9 @@ class WSF_INPUT_CONTROL extends WSF_CONTROL 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) @@ -372,6 +378,8 @@ class WSF_FILE_CONTROL extends WSF_CONTROL @uploading = false start_upload: ()-> + if @$el[0].files.length==0 + return if @uploading return @uploading = true @@ -415,14 +423,15 @@ class WSF_FILE_CONTROL extends WSF_CONTROL change: ()-> #update local state - @state['file'] = null - @state['type'] = null - @state['size'] = null + @state['file_name'] = null + @state['file_type'] = null + @state['file_size'] = null + @state['file_id'] = null if @$el[0].files.length>0 file = @$el[0].files[0] - @state['file'] = file.name - @state['type'] = file.type - @state['size'] = file.size + @state['file_name'] = file.name + @state['file_type'] = file.type + @state['file_size'] = file.size if @state['callback_change'] @trigger_callback(@control_name, 'change') @trigger('change') @@ -431,10 +440,49 @@ class WSF_FILE_CONTROL extends WSF_CONTROL return @$el.val() update: (state) -> - if state.upload_file? - @progressbar.hide() - @$el.parent().append($("""

""").addClass("form-control-static").text(@state['file'])) - @state['upload_file'] = state.upload_file + if state.disabled != undefined + @state['disabled'] = state.disabled + @$el.prop('disabled', state.disabled) + @refresh() + if state.file_name != undefined + @state['file_name'] = state.file_name + if state.file_type != undefined + @state['file_type'] = state.file_type + if state.file_size != undefined + @state['file_size'] = state.file_size + if state.file_id != undefined + if @state['file_id'] != state.file_id + @state['file_id'] = state.file_id + if @state['callback_uploaddone'] + @trigger_callback(@control_name, 'uploaddone') + @uploading = false + @refresh() + + refresh: ()-> + if @uploading + return + @progressbar.remove() + @$el.parent().find("p").remove() + if @state['file_id'] != null + @$el.hide() + fname = $("""

""").addClass("form-control-static").text(@state['file_name']) + @$el.parent().append(fname) + if not @state['disabled'] + fname.append(" "); + removebtn = $("