Path: blob/master/webroot/rsrc/js/application/herald/HeraldRuleEditor.js
12241 views
/**1* @requires multirow-row-manager2* javelin-install3* javelin-util4* javelin-dom5* javelin-stratcom6* javelin-json7* phabricator-prefab8* @provides herald-rule-editor9* @javelin10*/1112JX.install('HeraldRuleEditor', {13construct : function(config) {14var root = JX.$(config.root);15this._root = root;1617JX.DOM.listen(18root,19'click',20'create-condition',21JX.bind(this, this._onnewcondition));2223JX.DOM.listen(24root,25'click',26'create-action',27JX.bind(this, this._onnewaction));2829JX.DOM.listen(root, 'change', null, JX.bind(this, this._onchange));30JX.DOM.listen(root, 'submit', null, JX.bind(this, this._onsubmit));3132var conditionsTable = JX.DOM.find(root, 'table', 'rule-conditions');33var actionsTable = JX.DOM.find(root, 'table', 'rule-actions');3435this._conditionsRowManager = new JX.MultirowRowManager(conditionsTable);36this._conditionsRowManager.listen(37'row-removed',38JX.bind(this, function(row_id) {39delete this._config.conditions[row_id];40}));4142this._actionsRowManager = new JX.MultirowRowManager(actionsTable);43this._actionsRowManager.listen(44'row-removed',45JX.bind(this, function(row_id) {46delete this._config.actions[row_id];47}));4849this._conditionGetters = {};50this._conditionTypes = {};51this._actionGetters = {};52this._actionTypes = {};5354this._config = config;5556var conditions = this._config.conditions;57this._config.conditions = [];5859var actions = this._config.actions;60this._config.actions = [];6162this._renderConditions(conditions);63this._renderActions(actions);64},6566members : {67_config : null,68_root : null,69_conditionGetters : null,70_conditionTypes : null,71_actionGetters : null,72_actionTypes : null,73_conditionsRowManager : null,74_actionsRowManager : null,7576_onnewcondition : function(e) {77this._newCondition();78e.kill();79},80_onnewaction : function(e) {81this._newAction();82e.kill();83},84_onchange : function(e) {85var target = e.getTarget();8687var row = e.getNode(JX.MultirowRowManager.getRowSigil());88if (!row) {89// Changing the "when all of / any of these..." dropdown.90return;91}9293if (JX.Stratcom.hasSigil(target, 'field-select')) {94this._onfieldchange(row);95} else if (JX.Stratcom.hasSigil(target, 'condition-select')) {96this._onconditionchange(row);97} else if (JX.Stratcom.hasSigil(target, 'action-select')) {98this._onactionchange(row);99}100},101_onsubmit : function() {102var rule = JX.DOM.find(this._root, 'input', 'rule');103104var k;105106for (k in this._config.conditions) {107this._config.conditions[k][2] = this._getConditionValue(k);108}109110for (k in this._config.actions) {111this._config.actions[k][1] = this._getActionTarget(k);112}113rule.value = JX.JSON.stringify({114conditions: this._config.conditions,115actions: this._config.actions116});117},118119_getConditionValue : function(id) {120if (this._conditionGetters[id]) {121return this._conditionGetters[id]();122}123return this._config.conditions[id][2];124},125126_getActionTarget : function(id) {127if (this._actionGetters[id]) {128return this._actionGetters[id]();129}130return this._config.actions[id][1];131},132133_onactionchange : function(r) {134var target = JX.DOM.find(r, 'select', 'action-select');135var row_id = this._actionsRowManager.getRowID(r);136137this._config.actions[row_id][0] = target.value;138139var target_cell = JX.DOM.find(r, 'td', 'target-cell');140var target_input = this._renderTargetInputForRow(row_id);141142JX.DOM.setContent(target_cell, target_input);143},144_onfieldchange : function(r) {145var target = JX.DOM.find(r, 'select', 'field-select');146var row_id = this._actionsRowManager.getRowID(r);147148this._config.conditions[row_id][0] = target.value;149150var condition_cell = JX.DOM.find(r, 'td', 'condition-cell');151var condition_select = this._renderSelect(152this._selectKeys(153this._config.info.conditions,154this._config.info.conditionMap[target.value]),155this._config.conditions[row_id][1],156'condition-select');157158JX.DOM.setContent(condition_cell, condition_select);159160this._onconditionchange(r);161162var condition_name = this._config.conditions[row_id][1];163if (condition_name == 'unconditionally') {164JX.DOM.hide(condition_select);165}166},167_onconditionchange : function(r) {168var target = JX.DOM.find(r, 'select', 'condition-select');169var row_id = this._conditionsRowManager.getRowID(r);170171this._config.conditions[row_id][1] = target.value;172173var value_cell = JX.DOM.find(r, 'td', 'value-cell');174var value_input = this._renderValueInputForRow(row_id);175JX.DOM.setContent(value_cell, value_input);176},177178_renderTargetInputForRow : function(row_id) {179var action = this._config.actions[row_id];180var type = this._config.info.targets[action[0]];181182var input = this._buildInput(type);183var node = input[0];184var get_fn = input[1];185var set_fn = input[2];186187if (node) {188JX.Stratcom.addSigil(node, 'action-target');189}190191var old_type = this._actionTypes[row_id];192if (old_type == type || !old_type) {193set_fn(this._getActionTarget(row_id));194}195196this._actionTypes[row_id] = type;197this._actionGetters[row_id] = get_fn;198199return node;200},201202_buildInput : function(type) {203var spec = this._config.info.valueMap[type];204205var input;206var get_fn;207var set_fn;208switch (spec.control) {209case 'herald.control.none':210input = null;211get_fn = JX.bag;212set_fn = JX.bag;213break;214case 'herald.control.text':215input = JX.$N('input', {type: 'text'});216get_fn = function() { return input.value; };217set_fn = function(v) { input.value = v; };218break;219case 'herald.control.remarkup':220input = JX.$N('textarea');221get_fn = function() { return input.value; };222set_fn = function(v) { input.value = v; };223break;224case 'herald.control.select':225var options;226227// NOTE: This is a hacky special case for "Another Herald Rule",228// which we don't currently generate normal options for.229230if (spec.key == 'select.rule') {231options = this._config.template.rules;232} else {233options = spec.template.options;234}235236input = this._renderSelect(options);237get_fn = function() { return input.value; };238set_fn = function(v) { input.value = v; };239if (spec.template.default) {240set_fn(spec.template.default);241}242break;243case 'herald.control.tokenizer':244var tokenizer = this._newTokenizer(spec.template.tokenizer);245input = tokenizer[0];246get_fn = tokenizer[1];247set_fn = tokenizer[2];248break;249default:250JX.$E('No rules to build control "' + spec.control + '".');251break;252}253254return [input, get_fn, set_fn];255},256257_renderValueInputForRow : function(row_id) {258var cond = this._config.conditions[row_id];259var type = this._config.info.values[cond[0]][cond[1]];260261var input = this._buildInput(type);262var node = input[0];263var get_fn = input[1];264var set_fn = input[2];265266if (node) {267JX.Stratcom.addSigil(node, 'condition-value');268}269270var old_type = this._conditionTypes[row_id];271if (old_type == type || !old_type) {272set_fn(this._getConditionValue(row_id));273}274275this._conditionTypes[row_id] = type;276this._conditionGetters[row_id] = get_fn;277278return node;279},280281_newTokenizer : function(spec) {282var tokenizerConfig = {283src: spec.datasourceURI,284placeholder: spec.placeholder,285browseURI: spec.browseURI,286limit: spec.limit287};288289var build = JX.Prefab.newTokenizerFromTemplate(290this._config.template.markup,291tokenizerConfig);292build.tokenizer.start();293294return [295build.node,296function() {297return build.tokenizer.getTokens();298},299function(map) {300for (var k in map) {301var v = map[k];302303// The control value may be set from wire values from the server,304// or a transformed value from another control, or a bare string305// value from another control.306if (typeof v == 'string') {307v = v;308} else if (!v.hasOwnProperty('id')) {309v = JX.Prefab.transformDatasourceResults(v);310}311312build.tokenizer.addToken(k, v);313}314}];315},316_selectKeys : function(map, keys) {317var r = {};318for (var ii = 0; ii < keys.length; ii++) {319r[keys[ii]] = map[keys[ii]];320}321return r;322},323_renderConditions : function(conditions) {324for (var k in conditions) {325this._newCondition(conditions[k]);326}327},328_newCondition : function(data) {329var row = this._conditionsRowManager.addRow([]);330var row_id = this._conditionsRowManager.getRowID(row);331332var default_condition = [333this._config.default.field,334this._config.default.condition,335null336];337this._config.conditions[row_id] = data || default_condition;338339var r = this._conditionsRowManager.updateRow(340row_id,341this._renderCondition(row_id));342343this._onfieldchange(r);344},345_renderCondition : function(row_id) {346var groups = this._config.info.fields;347348var attrs = {349sigil: 'field-select'350};351352var field_select = this._renderGroupSelect(353groups,354attrs,355this._config.conditions[row_id][0]);356357var field_cell = JX.$N('td', {sigil: 'field-cell'}, field_select);358359var condition_cell = JX.$N('td', {sigil: 'condition-cell'});360var value_cell = JX.$N('td', {className : 'value', sigil: 'value-cell'});361362return [field_cell, condition_cell, value_cell];363},364_renderActions : function(actions) {365for (var k in actions) {366this._newAction(actions[k]);367delete actions[k];368}369},370371_renderGroupSelect: function(groups, attrs, value) {372var optgroups = [];373for (var ii = 0; ii < groups.length; ii++) {374var group = groups[ii];375var options = [];376for (var k in group.options) {377var option = group.options[k];378379var name = option.name;380var available = option.available;381382// See T7961. If the option is not marked as "available", we only383// include it in the dropdown if the dropdown already has it as a384// value. We want to hide options provided by applications which are385// not installed, but do not want to break existing rules.386387if (available || (k === value)) {388options.push(JX.$N('option', {value: k}, name));389}390}391if (options.length) {392optgroups.push(JX.$N('optgroup', {label: group.label}, options));393}394}395396var select = JX.$N('select', attrs, optgroups);397398if (value !== undefined) {399select.value = value;400}401402return select;403},404405_newAction : function(data) {406var default_action = [407this._config.default.action,408null409];410411data = data || default_action;412var temprow = this._actionsRowManager.addRow([]);413var row_id = this._actionsRowManager.getRowID(temprow);414this._config.actions[row_id] = data;415var r = this._actionsRowManager.updateRow(row_id,416this._renderAction(data));417this._onactionchange(r);418},419420_renderAction : function(action) {421var groups = this._config.info.actions;422var attrs = {423sigil: 'action-select'424};425426var action_select = this._renderGroupSelect(427groups,428attrs,429action[0]);430431var action_cell = JX.$N('td', {sigil: 'action-cell'}, action_select);432433var target_cell = JX.$N(434'td',435{className : 'target', sigil : 'target-cell'});436437return [action_cell, target_cell];438},439_renderSelect : function(map, selected, sigil) {440var attrs = {441sigil : sigil442};443return JX.Prefab.renderSelect(map, selected, attrs);444}445}446});447448449