diff --git a/CHANGELOG.md b/CHANGELOG.md index d2373d44d..0925bc8a5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# 6.9 # +08/22/2017 +* General Changelog / Developer Notes + * Miscellaneous UI enhancements + * Green login logo + * Miscellaneous bug fixes + * Login token bug fix + * UI bug fixes + # 6.6 # 5/17/2017 * General Changelog / Developer Notes diff --git a/README.md b/README.md index 5e62aa542..6749ef8bb 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,11 @@ -# 6.6 # -5/17/2017 +# 6.9 # +08/22/2017 * General Changelog / Developer Notes - * Campaign Improvements - * Email campaigns can now be scheduled - * Added email opened time and location to campaign launch grid - * Improved database performance of campaign email sends - * X2Touch Improvements - * Added barcode scanner to import product barcode identifier - * Mobile publisher now supports voice to text notes - * X2Workflow Improvements - * New Location Trigger to support general workflow operations when a Location is logged - * Added AddToNewsletter workflow action to manage web leads on newsletter contact lists - * Added new feature tours - * Improved email client IMAP server compatibility, including with Dovecot, Exchange, Office365, and Rackspace - * Added new import console command with 'rollback' operation - * Added an admin page to locate missing records that may have been inadvertently hidden - * New RackspaceEmail account type - * Fixed bug in email subject replacement - * Enabled mass execute macro on Contact list grids - * Added option to weblead form designer to disable dupe detection by X2Identity on a per-form basis - * X2Packager stability fixes - * Fixed date format issue under French locale * Miscellaneous UI enhancements + * Green login logo * Miscellaneous bug fixes -* Tracked Bug Fixes: - * [3703](http://x2software.com/index.php/bugReports/3703): Undefined index: X2List - * [5825](http://x2software.com/index.php/bugReports/5825): Call to a member function asa() on null - * [5966](http://x2software.com/index.php/bugReports/5966): Trying to get property of non-object - * [5971](http://x2software.com/index.php/bugReports/5971): Function mcrypt_create_iv() is deprecated + * Login token bug fix + * UI bug fixes # Introduction # Welcome to X2CRM! diff --git a/x2engine/js/X2Flow/X2FlowEditor.js b/x2engine/js/X2Flow/X2FlowEditor.js index c4ed731c7..b2fdf9006 100644 --- a/x2engine/js/X2Flow/X2FlowEditor.js +++ b/x2engine/js/X2Flow/X2FlowEditor.js @@ -1,6 +1,6 @@ /*********************************************************************************** - * X2Engine Open Source Edition is a customer relationship management program developed by - * X2 Engine, Inc. Copyright (C) 2011-2017 X2 Engine Inc. + * X2CRM is a customer relationship management program developed by + * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc. * * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU Affero General Public License version 3 as published by the @@ -19,8 +19,9 @@ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301 USA. * - * You can contact X2Engine, Inc. P.O. Box 610121, Redwood City, - * California 94061, USA. or at email address contact@x2engine.com. + * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley, + * California 95067, USA. on our website at www.x2crm.com, or at our + * email address: contact@x2engine.com. * * The interactive user interfaces in modified source and object code versions * of this program must display Appropriate Legal Notices, as required under @@ -28,1844 +29,1767 @@ * * In accordance with Section 7(b) of the GNU Affero General Public License version 3, * these Appropriate Legal Notices must retain the display of the "Powered by - * X2 Engine" logo. If the display of the logo is not reasonably feasible for + * X2Engine" logo. If the display of the logo is not reasonably feasible for * technical reasons, the Appropriate Legal Notices must display the words - * "Powered by X2 Engine". + * "Powered by X2Engine". **********************************************************************************/ -$(function () { +$(function() { - var DEBUG = false && x2.DEBUG; +var DEBUG = false && x2.DEBUG; - x2.flow.hideShowCronUI = function () { - DEBUG && console.log('hideShowCronUI'); - var itemsWhichRequireCron = x2.flow.requiresCron; - var requiresCron = false; - for (var i in itemsWhichRequireCron) { - if ($('.x2flow-main').find($('.' + itemsWhichRequireCron[i])).length > 0) { - requiresCron = true; - break; - } - } - if (requiresCron) { - $('#test-cron-button').show(); - $('#view-log-button').show(); - } else { - $('#test-cron-button').hide(); - $('#view-log-button').hide(); +x2.flow.hideShowCronUI = function () { + DEBUG && console.log ('hideShowCronUI'); + var itemsWhichRequireCron = x2.flow.requiresCron; + var requiresCron = false; + for (var i in itemsWhichRequireCron) { + if ($('.x2flow-main').find ($('.' + itemsWhichRequireCron[i])).length > 0) { + requiresCron = true; + break; } - }; + } + if (requiresCron) { + $('#test-cron-button').show (); + $('#view-log-button').show (); + } else { + $('#test-cron-button').hide (); + $('#view-log-button').hide (); + } +}; - $.widget("x2.flowDraggable", $.ui.mouse, { +$.widget("x2.flowDraggable", $.ui.mouse, { - options: { - clone: true, - renderDzBoxes: false, - }, + options:{ + clone:true, + renderDzBoxes:false, + }, - nodeTree: [], - nextId: 0, + nodeTree:[], + nextId:0, - // parentBranch:$(), - // startingIndex:0, + // parentBranch:$(), + // startingIndex:0, - node: $(), - startingBranch: $(), - startingIndex: 0, + node:$(), + startingBranch:$(), + startingIndex:0, - nodeParent: null, // element previously containing the node - futureTarget: null, - nodeBefore: null, // element (if any) right before the node's last good position - nodeAfter: null, // element (if any) right after the node's last good position + nodeParent:null, // element previously containing the node + futureTarget:null, + nodeBefore:null, // element (if any) right before the node's last good position + nodeAfter:null, // element (if any) right after the node's last good position - helper: null, // a clone of the original to be dragged around - // placeholder:null, // the original (or a clone) to preview where the node will be dropped + helper:null, // a clone of the original to be dragged around + // placeholder:null, // the original (or a clone) to preview where the node will be dropped - mouseOffset: {}, // where the user clicked within the draggable - helperHalfHeight: 0, - helperHalfWidth: 0, + mouseOffset:{}, // where the user clicked within the draggable + helperHalfHeight:0, + helperHalfWidth:0, - deleteButton: null, - deleteTarget: $(), + deleteButton:null, + deleteTarget:$(), - dropTargets: null, - dropZones: [], // array of top-left and bottom-right coordinates for droppable zones + dropTargets:null, + dropZones:[], // array of top-left and bottom-right coordinates for droppable zones - hoverTarget: null, // current drop target being hovered over + hoverTarget:null, // current drop target being hovered over - moveTimer: null, // timeout to move things after a short delay + moveTimer:null, // timeout to move things after a short delay - _init: function () { + _init:function() { - var self = this; + var self = this; - this.deleteButton = $("#item-delete").clone().show().bind('click', function () { - if (window.flowEditor.isLoading()) { - return false; - } - if (flowEditor.currentItem.is(self.deleteTarget)) { - flowEditor.currentItem = $(); - flowEditor.openItemConfig(); - } - var oldParent = self.deleteTarget.parent(); - $(this).detach(); - self.deleteTarget.remove(); - self._toggleEmptyBox(oldParent); - x2.flow.hideShowCronUI(); + this.deleteButton = $("#item-delete").clone().show().bind('click', function() { + if (window.flowEditor.isLoading ()) { + return false; + } + if(flowEditor.currentItem.is(self.deleteTarget)) { + flowEditor.currentItem = $(); + flowEditor.openItemConfig(); + } + var oldParent = self.deleteTarget.parent(); + $(this).detach(); + self.deleteTarget.remove(); + self._toggleEmptyBox(oldParent); + x2.flow.hideShowCronUI (); + }); + + + if(!this.options.clone) { + $(this.element).on('mouseenter', '.x2flow-action, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', function() { + var offset = $(this).offset(); + self.deleteButton.appendTo(this).offset({top:offset.top-4, left:offset.left+36}); + self.deleteTarget = $(this).closest('.x2flow-node'); + }).on('mouseleave', '.x2flow-action, .x2flow-trigger, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', + function() { + + self.deleteTarget = $(); + self.deleteButton.detach(); }); + } + this._mouseInit(); // setup mouse handling + // $("#save-button").unbind().click(function() { + // console.debug(self._getNodeTree($("#x2flow-main > .x2flow-branch"))); + // }); + // $(".x2flow-node").disableSelection() + }, - if (!this.options.clone) { - $(this.element).on('mouseenter', '.x2flow-action, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', function () { - var offset = $(this).offset(); - self.deleteButton.appendTo(this).offset({top: offset.top - 4, left: offset.left + 36}); - self.deleteTarget = $(this).closest('.x2flow-node'); - }).on('mouseleave', '.x2flow-action, .x2flow-trigger, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', - function () { + _destroy:function() { + this._mouseDestroy(); + }, + /** + * decide whether to drag this thing + */ + _mouseCapture:function(e) { + return $(e.target).is( + '#item-box *, .X2FlowSwitch .icon, .X2FlowSplitter .icon, ' + + '.X2FlowSplitter .icon-inner, ' + + '.x2flow-node:not(.x2flow-trigger, .X2FlowSwitch, .X2FlowSplitter, .x2flow-empty)'); + //parents(".x2flow-node").length; + }, + /** + * start dragging; find drop targets, create helper element, etc + */ + _mouseStart:function(e) { + var self = this; + $(".x2flow-node").disableSelection(); + + this.node = $(e.target).closest(".x2flow-node"); + + this.deleteTarget = $(); + this.deleteButton.detach(); + + // this.nodeParent = this.node.parent(); + this.futureTarget = this.node.parent(); + this.nodeBefore = this.node.prev(); + if(this.options.clone || this.nodeBefore.length === 0) + this.nodeBefore = null; + + var offset = this.node.offset(); + this.mouseOffset = { + x: e.pageX - offset.left, + y: e.pageY - offset.top + }; + if(this.node.parent().is("#item-box")) { + this.mouseOffset.x = offset.left + 24; + this.mouseOffset.y = 24; + } - self.deleteTarget = $(); - self.deleteButton.detach(); - }); - } - this._mouseInit(); // setup mouse handling + // if(node.children(".icon").length) + // this.mouseOffset.x = this.mouseOffset.x - 14; - // $("#save-button").unbind().click(function() { - // console.debug(self._getNodeTree($("#x2flow-main > .x2flow-branch"))); - // }); - // $(".x2flow-node").disableSelection() - }, - - _destroy: function () { - this._mouseDestroy(); - }, - /** - * decide whether to drag this thing - */ - _mouseCapture: function (e) { - return $(e.target).is( - '#all *, .X2FlowSwitch .icon, .X2FlowSplitter .icon, ' + - '.X2FlowSplitter .icon-inner, ' + - '.x2flow-node:not(.x2flow-trigger, .X2FlowSwitch, .X2FlowSplitter, .x2flow-empty)'); - //parents(".x2flow-node").length; - }, - /** - * start dragging; find drop targets, create helper element, etc - */ - _mouseStart: function (e) { - var self = this; - $(".x2flow-node").disableSelection(); - - this.node = $(e.target).closest(".x2flow-node"); - - this.deleteTarget = $(); - this.deleteButton.detach(); - - this.futureTarget = this.node.parent(); - this.nodeBefore = this.node.prev(); - if (this.options.clone || this.nodeBefore.length === 0) - this.nodeBefore = null; - - var offset = this.node.offset(); - var nodeCenter = this.node.width() / 2; - - var startOffset = { - left: offset.left + nodeCenter, - top: offset.top - }; - this.mouseOffset = { - x: e.pageX - offset.left - nodeCenter, - y: e.pageY - offset.top - }; - - // create a copy of the node to be dragged around - this.helper = this.node.clone().disableSelection(); - - if (this.options.clone) { - this.node = this.node.clone().appendTo('#all'); // copy the original - } + // this.placeholder = node.clone() + // .disableSelection() - this.node.addClass("x2flow-placeholder"); - - + // create a copy of the node to be dragged around + this.helper = this.node.clone().disableSelection(); - this.dropTargets = $(); + if(this.options.clone) { + this.node = this.node.clone().appendTo("#item-box"); // copy the original + } - // we can drag it into other branches, or the trash, but not into a branch inside itself - $("#x2flow-main .x2flow-branch, .x2flow-trash").not(this.node.find(".x2flow-branch")). - each(function (i, elem) { + this.node.addClass("x2flow-placeholder"); - if (!(self.node.hasClass("X2FlowSwitch") || - self.node.hasClass("X2FlowSplitter")) || - ($(elem).children(".X2FlowSwitch").not(".x2flow-placeholder").length === 0 && - $(elem).children(".X2FlowSplitter").not(".x2flow-placeholder").length === 0)) { - // we can't drag a switch into a branch with an existing switch + this.dropTargets = $(); - self.dropTargets = self.dropTargets.add(elem); - } - }); - this.dropTargets.addClass("x2flow-active"); - - this._calculateDropZones(); - - this.helper - .addClass("x2flow-helper") - .disableSelection() - .css({position: "absolute", "z-index": "1"}) - .offset(startOffset) - .appendTo("body"); - }, - /** - * Called on mousemove event. Moves helper element, moves placeholder sometimes - */ - _mouseDrag: function (e) { - var self = this; - - var offset = { - left: e.pageX - this.mouseOffset.x, - top: e.pageY - this.mouseOffset.y - }; - - this.helper.offset(offset); // make the helper follow the mouse - - // update this.hoverTarget, and this.nodeBefore if possible - this.hoverTarget = this._getHoverTarget({x: e.pageX, y: e.pageY}); - if (this.hoverTarget !== null) { - this.nodeBefore = this._getNodeBefore(e.pageX); - this.nodeAfter = this._getNodeAfter(e.pageX); - } + // we can drag it into other branches, or the trash, but not into a branch inside itself + $("#x2flow-main .x2flow-branch, .x2flow-trash").not(this.node.find(".x2flow-branch")). + each(function(i, elem) { - // nodes with return val must be last in branch - if (this.nodeBefore && this.nodeBefore.hasClass('X2FlowPushWebContent')) { - return; - } else if (this.nodeAfter && this.node.hasClass('X2FlowPushWebContent')) { - return; + if(!(self.node.hasClass("X2FlowSwitch") || + self.node.hasClass("X2FlowSplitter")) || + ($(elem).children(".X2FlowSwitch").not(".x2flow-placeholder").length === 0 && + $(elem).children(".X2FlowSplitter").not(".x2flow-placeholder").length === 0)) { + // we can't drag a switch into a branch with an existing switch + + self.dropTargets = self.dropTargets.add(elem); } + }); + this.dropTargets.addClass("x2flow-active"); + + this._calculateDropZones(); + + this.helper + // .width(this.node.width()) // lock width to fix the stupid margin issue + .addClass("x2flow-helper") + .disableSelection() + .css({position:"absolute", "z-index":"1"}) + .offset(offset) + .appendTo("body"); + }, + /** + * Called on mousemove event. Moves helper element, moves placeholder sometimes + */ + _mouseDrag:function(e) { + var self = this; - if (this.hoverTarget === null) { - if (this.options.clone) { // if this is a new item being dragged in, - // move the placeholder back to the menu box; - this.nodeBefore = this.futureTarget.children().last(); - - if (this.futureTarget.attr("id") !== 'all') { - this.futureTarget = $('#all'); - clearTimeout(this.moveTimer); - this.moveTimer = setTimeout(function () { - self._moveNode(); - self._calculateDropZones(); - }, 500); - } - } else { + var offset = { + left: e.pageX-this.mouseOffset.x, + top: e.pageY-this.mouseOffset.y + }; + + this.helper.offset(offset); // make the helper follow the mouse + + // update this.hoverTarget, and this.nodeBefore if possible + this.hoverTarget = this._getHoverTarget({x:e.pageX, y:e.pageY}); + if(this.hoverTarget !== null) { + this.nodeBefore = this._getNodeBefore(e.pageX); + this.nodeAfter = this._getNodeAfter (e.pageX); + } + + // nodes with return val must be last in branch + if (this.nodeBefore && this.nodeBefore.hasClass ('X2FlowPushWebContent')) { + return; + } else if (this.nodeAfter && this.node.hasClass ('X2FlowPushWebContent')) { + return; + } + + if(this.hoverTarget === null) { + if(this.options.clone) { // if this is a new item being dragged in, + // move the placeholder back to the menu box; + this.nodeBefore = this.futureTarget.children().last(); + if(this.futureTarget.attr("id") !== "item-box") { + this.futureTarget = $("#item-box"); clearTimeout(this.moveTimer); + this.moveTimer = setTimeout(function(){ + self._moveNode(); self._calculateDropZones(); + }, 500); } - } else if (this.nodeBefore !== null) { // we're good to go, move the node - if (x2.DEBUG && x2.flow.freezeHoverTarget) - return; - this.hoverTarget.addClass("x2flow-hover"); - - if (!this.hoverTarget.is(this.node.parent())) { - // trying to drag to a different branch - - if (!this.hoverTarget.is(this.futureTarget)) { - this.futureTarget = this.hoverTarget; - clearTimeout(this.moveTimer); - this.moveTimer = setTimeout(function () { - self._moveNode(); - self._calculateDropZones(); - }, 100); - } - } else if (!this.nodeBefore.is(this.node.prev(".x2flow-node"))) { - this.node.insertAfter(this.nodeBefore); // dragging within branch + } else { + clearTimeout(this.moveTimer); + } + } else if(this.nodeBefore !== null) { // we're good to go, move the node + if (x2.DEBUG && x2.flow.freezeHoverTarget) return; + this.hoverTarget.addClass("x2flow-hover"); + + if(!this.hoverTarget.is(this.node.parent())) { + // trying to drag to a different branch + + if(!this.hoverTarget.is(this.futureTarget)) { + this.futureTarget = this.hoverTarget; + clearTimeout(this.moveTimer); + this.moveTimer = setTimeout(function(){ + self._moveNode();self._calculateDropZones(); + }, 100); } + } else if(!this.nodeBefore.is(this.node.prev(".x2flow-node"))) { + this.node.insertAfter(this.nodeBefore); // dragging within branch } + } - }, + }, - // stop dragging, either destroy the helper or drop it into whatever thing - _mouseStop: function (e) { - if (x2.DEBUG && x2.flow.dragDisable) - return; - clearTimeout(this.moveTimer); + // stop dragging, either destroy the helper or drop it into whatever thing + _mouseStop:function(e) { + if (x2.DEBUG && x2.flow.dragDisable) return; + clearTimeout(this.moveTimer); - this.helper.remove(); - this.node.removeClass("x2flow-placeholder"); - this.dropTargets.removeClass("x2flow-active x2flow-hover"); - this.futureTarget = $(); + this.helper.remove(); + this.node.removeClass("x2flow-placeholder"); + this.dropTargets.removeClass("x2flow-active x2flow-hover"); + this.futureTarget = $(); - if (this.node.parent().attr("id") === 'all') { + if(this.node.parent().attr("id") === "item-box") { + this.node.remove(); + } else { + if(this.hoverTarget !== null && this.hoverTarget.hasClass("x2flow-trash")) { + var oldParent = this.node.parent(); this.node.remove(); - } else { - if (this.options.clone) { // node has been added, so open up the config - if (this.node.hasClass("X2FlowSwitch") || this.node.hasClass('X2FlowSplitter')) { - // switches can only be clicked on their icons - this.node.children(".icon").click(); - } else { - this.node.click(); - } - + this._toggleEmptyBox(oldParent); + flowEditor.currentItem = $(); + flowEditor.openItemConfig(); + } + if(this.options.clone) { // node has been added, so open up the config + if(this.node.hasClass("X2FlowSwitch") || this.node.hasClass ('X2FlowSplitter')) { + // switches can only be clicked on their icons + this.node.children(".icon").click(); + } else { + this.node.click(); } - x2.flow.hideShowCronUI(); + } + x2.flow.hideShowCronUI (); + } - }, - _moveNode: function () { - DEBUG && console.log('this.nodeBefore = '); - DEBUG && console.log(this.nodeBefore); + }, + _moveNode:function() { + DEBUG && console.log ('this.nodeBefore = '); + DEBUG && console.log (this.nodeBefore); - // nodes with return val must be last in branch - if (this.nodeBefore.hasClass('X2FlowPushWebContent') || - this.node.hasClass('X2FlowPushWebContent') && - this.nodeBefore.next('.x2flow-node').not('.x2flow-empty').length) { - return; - } + // nodes with return val must be last in branch + if (this.nodeBefore.hasClass ('X2FlowPushWebContent') || + this.node.hasClass ('X2FlowPushWebContent') && + this.nodeBefore.next ('.x2flow-node').not ('.x2flow-empty').length) { + return; + } - var oldParent = this.node.parent(); - this.node.insertAfter(this.nodeBefore); - this._toggleEmptyBox(oldParent); - this._toggleEmptyBox(this.futureTarget); - // this.nodeParent = this.hoverTarget; - this._calculateDropZones(); - }, - _toggleEmptyBox: function (elem) { - if (!elem.hasClass("x2flow-trash")) { - if (elem.children(".x2flow-node:not(.x2flow-empty)").length) { - elem.children(".x2flow-empty").remove(); - } else if (elem.children(".x2flow-empty").length === 0) { - $(document.createElement("div")).addClass("x2flow-node x2flow-empty"). - appendTo(elem); - } + var oldParent = this.node.parent(); + this.node.insertAfter(this.nodeBefore); + this._toggleEmptyBox(oldParent); + this._toggleEmptyBox(this.futureTarget); + // this.nodeParent = this.hoverTarget; + this._calculateDropZones(); + }, + _toggleEmptyBox:function(elem) { + if(!elem.hasClass("x2flow-trash")) { + if(elem.children(".x2flow-node:not(.x2flow-empty)").length) { + elem.children(".x2flow-empty").remove(); + } else if(elem.children(".x2flow-empty").length == 0) { + $(document.createElement("div")).addClass("x2flow-node x2flow-empty"). + appendTo(elem); } - }, - // get the bounding coordinates for each droppable zone - _calculateDropZones: function () { - this.dropZones = []; - /* loop through all the droppable branches in reverse order, so deepest in the tree comes - first */ - for (var i = this.dropTargets.length - 1; i >= 0; i--) { - var target = $(this.dropTargets[i]), - offset = target.offset(), - h = target.height(), - w = target.width(); - - if (target.hasClass("x2flow-branch")) { - this.dropZones.push({ - x0: offset.left, - y0: offset.top + (h / 2) - 25, - x1: offset.left + w + 50, // give it some extra space at the bottom - y1: offset.top + (h / 2) + 25, - elem: target - }); - } else { - this.dropZones.push({ - x0: offset.left, - y0: offset.top, - x1: offset.left + w, - y1: offset.top + h, - elem: target - }); - } + } + }, + // get the bounding coordinates for each droppable zone + _calculateDropZones:function() { + this.dropZones = []; + /* loop through all the droppable branches in reverse order, so deepest in the tree comes + first */ + for(var i = this.dropTargets.length-1;i>=0;i--) { + var target = $(this.dropTargets[i]), + offset = target.offset(), + h = target.height(), + w = target.width(); + + if(target.hasClass("x2flow-branch")) { + this.dropZones.push({ + x0: offset.left, + y0: offset.top + (h/2) - 25, + x1: offset.left + w + 50, // give it some extra space at the bottom + y1: offset.top + (h/2) + 25, + elem: target + }); + } else { + this.dropZones.push({ + x0: offset.left, + y0: offset.top, + x1: offset.left + w, + y1: offset.top + h, + elem: target + }); } - // render dropzone boxes - if (this.options.renderDzBoxes) { - $(".dz").remove(); - for (var i in this.dropZones) { - var z = this.dropZones[i]; - $(document.createElement("div")) - .addClass("dz") - .offset({left: z.x0, top: z.y0}) - .width(z.x1 - z.x0) - .height(z.y1 - z.y0).appendTo("body"); - } + } + // render dropzone boxes + if(this.options.renderDzBoxes) { + $(".dz").remove(); + for(var i in this.dropZones) { + var z = this.dropZones[i]; + $(document.createElement("div")) + .addClass("dz") + .offset({left:z.x0, top:z.y0}) + .width(z.x1-z.x0) + .height(z.y1-z.y0).appendTo("body"); } - }, - /** - * Scans through this.dropZones rectangles to determine the branch - * farthest down the tree containing the given mouse coordinates. - */ - _getHoverTarget: function (coords) { - var hoverTarget = null; - - for (var i = 0; i < this.dropZones.length; i++) { - var dropZone = this.dropZones[i]; - // we only want to highlight one element, so - // this.hoverTarget will be set to the first one found - if (hoverTarget === null - && coords.x > dropZone.x0 - && coords.x < dropZone.x1 - && coords.y > dropZone.y0 - && coords.y < dropZone.y1 - ) { - hoverTarget = $(dropZone.elem) - } else { - $(dropZone.elem).removeClass("x2flow-hover"); // remove this from everything else - } + } + }, + /** + * Scans through this.dropZones rectangles to determine the branch + * farthest down the tree containing the given mouse coordinates. + */ + _getHoverTarget:function(coords) { + var hoverTarget = null; + + for(var i=0;i dropZone.x0 + && coords.x < dropZone.x1 + && coords.y > dropZone.y0 + && coords.y < dropZone.y1 + ) { + hoverTarget = $(dropZone.elem) + } else { + $(dropZone.elem).removeClass("x2flow-hover"); // remove this from everything else } + } - return hoverTarget; - }, - _getNodeAfter: function (pageX) { - if (this.hoverTarget === null) - return null; - if (this.node.hasClass("X2FlowSwitch") || - this.node.hasClass("X2FlowSplitter")) { - // if we're dragging a switch, it has to go at the end - return null; - } + return hoverTarget; + }, + _getNodeAfter:function(pageX) { + if(this.hoverTarget === null) + return null; + if(this.node.hasClass("X2FlowSwitch") || + this.node.hasClass("X2FlowSplitter")) { + // if we're dragging a switch, it has to go at the end + return null + } - // ignore the placeholder itself, the bracket element, and any .empty boxes - var targetSiblings = this.hoverTarget.children(".x2flow-node"). - not(".x2flow-placeholder, .x2flow-empty"); - var nodeAfter = null; + // ignore the placeholder itself, the bracket element, and any .empty boxes + var targetSiblings = this.hoverTarget.children(".x2flow-node"). + not(".x2flow-placeholder, .x2flow-empty"); + var nodeAfter = null; - for (var j = 0; j < targetSiblings.length; j++) { - var sibling = $(targetSiblings[j]); + for(var j = 0; j < targetSiblings.length; j++) { + var sibling = $(targetSiblings[j]); - if (sibling.offset().left + sibling.width() / 2 < pageX) { - continue; - } else { - nodeAfter = sibling; - break; - } - } - return nodeAfter; - }, - /* - * Scans through elements in this.hoverTarget and determines - * which one is right before the placeholder. Returns the element (nodeBefore) - * or null if it's at the end or hoverTarget is empty. - */ - _getNodeBefore: function (pageX) { - if (this.hoverTarget === null) - return null; - - if (this.node.hasClass("X2FlowSwitch") || - this.node.hasClass("X2FlowSplitter")) { - // if we're dragging a switch, it has to go at the end - return this.hoverTarget.children(".x2flow-node:not(.x2flow-placeholder)").last(); + if(sibling.offset().left + sibling.width() / 2 < pageX) { + continue; + } else { + nodeAfter = sibling; + break; } + } + return nodeAfter; + }, + /* + * Scans through elements in this.hoverTarget and determines + * which one is right before the placeholder. Returns the element (nodeBefore) + * or null if it's at the end or hoverTarget is empty. + */ + _getNodeBefore:function(pageX) { + if(this.hoverTarget === null) + return null; - // ignore the placeholder itself, the bracket element, and any .empty boxes - var targetSiblings = this.hoverTarget.children(".x2flow-node"). - not(".x2flow-placeholder, .x2flow-empty, .X2FlowSwitch, .X2FlowSplitter"); - var nodeBefore = null; + if(this.node.hasClass("X2FlowSwitch") || + this.node.hasClass("X2FlowSplitter")) { + // if we're dragging a switch, it has to go at the end + return this.hoverTarget.children(".x2flow-node:not(.x2flow-placeholder)").last(); + } - for (var j = 0; j < targetSiblings.length; j++) { - var sibling = $(targetSiblings[j]); + // ignore the placeholder itself, the bracket element, and any .empty boxes + var targetSiblings = this.hoverTarget.children(".x2flow-node"). + not(".x2flow-placeholder, .x2flow-empty, .X2FlowSwitch, .X2FlowSplitter"); + var nodeBefore = null; - if (sibling.offset().left + sibling.width() / 2 < pageX) { - nodeBefore = sibling; - } else { - break; - } + for(var j = 0; j < targetSiblings.length; j++) { + var sibling = $(targetSiblings[j]); + + if (sibling.offset().left+sibling.width()/2 < pageX) { + nodeBefore = sibling; + } else { + break; } - if (nodeBefore !== null) - return nodeBefore; - return this.hoverTarget.children(".bracket:first"); } - }); + if(nodeBefore !== null) + return nodeBefore; + return this.hoverTarget.children(".bracket:first"); + } +}); - $("#all").flowDraggable({distance: 0, clone: true}); - $("#x2flow-main").flowDraggable({distance: 20, clone: false}); - window.flowEditor = { - version: '5.2', - idCounter: 0, - loading: 0, // when this value is less than 0, config menu is loading +$("#item-box").flowDraggable({distance:0, clone:true}); +$("#x2flow-main").flowDraggable({distance:20, clone:false}); - currentItem: $(), - currentConfig: {}, - trigger: $("#trigger"), - flowItemOptionCache: {}, - conditionParamCache: {}, +window.flowEditor = { - showIdItems: [ - 'X2FlowWait', - 'X2FlowEmail', - 'X2FlowRecordEmail' - ], + version: '5.2', + idCounter: 0, + loading: 0, // when this value is less than 0, config menu is loading - init: function () { - var that = this; + currentItem:$(), + currentConfig:{}, - // listen for changes in fields on these parent elements - // x2.fieldUtils.addChangeListener("#x2flow-conditions, #x2flow-attributes"); - x2.fieldUtils.addChangeListener("#x2flow-config-box"); - // we want the "changed" comparison for any attribute conditions - x2.fieldUtils.enableChangedOperator = true; + trigger:$("#trigger"), - $('#x2flow-main').resizable({ - handles: 's', - stop: function () { - $(this).css('width', ''); + flowItemOptionCache:{}, + conditionParamCache:{}, + + showIdItems:[ + 'X2FlowWait', + 'X2FlowEmail', + 'X2FlowRecordEmail' + ], + + init:function() { + var that = this; + + // listen for changes in fields on these parent elements + // x2.fieldUtils.addChangeListener("#x2flow-conditions, #x2flow-attributes"); + x2.fieldUtils.addChangeListener("#x2flow-config-box"); + // we want the "changed" comparison for any attribute conditions + x2.fieldUtils.enableChangedOperator = true; + + $('#x2flow-main').resizable ({ + handles: 's', + stop: function () { + $(this).css ('width', ''); + } + }); + + // Listen for clicks on the "delete condition" buttom + $("#x2flow-conditions, #x2flow-attributes, #x2flow-headers").on( + "click", "a.del", function() { + + $(this).closest("li").slideUp(200, function(){ + if ($(this).siblings ('li').length === 0) { + $(this).parent ('ol').prev ('.x2flow-api-attributes-section-header').hide (); } + $(this).remove(); }); + }); - // Listen for clicks on the "delete condition" buttom - $("#x2flow-conditions, #x2flow-attributes, #x2flow-headers").on( - "click", "a.del", function () { + // Listen for clicks on actual flow items, and load the config for them + $("#x2flow-main"). + on('click', + '.x2flow-action, .x2flow-trigger, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', + function() { - $(this).closest("li").slideUp(200, function () { - if ($(this).siblings('li').length === 0) { - $(this).parent('ol').prev('.x2flow-api-attributes-section-header').hide(); - } - $(this).remove(); - }); - }); + if (that.isLoading ()) { + return false; + } - // Listen for clicks on actual flow items, and load the config for them - $("#x2flow-main"). - on('click', - '.x2flow-action, .x2flow-trigger, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', - function () { - - if (that.isLoading()) { - return false; - } - - if ($(this).is(flowEditor.currentItem)) - return; - // if(item.attr("id") !== "trigger" || $(this.currentItem.attr("id") !== "trigger")) - // don't save if we're changing the trigger, this screws stuff up - // but otherwise, save the data for the previous flow item - // this.saveCurrentConfig(); - flowEditor.saveCurrentConfig(); - //$('.x2flow-main').find ('.x2flow-node.selected').removeClass ('selected'); - flowEditor.currentItem.removeClass("selected"); - flowEditor.currentItem = $(this).closest(".x2flow-node"); // set new current item - flowEditor.currentItem.addClass("selected"); - - flowEditor.openItemConfig(); - }); - - // "Add Condition" button (only visible on triggers and switches) - $("#x2flow-add-condition").click(function () { - var type = $("#x2flow-condition-type").val(); - if (type === 'attribute') { - var modelClass = flowEditor.getModelClass(); - if (modelClass !== null) { - that.lockConfig(); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function (attributes) { - that.unlockConfig(); - x2.fieldUtils.createAttrListItem(modelClass, attributes) - // mark as a multiselect so it can be toggled back and forth later - .data({"type": type, "multiple": 1}) - .hide() - .appendTo("#x2flow-conditions ol") - .slideDown(200) - }); - } - } else { - if (type !== 'workflow_status') { - that.lockConfig(); - if (type === 'email_open') { - flowEditor.queryConditionParams(type, function (params) { - that.unlockConfig(); - flowEditor.createItemCondition(params) - .data("type", type) - .hide() - .appendTo("#x2flow-conditions ol") - .addClass('email-open-condition') - .slideDown(200); - that.updateEmailOpenConditions(); - }); - - } else { - flowEditor.queryConditionParams(type, function (params) { - that.unlockConfig(); - flowEditor.createItemCondition(params) - .data("type", type) - .hide() - .appendTo("#x2flow-conditions ol") - .slideDown(200); - }); - } - } else { - flowEditor.createWorkflowStatusCondition() + if($(this).is(flowEditor.currentItem)) + return; + // if(item.attr("id") !== "trigger" || $(this.currentItem.attr("id") !== "trigger")) + // don't save if we're changing the trigger, this screws stuff up + // but otherwise, save the data for the previous flow item + // this.saveCurrentConfig(); + flowEditor.saveCurrentConfig(); + //$('.x2flow-main').find ('.x2flow-node.selected').removeClass ('selected'); + flowEditor.currentItem.removeClass("selected"); + flowEditor.currentItem = $(this).closest(".x2flow-node"); // set new current item + flowEditor.currentItem.addClass("selected"); + + flowEditor.openItemConfig(); + }); + + // "Add Condition" button (only visible on triggers and switches) + $("#x2flow-add-condition").click(function() { + var type = $("#x2flow-condition-type").val(); + if(type == 'attribute') { + var modelClass = flowEditor.getModelClass(); + if(modelClass !== null) { + that.lockConfig (); + x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributes) { + that.unlockConfig (); + x2.fieldUtils.createAttrListItem(modelClass, attributes) + // mark as a multiselect so it can be toggled back and forth later + .data({"type":type, "multiple":1}) + .hide() + .appendTo("#x2flow-conditions ol") + .slideDown(200) + }); + } + } else { + if (type !== 'workflow_status') { + that.lockConfig (); + if(type === 'email_open') { + flowEditor.queryConditionParams(type, function(params) { + that.unlockConfig (); + flowEditor.createItemCondition(params) .data("type", type) .hide() .appendTo("#x2flow-conditions ol") + .addClass('email-open-condition') .slideDown(200); - } - } - }); - - // add an attribute row (on flow actions) - $("#x2flow-add-attribute").click(function () { - var modelClass = flowEditor.getModelClass(); - that.lockConfig(); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function (attributeList) { - that.unlockConfig(); - if (modelClass === "API_params") { - $('#x2flow-attributes .x2flow-api-attributes-section-header').show(); - if (attributeList[0] && attributeList[0].type === "API_params") { - flowEditor.createApiParam(attributeList[0].name, attributeList[0].value) - .hide() - .appendTo("#x2flow-attributes ol") - .data({type: "attribute", modelClass: modelClass}) - .slideDown(200); - } - } else if (attributeList) { - x2.fieldUtils.createAttrListItem(modelClass, attributeList, null, false) + that.updateEmailOpenConditions(); + }); + + }else{ + flowEditor.queryConditionParams(type, function(params) { + that.unlockConfig (); + flowEditor.createItemCondition(params) + .data("type", type) .hide() - .appendTo("#x2flow-attributes ol") - .data({type: "attribute", modelClass: modelClass}) + .appendTo("#x2flow-conditions ol") .slideDown(200); + }); } - }); - }); - - $("#x2flow-add-header").click(function () { - var modelClass = flowEditor.getModelClass(); - that.lockConfig(); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function (attributeList) { - that.unlockConfig(); - $('#x2flow-headers .x2flow-api-attributes-section-header').show(); - flowEditor.createApiParam(attributeList[0].name, attributeList[0].value, false) + } else { + flowEditor.createWorkflowStatusCondition () + .data("type", type) + .hide () + .appendTo("#x2flow-conditions ol") + .slideDown(200); + } + } + }); + // add an attribute row (on flow actions) + $("#x2flow-add-attribute").click(function() { + var modelClass = flowEditor.getModelClass(); + that.lockConfig (); + x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributeList) { + that.unlockConfig (); + if(modelClass === "API_params") { + $('#x2flow-attributes .x2flow-api-attributes-section-header').show (); + if(attributeList[0] && attributeList[0].type === "API_params") { + flowEditor.createApiParam(attributeList[0].name, attributeList[0].value) .hide() - .appendTo("#x2flow-headers ol") - .data({type: "headers", modelClass: modelClass}) + .appendTo("#x2flow-attributes ol") + .data({type:"attribute", modelClass:modelClass}) .slideDown(200); - }); - }); - - // Trigger menu - onchange opens trigger config (which then sets the flow's trigger and - // modelClass properties) - $("#trigger-selector").change(function (e) { - var prevTrigger = flowEditor.trigger.attr('class').match(/[^ ]+Trigger/); - if (that.isLoading()) { - $('#trigger-selector').val(prevTrigger); - return false; - } - - // change to or from targeted content request trigger - if ($(e.target).val().match(/TargetedContentRequestTrigger/)) { - that._targetedContentRequestTriggerChange(); - } else if (prevTrigger && prevTrigger[0] === 'TargetedContentRequestTrigger') { - if (!that._targetedContentRequestTriggerTearDown()) { - return; } + } else if(attributeList) { + x2.fieldUtils.createAttrListItem(modelClass, attributeList, null, false) + .hide() + .appendTo("#x2flow-attributes ol") + .data({type:"attribute", modelClass:modelClass}) + .slideDown(200); } - - flowEditor.trigger - .attr('class', '') - // .removeClass() // Doesn't work for some reason. - .removeData("config") - .addClass("x2flow-node x2flow-trigger " + $(e.target).val()) - .attr("title", $(e.target).find("option:selected").text()); - if (!x2.flow.showLabels) - $(flowEditor.trigger).addClass("no-label"); - flowEditor.currentItem.removeClass("selected"); - flowEditor.currentItem = flowEditor.trigger; - flowEditor.currentItem.addClass("selected"); - $(flowEditor.trigger).children('.x2flow-icon-label').html( - $("#trigger-selector").find("option:selected").text()); - - flowEditor.openItemConfig(); - x2.flow.hideShowCronUI(); }); - - // listen for changes in model type; remove attribute conditions from the previous type - $("#x2flow-main-config").on("change", "fieldset[name='modelClass'] select", function (e) { - var newModelClass = $(e.target).val(); - var config = flowEditor.currentItem.data("config"); - if (typeof config === "object") - config.modelClass = newModelClass; - - $("#x2flow-attributes ol").empty(); // clear this item's attributes - $("#x2flow-conditions li").each(function (i, condition) { - if ($(condition).data("type") === "attribute") - $(condition).remove(); // clear any attribute conditions for this item - }); - $(document).trigger("modelClassChange", [newModelClass]); + }); + + $("#x2flow-add-header").click(function() { + var modelClass = flowEditor.getModelClass(); + that.lockConfig (); + x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributeList) { + that.unlockConfig (); + $('#x2flow-headers .x2flow-api-attributes-section-header').show (); + flowEditor.createApiParam(attributeList[0].name, attributeList[0].value, false) + .hide() + .appendTo("#x2flow-headers ol") + .data({type:"headers", modelClass:modelClass}) + .slideDown(200); }); + }); + + // Trigger menu - onchange opens trigger config (which then sets the flow's trigger and + // modelClass properties) + $("#trigger-selector").change(function(e) { + var prevTrigger = flowEditor.trigger.attr ('class').match (/[^ ]+Trigger/); + if (that.isLoading ()) { + $('#trigger-selector').val (prevTrigger); + return false; + } - // we changed the global modelClass; loop through all the items and delete - // attributes/conditions - $(document).bind("modelClassChange", function (evt, modelClass) { - $("#x2flow-main .x2flow-node").each(function (i, item) { - var itemConfig = $(item).data("config"); - if (typeof itemConfig === 'undefined') - return; - // if this item doens't define its own model class, - if (typeof itemConfig.modelClass === 'undefined') { - // the attributes must refer to the trigger so they're now invalid - delete itemConfig.attributes; - delete itemConfig.conditions; - } - }); - }); + // change to or from targeted content request trigger + if ($(e.target).val ().match (/TargetedContentRequestTrigger/)) { + that._targetedContentRequestTriggerChange (); + } else if (prevTrigger && prevTrigger[0] === 'TargetedContentRequestTrigger') { + if (!that._targetedContentRequestTriggerTearDown ()) { + return; + } + } - $("#x2flow-main-config").on("change", "fieldset[name='linkField'] select", function (e) { - var selectedOption = $("option:selected", this); - flowEditor.currentItem.data("config").linkType = $(selectedOption).attr('data-value'); - flowEditor.clearFutureNodeAttributes(flowEditor.currentItem.next()); + flowEditor.trigger + .attr('class','') + // .removeClass() // Doesn't work for some reason. + .removeData("config") + .addClass("x2flow-node x2flow-trigger "+$(e.target).val()) + .attr("title", $(e.target).find("option:selected").text()); + if (!x2.flow.showLabels) $(flowEditor.trigger).addClass ("no-label"); + flowEditor.currentItem.removeClass("selected"); + flowEditor.currentItem = flowEditor.trigger; + flowEditor.currentItem.addClass("selected"); + $(flowEditor.trigger).children ('.x2flow-icon-label').html ( + $("#trigger-selector").find ("option:selected").text ()); + + flowEditor.openItemConfig(); + x2.flow.hideShowCronUI (); + //$('.x2flow-main').find ('.x2flow-node.selected').removeClass ('selected'); + }); + + // listen for changes in model type; remove attribute conditions from the previous type + $("#x2flow-main-config").on("change", "fieldset[name='modelClass'] select", function(e) { + var newModelClass = $(e.target).val(); + var config = flowEditor.currentItem.data("config"); + if(typeof config === "object") + config.modelClass = newModelClass; + + $("#x2flow-attributes ol").empty(); // clear this item's attributes + $("#x2flow-conditions li").each(function(i, condition) { + if($(condition).data("type") === "attribute") + $(condition).remove(); // clear any attribute conditions for this item }); + $(document).trigger("modelClassChange", [newModelClass]); + }); - - $('.x2flow-start form#submitForm').submit(function () { - flowEditor.save(); - }); - $("#save-button").unbind().click(function () { - $('#submitForm').submit(); + // we changed the global modelClass; loop through all the items and delete + // attributes/conditions + $(document).bind("modelClassChange", function(evt, modelClass) { + $("#x2flow-main .x2flow-node").each(function(i, item) { + var itemConfig = $(item).data("config"); + if(typeof itemConfig === 'undefined') + return; + // if this item doens't define its own model class, + if(typeof itemConfig.modelClass === 'undefined') { + // the attributes must refer to the trigger so they're now invalid + delete itemConfig.attributes; + delete itemConfig.conditions; + } }); - }, - _getIds: function () {}, - /** - * Generate a unique node id - * @return Number - */ - _generateId: function () { - return ++this.idCounter; - }, - /* - Parameters: - node - instance of x2.flowDraggable - */ - _deleteNode: function (node) { - var nodeParent = node.parent(); - - // replace empty node after switch - if (nodeParent.hasClass('x2flow-branch') && - nodeParent.children('.x2flow-node').first().is($(node))) { - $(nodeParent).append($('
', {'class': 'x2flow-node x2flow-empty'})); - } - node.detach(); - x2.flow.hideShowCronUI(); - }, - _targetedContentRequestTriggerChange: function () { - $('#all').find('.X2FlowPushWebContent').show(); - $('#targeted-content-embed-code-container').fadeIn(1000); - }, - - /** - * Removes targeted content trigger specific UI elements if change is confirmed - * @returns true if the user confirms trigger change or if there's no content to be lost, - * @returns false otherwise - */ - _targetedContentRequestTriggerTearDown: function () { - var that = this; - - if (window.configFormEditor.getData() && - !window.confirm(x2.flow.translations['targetedContentTriggerChange'])) { - - return false; - } - /*$('.x2flow-main').find ('.x2flow-node.X2FlowPushWebContent').each (function () { - that._deleteNode ($(this)); - });*/ - $('#all').find('.X2FlowPushWebContent').hide(); - $('#targeted-content-embed-code-container').hide(); - return true; - }, - - /** - * Saves workflow flow - * - * @returns {undefined} - */ - save: function () { - /* update current item's settings since normally - they only get updated when you click a different item */ - this.saveCurrentConfig(); - this.invalidateDropdownCaches(this.trigger.data('config')); - - var flow = { - version: this.version, - idCounter: this.idCounter, - trigger: this.trigger.data("config"), - items: this.getNodeTree($("#x2flow-main > .x2flow-branch")) - }; - $("#flowDataField").val(JSON.stringify(flow)); - }, - - /** - * Loads workflow flow - * - * @param {type} flowData - * @returns {undefined} - */ - loadFlow: function (flowData) { - if (typeof flowData !== 'object') - flowData = JSON.parse(flowData); - this.idCounter = flowData.idCounter || 0; - // console.debug(flowData); - if (flowData.trigger !== undefined && flowData.trigger.type !== undefined) { - $("#trigger-selector").val(flowData.trigger.type); - flowEditor.trigger - .removeClass() - .data("config", flowData.trigger) - .addClass("x2flow-node x2flow-trigger " + flowData.trigger.type) - .attr("title", $("#trigger-selector").find("option:selected").text()) - .click(); - if (!x2.flow.showLabels) - $(flowEditor.trigger).addClass("no-label"); - $(flowEditor.trigger).children('.x2flow-icon-label').html( - //x2.flow.translations[$('#trigger-selector').find ("option:selected").val ()]); - $("#trigger-selector").find("option:selected").text()); - } - if (flowData.items.length) { - $("#x2flow-main > .x2flow-branch").empty().append(this.populateBranch(flowData.items)); - } - x2.flow.hideShowCronUI(); - }, - - /** - * Populates branch - * - * @param {type} items - * @returns {jQuery|Window.flowEditor.populateBranch.branch|window.flowEditor.populateBranch.branch|window.$|$} - */ - populateBranch: function (items) { - var branch = $('
'); - for (var i in items) { - var item = items[i]; - if (item.type === "X2FlowSwitch" || item.type === 'X2FlowSplitter') { - var flowSwitch = $("#all ." + item.type).clone().data("config", item); - var rightChildName = item.type === 'X2FlowSwitch' ? 'trueBranch' : 'upperBranch'; - var leftChildName = item.type === 'X2FlowSwitch' ? 'falseBranch' : 'lowerBranch'; - - var branches = - flowSwitch.children(".x2flow-branch-wrapper").children(".x2flow-branch"); - if (item[rightChildName].length) { - // create the branches all recursive-like n stuff - $(branches[0]).empty(".x2flow-empty").append( - this.populateBranch(item[rightChildName])); - } - if (item[leftChildName].length) { - $(branches[1]).empty(".x2flow-empty").append( - this.populateBranch(item[leftChildName])); - } - delete item[rightChildName]; - delete item[leftChildName]; // we don't want this stuff going into $.data() + }); + + $("#x2flow-main-config").on("change", "fieldset[name='linkField'] select", function(e) { + var selectedOption = $("option:selected", this); + flowEditor.currentItem.data("config").linkType = $(selectedOption).attr('data-value'); + flowEditor.clearFutureNodeAttributes(flowEditor.currentItem.next()); + }); + + + $('.x2flow-start form#submitForm').submit (function () { + flowEditor.save(); + }); + $("#save-button").unbind().click(function(){ + $('#submitForm').submit (); + }); + }, + _getIds: function () { + }, + /** + * Generate a unique node id + * @return Number + */ + _generateId: function () { + return ++this.idCounter; + }, + /** + * Generates unique ids for olds flow actions that don't yet have ids + */ +// _generateIds: function () { +// var nodes = $.makeArray ($('#content .x2flow-node')); +// for (var i in nodes) { +// var data = $(nodes[i]).data ('config'); +// if (typeof data !== 'undefined') { +// if (typeof data.id === 'undefined') { +// console.log ('new id'); +// data.id = ++this.idCounter; +// } +// } +// } +// }, + /* + Parameters: + node - instance of x2.flowDraggable + */ + _deleteNode: function (node) { + var nodeParent = node.parent (); + + // replace empty node after switch + if (nodeParent.hasClass ('x2flow-branch') && + nodeParent.children ('.x2flow-node').first ().is ($(node))) { + $(nodeParent).append ($('
', { 'class': 'x2flow-node x2flow-empty' })); + } + node.detach (); + x2.flow.hideShowCronUI (); + }, + _targetedContentRequestTriggerChange: function () { + $('#item-box').find ('.X2FlowPushWebContent').show (); + $('#targeted-content-embed-code-container').fadeIn (1000); + }, + /* + Removes targeted content trigger specific UI elements if change is confirmed + Returns: + true if the user confirms trigger change or if there's no content to be lost, + false otherwise + */ + _targetedContentRequestTriggerTearDown: function () { + var that = this; + + if (window.configFormEditor.getData () && + !window.confirm (x2.flow.translations['targetedContentTriggerChange'])) { + + return false; + } + /*$('.x2flow-main').find ('.x2flow-node.X2FlowPushWebContent').each (function () { + that._deleteNode ($(this)); + });*/ + $('#item-box').find ('.X2FlowPushWebContent').hide (); + $('#targeted-content-embed-code-container').hide (); + return true; + }, + save:function() { + // var d1 = new Date(); + + /* update current item's settings since normally + they only get updated when you click a different item */ + this.saveCurrentConfig(); + this.invalidateDropdownCaches (this.trigger.data ('config')); + + var flow = { + version:this.version, + idCounter: this.idCounter, + trigger:this.trigger.data("config"), + items:this.getNodeTree($("#x2flow-main > .x2flow-branch")) + }; + $("#flowDataField").val(JSON.stringify(flow)); + }, + loadFlow:function(flowData) { + if(typeof flowData !== 'object') + flowData = JSON.parse(flowData); + this.idCounter = flowData.idCounter || 0; + // console.debug(flowData); + if(flowData.trigger !== undefined && flowData.trigger.type !== undefined) { + $("#trigger-selector").val(flowData.trigger.type); + flowEditor.trigger + .removeClass() + .data("config", flowData.trigger) + .addClass("x2flow-node x2flow-trigger "+flowData.trigger.type) + .attr("title", $("#trigger-selector").find("option:selected").text()) + .click(); + if (!x2.flow.showLabels) $(flowEditor.trigger).addClass ("no-label"); + $(flowEditor.trigger).children ('.x2flow-icon-label').html ( + //x2.flow.translations[$('#trigger-selector').find ("option:selected").val ()]); + $("#trigger-selector").find ("option:selected").text ()); + } + if(flowData.items.length) { + $("#x2flow-main > .x2flow-branch").empty().append(this.populateBranch(flowData.items)); + } + //this._generateIds (); + x2.flow.hideShowCronUI (); + }, + populateBranch:function(items) { + var branch = $('
'); + for(var i in items) { + var item = items[i]; + if(item.type === "X2FlowSwitch" || item.type === 'X2FlowSplitter') { + var flowSwitch = $("#item-box ." + item.type).clone().data("config", item); + var rightChildName = item.type === 'X2FlowSwitch' ? 'trueBranch' : 'upperBranch'; + var leftChildName = item.type === 'X2FlowSwitch' ? 'falseBranch' : 'lowerBranch'; + + var branches = + flowSwitch.children(".x2flow-branch-wrapper").children(".x2flow-branch"); + if(item[rightChildName].length) { + // create the branches all recursive-like n stuff + $(branches[0]).empty(".x2flow-empty").append( + this.populateBranch(item[rightChildName])); + } + if(item[leftChildName].length) { + $(branches[1]).empty(".x2flow-empty").append( + this.populateBranch(item[leftChildName])); + } + delete item[rightChildName]; + delete item[leftChildName]; // we don't want this stuff going into $.data() - branch = branch.add(flowSwitch); - } else { - var template = $("#all ." + item.type); - if (template.length) { - var flowItem = template.clone().data("config", item); - if (flowItem.attr('style') && - flowItem.attr('style').match(/display[ ]*:[ ]*none;/)) { + branch = branch.add(flowSwitch); + } else { + var template = $("#item-box ."+item.type); + if(template.length) { + var flowItem = template.clone().data("config", item); + if (flowItem.attr ('style') && + flowItem.attr ('style').match (/display[ ]*:[ ]*none;/)) { - flowItem.attr('style', ''); - } - branch = branch.add(flowItem); + flowItem.attr ('style', ''); } + branch = branch.add(flowItem); } } - if (branch.length === 0) { - // if for some reason this branch is empty, generate a placeholder item - return $(document.createElement('div')).addClass("x2flow-node x2flow-empty"); - } - return branch; - }, - - /** - * Dependent dropdown caches should not be saved with the flow data. They should only persist - * over the course of the lifetime of a single page. Clear these out of the config cache before - * saving the flow. - * @param object config - */ - invalidateDropdownCaches: function (config) { - if (typeof config === 'undefined') - return; + } + if(branch.length === 0) { + // if for some reason this branch is empty, generate a placeholder item + return $(document.createElement('div')).addClass("x2flow-node x2flow-empty"); + } + return branch; + }, + /** + * Dependent dropdown caches should not be saved with the flow data. They should only persist + * over the course of the lifetime of a single page. Clear these out of the config cache before + * saving the flow. + * @param object config + */ + invalidateDropdownCaches:function(config) { + if (typeof config === 'undefined') return; - for (var i in config.options) { - delete config.options[i].dropdownCache; - } - }, - - /** - * Gets node tree - * - * @param {type} branch - * @returns {Array|window.flowEditor.getNodeTree.items|Window.flowEditor.getNodeTree.items} - */ - getNodeTree: function (branch) { - var children = $(branch).children(".x2flow-node").not(".x2flow-empty"); - var items = []; - - for (var i = 0; i < children.length; i++) { - var node = $(children[i]); - var nodeData = node.data("config"); - this.invalidateDropdownCaches(nodeData); - if (nodeData === undefined) - continue; - - if (nodeData.type === "X2FlowSwitch" || nodeData.type === 'X2FlowSplitter') { - var branches = node.children(".x2flow-branch-wrapper").children(".x2flow-branch"); - if (branches.length !== 2) - break; // something's seriously jacked up here - - var rightChildName = nodeData.type === 'X2FlowSwitch' ? - 'trueBranch' : 'upperBranch'; - var leftChildName = nodeData.type === 'X2FlowSwitch' ? - 'falseBranch' : 'lowerBranch'; - nodeData[rightChildName] = this.getNodeTree(branches[0]); - nodeData[leftChildName] = this.getNodeTree(branches[1]); - } - items.push(nodeData); + for (var i in config.options) { + delete config.options[i].dropdownCache; + } + }, + getNodeTree:function(branch) { + + var children = $(branch).children(".x2flow-node").not(".x2flow-empty"); + + var items = []; + + for(var i=0; i= 0) { - $("#x2flow-config-box").removeClass("loading"); - } - }, - - /** - * Loads the config panel for a flow item. - * Calls {@link queryItemParams()} to load the allowed params from AJAX/cache, - * then calls {@link createMainConfigForm()} and loops through any saved attributes/conditions - * with {@link createAttrListItem()} and {@link createAttrListItem()} - */ - openItemConfig: function () { - if (this.flowItem) - this.flowItem.destroy(); - var itemType = this.getItemType(this.currentItem); - - // clear out the old config panel - $("#x2flow-main-config, #x2flow-conditions ol, #x2flow-attributes ol, #x2flow-headers ol"). - empty(); - - // if we're just cleaning stuff, we're done - if (this.currentItem.length === 0) - return; + } + return null; + }, + isLoading: function () { + return this.loading < 0; + }, + lockConfig: function () { + this.loading--; + //console.log ('lock' + this.loading); + $("#x2flow-config-box").addClass ("loading"); + }, + unlockConfig: function () { + this.loading++; + //console.log ('unlockConfig' + this.loading); + if (this.loading >=0) { + $("#x2flow-config-box").removeClass ("loading"); + } + }, + /** + * Loads the config panel for a flow item. + * Calls {@link queryItemParams()} to load the allowed params from AJAX/cache, + * then calls {@link createMainConfigForm()} and loops through any saved attributes/conditions + * with {@link createAttrListItem()} and {@link createAttrListItem()} + */ + openItemConfig:function() { + if (this.flowItem) this.flowItem.destroy (); + var itemType = this.getItemType(this.currentItem); - $("#x2flow-add-condition, #x2flow-condition-type, #x2flow-add-attribute, #x2flow-conditions-hr").hide(); + // clear out the old config panel + $("#x2flow-main-config, #x2flow-conditions ol, #x2flow-attributes ol, #x2flow-headers ol"). + empty(); - var isTrigger = - (this.currentItem.hasClass("x2flow-trigger") || - this.currentItem.hasClass("X2FlowSwitch")); + if(this.currentItem.length === 0) // if we're just clearning stuff, we're done + return; - if (itemType === 'X2FlowApiCall') { - $('#x2flow-add-header').show(); - } else { - $('#x2flow-add-header').hide(); - $('.x2flow-api-attributes-section-header').hide(); - } + $("#x2flow-add-condition, #x2flow-condition-type, #x2flow-add-attribute").hide(); - // load the options (via ajax if not cached), then run this function - var that = this; - this.lockConfig(); - this.queryItemParams(itemType, function (params) { - if (params !== false) { - var config = flowEditor.currentItem.data("config"); - if (config === undefined) { - // this item just got added, time to initialize the config - - config = { - id: that._generateId(), - type: itemType, - options: {} - }; - - if (params.modelClass !== undefined) { - // set modelClass of this flow item (if applicable) - config.modelClass = params.modelClass; - } - flowEditor.currentItem.data("config", config); // save it + var isTrigger = + (this.currentItem.hasClass("x2flow-trigger") || + this.currentItem.hasClass("X2FlowSwitch")); + + if (itemType === 'X2FlowApiCall') { + $('#x2flow-add-header').show (); + } else { + $('#x2flow-add-header').hide (); + $('.x2flow-api-attributes-section-header').hide (); + } + + // load the options (via ajax if not cached), then run this function + var that = this; + this.lockConfig (); + this.queryItemParams(itemType, function(params) { + if(params !== false) { + var config = flowEditor.currentItem.data("config"); + if(config === undefined) { + // this item just got added, time to initialize the config + + config = { + id: that._generateId (), + type:itemType, + options:{} + }; + + if(params.modelClass !== undefined) { + // set modelClass of this flow item (if applicable) + config.modelClass = params.modelClass; } + flowEditor.currentItem.data("config", config); // save it + } - // create main form (with previous settings) - var form = flowEditor.createMainConfigForm(params, isTrigger, config.options); - - $("#x2flow-main-config").append( - $('

').text(params.title)).append(form); - if (that.showIdItems.indexOf(config.type) > -1) { - $("#x2flow-main-config h2").after( - $('
', { - 'class': 'x2flow-action-id' - }).append( - $('').text(' (ID: ' + config.id + ') '), - x2.forms.hint(x2.flow.translations['idHint' + config.type]) - ) - ); + // create main form (with previous settings) + var form = flowEditor.createMainConfigForm(params, isTrigger, config.options); + + $("#x2flow-main-config").append ( + $('

').text(params.title)).append (form); + if (that.showIdItems.indexOf(config.type) > -1) { + $("#x2flow-main-config h2").after ( + $('
', { + 'class': 'x2flow-action-id' + }).append ( + $('').text ('ID: '), + $('').text (config.id), + x2.forms.hint (x2.flow.translations['idHint'+config.type]) + ) + ); + } + x2.forms.initializeDefaultFields (); + x2.fieldUtils.updateDependentDropdowns (form); + // $("#x2flow-main-config select").change(); // trigger modelClass event, etc + + // create attribute and/or generic condition lists + flowEditor.loadAttributes(config.attributes); + flowEditor.loadLinkAttributes(config); + if (itemType === 'X2FlowApiCall') { + flowEditor.loadHeaders(config.headerRows); + } + flowEditor.loadConditions(config.conditions); + + // instantiate ckeditor for field with richtext type + if ($('#configFormEditor').length !== 0) { + var ckeditorParams = { + //fullPage: true, + fullPage: false, + height: 130, } - x2.forms.initializeDefaultFields(); - x2.fieldUtils.updateDependentDropdowns(form); - - // create attribute and/or generic condition lists - flowEditor.loadAttributes(config.attributes); - flowEditor.loadLinkAttributes(config); - if (itemType === 'X2FlowApiCall') { - flowEditor.loadHeaders(config.headerRows); + + if (itemType === 'X2FlowPushWebContent' || + itemType === 'TargetedContentRequestTrigger') { + var toolbar = 'MyTargetedContentToolbar'; } - flowEditor.loadConditions(config.conditions); - - // instantiate ckeditor for field with richtext type - if ($('#configFormEditor').length !== 0) { - var ckeditorParams = { - //fullPage: true, - fullPage: false, - height: 130 - }; - - if (itemType === 'X2FlowPushWebContent' || - itemType === 'TargetedContentRequestTrigger') { - var toolbar = 'MyTargetedContentToolbar'; - } - // use insertable attributes associated with model class corresponding with - // trigger, if both the trigger and the model class exist - if ($(trigger).data() && $(trigger).data().config && - $(trigger).data().config.modelClass && !isTrigger) { + // use insertable attributes associated with model class corresponding with + // trigger, if both the trigger and the model class exist + if ($(trigger).data () && $(trigger).data ().config && + $(trigger).data ().config.modelClass && !isTrigger) { - var modelClass = $(trigger).data().config.modelClass; - ckeditorParams['insertableAttributes'] = {}; - ckeditorParams['insertableAttributes'][modelClass + ' Attributes'] = - x2.flow.insertableAttributes[modelClass]; - } - that.lockConfig(); - window.configFormEditor = createCKEditor( - 'configFormEditor', ckeditorParams, function () { - that.unlockConfig(); - }, toolbar); + var modelClass = $(trigger).data ().config.modelClass; + ckeditorParams['insertableAttributes'] = {}; + ckeditorParams['insertableAttributes'][modelClass + ' Attributes'] = + x2.flow.insertableAttributes[modelClass]; } + that.lockConfig (); + window.configFormEditor = createCKEditor ( + 'configFormEditor', ckeditorParams, function () { + that.unlockConfig (); + }, toolbar); } - - // instantiate item-specific manager class - if (x2.X2FlowItem && x2[itemType] && x2[itemType].prototype && - (x2[itemType].prototype instanceof x2.X2FlowItem)) { - that.flowItem = new x2[itemType]({ - form$: $(form) - }); - } - that.unlockConfig(); - }); - }, - - /** - * Initializes main configuration form for provided node under main stage - */ - createMainConfigForm: function (params, isTrigger, prevOptions) { - DEBUG && console.log(params); - - // If prevOptions is undefined, initialize to empty object - if (prevOptions === undefined) - prevOptions = {}; - - // Loads node description - var form = $(document.createElement('div')); - if (params.info) { - form.html($('
', {text: params.info, "class": 'x2-flow-config-info'})); } - // If options available, append an
for UI purposes - if (params.options.length > 0) { - form.append('
'); + // instantiate item-specific manager class + if (x2.X2FlowItem && x2[itemType] && x2[itemType].prototype && + (x2[itemType].prototype instanceof x2.X2FlowItem)) { + that.flowItem = new x2[itemType] ({ + form$: $(form) + }); + } + that.unlockConfig (); + }); + }, + createMainConfigForm:function(params, isTrigger, prevOptions) { + DEBUG && console.log (params); + if(prevOptions === undefined) + prevOptions = {}; + + var form = $(document.createElement('div')); + if(params.info) + form.html($('
', { text: params.info, "class": 'x2-flow-config-info' })); + + // if suboptions are specified, add them to the list of options with start and end + // delimiters. The delimiters will be used to determine when the subform element + // should begin and end, respectively. + /*if (params.suboptions) { + params.options.push ('suboptionsStart'); // end delimiter + params.options = params.options.concat (params.suboptions); + params.options.push ('suboptionsEnd'); // start delimiter + // this will contain the sub options + var subForm = $('
', { + style: 'display: none;' + }); + var formTemp; + }*/ + + for(var i in params.options) { + var optionParams = params.options[i]; + /*if (optionParams === 'suboptionsStart') { + // start the subform, store the parent form in a temporary variable, and replace + // the form with the subform. That way, all suboptions will be added to the subform + // instead of to the form + formTemp = form; + form = subForm; + continue; + } else if (optionParams === 'suboptionsEnd') { + // end the subform and append it to the parent form + form = formTemp; + form.append (subForm); + continue; + }*/ + + /*if($(form).is (subForm) && optionParams.name === 'dependency') { + // this is the subform dependency. add the event handler which hides/shows the + // subform when the element depended upon in the parent form is clicked. + $(document).off ('change', '[name="' + optionParams.dependentOn + '"] input'). + on ('change', '[name="' + optionParams.dependentOn + '"] input', function () { + + if ($(this).is (':checked')) { + $(subForm).slideDown (); + } else { + $(subForm).slideUp (); + } + }); + continue; + } else */if (optionParams.name === 'attributes' || optionParams.name === 'headers') { + $("#x2flow-add-attribute").show(); + continue; } - // Iterate through parameter options - for (var i in params.options) { - var optionParams = params.options[i]; - - // If attribute, add then skip to next option - if (optionParams.name === 'attributes' || optionParams.name === 'headers') { - $("#x2flow-add-attribute").show(); - continue; - } - - // Creates container for field - var row = $(document.createElement('div')).addClass('row').appendTo(form); - - // Creates fieldset container - var fieldset = $(document.createElement('fieldset')).attr("name", optionParams.name). - appendTo(row); - var val = undefined, op = undefined, dropdownCache = undefined; + var row = $(document.createElement('div')).addClass('row').appendTo(form); + var fieldset = $(document.createElement('fieldset')).attr("name", optionParams.name). + appendTo(row); + var val = undefined, + op = undefined, + dropdownCache = undefined; - // If htmlOptions provided, add to flowEditor - if (typeof optionParams.htmlOptions !== 'undefined') { - flowEditor.addHtmlOptions(fieldset, optionParams.htmlOptions); - } + if (typeof optionParams.htmlOptions !== 'undefined') { + flowEditor.addHtmlOptions (fieldset, optionParams.htmlOptions); + } - if (prevOptions[optionParams.name] !== undefined) { - var field = prevOptions[optionParams.name]; - if (typeof field === 'object') { // is it a multipart field or a simple field? - val = field.value; - op = field.operator; - dropdownCache = field.dropdownCache; - } else { - val = field; - } + if(prevOptions[optionParams.name] !== undefined) { + var field = prevOptions[optionParams.name]; + if(typeof field === 'object') { // is it a multipart field or a simple field? + val = field.value; + op = field.operator; + dropdownCache = field.dropdownCache; + } else { + val = field; } + } - // Displays field label - if (optionParams.label !== undefined) { - $(document.createElement('label')).html(optionParams.label).appendTo(fieldset); - } + if(optionParams.label !== undefined) { + $(document.createElement('label')).html (optionParams.label).appendTo(fieldset); + //.attr('for', optionParams.name) + } - if (optionParams.operators !== undefined) { - var operatorCell = - $(document.createElement("div")).addClass("cell x2fields-operator"). - appendTo(fieldset); - var dropdown = x2.fieldUtils.buildOperatorDropdown(optionParams.operators, op); - $(operatorCell).append(dropdown); - } + if(optionParams.operators !== undefined) { + var operatorCell = + $(document.createElement("div")).addClass("cell x2fields-operator"). + appendTo(fieldset); + var dropdown = x2.fieldUtils.buildOperatorDropdown(optionParams.operators, op); + $(operatorCell).append (dropdown); + } - var fieldOptions = $.extend({}, optionParams); + var fieldOptions = $.extend({}, optionParams); - if (val !== undefined) { - // if there is saved data, insert field values into the fieldOptions object - fieldOptions.value = val; //prevOptions[fieldOptions.name]; - } - if (dropdownCache) { - fieldOptions.dropdownCache = dropdownCache; - } + if(val !== undefined) { + // if there is saved data, insert field values into the fieldOptions object + fieldOptions.value = val; //prevOptions[fieldOptions.name]; + } + if (dropdownCache) { + fieldOptions.dropdownCache = dropdownCache; + } - var valueCell = $(document.createElement("div")).addClass("cell x2fields-value"). - appendTo(fieldset); + var valueCell = $(document.createElement("div")).addClass("cell x2fields-value"). + appendTo(fieldset); - fieldOptions.name = "value"; - var input = x2.fieldUtils.createInput(fieldOptions); + fieldOptions.name = "value"; + //var input = x2.fieldUtils.createInput(fieldOptions).appendTo(valueCell); + var input = x2.fieldUtils.createInput(fieldOptions); + $(valueCell).append (input); - + // id will be used to instantiate ckeditor after form is appended to DOM node + if (fieldOptions.type === 'richtext') { + $(input).attr ('id', 'configFormEditor'); + $(input).attr ('class', 'rich-text'); + $(valueCell).addClass ('editor-container'); + } - $(valueCell).append(input); + if(optionParams.name === 'modelClass') { + var config = this.currentItem.data("config"); + if(typeof config === "object") + config.modelClass = $(input).val(); + } - // id will be used to instantiate ckeditor after form is appended to DOM node - if (fieldOptions.type === 'richtext') { - $(input).attr('id', 'configFormEditor'); - $(input).attr('class', 'rich-text'); - $(valueCell).addClass('editor-container'); - } + // instantiate qtips if they're present in the config menu + $(fieldset).find ('.x2-hint').each (function () { + $(this).qtip (); + }); + } - if (optionParams.name === 'modelClass') { - var config = this.currentItem.data("config"); - if (typeof config === "object") - config.modelClass = $(input).val(); - } + if(isTrigger) { + $("#x2flow-add-condition, #x2flow-condition-type").show(); - // instantiate qtips if they're present in the config menu - $(fieldset).find('.x2-hint').each(function () { - $(this).qtip(); - }); + if(flowEditor.getModelClass() === null) { + $("#x2flow-condition-type option:first").attr("disabled", "disabled"); + $("#x2flow-condition-type").val($("#x2flow-condition-type option:nth-child(2)"). + attr("value")); + } else { + $("#x2flow-condition-type option:first").removeAttr("disabled"); + $("#x2flow-condition-type").val($("#x2flow-condition-type option:first"). + attr("value")); } + } - // If displayed node is trigger, show add-condition options - if (isTrigger) { - $("#x2flow-add-condition, #x2flow-condition-type, #x2flow-conditions-hr").show(); + if (params['class'] === 'X2FlowRecordEmail' || params['class'] === 'X2FlowEmail') { + this.setUpEmailForm (form); + } - if (flowEditor.getModelClass() === null) { - $("#x2flow-condition-type option:first").attr("disabled", "disabled"); - $("#x2flow-condition-type").val($("#x2flow-condition-type option:nth-child(2)"). - attr("value")); - } else { - $("#x2flow-condition-type option:first").removeAttr("disabled"); - $("#x2flow-condition-type").val($("#x2flow-condition-type option:first"). - attr("value")); - } + return form; + }, + /* + Add html options as to the fieldset in the main config menu + */ + addHtmlOptions: function (fieldset, htmlOptions) { + var option; + for (var i in htmlOptions) { + option = htmlOptions[i]; + switch (i) { + case 'class': + $(fieldset).addClass (option); + break; + default: + $(fieldset).attr (i, option); + break; } + } + }, + /* + Sets up email template feature + */ + setUpEmailForm:function(form) { + + function templateSwitchConfirm() { + var proceed = true; + var noChange = !window.configFormEditor.checkDirty(); + if(!noChange) + proceed = window.confirm(x2.flow.translations['templateChangeConfirm']); + return proceed; + } - // Sets up email form if node is X2FlowRecordEmail or X2FlowEmail - if (params['class'] === 'X2FlowRecordEmail' || params['class'] === 'X2FlowEmail') { - this.setUpEmailForm(form); - } + DEBUG && console.log ('setUpEmailForm'); + DEBUG && console.log ($(form)); + DEBUG && console.log ($(form).find ('[name="template"]')); - return form; - }, - - /** - * Adds html options as to the fieldset in the main config menu - */ - addHtmlOptions: function (fieldset, htmlOptions) { - var option; - for (var i in htmlOptions) { - option = htmlOptions[i]; - switch (i) { - case 'class': - $(fieldset).addClass(option); - break; - default: - $(fieldset).attr(i, option); - break; - } - } - }, - - /** - * Sets up email template feature - */ - setUpEmailForm: function (form) { - - function templateSwitchConfirm() { - var proceed = true; - var noChange = !window.configFormEditor.checkDirty(); - if (!noChange) - proceed = window.confirm(x2.flow.translations['templateChangeConfirm']); - return proceed; - } + $(form).find ('[name="template"]').find ('select').change(function() { + DEBUG && console.log ('select'); - DEBUG && console.log('setUpEmailForm'); - DEBUG && console.log($(form)); - DEBUG && console.log($(form).find('[name="template"]')); - - $(form).find('[name="template"]').find('select').change(function () { - DEBUG && console.log('select'); - - var template = $(this).val(); - DEBUG && console.log(template); - if (template !== "" && templateSwitchConfirm()) { - - $.ajax({ - url: yii.baseUrl + "/index.php/docs/fullView/" + template + "?json=1", - type: "GET", - dataType: "json" - }).done(function (data) { - window.configFormEditor.setData(data.body); - $(form).find('[name="subject"]').find('input').val(data.subject); - if (typeof data.to !== 'undefined' && data.to !== '') - $(form).find('[name="to"]').find('input').val(data.to); - window.configFormEditor.document.on("keyup", function () { - $(form).find('[name="template"]').find('select').val("0"); - }); + var template = $(this).val(); + DEBUG && console.log (template); + if(template !== "" && templateSwitchConfirm ()) { + + $.ajax({ + url:yii.baseUrl+"/index.php/docs/fullView/"+template+"?json=1", + type:"GET", + dataType:"json" + }).done(function(data) { + window.configFormEditor.setData(data.body); + $(form).find ('[name="subject"]').find ('input').val (data.subject); + if (typeof data.to !== 'undefined' && data.to !== '') + $(form).find ('[name="to"]').find ('input').val(data.to); + window.configFormEditor.document.on("keyup", function(){ + $(form).find ('[name="template"]').find ('select').val ("0"); }); - } - }); - }, - - /** - * Updates lists of email opened options on condition attributes - */ - updateEmailOpenConditions: function () { - var that = this; - var emailIds = []; - $("#x2flow-main .x2flow-node").each(function (i, item) { - var itemConfig = $(item).data("config"); - if (typeof itemConfig === 'undefined') - return; - if (itemConfig['type'] === 'X2FlowEmail' || - itemConfig['type'] === 'X2FlowRecordEmail') { - emailIds.push(itemConfig['id']); - } - }); - $(".email-open-condition select").empty(); - for (var i = 0; i < emailIds.length; i++) { - $(".email-open-condition select").append($("').attr('value', 'original').attr('data-value', triggerConfig.modelClass).text('Original Record')); - for (var i in attributeList) { - if (typeof attributeList[i]['name'] !== 'undefined' - && typeof attributeList[i]['label'] !== 'undefined') { - if (i === 0 && config.type === 'X2FlowRecordChange') { + }); + $(".email-open-condition select").empty(); + for(var i = 0; i < emailIds.length; i++){ + $(".email-open-condition select").append($("').attr('value','original').attr('data-value', triggerConfig.modelClass).text('Original Record')); + for(var i in attributeList){ + if(typeof attributeList[i]['name'] !== 'undefined' + && typeof attributeList[i]['label'] !== 'undefined'){ + if(i == 0 && config.type == 'X2FlowRecordChange'){ + config.linkType = attributeList[i]['linkType']; + } + var option = $('').attr('value',attributeList[i]['name']).attr('data-value', attributeList[i]['linkType']).text(attributeList[i]['label']); + if(selectedField === attributeList[i]['name']){ + if(config.type == 'X2FlowRecordChange'){ config.linkType = attributeList[i]['linkType']; } - var option = $('').attr('value', attributeList[i]['name']).attr('data-value', attributeList[i]['linkType']).text(attributeList[i]['label']); - if (selectedField === attributeList[i]['name']) { - if (config.type === 'X2FlowRecordChange') { - config.linkType = attributeList[i]['linkType']; - } - option.attr('selected', 'selected'); - } - $('fieldset[name="linkField"] select').append(option); + option.attr('selected','selected'); } + $('fieldset[name="linkField"] select').append(option); } - }); - }, - - /** - * Creates an attribute entry in #x2flow-attributes for each attribute in the list provided - */ - loadAttributes: function (attributes) { - var that = this; - // console.debug(attributes); - if (attributes === undefined) - return; - - var modelClass = this.getModelClass(); - - // loop through any saved attributes - this.lockConfig(); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function (attributeList) { - that.unlockConfig(); - - for (var i in attributes) { - var attr = attributes[i]; - - if (modelClass === "API_params") { - $('#x2flow-attributes .x2flow-api-attributes-section-header').show(); - flowEditor.createApiParam(attr.name, attr.value) - .appendTo("#x2flow-attributes ol"); - /*flowEditor.createApiParam(attr.name, attr.value, false) - .appendTo("#x2flow-headers ol");*/ - } else if (modelClass !== null) { - // there is no operator, tell createAttrListItem() not to add a selector - if (attr.operator === undefined) - // (this must be a flow action, where the attributes are just being set, - // not tested) - attr.operator = false; - x2.fieldUtils.createAttrListItem( - modelClass, attributeList, attr.name, attr.operator, attr.value) - .data("type", "attribute") - .appendTo("#x2flow-attributes ol"); - } + } + }); + }, + + /** + * Creates an attribute entry in #x2flow-attributes for each attribute in the list provided + */ + loadAttributes:function(attributes) { + var that = this; + // console.debug(attributes); + if(attributes === undefined) + return; + + var modelClass = this.getModelClass(); + + // loop through any saved attributes + this.lockConfig (); + x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributeList) { + that.unlockConfig (); + + for(var i in attributes) { + var attr = attributes[i]; + + if(modelClass === "API_params") { + $('#x2flow-attributes .x2flow-api-attributes-section-header').show (); + flowEditor.createApiParam(attr.name, attr.value) + .appendTo("#x2flow-attributes ol"); + /*flowEditor.createApiParam(attr.name, attr.value, false) + .appendTo("#x2flow-headers ol");*/ + } else if(modelClass !== null) { + // there is no operator, tell createAttrListItem() not to add a selector + if(attr.operator === undefined) + // (this must be a flow action, where the attributes are just being set, + // not tested) + attr.operator = false; + x2.fieldUtils.createAttrListItem( + modelClass, attributeList, attr.name, attr.operator, attr.value) + .data("type", "attribute") + .appendTo("#x2flow-attributes ol"); } - }); - }, - - loadHeaders: function (headers) { - // loop through any saved headers - for (var i in headers) { - $('#x2flow-headers .x2flow-api-attributes-section-header').show(); - var header = headers[i]; - - flowEditor.createApiParam(header.name, header.value, false) - .appendTo("#x2flow-headers ol"); } - }, + }); + }, + loadHeaders:function(headers) { + + // loop through any saved headers + for(var i in headers) { + $('#x2flow-headers .x2flow-api-attributes-section-header').show (); + var header = headers[i]; - loadConditions: function (conditions) { - var that = this; - // console.debug(conditions); - if (conditions === undefined) + flowEditor.createApiParam(header.name, header.value, false) + .appendTo("#x2flow-headers ol"); + } + }, + loadConditions:function(conditions) { + var that = this; + // console.debug(conditions); + if(conditions === undefined) + return; + $.each(conditions, function(i, condition) { + if(condition.type === undefined) return; - $.each(conditions, function (i, condition) { - if (condition.type === undefined) - return; - if (condition.type === "attribute") { - var modelClass = that.getModelClass(); - if (modelClass !== null) { - that.lockConfig(); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function (attributeList) { - that.unlockConfig(); - // console.debug(condition); - x2.fieldUtils.createAttrListItem( - modelClass, attributeList, condition.name, condition.operator, - condition.value) - .data("type", "attribute") - .appendTo("#x2flow-conditions ol"); - }); - } - } else { - if (condition.type !== 'workflow_status') { - that.lockConfig(); - if (condition.type === 'email_open') { - that.queryConditionParams(condition.type, function (data) { - that.unlockConfig(); - data.operator = condition.operator; - data.value = condition.value; - flowEditor.createItemCondition(data) - .data("type", condition.type) - .addClass('email-open-condition') - .attr('data-value', data.value) - .appendTo("#x2flow-conditions ol"); - that.updateEmailOpenConditions(); - }); - } else { - that.queryConditionParams(condition.type, function (data) { - that.unlockConfig(); - data.operator = condition.operator; - data.value = condition.value; - flowEditor.createItemCondition(data) - .data("type", condition.type) - .appendTo("#x2flow-conditions ol"); - }); - } - } else { - flowEditor.createWorkflowStatusCondition(condition) + if(condition.type === "attribute") { + var modelClass = that.getModelClass(); + if(modelClass !== null) { + that.lockConfig (); + x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributeList) { + that.unlockConfig (); + // console.debug(condition); + x2.fieldUtils.createAttrListItem( + modelClass, attributeList, condition.name, condition.operator, + condition.value) + .data("type", "attribute") + .appendTo("#x2flow-conditions ol"); + }); + } + } else { + if (condition.type !== 'workflow_status') { + that.lockConfig (); + if (condition.type === 'email_open') { + that.queryConditionParams(condition.type, function(data) { + that.unlockConfig (); + data.operator = condition.operator; + data.value = condition.value; + flowEditor.createItemCondition(data) .data("type", condition.type) + .addClass('email-open-condition') + .attr('data-value', data.value) .appendTo("#x2flow-conditions ol"); + that.updateEmailOpenConditions(); + }); + } else { + that.queryConditionParams(condition.type, function(data) { + that.unlockConfig (); + data.operator = condition.operator; + data.value = condition.value; + flowEditor.createItemCondition(data) + .data("type", condition.type) + .appendTo("#x2flow-conditions ol"); + }); } + } else { + flowEditor.createWorkflowStatusCondition (condition) + .data("type", condition.type) + .appendTo("#x2flow-conditions ol"); } - }); - }, - /** - * Creates a workflow status condition - * @return object jQuery
  • element containing the workflow status condition fieldset - */ - createWorkflowStatusCondition: function createWorkflowStatusCondition(condition) { - var condition = typeof condition === 'undefined' ? {} : condition; - - var li = x2.fieldUtils.templates.workflowStatusConditionForm.clone(); - var stageDropdown = $(li).find('[name="stageNumber"]'); - var workflowDropdown = $(li).find('[name="workflowId"]'); - var stateDropdown = $(li).find('[name="stageState"]'); - - // check if saved workflow id is different that the default workflow id - var fetchNewStageDropdown = false; - if ($(workflowDropdown).val() !== condition.workflowId) { - // stages for the workflow with saved workflow id must be fetched - fetchNewStageDropdown = true; - } - - if (typeof createWorkflowStatusCondition.cache === 'undefined') { - // used to cache the requested stage name options - createWorkflowStatusCondition.cache = {}; - - // add default option to the cache - var defaultOptions = []; - $(stageDropdown).find('option').each(function () { - defaultOptions.push([$(this).val(), $(this).html()]); - }); - createWorkflowStatusCondition.cache[$(workflowDropdown).val()] = defaultOptions; } + }); + }, + /** + * Creates a workflow status condition + * @return object jQuery
  • element containing the workflow status condition fieldset + */ + createWorkflowStatusCondition: function createWorkflowStatusCondition (condition) { + var condition = typeof condition === 'undefined' ? {} : condition; + + var li = x2.fieldUtils.templates.workflowStatusConditionForm.clone(); + var stageDropdown = $(li).find ('[name="stageNumber"]'); + var workflowDropdown = $(li).find ('[name="workflowId"]'); + var stateDropdown = $(li).find ('[name="stageState"]'); + + // check if saved workflow id is different that the default workflow id + var fetchNewStageDropdown = false; + if ($(workflowDropdown).val () !== condition.workflowId) { + // stages for the workflow with saved workflow id must be fetched + fetchNewStageDropdown = true; + } - // set saved values - $(stageDropdown).val(condition.stageNumber); - $(workflowDropdown).val(condition.workflowId); - $(stateDropdown).val(condition.stageState); + if (typeof createWorkflowStatusCondition.cache === 'undefined') { + // used to cache the requested stage name options + createWorkflowStatusCondition.cache = {}; - // replaces old stage name dropdown with a new one using options either from the cache - // or from an AJAX response - function buildNewDropdown(data) { - var $newDropdown = x2.fieldUtils.buildDropdown(data, { - name: $(stageDropdown).attr('name') - }); - $(stageDropdown).replaceWith($newDropdown); - stageDropdown = $newDropdown; - stageDropdown.val(condition.stageNumber); - } + // add default option to the cache + var defaultOptions = []; + $(stageDropdown).find ('option').each (function () { + defaultOptions.push ([$(this).val (), $(this).html ()]); + }); + createWorkflowStatusCondition.cache[$(workflowDropdown).val ()] = defaultOptions; + } - // fetch new stage select options when workflow id select changes - $(workflowDropdown).unbind('change'). - bind('change', function fetchStageOptions() { + // set saved values + $(stageDropdown).val (condition.stageNumber); + $(workflowDropdown).val (condition.workflowId); + $(stateDropdown).val (condition.stageState); - var workflowId = $(this).val(); - // check the cache first - if (typeof createWorkflowStatusCondition.cache[workflowId] !== 'undefined') { - var data = createWorkflowStatusCondition.cache[workflowId]; - buildNewDropdown(data); - return; - } + // replaces old stage name dropdown with a new one using options either from the cache + // or from an AJAX response + function buildNewDropdown (data) { + var $newDropdown = x2.fieldUtils.buildDropdown (data, { + name: $(stageDropdown).attr ('name') + }); + $(stageDropdown).replaceWith ($newDropdown); + stageDropdown = $newDropdown; + stageDropdown.val (condition.stageNumber); + } - // cache miss, request the options - x2.forms.inputLoading(stageDropdown); - $.ajax({ - url: yii.scriptUrl + '/workflow/workflow/getStageNames', - type: 'get', - dataType: 'json', - data: { - workflowId: workflowId, - optional: false - }, - success: function (data) { - x2.forms.inputLoadingStop(stageDropdown); - buildNewDropdown(data); - - // cache the results - createWorkflowStatusCondition.cache[workflowId] = data; - } - }); - }); + // fetch new stage select options when workflow id select changes + $(workflowDropdown).unbind ('change'). + bind ('change', function fetchStageOptions () { - if (fetchNewStageDropdown) { - $(workflowDropdown).change(); - } - return li; - }, - createItemCondition: function (condition) { - // clone template condition form - var li = x2.fieldUtils.templates.conditionForm.clone(); - var fieldset = li.find('fieldset').first(); - $(document.createElement("div")).addClass("cell inline-label").text(condition.label). - appendTo(fieldset); + var workflowId = $(this).val (); + // check the cache first + if (typeof createWorkflowStatusCondition.cache[workflowId] !== 'undefined') { + var data = createWorkflowStatusCondition.cache[workflowId]; + buildNewDropdown (data); + return; + } - if (condition.operators) { - x2.fieldUtils.createOperatorCell( - condition.operators, condition.operator).appendTo(fieldset); - li.on("change", ".x2fields-operator select", function () { - x2.fieldUtils.updateValueCell(this); + // cache miss, request the options + x2.forms.inputLoading (stageDropdown); + $.ajax ({ + url: yii.scriptUrl + '/workflow/workflow/getStageNames', + type: 'get', + dataType: 'json', + data: { + workflowId: workflowId, + optional: false + }, + success: function (data) { + x2.forms.inputLoadingStop (stageDropdown); + buildNewDropdown (data); + + // cache the results + createWorkflowStatusCondition.cache[workflowId] = data; + } }); - if (condition.multiple) - li.data("multiple", true); - } - x2.fieldUtils.createValueCell(condition).appendTo(fieldset); - - return li; - }, - createApiParam: function (name, val, params) { - var params = typeof params === 'undefined' ? true : params; - // clone template condition form - var li = x2.fieldUtils.templates.conditionForm.clone(); - if (params) { - var fieldset = li.find('fieldset').replaceWith($("#condition-templates .API_params"). - clone()); - } else { - var fieldset = li.find('fieldset').replaceWith($("#condition-templates .APIHeaders"). - clone()); - } - li.find(".x2fields-attribute input").val(name); - li.find(".x2fields-value input").val(val); - - return li; - } - }; + }); - /** - * Shows trace box - */ - $("#show-trace-button").on("click", function () { - if ($("#x2flow-trace-box").is(":visible")) { - $("#x2flow-trace-box").slideUp(); - } else { - $("#x2flow-trace-box").slideDown(); - $("html,body").animate({ - scrollTop: ($("#x2flow-trace-box").offset().top - 100) - }, 300); + if (fetchNewStageDropdown) { + $(workflowDropdown).change (); } - }); - - /** - * Toggles flow labels - */ - $("#x2flow-show-labels-checkbox").on("change", function () { - if (this.checked) { // show labels - $(".x2flow-main").find(".x2flow-icon-label").show(); - $(".x2flow-node.x2flow-action").each(function () { - if ($(this).children().first().hasClass("x2flow-icon-label")) { - $(this).children().first().attr("style", ""); - } + return li; + }, + createItemCondition:function(condition) { + // console.debug(conditionParams); + //if(condition.value === undefined) // default to the first attribute + // var val = ''; + + // clone template condition form + var li = x2.fieldUtils.templates.conditionForm.clone(); + var fieldset = li.find('fieldset').first(); + $(document.createElement("div")).addClass("cell inline-label").text(condition.label). + appendTo(fieldset); + + if(condition.operators) { + x2.fieldUtils.createOperatorCell( + condition.operators, condition.operator).appendTo(fieldset); + li.on("change", ".x2fields-operator select", function() { + x2.fieldUtils.updateValueCell(this); }); - $(".x2flow-node").each(function () { - if ($(this).children().first().hasClass("x2flow-icon-label")) { - $(this).removeClass("no-label"); // used to position arrows - } - }); - $("#trigger").find(".x2flow-icon-label").attr("style", ""); - $("#trigger").removeClass("no-label"); - auxlib.saveMiscLayoutSetting("x2flowShowLabels", 1); - x2.flow.showLabels = true; - } else { // hide labels - $(".x2flow-main").find(".x2flow-icon-label").hide(); - $(".x2flow-node.x2flow-action").each(function () { - if ($(this).children().first().hasClass("x2flow-icon-label")) { - $(this).children().first().attr("style", "display: none;"); - } - }); - $(".x2flow-node").each(function () { - if ($(this).children().first().hasClass("x2flow-icon-label")) { - $(this).addClass("no-label"); // used to position arrows - } - }); - $("#trigger").find(".x2flow-icon-label").attr("style", "display: none;"); - $("#trigger").addClass("no-label"); - auxlib.saveMiscLayoutSetting("x2flowShowLabels", 0); - x2.flow.showLabels = false; + if(condition.multiple) + li.data("multiple", true); } - }); + x2.fieldUtils.createValueCell(condition).appendTo(fieldset); + + return li; + }, + createApiParam:function(name, val, params) { + var params = typeof params === 'undefined' ? true : params; + // clone template condition form + var li = x2.fieldUtils.templates.conditionForm.clone(); + if (params) { + var fieldset = li.find('fieldset').replaceWith($("#condition-templates .API_params"). + clone()); + } else { + var fieldset = li.find('fieldset').replaceWith($("#condition-templates .APIHeaders"). + clone()); + } + li.find(".x2fields-attribute input").val(name); + li.find(".x2fields-value input").val(val); - + return li; + } +}; - flowEditor.init(); +flowEditor.init(); - if (x2.flowData !== null) - flowEditor.loadFlow(x2.flowData); +if(x2.flowData !== null) + flowEditor.loadFlow(x2.flowData); }); diff --git a/x2engine/js/X2Flow/x2flowEditor.js b/x2engine/js/X2Flow/x2flowEditor.js deleted file mode 100644 index b2fdf9006..000000000 --- a/x2engine/js/X2Flow/x2flowEditor.js +++ /dev/null @@ -1,1795 +0,0 @@ -/*********************************************************************************** - * X2CRM is a customer relationship management program developed by - * X2Engine, Inc. Copyright (C) 2011-2016 X2Engine Inc. - * - * This program is free software; you can redistribute it and/or modify it under - * the terms of the GNU Affero General Public License version 3 as published by the - * Free Software Foundation with the addition of the following permission added - * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK - * IN WHICH THE COPYRIGHT IS OWNED BY X2ENGINE, X2ENGINE DISCLAIMS THE WARRANTY - * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS. - * - * This program is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS - * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more - * details. - * - * You should have received a copy of the GNU Affero General Public License along with - * this program; if not, see http://www.gnu.org/licenses or write to the Free - * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301 USA. - * - * You can contact X2Engine, Inc. P.O. Box 66752, Scotts Valley, - * California 95067, USA. on our website at www.x2crm.com, or at our - * email address: contact@x2engine.com. - * - * The interactive user interfaces in modified source and object code versions - * of this program must display Appropriate Legal Notices, as required under - * Section 5 of the GNU Affero General Public License version 3. - * - * In accordance with Section 7(b) of the GNU Affero General Public License version 3, - * these Appropriate Legal Notices must retain the display of the "Powered by - * X2Engine" logo. If the display of the logo is not reasonably feasible for - * technical reasons, the Appropriate Legal Notices must display the words - * "Powered by X2Engine". - **********************************************************************************/ - - - -$(function() { - -var DEBUG = false && x2.DEBUG; - -x2.flow.hideShowCronUI = function () { - DEBUG && console.log ('hideShowCronUI'); - var itemsWhichRequireCron = x2.flow.requiresCron; - var requiresCron = false; - for (var i in itemsWhichRequireCron) { - if ($('.x2flow-main').find ($('.' + itemsWhichRequireCron[i])).length > 0) { - requiresCron = true; - break; - } - } - if (requiresCron) { - $('#test-cron-button').show (); - $('#view-log-button').show (); - } else { - $('#test-cron-button').hide (); - $('#view-log-button').hide (); - } -}; - -$.widget("x2.flowDraggable", $.ui.mouse, { - - options:{ - clone:true, - renderDzBoxes:false, - }, - - nodeTree:[], - nextId:0, - - // parentBranch:$(), - // startingIndex:0, - - node:$(), - startingBranch:$(), - startingIndex:0, - - nodeParent:null, // element previously containing the node - futureTarget:null, - nodeBefore:null, // element (if any) right before the node's last good position - nodeAfter:null, // element (if any) right after the node's last good position - - helper:null, // a clone of the original to be dragged around - // placeholder:null, // the original (or a clone) to preview where the node will be dropped - - mouseOffset:{}, // where the user clicked within the draggable - helperHalfHeight:0, - helperHalfWidth:0, - - deleteButton:null, - deleteTarget:$(), - - dropTargets:null, - dropZones:[], // array of top-left and bottom-right coordinates for droppable zones - - hoverTarget:null, // current drop target being hovered over - - moveTimer:null, // timeout to move things after a short delay - - _init:function() { - - var self = this; - - this.deleteButton = $("#item-delete").clone().show().bind('click', function() { - if (window.flowEditor.isLoading ()) { - return false; - } - if(flowEditor.currentItem.is(self.deleteTarget)) { - flowEditor.currentItem = $(); - flowEditor.openItemConfig(); - } - var oldParent = self.deleteTarget.parent(); - $(this).detach(); - self.deleteTarget.remove(); - self._toggleEmptyBox(oldParent); - x2.flow.hideShowCronUI (); - }); - - - if(!this.options.clone) { - $(this.element).on('mouseenter', '.x2flow-action, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', function() { - var offset = $(this).offset(); - self.deleteButton.appendTo(this).offset({top:offset.top-4, left:offset.left+36}); - self.deleteTarget = $(this).closest('.x2flow-node'); - }).on('mouseleave', '.x2flow-action, .x2flow-trigger, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', - function() { - - self.deleteTarget = $(); - self.deleteButton.detach(); - }); - } - this._mouseInit(); // setup mouse handling - - // $("#save-button").unbind().click(function() { - // console.debug(self._getNodeTree($("#x2flow-main > .x2flow-branch"))); - // }); - // $(".x2flow-node").disableSelection() - }, - - _destroy:function() { - this._mouseDestroy(); - }, - /** - * decide whether to drag this thing - */ - _mouseCapture:function(e) { - return $(e.target).is( - '#item-box *, .X2FlowSwitch .icon, .X2FlowSplitter .icon, ' + - '.X2FlowSplitter .icon-inner, ' + - '.x2flow-node:not(.x2flow-trigger, .X2FlowSwitch, .X2FlowSplitter, .x2flow-empty)'); - //parents(".x2flow-node").length; - }, - /** - * start dragging; find drop targets, create helper element, etc - */ - _mouseStart:function(e) { - var self = this; - $(".x2flow-node").disableSelection(); - - this.node = $(e.target).closest(".x2flow-node"); - - this.deleteTarget = $(); - this.deleteButton.detach(); - - // this.nodeParent = this.node.parent(); - this.futureTarget = this.node.parent(); - this.nodeBefore = this.node.prev(); - if(this.options.clone || this.nodeBefore.length === 0) - this.nodeBefore = null; - - var offset = this.node.offset(); - this.mouseOffset = { - x: e.pageX - offset.left, - y: e.pageY - offset.top - }; - if(this.node.parent().is("#item-box")) { - this.mouseOffset.x = offset.left + 24; - this.mouseOffset.y = 24; - } - - // if(node.children(".icon").length) - // this.mouseOffset.x = this.mouseOffset.x - 14; - - // this.placeholder = node.clone() - // .disableSelection() - - // create a copy of the node to be dragged around - this.helper = this.node.clone().disableSelection(); - - if(this.options.clone) { - this.node = this.node.clone().appendTo("#item-box"); // copy the original - } - - this.node.addClass("x2flow-placeholder"); - - this.dropTargets = $(); - - // we can drag it into other branches, or the trash, but not into a branch inside itself - $("#x2flow-main .x2flow-branch, .x2flow-trash").not(this.node.find(".x2flow-branch")). - each(function(i, elem) { - - if(!(self.node.hasClass("X2FlowSwitch") || - self.node.hasClass("X2FlowSplitter")) || - ($(elem).children(".X2FlowSwitch").not(".x2flow-placeholder").length === 0 && - $(elem).children(".X2FlowSplitter").not(".x2flow-placeholder").length === 0)) { - // we can't drag a switch into a branch with an existing switch - - self.dropTargets = self.dropTargets.add(elem); - } - }); - this.dropTargets.addClass("x2flow-active"); - - this._calculateDropZones(); - - this.helper - // .width(this.node.width()) // lock width to fix the stupid margin issue - .addClass("x2flow-helper") - .disableSelection() - .css({position:"absolute", "z-index":"1"}) - .offset(offset) - .appendTo("body"); - }, - /** - * Called on mousemove event. Moves helper element, moves placeholder sometimes - */ - _mouseDrag:function(e) { - var self = this; - - var offset = { - left: e.pageX-this.mouseOffset.x, - top: e.pageY-this.mouseOffset.y - }; - - this.helper.offset(offset); // make the helper follow the mouse - - // update this.hoverTarget, and this.nodeBefore if possible - this.hoverTarget = this._getHoverTarget({x:e.pageX, y:e.pageY}); - if(this.hoverTarget !== null) { - this.nodeBefore = this._getNodeBefore(e.pageX); - this.nodeAfter = this._getNodeAfter (e.pageX); - } - - // nodes with return val must be last in branch - if (this.nodeBefore && this.nodeBefore.hasClass ('X2FlowPushWebContent')) { - return; - } else if (this.nodeAfter && this.node.hasClass ('X2FlowPushWebContent')) { - return; - } - - if(this.hoverTarget === null) { - if(this.options.clone) { // if this is a new item being dragged in, - // move the placeholder back to the menu box; - this.nodeBefore = this.futureTarget.children().last(); - if(this.futureTarget.attr("id") !== "item-box") { - this.futureTarget = $("#item-box"); - clearTimeout(this.moveTimer); - this.moveTimer = setTimeout(function(){ - self._moveNode(); self._calculateDropZones(); - }, 500); - } - } else { - clearTimeout(this.moveTimer); - } - } else if(this.nodeBefore !== null) { // we're good to go, move the node - if (x2.DEBUG && x2.flow.freezeHoverTarget) return; - this.hoverTarget.addClass("x2flow-hover"); - - if(!this.hoverTarget.is(this.node.parent())) { - // trying to drag to a different branch - - if(!this.hoverTarget.is(this.futureTarget)) { - this.futureTarget = this.hoverTarget; - clearTimeout(this.moveTimer); - this.moveTimer = setTimeout(function(){ - self._moveNode();self._calculateDropZones(); - }, 100); - } - } else if(!this.nodeBefore.is(this.node.prev(".x2flow-node"))) { - this.node.insertAfter(this.nodeBefore); // dragging within branch - } - } - - }, - - // stop dragging, either destroy the helper or drop it into whatever thing - _mouseStop:function(e) { - if (x2.DEBUG && x2.flow.dragDisable) return; - clearTimeout(this.moveTimer); - - this.helper.remove(); - this.node.removeClass("x2flow-placeholder"); - this.dropTargets.removeClass("x2flow-active x2flow-hover"); - this.futureTarget = $(); - - if(this.node.parent().attr("id") === "item-box") { - this.node.remove(); - } else { - if(this.hoverTarget !== null && this.hoverTarget.hasClass("x2flow-trash")) { - var oldParent = this.node.parent(); - this.node.remove(); - this._toggleEmptyBox(oldParent); - flowEditor.currentItem = $(); - flowEditor.openItemConfig(); - } - if(this.options.clone) { // node has been added, so open up the config - if(this.node.hasClass("X2FlowSwitch") || this.node.hasClass ('X2FlowSplitter')) { - // switches can only be clicked on their icons - this.node.children(".icon").click(); - } else { - this.node.click(); - } - - } - x2.flow.hideShowCronUI (); - } - - - }, - _moveNode:function() { - DEBUG && console.log ('this.nodeBefore = '); - DEBUG && console.log (this.nodeBefore); - - // nodes with return val must be last in branch - if (this.nodeBefore.hasClass ('X2FlowPushWebContent') || - this.node.hasClass ('X2FlowPushWebContent') && - this.nodeBefore.next ('.x2flow-node').not ('.x2flow-empty').length) { - return; - } - - var oldParent = this.node.parent(); - this.node.insertAfter(this.nodeBefore); - this._toggleEmptyBox(oldParent); - this._toggleEmptyBox(this.futureTarget); - // this.nodeParent = this.hoverTarget; - this._calculateDropZones(); - }, - _toggleEmptyBox:function(elem) { - if(!elem.hasClass("x2flow-trash")) { - if(elem.children(".x2flow-node:not(.x2flow-empty)").length) { - elem.children(".x2flow-empty").remove(); - } else if(elem.children(".x2flow-empty").length == 0) { - $(document.createElement("div")).addClass("x2flow-node x2flow-empty"). - appendTo(elem); - } - } - }, - // get the bounding coordinates for each droppable zone - _calculateDropZones:function() { - this.dropZones = []; - /* loop through all the droppable branches in reverse order, so deepest in the tree comes - first */ - for(var i = this.dropTargets.length-1;i>=0;i--) { - var target = $(this.dropTargets[i]), - offset = target.offset(), - h = target.height(), - w = target.width(); - - if(target.hasClass("x2flow-branch")) { - this.dropZones.push({ - x0: offset.left, - y0: offset.top + (h/2) - 25, - x1: offset.left + w + 50, // give it some extra space at the bottom - y1: offset.top + (h/2) + 25, - elem: target - }); - } else { - this.dropZones.push({ - x0: offset.left, - y0: offset.top, - x1: offset.left + w, - y1: offset.top + h, - elem: target - }); - } - } - // render dropzone boxes - if(this.options.renderDzBoxes) { - $(".dz").remove(); - for(var i in this.dropZones) { - var z = this.dropZones[i]; - $(document.createElement("div")) - .addClass("dz") - .offset({left:z.x0, top:z.y0}) - .width(z.x1-z.x0) - .height(z.y1-z.y0).appendTo("body"); - } - } - }, - /** - * Scans through this.dropZones rectangles to determine the branch - * farthest down the tree containing the given mouse coordinates. - */ - _getHoverTarget:function(coords) { - var hoverTarget = null; - - for(var i=0;i dropZone.x0 - && coords.x < dropZone.x1 - && coords.y > dropZone.y0 - && coords.y < dropZone.y1 - ) { - hoverTarget = $(dropZone.elem) - } else { - $(dropZone.elem).removeClass("x2flow-hover"); // remove this from everything else - } - } - - return hoverTarget; - }, - _getNodeAfter:function(pageX) { - if(this.hoverTarget === null) - return null; - if(this.node.hasClass("X2FlowSwitch") || - this.node.hasClass("X2FlowSplitter")) { - // if we're dragging a switch, it has to go at the end - return null - } - - // ignore the placeholder itself, the bracket element, and any .empty boxes - var targetSiblings = this.hoverTarget.children(".x2flow-node"). - not(".x2flow-placeholder, .x2flow-empty"); - var nodeAfter = null; - - for(var j = 0; j < targetSiblings.length; j++) { - var sibling = $(targetSiblings[j]); - - if(sibling.offset().left + sibling.width() / 2 < pageX) { - continue; - } else { - nodeAfter = sibling; - break; - } - } - return nodeAfter; - }, - /* - * Scans through elements in this.hoverTarget and determines - * which one is right before the placeholder. Returns the element (nodeBefore) - * or null if it's at the end or hoverTarget is empty. - */ - _getNodeBefore:function(pageX) { - if(this.hoverTarget === null) - return null; - - if(this.node.hasClass("X2FlowSwitch") || - this.node.hasClass("X2FlowSplitter")) { - // if we're dragging a switch, it has to go at the end - return this.hoverTarget.children(".x2flow-node:not(.x2flow-placeholder)").last(); - } - - // ignore the placeholder itself, the bracket element, and any .empty boxes - var targetSiblings = this.hoverTarget.children(".x2flow-node"). - not(".x2flow-placeholder, .x2flow-empty, .X2FlowSwitch, .X2FlowSplitter"); - var nodeBefore = null; - - for(var j = 0; j < targetSiblings.length; j++) { - var sibling = $(targetSiblings[j]); - - if (sibling.offset().left+sibling.width()/2 < pageX) { - nodeBefore = sibling; - } else { - break; - } - } - if(nodeBefore !== null) - return nodeBefore; - return this.hoverTarget.children(".bracket:first"); - } -}); - - - -$("#item-box").flowDraggable({distance:0, clone:true}); -$("#x2flow-main").flowDraggable({distance:20, clone:false}); - - - -window.flowEditor = { - - version: '5.2', - idCounter: 0, - loading: 0, // when this value is less than 0, config menu is loading - - currentItem:$(), - currentConfig:{}, - - trigger:$("#trigger"), - - flowItemOptionCache:{}, - conditionParamCache:{}, - - showIdItems:[ - 'X2FlowWait', - 'X2FlowEmail', - 'X2FlowRecordEmail' - ], - - init:function() { - var that = this; - - // listen for changes in fields on these parent elements - // x2.fieldUtils.addChangeListener("#x2flow-conditions, #x2flow-attributes"); - x2.fieldUtils.addChangeListener("#x2flow-config-box"); - // we want the "changed" comparison for any attribute conditions - x2.fieldUtils.enableChangedOperator = true; - - $('#x2flow-main').resizable ({ - handles: 's', - stop: function () { - $(this).css ('width', ''); - } - }); - - // Listen for clicks on the "delete condition" buttom - $("#x2flow-conditions, #x2flow-attributes, #x2flow-headers").on( - "click", "a.del", function() { - - $(this).closest("li").slideUp(200, function(){ - if ($(this).siblings ('li').length === 0) { - $(this).parent ('ol').prev ('.x2flow-api-attributes-section-header').hide (); - } - $(this).remove(); - }); - }); - - // Listen for clicks on actual flow items, and load the config for them - $("#x2flow-main"). - on('click', - '.x2flow-action, .x2flow-trigger, .X2FlowSwitch > .icon, .X2FlowSplitter > .icon', - function() { - - if (that.isLoading ()) { - return false; - } - - if($(this).is(flowEditor.currentItem)) - return; - // if(item.attr("id") !== "trigger" || $(this.currentItem.attr("id") !== "trigger")) - // don't save if we're changing the trigger, this screws stuff up - // but otherwise, save the data for the previous flow item - // this.saveCurrentConfig(); - flowEditor.saveCurrentConfig(); - //$('.x2flow-main').find ('.x2flow-node.selected').removeClass ('selected'); - flowEditor.currentItem.removeClass("selected"); - flowEditor.currentItem = $(this).closest(".x2flow-node"); // set new current item - flowEditor.currentItem.addClass("selected"); - - flowEditor.openItemConfig(); - }); - - // "Add Condition" button (only visible on triggers and switches) - $("#x2flow-add-condition").click(function() { - var type = $("#x2flow-condition-type").val(); - if(type == 'attribute') { - var modelClass = flowEditor.getModelClass(); - if(modelClass !== null) { - that.lockConfig (); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributes) { - that.unlockConfig (); - x2.fieldUtils.createAttrListItem(modelClass, attributes) - // mark as a multiselect so it can be toggled back and forth later - .data({"type":type, "multiple":1}) - .hide() - .appendTo("#x2flow-conditions ol") - .slideDown(200) - }); - } - } else { - if (type !== 'workflow_status') { - that.lockConfig (); - if(type === 'email_open') { - flowEditor.queryConditionParams(type, function(params) { - that.unlockConfig (); - flowEditor.createItemCondition(params) - .data("type", type) - .hide() - .appendTo("#x2flow-conditions ol") - .addClass('email-open-condition') - .slideDown(200); - that.updateEmailOpenConditions(); - }); - - }else{ - flowEditor.queryConditionParams(type, function(params) { - that.unlockConfig (); - flowEditor.createItemCondition(params) - .data("type", type) - .hide() - .appendTo("#x2flow-conditions ol") - .slideDown(200); - }); - } - } else { - flowEditor.createWorkflowStatusCondition () - .data("type", type) - .hide () - .appendTo("#x2flow-conditions ol") - .slideDown(200); - } - } - }); - // add an attribute row (on flow actions) - $("#x2flow-add-attribute").click(function() { - var modelClass = flowEditor.getModelClass(); - that.lockConfig (); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributeList) { - that.unlockConfig (); - if(modelClass === "API_params") { - $('#x2flow-attributes .x2flow-api-attributes-section-header').show (); - if(attributeList[0] && attributeList[0].type === "API_params") { - flowEditor.createApiParam(attributeList[0].name, attributeList[0].value) - .hide() - .appendTo("#x2flow-attributes ol") - .data({type:"attribute", modelClass:modelClass}) - .slideDown(200); - } - } else if(attributeList) { - x2.fieldUtils.createAttrListItem(modelClass, attributeList, null, false) - .hide() - .appendTo("#x2flow-attributes ol") - .data({type:"attribute", modelClass:modelClass}) - .slideDown(200); - } - }); - }); - - $("#x2flow-add-header").click(function() { - var modelClass = flowEditor.getModelClass(); - that.lockConfig (); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributeList) { - that.unlockConfig (); - $('#x2flow-headers .x2flow-api-attributes-section-header').show (); - flowEditor.createApiParam(attributeList[0].name, attributeList[0].value, false) - .hide() - .appendTo("#x2flow-headers ol") - .data({type:"headers", modelClass:modelClass}) - .slideDown(200); - }); - }); - - // Trigger menu - onchange opens trigger config (which then sets the flow's trigger and - // modelClass properties) - $("#trigger-selector").change(function(e) { - var prevTrigger = flowEditor.trigger.attr ('class').match (/[^ ]+Trigger/); - if (that.isLoading ()) { - $('#trigger-selector').val (prevTrigger); - return false; - } - - // change to or from targeted content request trigger - if ($(e.target).val ().match (/TargetedContentRequestTrigger/)) { - that._targetedContentRequestTriggerChange (); - } else if (prevTrigger && prevTrigger[0] === 'TargetedContentRequestTrigger') { - if (!that._targetedContentRequestTriggerTearDown ()) { - return; - } - } - - flowEditor.trigger - .attr('class','') - // .removeClass() // Doesn't work for some reason. - .removeData("config") - .addClass("x2flow-node x2flow-trigger "+$(e.target).val()) - .attr("title", $(e.target).find("option:selected").text()); - if (!x2.flow.showLabels) $(flowEditor.trigger).addClass ("no-label"); - flowEditor.currentItem.removeClass("selected"); - flowEditor.currentItem = flowEditor.trigger; - flowEditor.currentItem.addClass("selected"); - $(flowEditor.trigger).children ('.x2flow-icon-label').html ( - $("#trigger-selector").find ("option:selected").text ()); - - flowEditor.openItemConfig(); - x2.flow.hideShowCronUI (); - //$('.x2flow-main').find ('.x2flow-node.selected').removeClass ('selected'); - }); - - // listen for changes in model type; remove attribute conditions from the previous type - $("#x2flow-main-config").on("change", "fieldset[name='modelClass'] select", function(e) { - var newModelClass = $(e.target).val(); - var config = flowEditor.currentItem.data("config"); - if(typeof config === "object") - config.modelClass = newModelClass; - - $("#x2flow-attributes ol").empty(); // clear this item's attributes - $("#x2flow-conditions li").each(function(i, condition) { - if($(condition).data("type") === "attribute") - $(condition).remove(); // clear any attribute conditions for this item - }); - $(document).trigger("modelClassChange", [newModelClass]); - }); - - // we changed the global modelClass; loop through all the items and delete - // attributes/conditions - $(document).bind("modelClassChange", function(evt, modelClass) { - $("#x2flow-main .x2flow-node").each(function(i, item) { - var itemConfig = $(item).data("config"); - if(typeof itemConfig === 'undefined') - return; - // if this item doens't define its own model class, - if(typeof itemConfig.modelClass === 'undefined') { - // the attributes must refer to the trigger so they're now invalid - delete itemConfig.attributes; - delete itemConfig.conditions; - } - }); - }); - - $("#x2flow-main-config").on("change", "fieldset[name='linkField'] select", function(e) { - var selectedOption = $("option:selected", this); - flowEditor.currentItem.data("config").linkType = $(selectedOption).attr('data-value'); - flowEditor.clearFutureNodeAttributes(flowEditor.currentItem.next()); - }); - - - $('.x2flow-start form#submitForm').submit (function () { - flowEditor.save(); - }); - $("#save-button").unbind().click(function(){ - $('#submitForm').submit (); - }); - }, - _getIds: function () { - }, - /** - * Generate a unique node id - * @return Number - */ - _generateId: function () { - return ++this.idCounter; - }, - /** - * Generates unique ids for olds flow actions that don't yet have ids - */ -// _generateIds: function () { -// var nodes = $.makeArray ($('#content .x2flow-node')); -// for (var i in nodes) { -// var data = $(nodes[i]).data ('config'); -// if (typeof data !== 'undefined') { -// if (typeof data.id === 'undefined') { -// console.log ('new id'); -// data.id = ++this.idCounter; -// } -// } -// } -// }, - /* - Parameters: - node - instance of x2.flowDraggable - */ - _deleteNode: function (node) { - var nodeParent = node.parent (); - - // replace empty node after switch - if (nodeParent.hasClass ('x2flow-branch') && - nodeParent.children ('.x2flow-node').first ().is ($(node))) { - $(nodeParent).append ($('
    ', { 'class': 'x2flow-node x2flow-empty' })); - } - node.detach (); - x2.flow.hideShowCronUI (); - }, - _targetedContentRequestTriggerChange: function () { - $('#item-box').find ('.X2FlowPushWebContent').show (); - $('#targeted-content-embed-code-container').fadeIn (1000); - }, - /* - Removes targeted content trigger specific UI elements if change is confirmed - Returns: - true if the user confirms trigger change or if there's no content to be lost, - false otherwise - */ - _targetedContentRequestTriggerTearDown: function () { - var that = this; - - if (window.configFormEditor.getData () && - !window.confirm (x2.flow.translations['targetedContentTriggerChange'])) { - - return false; - } - /*$('.x2flow-main').find ('.x2flow-node.X2FlowPushWebContent').each (function () { - that._deleteNode ($(this)); - });*/ - $('#item-box').find ('.X2FlowPushWebContent').hide (); - $('#targeted-content-embed-code-container').hide (); - return true; - }, - save:function() { - // var d1 = new Date(); - - /* update current item's settings since normally - they only get updated when you click a different item */ - this.saveCurrentConfig(); - this.invalidateDropdownCaches (this.trigger.data ('config')); - - var flow = { - version:this.version, - idCounter: this.idCounter, - trigger:this.trigger.data("config"), - items:this.getNodeTree($("#x2flow-main > .x2flow-branch")) - }; - $("#flowDataField").val(JSON.stringify(flow)); - }, - loadFlow:function(flowData) { - if(typeof flowData !== 'object') - flowData = JSON.parse(flowData); - this.idCounter = flowData.idCounter || 0; - // console.debug(flowData); - if(flowData.trigger !== undefined && flowData.trigger.type !== undefined) { - $("#trigger-selector").val(flowData.trigger.type); - flowEditor.trigger - .removeClass() - .data("config", flowData.trigger) - .addClass("x2flow-node x2flow-trigger "+flowData.trigger.type) - .attr("title", $("#trigger-selector").find("option:selected").text()) - .click(); - if (!x2.flow.showLabels) $(flowEditor.trigger).addClass ("no-label"); - $(flowEditor.trigger).children ('.x2flow-icon-label').html ( - //x2.flow.translations[$('#trigger-selector').find ("option:selected").val ()]); - $("#trigger-selector").find ("option:selected").text ()); - } - if(flowData.items.length) { - $("#x2flow-main > .x2flow-branch").empty().append(this.populateBranch(flowData.items)); - } - //this._generateIds (); - x2.flow.hideShowCronUI (); - }, - populateBranch:function(items) { - var branch = $('
    '); - for(var i in items) { - var item = items[i]; - if(item.type === "X2FlowSwitch" || item.type === 'X2FlowSplitter') { - var flowSwitch = $("#item-box ." + item.type).clone().data("config", item); - var rightChildName = item.type === 'X2FlowSwitch' ? 'trueBranch' : 'upperBranch'; - var leftChildName = item.type === 'X2FlowSwitch' ? 'falseBranch' : 'lowerBranch'; - - var branches = - flowSwitch.children(".x2flow-branch-wrapper").children(".x2flow-branch"); - if(item[rightChildName].length) { - // create the branches all recursive-like n stuff - $(branches[0]).empty(".x2flow-empty").append( - this.populateBranch(item[rightChildName])); - } - if(item[leftChildName].length) { - $(branches[1]).empty(".x2flow-empty").append( - this.populateBranch(item[leftChildName])); - } - delete item[rightChildName]; - delete item[leftChildName]; // we don't want this stuff going into $.data() - - branch = branch.add(flowSwitch); - } else { - var template = $("#item-box ."+item.type); - if(template.length) { - var flowItem = template.clone().data("config", item); - if (flowItem.attr ('style') && - flowItem.attr ('style').match (/display[ ]*:[ ]*none;/)) { - - flowItem.attr ('style', ''); - } - branch = branch.add(flowItem); - } - } - } - if(branch.length === 0) { - // if for some reason this branch is empty, generate a placeholder item - return $(document.createElement('div')).addClass("x2flow-node x2flow-empty"); - } - return branch; - }, - /** - * Dependent dropdown caches should not be saved with the flow data. They should only persist - * over the course of the lifetime of a single page. Clear these out of the config cache before - * saving the flow. - * @param object config - */ - invalidateDropdownCaches:function(config) { - if (typeof config === 'undefined') return; - - for (var i in config.options) { - delete config.options[i].dropdownCache; - } - }, - getNodeTree:function(branch) { - - var children = $(branch).children(".x2flow-node").not(".x2flow-empty"); - - var items = []; - - for(var i=0; i=0) { - $("#x2flow-config-box").removeClass ("loading"); - } - }, - /** - * Loads the config panel for a flow item. - * Calls {@link queryItemParams()} to load the allowed params from AJAX/cache, - * then calls {@link createMainConfigForm()} and loops through any saved attributes/conditions - * with {@link createAttrListItem()} and {@link createAttrListItem()} - */ - openItemConfig:function() { - if (this.flowItem) this.flowItem.destroy (); - var itemType = this.getItemType(this.currentItem); - - // clear out the old config panel - $("#x2flow-main-config, #x2flow-conditions ol, #x2flow-attributes ol, #x2flow-headers ol"). - empty(); - - if(this.currentItem.length === 0) // if we're just clearning stuff, we're done - return; - - $("#x2flow-add-condition, #x2flow-condition-type, #x2flow-add-attribute").hide(); - - var isTrigger = - (this.currentItem.hasClass("x2flow-trigger") || - this.currentItem.hasClass("X2FlowSwitch")); - - if (itemType === 'X2FlowApiCall') { - $('#x2flow-add-header').show (); - } else { - $('#x2flow-add-header').hide (); - $('.x2flow-api-attributes-section-header').hide (); - } - - // load the options (via ajax if not cached), then run this function - var that = this; - this.lockConfig (); - this.queryItemParams(itemType, function(params) { - if(params !== false) { - var config = flowEditor.currentItem.data("config"); - if(config === undefined) { - // this item just got added, time to initialize the config - - config = { - id: that._generateId (), - type:itemType, - options:{} - }; - - if(params.modelClass !== undefined) { - // set modelClass of this flow item (if applicable) - config.modelClass = params.modelClass; - } - flowEditor.currentItem.data("config", config); // save it - } - - // create main form (with previous settings) - var form = flowEditor.createMainConfigForm(params, isTrigger, config.options); - - $("#x2flow-main-config").append ( - $('

    ').text(params.title)).append (form); - if (that.showIdItems.indexOf(config.type) > -1) { - $("#x2flow-main-config h2").after ( - $('
    ', { - 'class': 'x2flow-action-id' - }).append ( - $('').text ('ID: '), - $('').text (config.id), - x2.forms.hint (x2.flow.translations['idHint'+config.type]) - ) - ); - } - x2.forms.initializeDefaultFields (); - x2.fieldUtils.updateDependentDropdowns (form); - // $("#x2flow-main-config select").change(); // trigger modelClass event, etc - - // create attribute and/or generic condition lists - flowEditor.loadAttributes(config.attributes); - flowEditor.loadLinkAttributes(config); - if (itemType === 'X2FlowApiCall') { - flowEditor.loadHeaders(config.headerRows); - } - flowEditor.loadConditions(config.conditions); - - // instantiate ckeditor for field with richtext type - if ($('#configFormEditor').length !== 0) { - var ckeditorParams = { - //fullPage: true, - fullPage: false, - height: 130, - } - - if (itemType === 'X2FlowPushWebContent' || - itemType === 'TargetedContentRequestTrigger') { - var toolbar = 'MyTargetedContentToolbar'; - } - - // use insertable attributes associated with model class corresponding with - // trigger, if both the trigger and the model class exist - if ($(trigger).data () && $(trigger).data ().config && - $(trigger).data ().config.modelClass && !isTrigger) { - - - var modelClass = $(trigger).data ().config.modelClass; - ckeditorParams['insertableAttributes'] = {}; - ckeditorParams['insertableAttributes'][modelClass + ' Attributes'] = - x2.flow.insertableAttributes[modelClass]; - } - that.lockConfig (); - window.configFormEditor = createCKEditor ( - 'configFormEditor', ckeditorParams, function () { - that.unlockConfig (); - }, toolbar); - } - } - - // instantiate item-specific manager class - if (x2.X2FlowItem && x2[itemType] && x2[itemType].prototype && - (x2[itemType].prototype instanceof x2.X2FlowItem)) { - that.flowItem = new x2[itemType] ({ - form$: $(form) - }); - } - that.unlockConfig (); - }); - }, - createMainConfigForm:function(params, isTrigger, prevOptions) { - DEBUG && console.log (params); - if(prevOptions === undefined) - prevOptions = {}; - - var form = $(document.createElement('div')); - if(params.info) - form.html($('
    ', { text: params.info, "class": 'x2-flow-config-info' })); - - // if suboptions are specified, add them to the list of options with start and end - // delimiters. The delimiters will be used to determine when the subform element - // should begin and end, respectively. - /*if (params.suboptions) { - params.options.push ('suboptionsStart'); // end delimiter - params.options = params.options.concat (params.suboptions); - params.options.push ('suboptionsEnd'); // start delimiter - // this will contain the sub options - var subForm = $('
    ', { - style: 'display: none;' - }); - var formTemp; - }*/ - - for(var i in params.options) { - var optionParams = params.options[i]; - /*if (optionParams === 'suboptionsStart') { - // start the subform, store the parent form in a temporary variable, and replace - // the form with the subform. That way, all suboptions will be added to the subform - // instead of to the form - formTemp = form; - form = subForm; - continue; - } else if (optionParams === 'suboptionsEnd') { - // end the subform and append it to the parent form - form = formTemp; - form.append (subForm); - continue; - }*/ - - /*if($(form).is (subForm) && optionParams.name === 'dependency') { - // this is the subform dependency. add the event handler which hides/shows the - // subform when the element depended upon in the parent form is clicked. - $(document).off ('change', '[name="' + optionParams.dependentOn + '"] input'). - on ('change', '[name="' + optionParams.dependentOn + '"] input', function () { - - if ($(this).is (':checked')) { - $(subForm).slideDown (); - } else { - $(subForm).slideUp (); - } - }); - continue; - } else */if (optionParams.name === 'attributes' || optionParams.name === 'headers') { - $("#x2flow-add-attribute").show(); - continue; - } - - var row = $(document.createElement('div')).addClass('row').appendTo(form); - var fieldset = $(document.createElement('fieldset')).attr("name", optionParams.name). - appendTo(row); - var val = undefined, - op = undefined, - dropdownCache = undefined; - - if (typeof optionParams.htmlOptions !== 'undefined') { - flowEditor.addHtmlOptions (fieldset, optionParams.htmlOptions); - } - - if(prevOptions[optionParams.name] !== undefined) { - var field = prevOptions[optionParams.name]; - if(typeof field === 'object') { // is it a multipart field or a simple field? - val = field.value; - op = field.operator; - dropdownCache = field.dropdownCache; - } else { - val = field; - } - } - - if(optionParams.label !== undefined) { - $(document.createElement('label')).html (optionParams.label).appendTo(fieldset); - //.attr('for', optionParams.name) - } - - if(optionParams.operators !== undefined) { - var operatorCell = - $(document.createElement("div")).addClass("cell x2fields-operator"). - appendTo(fieldset); - var dropdown = x2.fieldUtils.buildOperatorDropdown(optionParams.operators, op); - $(operatorCell).append (dropdown); - } - - var fieldOptions = $.extend({}, optionParams); - - if(val !== undefined) { - // if there is saved data, insert field values into the fieldOptions object - fieldOptions.value = val; //prevOptions[fieldOptions.name]; - } - if (dropdownCache) { - fieldOptions.dropdownCache = dropdownCache; - } - - var valueCell = $(document.createElement("div")).addClass("cell x2fields-value"). - appendTo(fieldset); - - fieldOptions.name = "value"; - //var input = x2.fieldUtils.createInput(fieldOptions).appendTo(valueCell); - var input = x2.fieldUtils.createInput(fieldOptions); - $(valueCell).append (input); - - // id will be used to instantiate ckeditor after form is appended to DOM node - if (fieldOptions.type === 'richtext') { - $(input).attr ('id', 'configFormEditor'); - $(input).attr ('class', 'rich-text'); - $(valueCell).addClass ('editor-container'); - } - - if(optionParams.name === 'modelClass') { - var config = this.currentItem.data("config"); - if(typeof config === "object") - config.modelClass = $(input).val(); - } - - // instantiate qtips if they're present in the config menu - $(fieldset).find ('.x2-hint').each (function () { - $(this).qtip (); - }); - } - - if(isTrigger) { - $("#x2flow-add-condition, #x2flow-condition-type").show(); - - if(flowEditor.getModelClass() === null) { - $("#x2flow-condition-type option:first").attr("disabled", "disabled"); - $("#x2flow-condition-type").val($("#x2flow-condition-type option:nth-child(2)"). - attr("value")); - } else { - $("#x2flow-condition-type option:first").removeAttr("disabled"); - $("#x2flow-condition-type").val($("#x2flow-condition-type option:first"). - attr("value")); - } - } - - if (params['class'] === 'X2FlowRecordEmail' || params['class'] === 'X2FlowEmail') { - this.setUpEmailForm (form); - } - - return form; - }, - /* - Add html options as to the fieldset in the main config menu - */ - addHtmlOptions: function (fieldset, htmlOptions) { - var option; - for (var i in htmlOptions) { - option = htmlOptions[i]; - switch (i) { - case 'class': - $(fieldset).addClass (option); - break; - default: - $(fieldset).attr (i, option); - break; - } - } - }, - /* - Sets up email template feature - */ - setUpEmailForm:function(form) { - - function templateSwitchConfirm() { - var proceed = true; - var noChange = !window.configFormEditor.checkDirty(); - if(!noChange) - proceed = window.confirm(x2.flow.translations['templateChangeConfirm']); - return proceed; - } - - DEBUG && console.log ('setUpEmailForm'); - DEBUG && console.log ($(form)); - DEBUG && console.log ($(form).find ('[name="template"]')); - - $(form).find ('[name="template"]').find ('select').change(function() { - DEBUG && console.log ('select'); - - var template = $(this).val(); - DEBUG && console.log (template); - if(template !== "" && templateSwitchConfirm ()) { - - $.ajax({ - url:yii.baseUrl+"/index.php/docs/fullView/"+template+"?json=1", - type:"GET", - dataType:"json" - }).done(function(data) { - window.configFormEditor.setData(data.body); - $(form).find ('[name="subject"]').find ('input').val (data.subject); - if (typeof data.to !== 'undefined' && data.to !== '') - $(form).find ('[name="to"]').find ('input').val(data.to); - window.configFormEditor.document.on("keyup", function(){ - $(form).find ('[name="template"]').find ('select').val ("0"); - }); - }); - } - }); - }, - /** - * Updates lists of email opened options on condition attributes - */ - updateEmailOpenConditions:function(){ - var that = this; - var emailIds = []; - $("#x2flow-main .x2flow-node").each(function(i, item) { - var itemConfig = $(item).data("config"); - if(typeof itemConfig === 'undefined') - return; - if(itemConfig['type'] === 'X2FlowEmail' || - itemConfig['type'] === 'X2FlowRecordEmail'){ - emailIds.push(itemConfig['id']); - } - }); - $(".email-open-condition select").empty(); - for(var i = 0; i < emailIds.length; i++){ - $(".email-open-condition select").append($("').attr('value','original').attr('data-value', triggerConfig.modelClass).text('Original Record')); - for(var i in attributeList){ - if(typeof attributeList[i]['name'] !== 'undefined' - && typeof attributeList[i]['label'] !== 'undefined'){ - if(i == 0 && config.type == 'X2FlowRecordChange'){ - config.linkType = attributeList[i]['linkType']; - } - var option = $('').attr('value',attributeList[i]['name']).attr('data-value', attributeList[i]['linkType']).text(attributeList[i]['label']); - if(selectedField === attributeList[i]['name']){ - if(config.type == 'X2FlowRecordChange'){ - config.linkType = attributeList[i]['linkType']; - } - option.attr('selected','selected'); - } - $('fieldset[name="linkField"] select').append(option); - } - } - }); - }, - - /** - * Creates an attribute entry in #x2flow-attributes for each attribute in the list provided - */ - loadAttributes:function(attributes) { - var that = this; - // console.debug(attributes); - if(attributes === undefined) - return; - - var modelClass = this.getModelClass(); - - // loop through any saved attributes - this.lockConfig (); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributeList) { - that.unlockConfig (); - - for(var i in attributes) { - var attr = attributes[i]; - - if(modelClass === "API_params") { - $('#x2flow-attributes .x2flow-api-attributes-section-header').show (); - flowEditor.createApiParam(attr.name, attr.value) - .appendTo("#x2flow-attributes ol"); - /*flowEditor.createApiParam(attr.name, attr.value, false) - .appendTo("#x2flow-headers ol");*/ - } else if(modelClass !== null) { - // there is no operator, tell createAttrListItem() not to add a selector - if(attr.operator === undefined) - // (this must be a flow action, where the attributes are just being set, - // not tested) - attr.operator = false; - x2.fieldUtils.createAttrListItem( - modelClass, attributeList, attr.name, attr.operator, attr.value) - .data("type", "attribute") - .appendTo("#x2flow-attributes ol"); - } - } - }); - }, - loadHeaders:function(headers) { - - // loop through any saved headers - for(var i in headers) { - $('#x2flow-headers .x2flow-api-attributes-section-header').show (); - var header = headers[i]; - - flowEditor.createApiParam(header.name, header.value, false) - .appendTo("#x2flow-headers ol"); - } - }, - loadConditions:function(conditions) { - var that = this; - // console.debug(conditions); - if(conditions === undefined) - return; - $.each(conditions, function(i, condition) { - if(condition.type === undefined) - return; - if(condition.type === "attribute") { - var modelClass = that.getModelClass(); - if(modelClass !== null) { - that.lockConfig (); - x2.fieldUtils.getModelAttributes(modelClass, 'all', function(attributeList) { - that.unlockConfig (); - // console.debug(condition); - x2.fieldUtils.createAttrListItem( - modelClass, attributeList, condition.name, condition.operator, - condition.value) - .data("type", "attribute") - .appendTo("#x2flow-conditions ol"); - }); - } - } else { - if (condition.type !== 'workflow_status') { - that.lockConfig (); - if (condition.type === 'email_open') { - that.queryConditionParams(condition.type, function(data) { - that.unlockConfig (); - data.operator = condition.operator; - data.value = condition.value; - flowEditor.createItemCondition(data) - .data("type", condition.type) - .addClass('email-open-condition') - .attr('data-value', data.value) - .appendTo("#x2flow-conditions ol"); - that.updateEmailOpenConditions(); - }); - } else { - that.queryConditionParams(condition.type, function(data) { - that.unlockConfig (); - data.operator = condition.operator; - data.value = condition.value; - flowEditor.createItemCondition(data) - .data("type", condition.type) - .appendTo("#x2flow-conditions ol"); - }); - } - } else { - flowEditor.createWorkflowStatusCondition (condition) - .data("type", condition.type) - .appendTo("#x2flow-conditions ol"); - } - } - }); - }, - /** - * Creates a workflow status condition - * @return object jQuery
  • element containing the workflow status condition fieldset - */ - createWorkflowStatusCondition: function createWorkflowStatusCondition (condition) { - var condition = typeof condition === 'undefined' ? {} : condition; - - var li = x2.fieldUtils.templates.workflowStatusConditionForm.clone(); - var stageDropdown = $(li).find ('[name="stageNumber"]'); - var workflowDropdown = $(li).find ('[name="workflowId"]'); - var stateDropdown = $(li).find ('[name="stageState"]'); - - // check if saved workflow id is different that the default workflow id - var fetchNewStageDropdown = false; - if ($(workflowDropdown).val () !== condition.workflowId) { - // stages for the workflow with saved workflow id must be fetched - fetchNewStageDropdown = true; - } - - if (typeof createWorkflowStatusCondition.cache === 'undefined') { - // used to cache the requested stage name options - createWorkflowStatusCondition.cache = {}; - - // add default option to the cache - var defaultOptions = []; - $(stageDropdown).find ('option').each (function () { - defaultOptions.push ([$(this).val (), $(this).html ()]); - }); - createWorkflowStatusCondition.cache[$(workflowDropdown).val ()] = defaultOptions; - } - - // set saved values - $(stageDropdown).val (condition.stageNumber); - $(workflowDropdown).val (condition.workflowId); - $(stateDropdown).val (condition.stageState); - - // replaces old stage name dropdown with a new one using options either from the cache - // or from an AJAX response - function buildNewDropdown (data) { - var $newDropdown = x2.fieldUtils.buildDropdown (data, { - name: $(stageDropdown).attr ('name') - }); - $(stageDropdown).replaceWith ($newDropdown); - stageDropdown = $newDropdown; - stageDropdown.val (condition.stageNumber); - } - - // fetch new stage select options when workflow id select changes - $(workflowDropdown).unbind ('change'). - bind ('change', function fetchStageOptions () { - - var workflowId = $(this).val (); - // check the cache first - if (typeof createWorkflowStatusCondition.cache[workflowId] !== 'undefined') { - var data = createWorkflowStatusCondition.cache[workflowId]; - buildNewDropdown (data); - return; - } - - // cache miss, request the options - x2.forms.inputLoading (stageDropdown); - $.ajax ({ - url: yii.scriptUrl + '/workflow/workflow/getStageNames', - type: 'get', - dataType: 'json', - data: { - workflowId: workflowId, - optional: false - }, - success: function (data) { - x2.forms.inputLoadingStop (stageDropdown); - buildNewDropdown (data); - - // cache the results - createWorkflowStatusCondition.cache[workflowId] = data; - } - }); - }); - - if (fetchNewStageDropdown) { - $(workflowDropdown).change (); - } - return li; - }, - createItemCondition:function(condition) { - // console.debug(conditionParams); - //if(condition.value === undefined) // default to the first attribute - // var val = ''; - - // clone template condition form - var li = x2.fieldUtils.templates.conditionForm.clone(); - var fieldset = li.find('fieldset').first(); - $(document.createElement("div")).addClass("cell inline-label").text(condition.label). - appendTo(fieldset); - - if(condition.operators) { - x2.fieldUtils.createOperatorCell( - condition.operators, condition.operator).appendTo(fieldset); - li.on("change", ".x2fields-operator select", function() { - x2.fieldUtils.updateValueCell(this); - }); - if(condition.multiple) - li.data("multiple", true); - } - x2.fieldUtils.createValueCell(condition).appendTo(fieldset); - - return li; - }, - createApiParam:function(name, val, params) { - var params = typeof params === 'undefined' ? true : params; - // clone template condition form - var li = x2.fieldUtils.templates.conditionForm.clone(); - if (params) { - var fieldset = li.find('fieldset').replaceWith($("#condition-templates .API_params"). - clone()); - } else { - var fieldset = li.find('fieldset').replaceWith($("#condition-templates .APIHeaders"). - clone()); - } - li.find(".x2fields-attribute input").val(name); - li.find(".x2fields-value input").val(val); - - return li; - } -}; - -flowEditor.init(); - -if(x2.flowData !== null) - flowEditor.loadFlow(x2.flowData); -}); diff --git a/x2engine/protected/messages/zh_cn/common.php b/x2engine/protected/messages/zh_cn/common.php index 86c06d099..f4875ea79 100644 --- a/x2engine/protected/messages/zh_cn/common.php +++ b/x2engine/protected/messages/zh_cn/common.php @@ -5,11 +5,11 @@ 'Create Date' => '创建日期', 'Description' => '说明', 'Last Updated' => '最后更新', - 'Updated By' => '更新', + 'Updated By' => '更新人', 'None' => '没有', - 'Visibility' => '隐私', + 'Visibility' => '可见性', 'Create' => '创建', - 'View' => '看', + 'View' => '查看', 'Update' => '更新', 'Delete' => '删除', 'Name' => '名称', @@ -19,10 +19,10 @@ 'Accounts' => '帐户', 'Account Attributes' => '帐户属性', 'Website' => '网站', - 'Phone' => '电话', + 'Phone' => '手机号', 'Associated Contacts' => '相关联系人', - 'City' => '市', - 'State' => '洲/省', + 'City' => '城市', + 'State' => '省份', 'Postal Code' => '邮政编码', 'Country' => '国家', 'Basic Information' => '基本信息', @@ -39,8 +39,8 @@ 'Low' => '低', 'Medium' => '中', 'High' => '高', - 'Public' => '公共', - 'Private' => '私人', + 'Public' => '公开', + 'Private' => '私有', 'No' => '不是', 'Yes' => '是', 'Create Action' => '创建任务', @@ -52,11 +52,11 @@ 'Contact' => '联系人', 'Account' => '帐户', 'Project' => '项目', - 'Opportunity' => '销售', + 'Opportunity' => '机遇', 'today' => '今日', 'Start Date' => '开始日期', 'End Date' => '结束日期', - 'Create Lead' => '创建潜在客户', + 'Create Lead' => '创建线索', 'Share Action' => '分担任务', 'Assigned to {name}' => '分配给 {name}', 'Contact Info' => '联系信息', @@ -68,8 +68,8 @@ 'Quote:' => '引用:', 'Case' => '案例', 'Completed By' => '完成', - 'Gii - A Code Generation Module' => '全球信息基础设施 - 代码生成模块', - 'X2Flow' => 'X2Flow', + 'Gii - A Code Generation Module' => 'Gii - 代码生成模块', + 'X2Flow' => 'X2流程', 'Hide' => '隐藏', 'Export' => '导出', 'Username' => '用户名', @@ -82,7 +82,7 @@ 'Tags' => '标签', 'Link' => '链接', 'Model Name' => '型号名称', - 'Custom' => '习俗', + 'Custom' => '自定义', 'Required' => '需要', 'Checkbox' => '复选框', 'Options' => '选项', @@ -92,12 +92,12 @@ 'Other' => '其他', 'Any' => '任何', 'Record Type' => '记录类型', - 'Model' => '模型', + 'Model' => '型号', 'Import Contacts' => '导入联系人', 'Export Contacts' => '导出联系人', 'Show' => '显示', 'AND' => ',', - 'Owner' => '业主', + 'Owner' => '归属人', 'E-Mail' => '电子邮件', 'Rating' => '评价', 'Edit Permissions' => '编辑权限', @@ -123,7 +123,7 @@ 'Actions Completed' => '动作完成', 'Go' => '转到', 'Verification Code' => '验证码', - 'Title' => '标题', + 'Title' => '职位', 'Example' => '例子', 'Share' => '分享', 'less than' => '小于', @@ -134,29 +134,29 @@ 'Docs' => '文件', 'Are you sure you want to delete this item?' => '您是否确定要删除这个项目?', 'Edit' => '编辑', - 'Groups' => '团体', + 'Groups' => '分组', 'Services' => '服务', 'Email Received' => '收到的电子邮件', 'All Rights Reserved.' => '保留所有权利。', 'Comments' => '评论', 'Background Color' => '背景颜色', 'Preview' => '预览', - 'Upload' => '上载', - 'Top Contacts' => '最常关注的联系人', + 'Upload' => '上传', + 'Top Contacts' => '常用联系人', 'Create Multiple' => '创建多个', - 'Add' => '加', + 'Add' => '添加', 'Remove' => '清除', - 'Tools' => '工具', + 'Tools' => '操作', 'Social' => '社交', - 'Social Feed' => '社会反馈', + 'Social Feed' => '社交订阅', 'Time Zone' => '时区', 'Menu Text Color' => '菜单文字颜色', 'Email Address' => '电邮地址', 'Quote' => '引用', - 'BugReports' => '臭虫回报', + 'BugReports' => '漏洞汇报', 'Add Comment' => '发表评论', 'User\'s Groups' => '用户\\组', - 'Social Post' => '社会后', + 'Social Post' => '社交帖子', 'Announcement' => '公告', 'Product Info' => '产品信息', 'Competitive Info' => '竞争性信息', @@ -165,21 +165,21 @@ 'Manage Users' => '管理用户', 'Workflow' => '工作流程', 'Email' => '电子邮件', - 'List' => '表', - 'Completed' => '完成', + 'List' => '列表', + 'Completed' => '已完成', 'Export to CSV' => '导出到CSV', 'Full Name' => '全名', - 'Lead Source' => '潜在客户的来源', - 'Lead Type' => '机会型', + 'Lead Source' => '线索来源', + 'Lead Type' => '线索类型', 'Value' => '值', - 'Won' => '韩元', - 'Lost' => '失落', + 'Won' => '获得', + 'Lost' => '失去', 'Deal Value' => '这笔交易的价值', 'Created By' => '创建者', - 'Total' => '总', + 'Total' => '总计', 'Last Login' => '最后登录', 'Records Updated' => '更新记录', - 'Pending' => '有待', + 'Pending' => '待定', 'First Name' => '名字', 'Last Name' => '姓氏', 'Google' => 'Google', @@ -201,12 +201,12 @@ 'contains' => '包含', 'does not contain' => '不包含', 'Background Info' => '背景资料', - 'Create Quote' => '创建报价', - 'Action Completed' => '行动已完成', - 'Record Created' => '创造的纪录', - 'Record Deleted' => '记录删除', - 'Create Web Form' => '创建Web窗体', - 'Service Cases Web Form' => '服务案例Web窗体', + 'Create Quote' => '创建引用', + 'Action Completed' => '操作已完成', + 'Record Created' => '记录已创建', + 'Record Deleted' => '记录已删除', + 'Create Web Form' => '创建Web表单', + 'Service Cases Web Form' => '服务案例Web表单', 'Create a public form to receive new services cases. When the form is submitted, a new service case will be created, and the case # will be sent to the email address provided in the form.' => '创建一个公共的形式来接收新的服务案例。当表单被提交时,可以创建一个新的服务的情况下,和#的情况下将被发送到的电子邮件地址的形式提供。', 'Campaign' => '运动', 'Sales Stage' => '销售阶段', @@ -259,13 +259,13 @@ 'Updated' => '更新', 'Edit Workflow' => '编辑工作流程', 'Delete Workflow' => '删除工作流', - 'Del' => '德尔', + 'Del' => '删除', 'Calendars' => '日历', 'Confidence' => '信心', - 'Deal Status' => '新政状态', + 'Deal Status' => '跟进状态', 'Template' => '模板', 'minutes' => '分钟', - 'New Web Lead' => '新的Web铅', + 'New Web Lead' => '新的网页线索', 'Invoice Status' => '发票状态', 'Invoice Created' => '建立发票', 'Invoice Issued' => '开具发票', @@ -313,9 +313,9 @@ 'Doc' => '文件', 'Help' => '帮助', 'Sent' => '发送', - 'Opened' => '开业', + 'Opened' => '已打开', 'Unsubscribed' => '退订', - 'Action History' => '行动历史', + 'Action History' => '操作记录', 'hours' => '小时', 'Cancel' => '取消', 'Version Info' => '版本信息', @@ -327,9 +327,9 @@ 'Flow item validation error' => '流项目验证错误', 'Activity Feed' => '活动资​​讯', 'Send As:' => '发送为:', - 'Lead Status' => '铅状态', - 'Lead Date' => '铅日期', - 'Lead Score' => '铅得分', + 'Lead Status' => '线索状态', + 'Lead Date' => '线索日期', + 'Lead Score' => '线索评分', 'Interest' => '兴趣', 'Close Date' => '关闭日期', 'NameID' => 'NameID', @@ -353,14 +353,14 @@ 'Assign records via lead-routing?' => '通过引入路由分配的记录?', 'Process Import' => '导入过程', 'Import Status' => '导入状态', - 'Please click the button below to begin the export. Do not close this page until the export is finished, which may take some time if you have a large number of records. A counter will keep you updated on how many records have been successfully updated.' => '请点击下面的按钮开始出口。直到导出完成后不要关闭这个页面,这可能需要一些时间,如果你有大量的记录。计数器将继续有多少记录已成功更新你更新。', - 'You are currently exporting: ' => '您当前出口:', + 'Please click the button below to begin the export. Do not close this page until the export is finished, which may take some time if you have a large number of records. A counter will keep you updated on how many records have been successfully updated.' => '请点击下面的按钮开始导出。直到导出完成后不要关闭这个页面,这可能需要一些时间,如果你有大量的记录。计数器将继续有多少记录已成功更新你更新。', + 'You are currently exporting: ' => '您当前正在导出:', 'Export Complete!' => '导出完成!', 'The application will attempt to automatically map your column headers to our fields in the database. If a match is not found, you will be given the option to choose one of our fields to map to, ignore the field, or create a new field within X2.' => '该应用程序会尝试自动将列标题映射到我们的数据库中的字段。如果未找到匹配项,您将得到选项来选择我们的一个字段映射到,忽略该字段,或在X2建立一个新的领域。', 'If you decide to map the "Create Date", "Last Updated", or any other explicit date field, be sure that you have a valid date format entered so that the software can convert to a UNIX Timestamp (if it is already a UNIX Timestamp even better). Visibility should be either "1" for Public or "0" for Private (it will default to 1 if not provided).' => '如果您决定映射“创建日期”,“最近更新”,或任何其他明确的日期字段,请确保您已输入,使得该软件可以转换为UNIX时间戳(如果它已经是一个UNIX的一个有效的日期格式时间戳甚至更好)。能见度应该是“1”Public或“0”私人(它会默认为1,如果未提供)。', 'Upload File' => '上传文件', 'Import a Theme' => '导入一个主题', - 'Import Flow' => '进口流程', + 'Import Flow' => '导入流程', 'Process Settings' => '流程设置', 'Date Range' => '日期范围', 'This Year' => '今年', @@ -370,15 +370,15 @@ 'Pause' => '暂停', 'minute' => '分钟', 'Failed to upload file.' => '上传文件失败。', - 'Preferences' => '首', + 'Preferences' => '首选项', 'Minimize' => '最小化', 'Make Sticky' => '设为置顶', 'Undo Sticky' => '撤消置顶', 'Like Post' => '像帖子', 'Unlike Post' => '不像帖子', 'Hide comments' => '隐藏评论', - 'Make Important' => '使重要', - 'Make Unimportant' => '让不重要', + 'Make Important' => '标记为重要', + 'Make Unimportant' => '标记为不重要', 'Edit Profile' => '编辑个人资料', 'Select' => '选择', 'less than or equal to' => '小于或等于', @@ -401,8 +401,8 @@ 'Share {module}' => '分享{module}', 'Delete {module}' => '删除{module}', '{module}:' => '{module} :', - 'Import {module}' => '进口{module}', - 'Export {module}' => '出口{module}', + 'Import {module}' => '导入{module}', + 'Export {module}' => '导出{module}', 'Modified Fields' => '修改的字段', 'All {module}' => '所有{module}', '{module} Attributes' => '{module}属性', @@ -499,7 +499,7 @@ 'week' => '周', 'Dates' => '日期', 'Grid Builder' => '网格生成器', - 'Lead Performance' => '铅性能', + 'Lead Performance' => '线索成效', 'User Activity' => '用户活动', 'Row Field' => '行字段', 'Column Field' => '列字段', @@ -512,7 +512,7 @@ 'hour' => '小时', '{contact} Attributes' => '{contact}属性', 'Timezone' => '时区', - 'Web Form' => 'Web窗体', + 'Web Form' => '网页表单', 'Unsubscribe' => '退订', '{contacts}' => '{contacts}', '{leads}' => '{leads}', @@ -521,11 +521,11 @@ 'Content' => '内容', 'Send Test Email' => '发送测试电子邮件', 'Invoice Paid' => '发票付费', - 'Max' => '马克斯', + 'Max' => '最大', '{opportunities}' => '{opportunities}', '{user}' => '{user}', 'Recent Items' => '最近的项目', - 'Log Call' => '登录呼叫', + 'Log Call' => '记录呼叫', 'Language' => '语言', 'Date Created' => '创建日期', 'Reverse IP' => '反向IP', @@ -545,7 +545,7 @@ 'Google Analytics Property ID (public)' => '谷歌分析物业编号(公共)', 'Google Analytics Property ID (internal)' => '谷歌分析物业编号(内部)', 'Twitter Integration' => 'Twitter的整合', - 'Add Social Profile' => '添加社交简介', + 'Add Social Profile' => '添加社交账户', 'Fingerprint' => '指印', 'Sales & Marketing' => '销售与市场营销', 'Action Timer' => '操作计时器', @@ -558,7 +558,7 @@ 'Filter' => '过滤', 'year' => '年', 'test' => '测试', - 'OR' => '要么', + 'OR' => '或者', 'Enter a new name:' => '输入新的名称:', 'Fingerprints' => '指纹', 'File' => '文件', @@ -569,17 +569,17 @@ 'quote' => '引用', 'Reports' => '报告', 'Service' => '服务', - '{attribute} is not secure enough (minimum length: {l})' => '{attribute}是不够安全(最小长度{l}', + '{attribute} is not secure enough (minimum length: {l})' => '{attribute}不够安全(最小长度:{l})', 'Contact List' => '联系人列表', 'Fingerprint ID' => '指纹ID', 'Phone 2' => '手机2', 'Twitter' => '推特', 'Linkedin' => 'LinkedIn', - 'Skype' => 'Skype的', + 'Skype' => 'Skype', 'Googleplus' => 'Googleplus', 'Address 2' => '地址2', 'Do Not Call' => '不要打电话', - 'Do Not Email' => '不要通过电子邮件', + 'Do Not Email' => '不要发邮件', 'Info' => '信息', 'Other Info' => '其他信息', 'To' => '至', @@ -600,11 +600,11 @@ 'Maximum number of X2Workflow trigger logs' => 'X2Workflow触发记录的最大数量', 'Invalid license key' => '无效许可证密钥', 'License Expiration Date' => '许可证的有效期', - '(expired)' => '(过期)', + '(expired)' => '(已过期)', 'License Max Users' => '许可最大用户', - 'Select a model' => '选择一个模型', - 'Okay' => '好', - 'Headquarters' => '司令部', + 'Select a model' => '选择一个型号', + 'Okay' => '好的', + 'Headquarters' => '总部', 'Mailing Address' => '邮寄地址', 'Details' => '详细信息', 'Select All' => '全选', @@ -616,12 +616,12 @@ 'Date/Time' => '约会时间', 'Stated Address' => '声明地址', 'Check-in comment' => '入住评论', - 'Email Opened' => '电子邮件开业', + 'Email Opened' => '电子邮件已打开', 'Email Clicked' => '电子邮件已点击', 'X2 Hub Services' => 'X2 Hub服务', - 'Manage User Count' => '管理用户计数', + 'Manage User Count' => '管理用户数量', 'Error' => '错误', 'View {module} on Map' => '在地图上查看{module}', - 'Disabled' => '残', + 'Disabled' => '已禁用', 'Enabled' => '启用', );