diff --git a/src/css/builder.css b/src/css/builder.css index 6ec9b547..d5953079 100644 --- a/src/css/builder.css +++ b/src/css/builder.css @@ -1344,6 +1344,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; @@ -1555,3 +1560,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 364f82fa..b9a6d49c 100644 --- a/src/js/adm.js +++ b/src/js/adm.js @@ -253,6 +253,22 @@ ADM.addEventType("activePageChanged"); */ ADM.addEventType("selectionChanged"); +/** + * Event sent by the ADM object when the theme of design + * changed. After this event happened, the theme of all widget + * will be changed to "default". + * + * @name ADM#themeChanged + * @event + * @param {Object} event Object including standard "id" and "name" + * properties. + * + * @param {Any} data The data you supplied to the bind() call. + * @see ADMEventSource.bind + * @see ADMEventSource.unbind + */ +ADM.addEventType("themeChanged"); + /** * Gets the singleton design root. * @@ -463,6 +479,53 @@ ADM.setSelected = function (nodeRef) { return false; }; +/** + * Set theme of all widgets. Sends a "themeChanged" event if the + * theme of current project actually changes. + */ +ADM.setTheme = function () { + var design, setNodeTheme, isSwatchExist; + + design = ADM.getDesignRoot(); + isSwatchExist = function (swatch, type) { + var swatches = BWidget.getPropertyOptions(type)['theme']; + //check whether swatch is in swatcher list of current theme + if (jQuery.inArray(swatch, swatches) >= 0) { + return true; + } + return false; + } + setNodeTheme = function (admNode) { + var i, type, children; + if (admNode instanceof ADMNode) { + type = admNode.getType(); + // Firstly we get theme property of node. If original swatch + // of theme can be found from current theme, we do nothing. + // Otherwise, we set property value as default + if (BWidget.propertyExists(type, 'theme') && + !isSwatchExist(admNode.getProperty('theme'), type)){ + admNode.setProperty('theme', 'default'); + } + + children = admNode.getChildren(); + if (children.length > 0) { + for (i = 0; i < children.length; i++) { + setNodeTheme(children[i]); + } + } + } else { + console.warn("warning: children of ADMNode must be ADMNode"); + } + }; + + design.suppressEvents(true); + // set theme value of all nodes as default + setNodeTheme(design); + design.suppressEvents(false); + // send themeChanged event to notify all views to update + ADM.fireEvent('themeChanged'); +} + /** * Initiates an atomic transaction from the user's point of view, that may * involve more than one change to the the ADM. The initial usage is when the @@ -1960,11 +2023,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; } @@ -2230,8 +2297,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); @@ -2266,6 +2342,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 @@ -2300,6 +2389,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/composer.js b/src/js/composer.js index ff72c9e5..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 @@ -898,6 +907,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/fs.js b/src/js/fs.js index 6a722584..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(/^\//, ""); @@ -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); } @@ -452,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(); } @@ -633,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/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, diff --git a/src/js/projects.js b/src/js/projects.js index fb82059d..c2ec3fe3 100644 --- a/src/js/projects.js +++ b/src/js/projects.js @@ -320,6 +320,9 @@ $(function () { $.rib.removeSandboxHeader('css', '/themes/' + oldThemeName + '.css'); $.rib.addSandboxHeader('css', '/themes/' + newThemeName + '.css'); } + pmUtils.setProperty(pid, 'theme', newThemeName); + // send themeChanged event to notify all views to update + ADM.setTheme(); } // if the item has schema then check the type pmUtils.setProperty(pid, i, properties[i]); @@ -685,7 +688,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(); @@ -978,10 +981,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 { @@ -1067,9 +1074,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; diff --git a/src/js/serialize.js b/src/js/serialize.js index 6063d3e7..5dd5dd2a 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); @@ -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/base.js b/src/js/views/base.js index ef493c62..8c37dbef 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.themeChanged = this._themeChangedHandler; // 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.themeChanged) { + a.bind("themeChanged", o.themeChanged, this); + } // Since model changed, need to call our designReset hander // to sync up the ADMDesign modelUpdated event handler @@ -154,6 +158,11 @@ // Finally, redraw our view since the ADMDesign root has changed widget.refresh && widget.refresh(event, widget); + }, + + _themeChangedHandler: function(event, widget) { + // redraw our view since theme has changed + widget.refresh && widget.refresh(event, widget); } }); })(jQuery); 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/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('
    ') diff --git a/src/js/views/project.js b/src/js/views/project.js index 6d82e466..984865cb 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); diff --git a/src/js/views/property.js b/src/js/views/property.js index 218f66cd..a28057de 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); + } } }, @@ -94,18 +102,16 @@ 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) { - node = ADM.getActivePage(); - if (node === null || node === undefined) { - content.empty() - .append(''); - return; - } + content.empty() + .append(''); + return; } type = node.getType(); @@ -383,9 +389,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 +481,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 = $('