From 2145ec66339d0e42cb07e6bdda1218db6190e34b Mon Sep 17 00:00:00 2001 From: Xuqing Kuang Date: Fri, 17 Aug 2012 13:52:32 +0800 Subject: [PATCH 01/16] [Widgets] Added event properties to widgets. --- src/js/widgets.js | 166 +++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 165 insertions(+), 1 deletion(-) diff --git a/src/js/widgets.js b/src/js/widgets.js index 6a71d67d..2041f152 100644 --- a/src/js/widgets.js +++ b/src/js/widgets.js @@ -203,6 +203,100 @@ var BWidgetRegistry = { type: "string", defaultValue: "", htmlAttribute: "id" + }, + + /* + * Event properties + */ + + // Touch events + tap: { + type: "event", + defaultValue: "", + visible: false, + }, + taphold: { + type: "event", + defaultValue: "", + visible: false, + }, + swipe: { + type: "event", + defaultValue: "", + visible: false, + }, + swipeleft: { + type: "event", + defaultValue: "", + visible: false, + }, + swiperight: { + type: "event", + defaultValue: "", + visible: false, + }, + + // Scroll events + scrollstart: { + type: "event", + defaultValue: "", + visible: false, + }, + scrollstop: { + type: "event", + defaultValue: "", + visible: false, + }, + + // Virtual mouse events + vmouseover: { + type: "event", + defaultValue: "", + visible: false, + }, + vmouseout: { + type: "event", + defaultValue: "", + visible: false, + }, + vmousedown: { + type: "event", + defaultValue: "", + visible: false, + }, + vmousemove: { + type: "event", + defaultValue: "", + visible: false, + }, + vmouseup: { + type: "event", + defaultValue: "", + visible: false, + }, + vclick: { + type: "event", + defaultValue: "", + visible: false, + }, + vmousecancel: { + type: "event", + defaultValue: "", + visible: false, + }, + + // Orientation change event + orientationchange: { + type: "event", + defaultValue: "", + visible: false, + }, + + // Layout events + updatelayout: { + type: "event", + defaultValue: "", + visible: false, } } }, @@ -332,6 +426,66 @@ var BWidgetRegistry = { type: "string", defaultValue: "", htmlAttribute: "data-title", + }, + pagebeforeload: { + type: "event", + defaultValue: "", + visible: false, + }, + pageload: { + type: "event", + defaultValue: "", + visible: false, + }, + pageloadfailed: { + type: "event", + defaultValue: "", + visible: false, + }, + pagebeforechange: { + type: "event", + defaultValue: "", + visible: false, + }, + pagechange: { + type: "event", + defaultValue: "", + visible: false, + }, + pagechangefailed: { + type: "event", + defaultValue: "", + visible: false, + }, + pagebeforeshow: { + type: "event", + defaultValue: "", + visible: false, + }, + pagebeforehide: { + type: "event", + defaultValue: "", + visible: false, + }, + pageshow: { + type: "event", + defaultValue: "", + visible: false, + }, + pagebeforecreate: { + type: "event", + defaultValue: "", + visible: false, + }, + pagecreate: { + type: "event", + defaultValue: "", + visible: false, + }, + pageremove: { + type: "event", + defaultValue: "", + visible: false, } }, redirect: { @@ -797,6 +951,11 @@ var BWidgetRegistry = { defaultValue: "POST", htmlAttribute: "method", forceAttribute: true + }, + submit: { + type: "event", + defaultValue: "", + visible: false, } } }, @@ -1102,7 +1261,12 @@ var BWidgetRegistry = { }), iconpos: $.extend({}, BCommonProperties.iconpos, { defaultValue: "right" - }) + }), + change: { + type: "event", + defaultValue: "", + visible: false + } }, zones: [ { From f9537829de358e607b5439f26477c1855f57c52e Mon Sep 17 00:00:00 2001 From: Xuqing Kuang Date: Wed, 22 Aug 2012 11:25:39 +0800 Subject: [PATCH 02/16] [ADM] Added force argument for generateUniqueProperty() For force generate ID property for event handler. --- src/js/adm.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/js/adm.js b/src/js/adm.js index 9ec85452..607d1d61 100644 --- a/src/js/adm.js +++ b/src/js/adm.js @@ -1937,11 +1937,15 @@ ADMNode.prototype.foreach = function (func) { * @param {String} The name of the property. * @return {String} The generated property value. */ -ADMNode.prototype.generateUniqueProperty = function (property) { +ADMNode.prototype.generateUniqueProperty = function (property, force) { var generate, design, myType, length, i, genLength, max, num, existing = []; myType = this.getType(); generate = BWidget.getPropertyAutoGenerate(myType, property); - if (!generate) { + // If force argument is set, then set the generate as myType and continue + // to run. + if (!generate && force) { + generate = myType.toLowerCase(); + } else if (!generate) { return undefined; } From b7a7e188b2c6577539f1b2805291cc822b9afae3 Mon Sep 17 00:00:00 2001 From: Xu Zhang Date: Wed, 22 Aug 2012 14:19:54 +0800 Subject: [PATCH 03/16] [Widgets] modify getPropertyOptions function * extend to get value of options by function --- src/js/widgets.js | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/src/js/widgets.js b/src/js/widgets.js index 6a71d67d..74d3eafc 100644 --- a/src/js/widgets.js +++ b/src/js/widgets.js @@ -66,7 +66,20 @@ var BCommonProperties = { }, theme: { type: "string", - options: [ "default", "a", "b", "c", "d", "e" ], + options: function() { + var pmUtils = $.rib.pmUtils, currentTheme, pid, + swatches =[]; + pid = pmUtils.getActive(); + if (pid) { + currentTheme = pmUtils.getProperty(pid, "theme"); + if (currentTheme === 'Default') { + swatches = ["default", "a", "b", "c", "d", "e"]; + } else { + swatches = pmUtils.themesList[currentTheme]; + } + } + return swatches; + }, defaultValue: "default", htmlAttribute: "data-theme" }, @@ -2178,7 +2191,7 @@ var BWidget = { * @throws {Error} If widgetType is invalid. */ getPropertyOptions: function (widgetType) { - var stack = [], options = {}, length, i, property, widget; + var stack = [], options = {}, length, i, property, widget, valueOptions; widget = BWidgetRegistry[widgetType]; if (typeof widget !== "object") { @@ -2188,7 +2201,12 @@ var BWidget = { for (property in widget.properties) { if (widget.properties.hasOwnProperty(property)) { - options[property] = widget.properties[property].options; + valueOptions = widget.properties[property].options; + if (typeof valueOptions === "function") { + options[property] = valueOptions(); + } else { + options[property] = valueOptions; + } } } return $.extend(true, {}, options); From 9f46eec0a18c40786f077f6b1c2ea05f47b99260 Mon Sep 17 00:00:00 2001 From: Xu Zhang Date: Thu, 23 Aug 2012 10:43:35 +0800 Subject: [PATCH 04/16] [Project] add default option for customized theme * provide a default option for imported theme file --- src/js/projects.js | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/js/projects.js b/src/js/projects.js index fb82059d..58f30755 100644 --- a/src/js/projects.js +++ b/src/js/projects.js @@ -1067,9 +1067,13 @@ $(function () { $.rib.fsUtils.read('/themes/' + themeFile.name, function (result) { try { var swatches = parseSwatches(result); - pmUtils.themesList[themeName] = swatches; - //update themes.json in sandbox - $.rib.fsUtils.write('/themes.json', JSON.stringify(pmUtils.themesList)); + if (swatches.length) { + pmUtils.themesList[themeName] = swatches; + // add default swatch into theme + pmUtils.themesList[themeName].unshift('default'); + //update themes.json in sandbox + $.rib.fsUtils.write('/themes.json', JSON.stringify(pmUtils.themesList)); + } } catch(e) { alert(e.stack); return false; From 81336325090ca51f0d13c95c25dd495ad542036a Mon Sep 17 00:00:00 2001 From: Xu Zhang Date: Tue, 21 Aug 2012 15:36:15 +0800 Subject: [PATCH 05/16] [UX] Add theme selector to the new page dialog --- src/js/views/page.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/js/views/page.js b/src/js/views/page.js index 87160f5b..d9941187 100644 --- a/src/js/views/page.js +++ b/src/js/views/page.js @@ -255,11 +255,24 @@ _dialogOpenHandler: function (e, ui) { try { - var dialog = $('#pageDialog') || $(this).dialog('option', 'newPageDialog'); + var swatches, id, s, + dialog = $('#pageDialog') || $(this).dialog('option', 'newPageDialog'), + swatchPicker = dialog.find('#swatchPicker'); + dialog.find('#pagePicker').get(0).selectedIndex = 0; dialog.find('#header_layout').attr("checked", true); dialog.find('#footer_layout').attr("checked", true); dialog.find('input:radio[name=Layout]')[0].checked = true; + swatchPicker.empty(); + // get swatches of current project theme + swatches = BWidget.getPropertyOptions('Page')['theme']; + for (s in swatches) { + id = swatches[s]; + $('') + .appendTo(swatchPicker); + } + // make the first option as default selected + swatchPicker.get(0).selectedIndex = 0; } catch (err) { console.error(err.message); @@ -287,6 +300,7 @@ layout.push('Footer'); } options.layout = layout; + options.theme = dialog.find("#swatchPicker").val(); newPage = $.rib.pageUtils.createNewPage(options); ADM.setActivePage(newPage); dialog.dialog("close"); @@ -333,6 +347,8 @@ '' + '' + '' + + '
  • ' + + '
  • ' + '
    ') .end() .append('
    ') From 35469af5b6f438a37a14ec9b70efdbb979fce195 Mon Sep 17 00:00:00 2001 From: Xu Zhang Date: Tue, 21 Aug 2012 16:25:32 +0800 Subject: [PATCH 06/16] [Page Template] support to set theme of page --- src/js/pageTemplate.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/js/pageTemplate.js b/src/js/pageTemplate.js index 58a623e2..81ea4d45 100644 --- a/src/js/pageTemplate.js +++ b/src/js/pageTemplate.js @@ -32,6 +32,7 @@ $(function() { pageTemplate = config.pageTemplate || this.options[pageTemplate], layout = this.options.layout.concat(config.layout), isDialog = config.isDialog || this.options.isDialog, + pageTheme = config.theme, newPage, result; if (!design.instanceOf("Design")) { @@ -53,6 +54,14 @@ $(function() { return null; } + // set theme of new page + if (pageTheme) { + result = ADM.setProperty(newPage, 'theme', pageTheme); + if (!result.result) { + return null; + } + } + // create initial page layout result = setPageLayout(newPage, layout); //TODO: if we have some specfic logic to handle with template, From 76bd720145508c44b9e472b286fa04ff4599b906 Mon Sep 17 00:00:00 2001 From: "Xiaoyang Yu (Max)" Date: Tue, 21 Aug 2012 12:01:58 +0800 Subject: [PATCH 07/16] [Properties] Highlight changed property --- src/js/views/property.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/js/views/property.js b/src/js/views/property.js index 218f66cd..296497a2 100644 --- a/src/js/views/property.js +++ b/src/js/views/property.js @@ -81,12 +81,20 @@ }, _modelUpdatedHandler: function(event, widget) { + var affectedWidget, id; + widget = widget || this; if (event && (event.type === "propertyChanged" && event.node.getType() === 'Design')) { return; } else { widget.refresh(event,widget); + if(event.type === 'propertyChanged') { + id = event.property + '-value'; + affectedWidget = widget.element.find('#' + id); + affectedWidget[0].scrollIntoViewIfNeeded(); + affectedWidget.effect("highlight", {}, 1000); + } } }, From 1278f277c226970fe249d5fe7b7854508cba8b3a Mon Sep 17 00:00:00 2001 From: Xuqing Kuang Date: Wed, 22 Aug 2012 16:06:00 +0800 Subject: [PATCH 08/16] [Project View] Edit name directly after click on project name. --- src/css/builder.css | 5 +++++ src/js/views/project.js | 29 ++++++++++++++++++++++++++++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/css/builder.css b/src/css/builder.css index b7d4ead5..0863ed53 100644 --- a/src/css/builder.css +++ b/src/css/builder.css @@ -1348,6 +1348,11 @@ div.propertyItems label[for|=id] { font-weight: bold; } +.titleBar input { + width: 150px; + margin: 3px; +} + .openButton { background-image: url("images/openButton_up.png"); background-repeat: no-repeat; diff --git a/src/js/views/project.js b/src/js/views/project.js index 0a5a40fd..a815d180 100644 --- a/src/js/views/project.js +++ b/src/js/views/project.js @@ -361,7 +361,33 @@ '-webkit-transform-origin':'2 2'}); }, _buttonEvents: function(box, pid, widget) { - var openHandler, cloneHandler, deleteHandler; + var renameHandler, openHandler, cloneHandler, deleteHandler; + renameHandler = function(e) { + var spanElement = $(this), + renameProject = function(e, inputElement, spanElement) { + var projectName = inputElement.val(); + if (projectName.trim() == '') + projectName = 'Untitled' + $.rib.pmUtils.setProperty(pid, "name", projectName); + spanElement.html(projectName); + spanElement.show(); + inputElement.remove(); + }; + + spanElement.hide(); + $('') + .attr('type', 'text') + .val(spanElement.html()) + .appendTo(spanElement.parent()) + .keydown(function(e) { + if(e.keyCode == '13') { + renameProject(e, $(this), spanElement); + } + }) + .blur(function(e) { + renameProject(e, $(this), spanElement); + }); + }; openHandler = function () { var success = function () { // show the layout tab @@ -397,6 +423,7 @@ $.rib.pmUtils.deleteProject(pid, success); }); }; + box.find('.titleBar > span').click(renameHandler); box.find('.openButton').click(openHandler); box.find('.clone.button').click(cloneHandler); box.find('.delete.button').click(deleteHandler); From c0504d929a746b7c9578602377f735a93aa24fb4 Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Thu, 23 Aug 2012 11:15:42 +0800 Subject: [PATCH 09/16] [Outline View] Keyboard focus follows mouse click focus --- src/js/views/tree.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/js/views/tree.js b/src/js/views/tree.js index 7d310156..7ea6660f 100644 --- a/src/js/views/tree.js +++ b/src/js/views/tree.js @@ -40,7 +40,7 @@ focused = selected; } items = $(this).find("a:visible"); - focusedIndex = items.index(focused); + focusedIndex = focusing = items.index(focused); if (focused === -1) { console.error("focused not in list items"); } @@ -66,8 +66,12 @@ default: break; } - focused.removeClass("focused"); - $(items[focusing]).addClass("focused"); + if (focused !== focusing) { + focused.removeClass("focused"); + $(items[focusing]).addClass("focused"); + items[focusing].scrollIntoViewIfNeeded(); + } + return false; }, _setOption: function(key, value) { @@ -175,6 +179,11 @@ .addClass('ui-selected') [0].scrollIntoViewIfNeeded(); domNode[0].scrollIntoViewIfNeeded(); + if (this.element.is(':focus')) { + this.element.find('.focused') + .removeClass("focused"); + domNode.find('> a').addClass("focused") + } } }, From 6ca86645fa8295ed0ab8701c30ce39429471ba53 Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Wed, 22 Aug 2012 14:44:31 +0800 Subject: [PATCH 10/16] [General] Fix inconsistent showings for selected 'null' in views This is intented to fix bug that if you click the blank space below footer in layout iframe, the delete button in property view and delete key will not work. --- src/js/composer.js | 6 +++++- src/js/views/outline.js | 17 +++++------------ src/js/views/property.js | 9 +++------ src/js/views/tree.js | 6 +++--- 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/js/composer.js b/src/js/composer.js index ff72c9e5..19ffcd69 100644 --- a/src/js/composer.js +++ b/src/js/composer.js @@ -898,6 +898,10 @@ $(function() { $(foo).focus(); $(bar).blur(); - adm.setSelected((item?$(item).attr('data-uid'):item)); + if (item) { + adm.setSelected($(item).attr('data-uid')); + } else { + adm.setSelected(adm.getActivePage()); + } } }); diff --git a/src/js/views/outline.js b/src/js/views/outline.js index 6d9d252f..855bfa72 100644 --- a/src/js/views/outline.js +++ b/src/js/views/outline.js @@ -50,22 +50,15 @@ } // Make sure we show the page as selected if no node is selected - if (event === null || event.node === null) { - node = widget.options.model.getDesignRoot() - .findNodeByUid(widget.options.model.getSelected()); - if (node === null || node === undefined) { - node = widget.options.model.getActivePage(); - if (node === null || node === undefined) { - return false; - } - } - } else { + if (event && event.node !== undefined) { node = event.node; + } else { + console.error('Event object may be discontruced, in outline selection handler.'); + return; } - // When a page is selected, we will close all other page subtrees // and ensure the selected pages' subtree is opened - if (node.getType() === 'Page') { + if (node && node.getType() === 'Page') { // node is element, need the "folder" before it var fldr = widget.findDomNode(node).find('> .folder').eq(0); // "Close" all other page folders diff --git a/src/js/views/property.js b/src/js/views/property.js index 218f66cd..3cbbf9de 100644 --- a/src/js/views/property.js +++ b/src/js/views/property.js @@ -100,12 +100,9 @@ // Clear the properties pane when nothing is selected if (node === null || node === undefined) { - node = ADM.getActivePage(); - if (node === null || node === undefined) { - content.empty() - .append(''); - return; - } + content.empty() + .append(''); + return; } type = node.getType(); diff --git a/src/js/views/tree.js b/src/js/views/tree.js index 7d310156..c60f00fb 100644 --- a/src/js/views/tree.js +++ b/src/js/views/tree.js @@ -167,10 +167,10 @@ }, _setSelected: function (domNode) { + this.element.find('.ui-selected') + .removeClass('ui-selected') + .removeClass('ui-state-active'); if (domNode[0]) { - this.element.find('.ui-selected') - .removeClass('ui-selected') - .removeClass('ui-state-active'); domNode.find('> a').addClass('ui-state-active') .addClass('ui-selected') [0].scrollIntoViewIfNeeded(); From 0c77975a01af0b7b1df1da122adb29cd286d3d28 Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Thu, 23 Aug 2012 12:40:03 +0800 Subject: [PATCH 11/16] [Layout View] Fix bug: some selected items lose green box after changing tab Descreption: selected check-box, toggle-switch or radio button in layout view will lose selected green box after changing tab to other views, for instance code view or preview. --- src/js/composer.js | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/js/composer.js b/src/js/composer.js index 19ffcd69..7f85a8c7 100644 --- a/src/js/composer.js +++ b/src/js/composer.js @@ -432,6 +432,15 @@ $(function() { delegateNode.addClass('delegation'); delegateNode.attr('data-uid', $(node).attr('data-uid')); } + if (admNode.isSelected() && !admNode.instanceOf('Page')) { + $(node).removeClass('ui-selected') + .removeClass('ui-selecting') + .removeClass('ui-unselecting'); + + $(delegateNode).removeClass('ui-unselecting') + .removeClass('ui-selecting') + .addClass('ui-selected'); + } } // Configure "select" handler From 44ba70d31453673b1f4a07c61ff3e87193ca7cec Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Thu, 23 Aug 2012 13:26:04 +0800 Subject: [PATCH 12/16] [FileIO] Fix deprecated 'BlobBuilder' message in console --- src/js/fs.js | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/js/fs.js b/src/js/fs.js index 6a722584..74dad75e 100644 --- a/src/js/fs.js +++ b/src/js/fs.js @@ -327,7 +327,7 @@ $(function () { var onError = error || fsUtils.onError; function write(fileEntry) { fileEntry.createWriter(function (fileWriter) { - var bb, mimeString, ab, ia; + var bb, ab, ia; fileWriter.onwriteend = function (progressEvent) { success && success(fileEntry); @@ -343,8 +343,6 @@ $(function () { fileWriter.write(contents); // Case 2: string contents, create a blob } else { - bb = new BlobBuilder(); // Create a new Blob on-the-fly. - mimeString = "text/plain"; if (binary) { // write the bytes of the string to an ArrayBuffer ab = new ArrayBuffer(contents.length); @@ -352,16 +350,19 @@ $(function () { for (var i = 0; i < contents.length; i++) { ia[i] = contents.charCodeAt(i); } - // write the ArrayBuffer to a blob, and you're done - bb.append(ab); - mimeString = ""; + contents = ia; } - else { + if (window.Blob) { + bb = new Blob([contents]); // Create a new Blob on-the-fly. + fileWriter.write(bb); + } else if (window.BlobBuilder){ + bb = new BlobBuilder(); // Create a new Blob on-the-fly. bb.append(contents); + fileWriter.write(bb.getBlob()); + } else { + console.error("No Blob or BlobBuilder constructor."); } - fileWriter.write(bb.getBlob(mimeString)); } - }, onError); } From f6380edde120d6f4eb2d1aafa5d5f83e4941b637 Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Thu, 23 Aug 2012 13:54:06 +0800 Subject: [PATCH 13/16] [General] Use dumplog to output debug infomation Output message in dumplog will not be printed out if DEBUG if false. --- src/js/fs.js | 14 +++++++------- src/js/projects.js | 2 +- src/js/serialize.js | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/js/fs.js b/src/js/fs.js index 74dad75e..be20e225 100644 --- a/src/js/fs.js +++ b/src/js/fs.js @@ -69,8 +69,8 @@ $(function () { // Create a sandbox filesystem successFS = function (filesystem) { fsUtils.fs = filesystem; - console.log("A sandbox filesystem: " + fsUtils.fs.name + " was created;"); - console.log(fsUtils.fs.name + " type: " + type + ", size: " + size ); + dumplog("A sandbox filesystem: " + fsUtils.fs.name + " was created;"); + dumplog(fsUtils.fs.name + " type: " + type + ", size: " + size ); // Create a default target window and append it // to the document.body if (!$('iframe#' + fsUtils.defaultTarget).length) { @@ -89,7 +89,7 @@ $(function () { }; if (!(storageInfo && requestFileSystem)) { - console.log("Filesystem not available"); + console.error("Filesystem not available"); return; } // Check the quota @@ -174,7 +174,7 @@ $(function () { msg += 'Unknown Error'; break; } - console.log(msg); + console.warn(msg); }, /** @@ -186,7 +186,7 @@ $(function () { */ pathToUrl: function (path) { if (typeof path !== "string") { - console.log("String type is needed for file which is to be read."); + console.error("String type is needed for file which is to be read."); return false; } var url = fsUtils.fs.root.toURL() + path.replace(/^\//, ""); @@ -453,7 +453,7 @@ $(function () { var onError = error || fsUtils.onError; fsUtils.fs.root[opt_recursive ? "getDirectory" : "getFile"](path, {create: false}, function (entry) { entry[opt_recursive ? "removeRecursively" : "remove"](function () { - console.log(path + ' is removed.'); + dumplog(path + ' is removed.'); if (success) { success(); } @@ -634,7 +634,7 @@ $(function () { } } } else { - console.warn("Don't support cookie or empty cookie."); + dumplog("Warning: don't support cookie or empty cookie."); } return value; }, diff --git a/src/js/projects.js b/src/js/projects.js index fb82059d..6698eb22 100644 --- a/src/js/projects.js +++ b/src/js/projects.js @@ -685,7 +685,7 @@ $(function () { var d, s, pid; pid = $.rib.pmUtils.getActive(); if (!pid) { - console.warn("No active project to update thumbnail."); + dumplog("Warning: No active project to update thumbnail."); return false; } d = $(liveDoc.documentElement).clone(); diff --git a/src/js/serialize.js b/src/js/serialize.js index 6063d3e7..64f5b2cc 100644 --- a/src/js/serialize.js +++ b/src/js/serialize.js @@ -118,7 +118,7 @@ $(function () { if (parentNode === undefined || parentNode === null || parentNode.length < 1) { // No sense adding it to the DOM if we can't find it's parent - console.info(parentSelector+' not found in Design View'); + dumplog("Warning" + parentSelector+' not found in Design View'); } domNodes = $(selector, parentNode); From b24ff519535886e47ffe2b38d5d822a83371a84a Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Thu, 23 Aug 2012 14:21:31 +0800 Subject: [PATCH 14/16] [FileIO] Fix bug: confirm removal of non-existent image files --- src/js/projects.js | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/js/projects.js b/src/js/projects.js index fb82059d..0cbb54ac 100644 --- a/src/js/projects.js +++ b/src/js/projects.js @@ -978,10 +978,14 @@ $(function () { // TODO: will list all the uploaded resource and show the reference // count of them. It will depend on the user if delete or not. if (pmUtils.resourceRef[refPath] <= 0) { - $.rib.confirm('Unused resource: "' + refPath + - '". \nWould you like to delete it from the project?', function () { - $.rib.fsUtils.rm(projectDir + refPath); - delete pmUtils.resourceRef[refPath]; + delete pmUtils.resourceRef[refPath]; + $.rib.fsUtils.pathToEntry(projectDir + refPath, function (entry) { + $.rib.confirm('Unused resource: "' + entry.name + + '". \nWould you like to delete it from the project?', function () { + $.rib.fsUtils.rm(entry.fullPath); + }, function () { + pmUtils.resourceRef[refPath] = 0; + }); }); } } else { From 3d42011f0cb9cbb1e1dae041adc8bc451fa3804a Mon Sep 17 00:00:00 2001 From: Xuqing Kuang Date: Thu, 19 Jul 2012 15:47:33 +0800 Subject: [PATCH 15/16] [General] Implemented event handler feature. Completed: 1. Event handler dialog construct 2. JS code exporting. 3. JS Preview. --- src/css/builder.css | 115 +++++++++++++ src/js/adm.js | 34 +++- src/js/serialize.js | 43 +++++ src/js/views/property.js | 353 +++++++++++++++++++++++++++++++++++++-- 4 files changed, 533 insertions(+), 12 deletions(-) diff --git a/src/css/builder.css b/src/css/builder.css index b7d4ead5..debb25a7 100644 --- a/src/css/builder.css +++ b/src/css/builder.css @@ -1559,3 +1559,118 @@ input.screenCoordinate::-webkit-inner-spin-button { #exportDialog .buttonStyle { margin: 0 0.6em; } + +#eventHandlerDialog { + overflow: hidden; +} + +#eventHandlerDialog .title { + position: relative; + height: 52px; + background-color: #E4E5DF; +} + +#eventHandlerDialog .title > label { + position: absolute; + left: 10px; + top: 16px; + font-size : 13px; + color: #4d4d4d; + font-family: OpenSans-SB; + font-weight: 600; /* semi-bold */ +} + + +#eventHandlerDialog .wrap_left { + position: relative; + width: 230px; + border-right: 1px solid #dededc; +} + +#eventHandlerDialog .wrap_left .container { + position: absolute; + width: 100%; + margin: 10px; +} + + +#eventHandlerDialog .wrap_left .container * { + display: inline-block; +} + +#eventHandlerDialog .wrap_left .container select { + position: absolute; + top: 10px; + left: 0px; + width: 270px; +} + +#eventHandlerDialog .wrap_left .container fieldset { + position: absolute; + top: 40px; + width: 244px; + height: 370px; + overflow: auto; +} + +#eventHandlerDialog .wrap_left .container fieldset ul { + margin: 0px; +} + +#eventHandlerDialog .wrap_left .container fieldset li { + display: block; + padding: 8px 8px 8px 8px; + margin-left: -36px; + width: 200px; + height: 34px; + border: 8px transparent; + border-bottom: 1px solid #CCC; +} + +#eventHandlerDialog .wrap_left .container fieldset li.ui-selected { + background-color: #4AE57B; +} + +#eventHandlerDialog .wrap_left .container fieldset li > a.link { + padding-top: 8px; + width: 160px; + height: 34px; +} + +#eventHandlerDialog .wrap_left .container fieldset a.ui-button { + margin-top: 0px; + float: right; +} + +#eventHandlerDialog .wrap_left .container button.doneButton { + position: absolute; + top: 440px; + left: 46px; + width: 180px; +} + +#eventHandlerDialog .wrap_right { + width: 630px; +} + +#eventHandlerDialog .wrap_right .container { + overflow: auto; + resize: none; + height: 600px; + width: 98%; + margin: 6px; +} + +#eventHandlerDialog .wrap_right .container .CodeMirror { + margin: 4px; + -webkit-border-radius: 4px; + -moz-border-radius: 4px; + -moz-box-shadow: inset 0 0 4px #000000; + -webkit-box-shadow: inset 0 0 4px #000000; + box-shadow: inset 0 0 4px #000000; + height: 470px; +} + +#eventHandlerDialog .wrap_right .container .CodeMirror .CodeMirror-scroll { + height: 464px; +} diff --git a/src/js/adm.js b/src/js/adm.js index 607d1d61..d07cfd18 100644 --- a/src/js/adm.js +++ b/src/js/adm.js @@ -2212,8 +2212,17 @@ ADMNode.prototype.isPropertyExplicit = function (property) { * relevant info for performing an undo of this operation. */ ADMNode.prototype.setProperty = function (property, value, data, raw) { - var orig, func, changed, type, rval = { }, defaultValue; - type = BWidget.getPropertyType(this.getType(), property); + var orig, func, changed, rval = { }, defaultValue, eventName, node, + oldValue, cfm, node = this, + type = BWidget.getPropertyType(this.getType(), property); + + // Test the node have event handlers + function haveEventHandlers() { + return !$.isEmptyObject(node.getMatchingProperties( + {'type': 'event', 'value': new RegExp('.+')} + )); + } + if (!type) { console.error("Error: attempted to set non-existent property: " + property); @@ -2248,6 +2257,19 @@ ADMNode.prototype.setProperty = function (property, value, data, raw) { console.error("Error: page id cannot be null"); return rval; } + + // When widget that have event handlers, after user make the ID + // property be blank, then popup a confirm dialog. + if (value === "" && haveEventHandlers()) { + oldValue = this.getProperty(property); + cfm = confirm( + 'All event handlers of this widget will stop working and can\'t be exported if the ID property is empty. Are you sure you want to set the ID property to empty?' + ); + if (!cfm) { + // Restore to old value. + return rval; + }; + } } // test for deep equality efficiently @@ -2282,6 +2304,14 @@ ADMNode.prototype.setProperty = function (property, value, data, raw) { { type: "propertyChanged", node: this, property: property, oldValue: orig, newValue: value }); + + // Event handler saving after ID/event property changed. + if (property == 'id') { + if (haveEventHandlers()) + $.rib.saveEventHandlers(); + } else if (type == 'event') { + $.rib.saveEventHandlers(); + } rval.result = true; } return rval; diff --git a/src/js/serialize.js b/src/js/serialize.js index 6063d3e7..73f4d993 100644 --- a/src/js/serialize.js +++ b/src/js/serialize.js @@ -907,6 +907,48 @@ $(function () { }, error); } + /** + * Save event handler codes to main.js + * + * @param {ADMNode} design ADM design root to be serialized. + * + * @return {None} as same as addCustomFile. + */ + function saveEventHandlers(design) { + var results, result, id, matchedProps, eventCode, eventName, + design = design || ADM.getDesignRoot(), + jsFileName = 'js/main.js', jsType = 'js', + jsHeader = '$(document).ready(function(e) {\n', + jsContent = '', + jsFooter = '});'; + + // Regenerate the whole event javascript codes + // and save to sandbox. + results = design.findNodesByProperty( + {'type': 'event', 'value': new RegExp('.+')} + ); + for (var i=0; (result = results[i]); i++) { + id = result.node.getProperty('id'); + if (!id) + continue + matchedProps = result.properties; + for (eventName in matchedProps) { + // Append the event code to the whole js code content. + jsContent += '$("#' + id + '").bind("' + eventName + '", function(e) {' + + '\n' + matchedProps[eventName] + '\n' + + '});\n\n'; + } + } + if (!jsContent) { + removeSandboxHeader(jsType, jsFileName); + $.rib.fsUtils.rm(jsFileName); + return null; + } + return addCustomFile( + jsFileName, jsType, js_beautify(jsHeader + jsContent + jsFooter) + ); + } + /***************** export functions out *********************/ // Export serialization functions into $.rib namespace $.rib.generateHTML = generateHTML; @@ -919,4 +961,5 @@ $(function () { $.rib.addSandboxHeader = addSandboxHeader; $.rib.removeSandboxHeader = removeSandboxHeader; $.rib.addCustomFile = addCustomFile; + $.rib.saveEventHandlers = saveEventHandlers; }); diff --git a/src/js/views/property.js b/src/js/views/property.js index 218f66cd..8fbe0b77 100644 --- a/src/js/views/property.js +++ b/src/js/views/property.js @@ -94,9 +94,10 @@ var labelId, labelVal, valueId, valueVal, count, widget = this, type, i, child, index, propType, p, props, options, code, o, propertyItems, label, value, + design = ADM.getDesignRoot(), title = this.element.parent().find('.property_title'), content = this.element.find('.property_content'), - continueToDelete; + continueToDelete, buttonsContainer; // Clear the properties pane when nothing is selected if (node === null || node === undefined) { @@ -383,9 +384,8 @@ .click({'p': p, 'value': value}, function(e) { var o, items = "", pages, id, value = e.data.value, p = e.data.p; - items += '
  • previous page
  • '; - pages = ADM.getDesignRoot().getChildren(); + pages = design.getChildren(); for (o = 0; o < pages.length; o++) { id = pages[o].getProperty('id'); items += '
  • #' + id + '
  • '; @@ -476,22 +476,355 @@ }); } - // add delete element button - $('
    ') + // add buttons container + buttonsContainer = $('
    ') .addClass('property_footer') - .children('button') + .appendTo(content) + .end(); + + // Add event handler button + $('') + .addClass('buttonStyle') + .attr('id', "eventHandlerElement") + .appendTo(buttonsContainer) + .bind('click', function(e) { + var generateEventSelectElement, generateEventHandlersList, + removeEventHandler, eventLinkClicked; + var formContainer, leftPannel, leftPannelContainer, + rightPannel, eventElement, eventSelectElement, + eventEditorContainer, eventEditor, formElement, + jsCode, eventHandlersList, id, + uniqueIdName = 'id'; + + // If node have no ID property, then return directly. + if(typeof(BWidget.propertyExists(node.getType(), uniqueIdName)) == 'undefined') { + alert('Event handler must be using with the element have ID property.'); + return false; + }; + + /* + * Call back functions. + */ + + // Remove event handler + removeEventHandler = function(e) { + e.preventDefault(); + var eventName = $(this).parent().attr('rel'); + $.rib.confirm( + 'Are you sure you want to delete the ' + + eventName + + ' event handler?', + function() { + node.setProperty(eventName, ''); + if (eventElement.val() == eventName) + eventEditor.setValue(''); + formElement.trigger('submit'); + } + ); + } + + // Event link clicked callback + eventLinkClicked = function(e) { + e.preventDefault(); + var eventName = $(this).parent().attr('rel'); + eventSelectElement.val(eventName); + formElement.trigger('submit'); + } + + /* + * Elements generation functions. + */ + + // Generate the event property select options. + generateEventSelectElement = function(selectElement, matchedProps) { + var selected, newSelectElement, result, matchedProps, + optionElement, eventName, optionsGroup; + + // Store the old selected event. + selected = selectElement.val(); + + // Clean up the origin options. + selectElement.find('option').remove(); + + // Search event properties + if (!matchedProps) + matchedProps = node.getMatchingProperties( + {'type': 'event'} + ); + + // Added a initial blank option; + $('') + .addClass('cm-inactive') + .appendTo(selectElement); + + // TODO: Classify the events and use optgroup to + // genereate it. + /* + optionsGroup = $( + '' + ) + .appendTo(selectElement); + */ + + // Generate event select options. + for (eventName in matchedProps) { + optionElement = $('
    ') + .append($('Event handlers')) + + ulElement = $('