From 4c78ca424cdfd50ebb5460f8e43aee8a8ae31d52 Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Mon, 27 Aug 2012 20:57:54 +0800 Subject: [PATCH 1/3] [Property View] Add a general function to create datalist --- src/js/views/property.js | 250 +++++++++++++++++++++++---------------- 1 file changed, 151 insertions(+), 99 deletions(-) diff --git a/src/js/views/property.js b/src/js/views/property.js index 5e7baf96..1423f39e 100644 --- a/src/js/views/property.js +++ b/src/js/views/property.js @@ -319,104 +319,30 @@ } }); break; - case "datalist": - $('
') - .append( - $('') - .attr('id', valueId) - .addClass('labelInput') - .click({'p': p, 'value': value}, function(e){ - var o, items = "", - value = e.data.value, p = e.data.p; - - for (o in options[p]) { - items += '
  • ' + options[p][o] + '
  • '; - } - value.find('ul') - .html("") - .append($(items)); - - $(this).toggleClass('datalist-input'); - value.find('.datalist').toggle(); - }) - .keyup({ 'p' : p, 'value' : value}, function(e){ - var matchedOptions = [], o, items = "", - inputedText = this.value, - value = e.data.value; - matchedOptions = $.grep(options[e.data.p], function(item, i){ - return item.indexOf(inputedText) >= 0; - }); - - for (o in matchedOptions) { - items += '
  • ' + matchedOptions[o] + '
  • '; - } - value.find('ul') - .html("") - .append(items); - - $(this).addClass('datalist-input'); - value.find('.datalist').show(); - }) - ) - .append( - $('
    ') - .addClass('datalist') - .append('
      ') - ) - .appendTo(value); - value.delegate(".datalist li", "click", function(e) { - $(this).parent().parent().parent().find('input') - .val($(this).text()).change().end() - .find('.datalist').hide().end(); - }); - value.find('#'+ valueId).val(valueVal); - break; case "targetlist": - $('
      ') - .append( - $('') - .attr('id', valueId) - .addClass('labelInput') - .click({'p': p, 'value': value}, function(e) { - var o, items = "", pages, id, - value = e.data.value, p = e.data.p; - items += '
    • previous page
    • '; - container = node.getParent(); - while (container !== null && - container.getType() !== "Page") { - container = container.getParent(); - } - pages = design.getChildren(); - for (o = 0; o < pages.length; o++) { - if (pages[o] === container) { - continue; - } - id = pages[o].getProperty('id'); - items += '
    • #' + id + '
    • '; - } - value.find('ul') - .html("") - .append($(items)); - - $(this).toggleClass('datalist-input'); - value.find('.datalist').toggle(); - }) - ) - .append( - $('
      ') - .addClass('datalist') - .append('
        ') - ) - .appendTo(value); - value.delegate(".datalist li", "click", function(e) { - $(this).parent().parent().parent().find('input') - .val($(this).text()).change().end() - .find('.datalist').hide().end(); - }); - if (valueVal === "back") { - } else { - value.find('#' + valueId).val(valueVal); + container = node.getParent(); + options[p] = ['previous page']; + while (container !== null && + container.getType() !== "Page") { + container = container.getParent(); } + var o, pages = ADM.getDesignRoot().getChildren(); + for (o = 0; o < pages.length; o++) { + if (pages[o] === container) { + continue; + } + options[p].push('#' + pages[o].getProperty('id')); + } + // Don't break to reuse logic of datalist + + case "datalist": + var datalist = createDatalist(options[p]); + if (!datalist) break; + datalist.addClass('title').appendTo(value); + datalist.find('input[type="text"]') + .attr('id', valueId) + .addClass('title labelInput') + .val(valueVal); break; default: // handle property has options @@ -458,13 +384,14 @@ // We have to look up the ":hover" class here to decide // which item is clicked selected = $(this).parent().find('.datalist ul li:hover'); + if (selected.length > 0) { + selected.click(); + return; + } if (node === null || node === undefined) { throw new Error("Missing node, prop change failed!"); } - if (selected.length > 0) { - $(this).val(selected.text()); - } value = validValue( node, $(this), BWidget.getPropertyType(node.getType(), updated) @@ -890,4 +817,129 @@ }; } }); + + /** + * Update options list according an array. + * @param {JQObject} optionsList Container options will be appended to + * @param {String} options Options array, item in the array can be string + * or object, which contains: + * { + * value: must have + * clickCallback: optional, the default handler is to + * fill the text input with this option's value. + * cssClass: special css class need to be added to list item + * stable: if the item is stable and fixed in the list, it means + * the item will always show in the list + * } + * @return {JQuery Object} return root object of datalist if success, false null. + * + */ + function updateOptions (optionsList, optionArray) { + var i, value, option, handler, + defaultHandler, cssClass, stable; + + // its value will fill the input + defaultHandler = function(e) { + var optionsWrapper = $(this).parents('.datalist:first'); + optionsWrapper.hide(); + optionsWrapper.prev('input') + .val($(this).text()) + .change(); + }; + // remove items which is not stable + optionsList.find(':not(.stable)').remove(); + // fill the optionsList + for (i in optionArray) { + option = optionArray[i]; + value = handler = null; + cssClass = ''; + if (option instanceof Object) { + value = option.value; + handler = option.clickCallback; + cssClass = option.cssClass; + if (option.stable) { + cssClass += ' stable'; + } + } else if (typeof option === 'string') { + value = option; + } + if (!value) continue; + if (typeof handler !== 'function') { + handler = defaultHandler; + } + $('
      • ' + value + '
      • ') + .click(handler) + .addClass(cssClass) + .appendTo(optionsList); + } + return; + } + + /** + * Create a datalist from an options array. + * @param {String} options Options array, item in the array can be string + * or object, which contains: + * { + * value: must have + * clickCallback: optional, the default handler is to + * fill the text input with this option's value. + * cssClass: special css class need to be added to list item + * stable: if the item is stable and fixed in the list, it means + * the item will always show in the list + * } + * @return {JQuery Object} return root object of datalist if success, false null. + * + */ + function createDatalist(options) { + var datalist, input, optionsList; + if (!(options instanceof Array)) { + console.error('Creating datalist error.'); + return null; + } + // create base structure + datalist = $('
        '); + input = $('').appendTo(datalist); + optionsList = $('
          '); + $('
          ') + .addClass('datalist') + .append(optionsList) + .appendTo(datalist); + + // close the options list when the whole datalist blur + input.blur(function (e){ + var dropDown, selected; + dropDown = $(this).nextAll('.datalist:first'); + selected = optionsList.find('li:hover'); + if (!selected.length) { + dropDown.hide(); + $(this).removeClass('datalist-input'); + } + return; + }); + + // bind event handler, to show the options + input.focus(function (e){ + var dropDown = $(this).nextAll('.datalist:first'); + dropDown.find('*').andSelf().show(); + $(this).addClass('datalist-input'); + }); + // bind keyup event handler to filter matched options + input.keyup(function (e){ + var inputedText = this.value, + dropDown = $(this).nextAll('.datalist:first'); + options = dropDown.find('li'); + dropDown.find('*').andSelf().show(); + $.each(options, function(i, item){ + if ($(item).hasClass('stable')) return; + if($(item).text().indexOf(inputedText) < 0) { + $(item).hide(); + } + return; + }); + }); + // fill the list initially + updateOptions(optionsList, options); + return datalist; + } + })(jQuery); From 9d37326ae664e42d8f30faa9b79c236af4665261 Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Mon, 27 Aug 2012 21:06:14 +0800 Subject: [PATCH 2/3] [Propery View] List all images options in a datalist --- src/css/builder.css | 11 ++++++++++ src/js/projects.js | 20 +++++++++++------ src/js/views/property.js | 46 ++++++++++++++++++++++++---------------- 3 files changed, 52 insertions(+), 25 deletions(-) diff --git a/src/css/builder.css b/src/css/builder.css index fc3e8a96..43fbd122 100644 --- a/src/css/builder.css +++ b/src/css/builder.css @@ -866,6 +866,17 @@ body.ui-tabs.ui-widget { overflow-x: hidden; overflow-y: auto; } +.propertyView .datalist .upload-button { + text-align: center; + border: 1px solid #a1a1a1; + line-height: 12px; + background-color: #fbfbfb; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; + margin-top: 2px; + margin-bottom: 2px; +} .propertyView .datalist li { clear:both; cursor: pointer; diff --git a/src/js/projects.js b/src/js/projects.js index fbb8a852..489d0a95 100644 --- a/src/js/projects.js +++ b/src/js/projects.js @@ -433,6 +433,8 @@ $(function () { pmUtils._projectsInfo[newPid] = {}; pmUtils.setProperties(newPid, properties); pmUtils.setProperty(newPid, "accessDate", new Date()); + // init resource usage status + $.rib.pmUtils.resourceRef = {}; if (design && (design instanceof ADMNode)) { ADM.setDesignRoot(design); @@ -534,7 +536,7 @@ $(function () { * @return {None}. */ pmUtils.openProject = function (pid, success, error) { - var designPath, successHandler; + var designPath, successHandler, imagePath; if (!pmUtils._projectsInfo[pid]) { console.error("Error: Invalid pid for project when opening project"); @@ -546,6 +548,7 @@ $(function () { return; } designPath = pmUtils.getDesignPath(pid); + imagePath = pmUtils.getProjectDir(pid) + 'images/'; successHandler = function (result) { var design, project; @@ -556,6 +559,15 @@ $(function () { pmUtils._activeProject = pid; // update access time pmUtils.setProperty(pid, "accessDate", new Date()); + // init pmUtils.resourceRef, reset usage status of resources + $.rib.pmUtils.resourceRef = {}; + $.rib.fsUtils.ls(imagePath, function (entries) { + $.each(entries, function (i, e) { + $.rib.pmUtils.resourceRef['images/'+e.name] = 0; + }); + }, function (e) { + dumplog('There is no image in this project.'); + }); // set the new design as design root ADM.setDesignRoot(design); success && success(); @@ -1013,12 +1025,6 @@ $(function () { if (!(node instanceof ADMNode)) { return false; } - // if the design is Design root - if (node.getType() === "Design") { - // reset pmUtils.resourceRef - pmUtils.resourceRef = {}; - upFlag = true; - } // Find all used sandbox resource nodes and the matched properties matched = node.findNodesByProperty($.rib.pmUtils.relativeFilter); for (i = 0; i < matched.length; i++) { diff --git a/src/js/views/property.js b/src/js/views/property.js index 1423f39e..b3ecda06 100644 --- a/src/js/views/property.js +++ b/src/js/views/property.js @@ -165,27 +165,37 @@ } break; case "url-uploadable": - $('') + var array, datalist, uploadClick; + uploadClick = function (e) { + var optionsWrapper, textInput, saveDir; + optionsWrapper = $(this).parents('.datalist:first'); + optionsWrapper.hide(); + textInput = optionsWrapper.prev('input'); + + saveDir = $.rib.pmUtils.ProjectDir + "/" + $.rib.pmUtils.getActive() + "/images/"; + $.rib.fsUtils.upload("image", $(this).parent(), function(file) { + // Write uploaded file to sandbox + $.rib.fsUtils.write(saveDir + file.name, file, function (newFile) { + textInput.val("images/" + newFile.name).change(); + }); + }); + }; + // merge all image files + array = [{ + value: "upload", + clickCallback: uploadClick, + cssClass: 'upload-button', + stable: true + }].concat(Object.keys($.rib.pmUtils.resourceRef)); + datalist = createDatalist(array); + if (!datalist) break; + datalist.addClass('title upload-datalist').appendTo(value); + datalist.find('input[type="text"]') .attr('id', valueId) .addClass('title labelInput') - .appendTo(value); - //set default value - value.find('#' + valueId).val(valueVal); - $('') - .addClass('buttonStyle') - .click(function (e) { - var target, saveDir; - target = $(this).prev("input:text"); - saveDir = $.rib.pmUtils.ProjectDir + "/" + $.rib.pmUtils.getActive() + "/images/"; - $.rib.fsUtils.upload("image", $(this).parent(), function(file) { - // Write uploaded file to sandbox - $.rib.fsUtils.write(saveDir + file.name, file, function (newFile) { - target.val("images/" + newFile.name); - target.trigger('change'); - }); - }); - }).appendTo(value); + .val(valueVal); break; + case "record-array": $('') .attr('id', 'selectOption') From 8688e2f75b00affd081d379e9aa7df9d914e2957 Mon Sep 17 00:00:00 2001 From: Donna Wu Date: Mon, 27 Aug 2012 19:59:58 +0800 Subject: [PATCH 3/3] [Propery View] Add event to update image options --- src/js/adm.js | 18 ++++++++++++++++++ src/js/projects.js | 3 +++ src/js/views/base.js | 4 ++++ src/js/views/property.js | 16 +++++++++++++++- 4 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/js/adm.js b/src/js/adm.js index aeecbe9f..32c7c120 100644 --- a/src/js/adm.js +++ b/src/js/adm.js @@ -253,6 +253,24 @@ ADM.addEventType("activePageChanged"); */ ADM.addEventType("selectionChanged"); +/** + * Event sent by the ADM object when the usage status of images change. + * When the active page changes, the selected widget is set to null + * automatically. + * + * @name ADM#imagesUpdated + * @event + * @param {Object} event Object including standard "id" and "name" + * properties, as well as a + * "usageStatus" is an object contains + * current usage status of + * all existing images + * @param {Any} data The data you supplied to the bind() call. + * @see ADMEventSource.bind + * @see ADMEventSource.unbind + */ +ADM.addEventType("imagesUpdated"); + /** * Gets the singleton design root. * diff --git a/src/js/projects.js b/src/js/projects.js index 489d0a95..8aaa32ac 100644 --- a/src/js/projects.js +++ b/src/js/projects.js @@ -973,6 +973,7 @@ $(function () { } else { pmUtils.resourceRef[refPath]++; } + ADM.fireEvent('imagesUpdated', {usageStatus: pmUtils.resourceRef}); return; }; @@ -995,8 +996,10 @@ $(function () { $.rib.confirm('Unused resource: "' + entry.name + '". \nWould you like to delete it from the project?', function () { $.rib.fsUtils.rm(entry.fullPath); + ADM.fireEvent('imagesUpdated', {usageStatus: pmUtils.resourceRef}); }, function () { pmUtils.resourceRef[refPath] = 0; + ADM.fireEvent('imagesUpdated', {usageStatus: pmUtils.resourceRef}); }); }); } diff --git a/src/js/views/base.js b/src/js/views/base.js index ef493c62..1afe3515 100644 --- a/src/js/views/base.js +++ b/src/js/views/base.js @@ -27,6 +27,7 @@ o.selectionChanged = this._selectionChangedHandler; o.activePageChanged = this._activePageChangedHandler; o.modelUpdated = this._modelUpdatedHandler; + o.imagesUpdated = this._imagesUpdatedHandler; // FIXME: This should work, but $.extend of options seems to be // creating a copy of the ADM, which will not containt the @@ -94,6 +95,9 @@ if (o.activePageChanged) { a.bind("activePageChanged", o.activePageChanged, this); } + if (o.imagesUpdated) { + a.bind("imagesUpdated", o.imagesUpdated, this); + } // Since model changed, need to call our designReset hander // to sync up the ADMDesign modelUpdated event handler diff --git a/src/js/views/property.js b/src/js/views/property.js index b3ecda06..afcfb63a 100644 --- a/src/js/views/property.js +++ b/src/js/views/property.js @@ -89,6 +89,16 @@ widget.refresh(event,widget); } }, + _imagesUpdatedHandler: function(event, widget) { + widget = widget || this; + var optionsList, options; + if (widget.options.imagesDatalist) { + options = event.usageStatus || $.rib.pmUtils.resourceRef; + optionsList = widget.options.imagesDatalist.find('ul'); + updateOptions(optionsList, Object.keys(options)); + } + return; + }, _showProperties: function(node) { var labelId, labelVal, valueId, valueVal, count, @@ -113,6 +123,8 @@ .addClass('title') .text(BWidget.getDisplayLabel(type)+' Properties'); content.empty(); + // git rib of old datalist element + this.options.imagesDatalist = null; propertyItems = $('
          ').addClass("propertyItems") .appendTo(content); props = node.getProperties(); @@ -189,11 +201,13 @@ }].concat(Object.keys($.rib.pmUtils.resourceRef)); datalist = createDatalist(array); if (!datalist) break; - datalist.addClass('title upload-datalist').appendTo(value); + datalist.addClass('title').appendTo(value); datalist.find('input[type="text"]') .attr('id', valueId) .addClass('title labelInput') .val(valueVal); + // save the datalist for update + this.options.imagesDatalist = $(this.options.imagesDatalist).add(datalist); break; case "record-array":