From 12fbff92f89b2ca5395a9db21954a691175f607a Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Tue, 19 Jun 2012 19:36:43 +0800 Subject: [PATCH 1/3] [General] Enable extra handler when serialize ADM to HTML --- src/js/serialize.js | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/js/serialize.js b/src/js/serialize.js index bc802fb5..2dc92daa 100644 --- a/src/js/serialize.js +++ b/src/js/serialize.js @@ -28,18 +28,29 @@ var DEBUG = true, }); }, - generateHTML = function () { - var doc = constructNewDocument($.rib.getDefaultHeaders()); + /** + * Generate HTML from ADM tree. + * + * @param {ADMNode} design ADM design root to be serialized. + * @param {function(ADMNode, DOMElement)=} extaHandler Extra handler for each node. + * + * @return {Object} return an object contains generated DOM object and related html string + */ + generateHTML = function (design, extraHandler) { + design = design || ADM.getDesignRoot(); + var doc = constructNewDocument($.rib.getDefaultHeaders(design)); - function renderClean(admNode, domNode) { + function renderer(admNode, domNode) { + // clean code $(domNode).data('uid', admNode.getUid()); if (domNode.hasClass("rib-remove")) { domNode.replaceWith(domNode.text()); } + // call extraHandler if needed + extraHandler && extraHandler(admNode, domNode); }; - serializeADMSubtreeToDOM(ADM.getDesignRoot(), $(doc).find('body'), - renderClean); + serializeADMSubtreeToDOM(design, $(doc).find('body'), renderer); return { doc: doc, html: formatHTML(xmlserializer.serializeToString(doc)) }; @@ -427,15 +438,16 @@ $(function() { } } - function getDefaultHeaders() { - var i, props, el; + function getDefaultHeaders(design) { + var i, props, el, designRoot; + designRoot = design || ADM.getDesignRoot(); $.rib.defaultHeaders = $.rib.defaultHeaders || []; if ($.rib.defaultHeaders.length > 0) return $.rib.defaultHeaders; - props = ADM.getDesignRoot().getProperty('metas'); + props = designRoot.getProperty('metas'); for (i in props) { // Skip design only header properties if (props[i].hasOwnProperty('designOnly') && props[i].designOnly) { @@ -454,7 +466,7 @@ $(function() { el = el + '>'; $.rib.defaultHeaders.push(el); } - props = ADM.getDesignRoot().getProperty('libs'); + props = designRoot.getProperty('libs'); for (i in props) { // Skip design only header properties if (props[i].hasOwnProperty('designOnly') && props[i].designOnly) { @@ -467,7 +479,7 @@ $(function() { el = el + '>'; $.rib.defaultHeaders.push(el); } - props = ADM.getDesignRoot().getProperty('css'); + props = designRoot.getProperty('css'); for (i in props) { // Skip design only header properties if (props[i].hasOwnProperty('designOnly') && props[i].designOnly) { From fb987e00cd64c1ddb6a996653937f944fc7dcf62 Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Mon, 25 Jun 2012 15:01:04 +0800 Subject: [PATCH 2/3] [General] Enable extra handler when transform between ADM and JSON. --- src/js/serialize.js | 107 +++++++++++++++++++++++--------------------- 1 file changed, 57 insertions(+), 50 deletions(-) diff --git a/src/js/serialize.js b/src/js/serialize.js index 2dc92daa..5baeab2b 100644 --- a/src/js/serialize.js +++ b/src/js/serialize.js @@ -290,70 +290,67 @@ $(function() { * AMD design root to this design, which sends a designReset event. * * @param {Object} obj The JSON object to parse + * @param {function(ADMNode, Object)=} eachHandler Extra handler for each pair of + * ADM node and the related object. + * * @return {ADMNode/null} the design build from the text if success, null if failed. */ - function JSONToProj(text) { - var result, design, parsedObject, resultProject = {}; + function JSONToProj(text, eachHandler) { + var result, design, parsedObject, resultProject = {}, JSObjectToADMNode; - function add_child(parent, nodes) { - if (typeof(nodes) !== "object") { - return false; - } + JSObjectToADMNode = function (admNode, jsObject) { + var children, child, zone, + properties, childNode, + item, val, result, i; - if (typeof(nodes.length) == "undefined") { + if ((typeof jsObject !== "object") || !(admNode instanceof ADMNode)) { return false; } + properties = jsObject.properties; + try { + // Set properties for current ADM node + for (item in properties) { + // parser the properties and set the value to the node + val = properties[item]; + // if we can't get value, we set item's value as default + if (!val){ + val = admNode.getPropertyDefault(item); + } - var child, childType, zone, - properties = {}, - item, val, node, result, i; - - for (i = 0; i < nodes.length; i++) { - node = nodes[i]; - childType = node.type; - zone = node.zone; - properties = node.properties; - try { - child = ADM.createNode(childType, true); + // NOTE: It's important that we pass "true" for the fourth + // parameter here (raw) to disable "property hook" + // functions like the grid one that adds or removes child + // Block elements based on the property change + admNode.setProperty(item, val, null, true); + } + // Scan children nodes + children = jsObject.children; + for (i = 0; i < children.length; i++) { + child = children[i]; + childNode = ADM.createNode(child.type, true); // add child node to current node - if (!parent.addChildToZone(child, zone)) { - dumplog("add child type "+ childType + " failed"); + if (!admNode.addChildToZone(childNode, child.zone)) { + dumplog("add child type "+ child.type + " failed"); return false; } - - // set properties of child - for (item in properties) { - // parser the properties and set the value to the node - val = properties[item]; - // if we can't get value, we set item's value as default - if (!val){ - val = child.getPropertyDefault(item); - } - - // NOTE: It's important that we pass "true" for the fourth - // parameter here (raw) to disable "property hook" - // functions like the grid one that adds or removes child - // Block elements based on the property change - child.setProperty(item, properties[item], null, true); - } - }catch (e) { - if (!confirm("Error creating " + childType + - (item ? " when setting property '" + - item + "'" : "") + " - " + e + - ".\n\nContinue loading the design?")) - return false; - } - - if (node.children.length !== 0) { - result = add_child(child, node.children); + result = JSObjectToADMNode(childNode, child); if (!result) { return false; } } + }catch (e) { + if (!confirm("Error when " + (i ? " adding new child '" + + child.type + "'" : "setting property '" + + item + "'") + " - " + e + + ".\n\nContinue loading the design?")) + return false; } + // call extra handler for each relative pair + eachHandler && eachHandler(admNode, jsObject); return true; - } + }; + /************************ JSObjectToADMNode function end *************************/ try { parsedObject = $.parseJSON(text); @@ -371,7 +368,7 @@ $(function() { // add children in ADM try { - result = add_child(design, parsedObject.children); + result = JSObjectToADMNode(design, parsedObject); } catch(e) { result = null; alert("Invalid design file."); @@ -413,7 +410,15 @@ $(function() { /******************************************************* * ADM to JSON Direction ******************************************************/ - function ADMToJSONObj(ADMTreeNode) { + /** + * Serialize ADMTree to an common javascript Object. + * + * @param {ADMNode} ADMTreeNode ADM node to be serialized. + * @param {function(ADMNode, Object)=} handler Extra handler for each pair of + * ADM node and the related object. + * @return {Boolean} return the serialized Object if success, null when fails + */ + function ADMToJSONObj(ADMTreeNode, handler) { ADMTreeNode = ADMTreeNode || ADM.getDesignRoot(); if (ADMTreeNode instanceof ADMNode) { // Save staff in ADMNode @@ -428,9 +433,11 @@ $(function() { children = ADMTreeNode.getChildren(); if (children.length > 0) { for (i = 0; i < children.length; i++) { - JSObject.children[i] = ADMToJSONObj(children[i]); + JSObject.children[i] = ADMToJSONObj(children[i], handler); } } + // run handler to handle every node + handler && handler(ADMTreeNode, JSObject); return JSObject; } else { console.log("warning: children of ADMNode must be ADMNode"); From 326397c40cf4b271fa4438c561b9ae907f3c60ac Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Wed, 27 Jun 2012 16:02:49 +0800 Subject: [PATCH 3/3] [FileIO] Add upload APIs in $.rib.fsUtils namespace --- src/js/fs.js | 125 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) diff --git a/src/js/fs.js b/src/js/fs.js index a8ed1c47..4350ba4e 100644 --- a/src/js/fs.js +++ b/src/js/fs.js @@ -26,6 +26,33 @@ $(function() { fsSize: 20*1024*1024, fs:null, deferredOperations:[], + /** + * Acceptable uploaded file types + * + * Each type object contains: + * mime {String} Recommended mimeType of related input element + * suffix {Array} Array of acceptable suffix of uploaded file + */ + fileTypes: { + js: { + mime: 'text/javascript', + suffix: ['js'], + }, + image: { + mime: 'image/*', + suffix: ['jpg', 'png', 'svg', 'bmp', + 'gif', 'jpeg', 'jpm', 'jp2', 'jpx', + 'xml', 'cgm', 'ief'], + }, + css: { + mime: 'text/css', + suffix: ['css'], + }, + any: { + mime: '*', + suffix: ['*'], + } + }, /** * Init the sandbox file system . @@ -446,6 +473,104 @@ $(function() { alert("Export window was blocked!"); } }, + /** + * Check if the uploaded file is acceptable, currently just check suffix + * + * @param {String} type File type to check + * @param {File} file Uploaded file object which is an instance of 'File' + * + * @return {Boolean} Return true if the file is acceptable, otherwise return false + */ + checkFileType: function (type, file) { + var arrString, rule; + arrString = fsUtils.fileTypes[type.toLowerCase()].suffix.join('|'); + rule = new RegExp("\\.(" + arrString + ")$", "i"); + // TODO: May need to read the "content-type" to check the type + return rule.test(file.name); + }, + + /** + * Trigger an native dialog to upload file in a container + * + * @param {String} type File type to upload + * @param {Jquery Object} container DOM element where native dialog will be triggered + * @param {function(File)=} success Success callback with uploaded file as its parameter + * @param {function()=} error Error callback + * + * @return {None} + */ + upload: function (fileType, container, success, error) { + var input, mimeType; + container = container || $('body'); + mimeType = fsUtils.fileTypes[fileType.toLowerCase()].mime; + input = $('') + .addClass('hidden-accessible').appendTo(container); + input.change(function (e) { + var file; + if (e.currentTarget.files.length === 1) { + file = e.currentTarget.files[0]; + if (fsUtils.checkFileType(fileType, file)) { + success && success(file) + } else { + console.warn("Unexpected uploaded file."); + // TODO: confirm with user if still use the file + error && error(); + } + } else { + if (e.currentTarget.files.length <= 1) { + console.warn("No files specified to import"); + } else { + console.warn("Multiple file import not supported"); + } + error && error(); + } + // remove the temp input element + input.remove(); + }); + input.click(); + }, + + /** + * Trigger an native dialog to upload file in a container, + * and save the file in a parent directory. If the parent directy is not exist, + * it will be create, but it only work once, if the grandparent directory is not + * exist, it will report error. + * + * @param {String} type File type to upload + * @param {String} destDir Directory where the uploaded file to be saved in + * @param {Jquery Object} container DOM element where native dialog will be triggered + * @param {function(File)=} success Success callback with uploaded file as its parameter + * @param {function()=} error Error callback + * + * @return {None} + */ + uploadAndSave: function (fileType, destDir, container, success, error) { + var handler = function (file) { + var successHandler, errorCreateDir; + successHandler = function (dirEntry) { + if (!dirEntry.isDirectory) { + console.error(dirEntry.fullPath + " is not a directory in sandbox."); + return; + } + // Write uploaded file to sandbox + fsUtils.write(destDir + file.name, file, function(newFile){ + success && success(newFile); + }); + }; + errorCreateDir = function (e) { + if (e.code === FileError.NOT_FOUND_ERR) { + // Create a Untitled project and open it in onEnd function + fsUtils.mkdir(destDir, successHandler); + } else { + fsUtils.onError(e); + error && error(); + } + }; + fsUtils.pathToEntry(destDir, successHandler, errorCreateDir); + }; + + fsUtils.upload(fileType, container, handler, error) + }, }, /**