diff options
Diffstat (limited to 'js')
-rw-r--r-- | js/App.js | 110 | ||||
-rw-r--r-- | js/CommonDialogs.js | 30 | ||||
-rw-r--r-- | js/CommonFilters.js | 5 | ||||
-rwxr-xr-x | js/FeedTree.js | 9 | ||||
-rw-r--r-- | js/Feeds.js | 19 | ||||
-rwxr-xr-x | js/Headlines.js | 152 | ||||
-rw-r--r-- | js/PluginHost.js | 12 | ||||
-rw-r--r-- | js/PrefHelpers.js | 9 | ||||
-rw-r--r-- | js/PrefUsers.js | 2 | ||||
-rwxr-xr-x | js/common.js | 3 | ||||
-rwxr-xr-x | js/form/Select.js | 64 |
11 files changed, 324 insertions, 91 deletions
@@ -17,6 +17,9 @@ const App = { hotkey_actions: {}, is_prefs: false, LABEL_BASE_INDEX: -1024, + UserAccessLevels: { + ACCESS_LEVEL_READONLY: -1 + }, _translations: {}, Hash: { get: function() { @@ -76,10 +79,15 @@ const App = { </select> ` }, - select_hash: function(name, value, values = {}, attributes = {}, id = "") { + select_hash: function(name, value, values = {}, attributes = {}, id = "", params = {}) { + let keys = Object.keys(values); + + if (params.numeric_sort) + keys = keys.sort((a,b) => a - b); + return ` <select name="${name}" dojoType="fox.form.Select" id="${App.escapeHtml(id)}" ${this.attributes_to_string(attributes)}> - ${Object.keys(values).map((vk) => + ${keys.map((vk) => `<option ${vk == value ? 'selected="selected"' : ''} value="${App.escapeHtml(vk)}">${App.escapeHtml(values[vk])}</option>` ).join("")} </select> @@ -278,7 +286,33 @@ const App = { }, isCombinedMode: function() { - return this.getInitParam("combined_display_mode"); + return !!this.getInitParam("combined_display_mode"); + }, + setCombinedMode: function(combined) { + const value = combined ? "true" : "false"; + + xhr.post("backend.php", {op: "rpc", method: "setpref", key: "COMBINED_DISPLAY_MODE", value: value}, () => { + this.setInitParam("combined_display_mode", + !this.getInitParam("combined_display_mode")); + + Article.close(); + Headlines.renderAgain(); + }) + }, + isExpandedMode: function() { + return !!this.getInitParam("cdm_expanded"); + }, + setExpandedMode: function(expand) { + if (App.isCombinedMode()) { + const value = expand ? "true" : "false"; + + xhr.post("backend.php", {op: "rpc", method: "setpref", key: "CDM_EXPANDED", value: value}, () => { + this.setInitParam("cdm_expanded", !this.getInitParam("cdm_expanded")); + Headlines.renderAgain(); + }); + } else { + alert(__("This function is only available in combined mode.")); + } }, getActionByHotkeySequence: function(sequence) { const hotkeys_map = this.getInitParam("hotkeys"); @@ -789,7 +823,7 @@ const App = { this.setLoadingProgress(50); this._widescreen_mode = this.getInitParam("widescreen"); - this.setWidescreen(this._widescreen_mode); + this.setWideScreenMode(this.isWideScreenMode(), true); Headlines.initScrollHandler(); @@ -812,6 +846,10 @@ const App = { App.updateRuntimeInfo(); }, 60 * 1000) + if (App.getInitParam("safe_mode") && this.isPrefs()) { + CommonDialogs.safeModeWarning(); + } + console.log("second stage ok"); }, @@ -870,7 +908,22 @@ const App = { } } }, - setWidescreen: function(wide) { + isWideScreenMode: function() { + return !!this._widescreen_mode; + }, + setWideScreenMode: function(wide, quiet = false) { + + if (this.isCombinedMode() && !quiet) { + alert(__("Widescreen is not available in combined mode.")); + return; + } + + // reset stored sizes because geometry changed + Cookie.set("ttrss_ci_width", 0); + Cookie.set("ttrss_ci_height", 0); + + this._widescreen_mode = wide; + const article_id = Article.getActive(); const headlines_frame = App.byId("headlines-frame"); const content_insert = dijit.byId("content-insert"); @@ -1189,39 +1242,16 @@ const App = { } }; this.hotkey_actions["toggle_widescreen"] = () => { - if (!this.isCombinedMode()) { - this._widescreen_mode = !this._widescreen_mode; - - // reset stored sizes because geometry changed - Cookie.set("ttrss_ci_width", 0); - Cookie.set("ttrss_ci_height", 0); - - this.setWidescreen(this._widescreen_mode); - } else { - alert(__("Widescreen is not available in combined mode.")); - } + this.setWideScreenMode(!this.isWideScreenMode()); }; this.hotkey_actions["help_dialog"] = () => { this.hotkeyHelp(); }; this.hotkey_actions["toggle_combined_mode"] = () => { - const value = this.isCombinedMode() ? "false" : "true"; - - xhr.post("backend.php", {op: "rpc", method: "setpref", key: "COMBINED_DISPLAY_MODE", value: value}, () => { - this.setInitParam("combined_display_mode", - !this.getInitParam("combined_display_mode")); - - Article.close(); - Headlines.renderAgain(); - }) + App.setCombinedMode(!App.isCombinedMode()); }; this.hotkey_actions["toggle_cdm_expanded"] = () => { - const value = this.getInitParam("cdm_expanded") ? "false" : "true"; - - xhr.post("backend.php", {op: "rpc", method: "setpref", key: "CDM_EXPANDED", value: value}, () => { - this.setInitParam("cdm_expanded", !this.getInitParam("cdm_expanded")); - Headlines.renderAgain(); - }); + App.setExpandedMode(!App.isExpandedMode()); }; this.hotkey_actions["article_span_grid"] = () => { Article.cdmToggleGridSpan(Article.getActive()); @@ -1282,18 +1312,14 @@ const App = { Feeds.toggleUnread(); break; case "qmcToggleWidescreen": - if (!this.isCombinedMode()) { - this._widescreen_mode = !this._widescreen_mode; - - // reset stored sizes because geometry changed - Cookie.set("ttrss_ci_width", 0); - Cookie.set("ttrss_ci_height", 0); - - this.setWidescreen(this._widescreen_mode); - } else { - alert(__("Widescreen is not available in combined mode.")); - } + App.setWideScreenMode(!App.isWideScreenMode()); break; + case "qmcToggleCombined": + App.setCombinedMode(!App.isCombinedMode()); + break; + case "qmcToggleExpanded": + App.setExpandedMode(!App.isExpandedMode()); + break; case "qmcHKhelp": this.hotkeyHelp() break; diff --git a/js/CommonDialogs.js b/js/CommonDialogs.js index a68dc8068..4cfc6ec70 100644 --- a/js/CommonDialogs.js +++ b/js/CommonDialogs.js @@ -11,6 +11,21 @@ const CommonDialogs = { const dialog = dijit.byId("infoBox"); if (dialog) dialog.hide(); }, + safeModeWarning: function() { + const dialog = new fox.SingleUseDialog({ + title: __("Safe mode"), + content: `<div class='alert alert-info'> + ${__('Tiny Tiny RSS is running in safe mode. All themes and plugins are disabled. You will need to log out and back in to disable it.')} + </div> + <footer class='text-center'> + <button dojoType='dijit.form.Button' type='submit' class='alt-primary'> + ${__('Close this window')} + </button> + </footer>` + }); + + dialog.show(); + }, subscribeToFeed: function() { xhr.json("backend.php", {op: "feeds", method: "subscribeToFeed"}, @@ -131,6 +146,9 @@ const CommonDialogs = { console.log(rc); switch (parseInt(rc['code'])) { + case 0: + dialog.show_error(__("You are already subscribed to this feed.")); + break; case 1: dialog.hide(); Notify.info(__("Subscribed to %s").replace("%s", feed_url)); @@ -175,8 +193,11 @@ const CommonDialogs = { case 6: dialog.show_error(__("XML validation failed: %s").replace("%s", rc['message'])); break; - case 0: - dialog.show_error(__("You are already subscribed to this feed.")); + case 7: + dialog.show_error(__("Error while creating feed database entry.")); + break; + case 8: + dialog.show_error(__("You are not allowed to perform this operation.")); break; } @@ -451,6 +472,7 @@ const CommonDialogs = { xhr.json("backend.php", {op: "pref-feeds", method: "editfeed", id: feed_id}, (reply) => { const feed = reply.feed; + const is_readonly = reply.user.access_level == App.UserAccessLevels.ACCESS_LEVEL_READONLY; // for unsub prompt dialog.feed_title = feed.title; @@ -524,7 +546,9 @@ const CommonDialogs = { <fieldset> <label>${__("Update interval:")}</label> - ${App.FormFields.select_hash("update_interval", feed.update_interval, reply.intervals.update)} + ${App.FormFields.select_hash("update_interval", is_readonly ? -1 : feed.update_interval, + reply.intervals.update, + {disabled: is_readonly})} </fieldset> <fieldset> <label>${__('Article purging:')}</label> diff --git a/js/CommonFilters.js b/js/CommonFilters.js index 8a20480f0..1a0ce1606 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -16,7 +16,8 @@ const Filters = { ACTION_SCORE: 6, ACTION_LABEL: 7, ACTION_PLUGIN: 9, - PARAM_ACTIONS: [4, 6, 7, 9], + ACTION_REMOVE_TAG: 10, + PARAM_ACTIONS: [4, 6, 7, 9, 10], filter_info: {}, test: function() { const test_dialog = new fox.SingleUseDialog({ @@ -397,6 +398,8 @@ const Filters = { xhr.post("backend.php", this.attr('value'), () => { dialog.hide(); + Notify.close(); + const tree = dijit.byId("filterTree"); if (tree) tree.reload(); }); diff --git a/js/FeedTree.js b/js/FeedTree.js index e0c44e2b5..f6c44a71e 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -102,6 +102,15 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co }})); menu.addChild(new dijit.MenuItem({ + label: __("Open site"), + onClick: function() { + App.postOpenWindow("backend.php", {op: "feeds", method: "opensite", + feed_id: this.getParent().row_id, csrf_token: __csrf_token}); + }})); + + menu.addChild(new dijit.MenuSeparator()); + + menu.addChild(new dijit.MenuItem({ label: __("Debug feed"), onClick: function() { /* global __csrf_token */ diff --git a/js/Feeds.js b/js/Feeds.js index 27586ab13..714eb77d2 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -278,21 +278,14 @@ const Feeds = { } if (App.getInitParam("safe_mode")) { - const dialog = new fox.SingleUseDialog({ - title: __("Safe mode"), - content: `<div class='alert alert-info'> - ${__('Tiny Tiny RSS is running in safe mode. All themes and plugins are disabled. You will need to log out and back in to disable it.')} - </div> - <footer class='text-center'> - <button dojoType='dijit.form.Button' type='submit' class='alt-primary'> - ${__('Close this window')} - </button> - </footer>` - }); - - dialog.show(); + /* global CommonDialogs */ + CommonDialogs.safeModeWarning(); } + dojo.connect(dijit.byId("main-catchup-dropdown"), 'onItemClick', + (item) => Feeds.catchupCurrent(item.option.value) + ); + // bw_limit disables timeout() so we request initial counters separately if (App.getInitParam("bw_limit")) { this.requestCounters(); diff --git a/js/Headlines.js b/js/Headlines.js index d01993838..2be3cd697 100755 --- a/js/Headlines.js +++ b/js/Headlines.js @@ -178,7 +178,7 @@ const Headlines = { if (scores.length != 0) { scores.forEach((score) => { promises.push(xhr.post("backend.php", - {op: "article", method: "setScore", "ids[]": ops.rescore[score].toString(), score: score})); + {op: "article", method: "setScore", "ids[]": ops.rescore[score], score: score})); }); } @@ -490,6 +490,7 @@ const Headlines = { id="RROW-${hl.id}" data-article-id="${hl.id}" data-orig-feed-id="${hl.feed_id}" + data-orig-feed-title="${App.escapeHtml(hl.feed_title)}" data-is-packed="1" data-content="${App.escapeHtml(hl.content)}" data-rendered-enclosures="${App.escapeHtml(Article.renderEnclosures(hl.enclosures))}" @@ -514,10 +515,8 @@ const Headlines = { ${hl.cdm_excerpt ? hl.cdm_excerpt : ""} </span> - <div class="feed"> - <a href="#" style="background-color: ${hl.feed_bg_color}" - onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}</a> - </div> + <a href="#" class="feed vfeedMenuAttach" style="background-color: ${hl.feed_bg_color}" data-feed-id="${hl.feed_id}" + onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}</a> <span class="updated" title="${hl.imported}">${hl.updated}</span> @@ -566,6 +565,7 @@ const Headlines = { row = `<div class="hl ${row_class} ${Article.getScoreClass(hl.score)}" id="RROW-${hl.id}" data-orig-feed-id="${hl.feed_id}" + data-orig-feed-title="${App.escapeHtml(hl.feed_title)}" data-article-id="${hl.id}" data-score="${hl.score}" data-article-title="${App.escapeHtml(hl.title)}" @@ -584,9 +584,9 @@ const Headlines = { ${Article.renderLabels(hl.id, hl.labels)} </span> </div> - <span class="feed"> - <a style="background : ${hl.feed_bg_color}" href="#" onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}</a> - </span> + <span class="feed vfeedMenuAttach" data-feed-id="${hl.feed_id}"> + <a style="background : ${hl.feed_bg_color}" href="#" onclick="Feeds.open({feed:${hl.feed_id}})">${hl.feed_title}</a> + </span> <div title="${hl.imported}"> <span class="updated">${hl.updated}</span> </div> @@ -626,6 +626,12 @@ const Headlines = { const search_query = Feeds._search_query ? Feeds._search_query.query : ""; const target = dijit.byId('toolbar-headlines'); + // TODO: is this needed? destroyDescendants() below might take care of it (?) + if (this._headlinesSelectClickHandle) + dojo.disconnect(this._headlinesSelectClickHandle); + + target.destroyDescendants(); + if (tb && typeof tb == 'object') { target.attr('innerHTML', ` @@ -646,27 +652,37 @@ const Headlines = { </span> <span class='right'> <span id='selected_prompt'></span> - <div class='select-articles-dropdown' dojoType='fox.form.DropDownButton' title='"${__('Select articles')}'> - <span>${__("Select...")}</span> - <div dojoType='dijit.Menu' style='display: none;'> - <div dojoType='dijit.MenuItem' onclick='Headlines.select("all")'>${__('All')}</div> - <div dojoType='dijit.MenuItem' onclick='Headlines.select("unread")'>${__('Unread')}</div> - <div dojoType='dijit.MenuItem' onclick='Headlines.select("invert")'>${__('Invert')}</div> - <div dojoType='dijit.MenuItem' onclick='Headlines.select("none")'>${__('None')}</div> - <div dojoType='dijit.MenuSeparator'></div> - <div dojoType='dijit.MenuItem' onclick='Headlines.selectionToggleUnread()'>${__('Toggle unread')}</div> - <div dojoType='dijit.MenuItem' onclick='Headlines.selectionToggleMarked()'>${__('Toggle starred')}</div> - <div dojoType='dijit.MenuItem' onclick='Headlines.selectionTogglePublished()'>${__('Toggle published')}</div> - <div dojoType='dijit.MenuSeparator'></div> - <div dojoType='dijit.MenuItem' onclick='Headlines.catchupSelection()'>${__('Mark as read')}</div> - <div dojoType='dijit.MenuItem' onclick='Article.selectionSetScore()'>${__('Set score')}</div> - ${tb.plugin_menu_items} + + <select class='select-articles-dropdown' + id='headlines-select-articles-dropdown' + data-prevent-value-change="true" + data-dropdown-skip-first="true" + dojoType="fox.form.Select" + title="${__('Show articles')}"> + <option value='' selected="selected">${__("Select...")}</option> + <option value='headlines_select_all'>${__('All')}</option> + <option value='headlines_select_unread'>${__('Unread')}</option> + <option value='headlines_select_invert'>${__('Invert')}</option> + <option value='headlines_select_none'>${__('None')}</option> + <option></option> + <option value='headlines_selectionToggleUnread'>${__('Toggle unread')}</option> + <option value='headlines_selectionToggleMarked'>${__('Toggle starred')}</option> + <option value='headlines_selectionTogglePublished'>${__('Toggle published')}</option> + <option></option> + <option value='headlines_catchupSelection'>${__('Mark as read')}</option> + <option value='article_selectionSetScore'>${__('Set score')}</option> + ${tb.plugin_menu_items != '' ? + ` + <option></option> + ${tb.plugin_menu_items} + ` : ''} ${headlines.id === 0 && !headlines.is_cat ? ` - <div dojoType='dijit.MenuSeparator'></div> - <div dojoType='dijit.MenuItem' class='text-error' onclick='Headlines.deleteSelection()'>${__('Delete permanently')}</div> + <option></option> + <option class='text-error' value='headlines_deleteSelection'>${__('Delete permanently')}</option> ` : ''} - </div> + </select> + ${tb.plugin_buttons} </span> `); @@ -675,6 +691,48 @@ const Headlines = { } dojo.parser.parse(target.domNode); + + this._headlinesSelectClickHandle = dojo.connect(dijit.byId("headlines-select-articles-dropdown"), 'onItemClick', + (item) => { + const action = item.option.value; + + switch (action) { + case 'headlines_select_all': + Headlines.select('all'); + break; + case 'headlines_select_unread': + Headlines.select('unread'); + break; + case 'headlines_select_invert': + Headlines.select('invert'); + break; + case 'headlines_select_none': + Headlines.select('none'); + break; + case 'headlines_selectionToggleUnread': + Headlines.selectionToggleUnread(); + break; + case 'headlines_selectionToggleMarked': + Headlines.selectionToggleMarked(); + break; + case 'headlines_selectionTogglePublished': + Headlines.selectionTogglePublished(); + break; + case 'headlines_catchupSelection': + Headlines.catchupSelection(); + break; + case 'article_selectionSetScore': + Article.selectionSetScore(); + break; + case 'headlines_deleteSelection': + Headlines.deleteSelection(); + break; + default: + if (!PluginHost.run_until(PluginHost.HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2, true, action)) + console.warn('unknown headlines action', action); + } + } + ); }, onLoaded: function (reply, offset, append) { console.log("Headlines.onLoaded: offset=", offset, "append=", append); @@ -1501,6 +1559,48 @@ const Headlines = { menu.startup(); } + /* vfeed menu */ + + if (!dijit.byId("vfeedMenu")) { + + const menu = new dijit.Menu({ + id: "vfeedMenu", + targetNodeIds: ["headlines-frame"], + selector: ".vfeedMenuAttach" + }); + + menu.addChild(new dijit.MenuItem({ + label: __("Mark as read"), + onClick: function() { + Feeds.catchupFeed(this.getParent().currentTarget.getAttribute("data-feed-id")); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("Edit feed"), + onClick: function() { + CommonDialogs.editFeed(this.getParent().currentTarget.getAttribute("data-feed-id"), false); + }})); + + menu.addChild(new dijit.MenuItem({ + label: __("Open site"), + onClick: function() { + App.postOpenWindow("backend.php", {op: "feeds", method: "opensite", + feed_id: this.getParent().currentTarget.getAttribute("data-feed-id"), csrf_token: __csrf_token}); + }})); + + menu.addChild(new dijit.MenuSeparator()); + + menu.addChild(new dijit.MenuItem({ + label: __("Debug feed"), + onClick: function() { + /* global __csrf_token */ + App.postOpenWindow("backend.php", {op: "feeds", method: "updatedebugger", + feed_id: this.getParent().currentTarget.getAttribute("data-feed-id"), csrf_token: __csrf_token}); + }})); + + menu.startup(); + } + /* vgroup feed title menu */ if (!dijit.byId("headlinesFeedTitleMenu")) { diff --git a/js/PluginHost.js b/js/PluginHost.js index deb7c0645..513429e4a 100644 --- a/js/PluginHost.js +++ b/js/PluginHost.js @@ -21,6 +21,7 @@ const PluginHost = { HOOK_HEADLINE_MUTATIONS_SYNCED: 16, HOOK_HEADLINES_RENDERED: 17, HOOK_HEADLINES_SCROLL_HANDLER: 18, + HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2: 19, hooks: [], register: function (name, callback) { if (typeof(this.hooks[name]) == 'undefined') @@ -36,6 +37,17 @@ const PluginHost = { this.hooks[name][i](args); } }, + run_until: function (name, check, ...args) { + //console.warn('PluginHost.run_until', name, check, args); + + if (typeof(this.hooks[name]) != 'undefined') + for (let i = 0; i < this.hooks[name].length; i++) { + if (this.hooks[name][i](args) == check) + return true; + } + + return false; + }, unregister: function (name, callback) { for (let i = 0; i < this.hooks[name].length; i++) if (this.hooks[name][i] == callback) diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index 30a4544fe..c0fff66c9 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -363,8 +363,15 @@ const Helpers = { xhr.json("backend.php", {op: "pref-prefs", method: "getPluginsList"}, (reply) => { this._list_of_plugins = reply; this.render_contents(); + }, (e) => { + this.render_error(e); }); }, + render_error: function(e) { + const container = document.querySelector(".prefs-plugin-list"); + + container.innerHTML = `<li class='text-error'>${__("Error while loading plugins list: %s.").replace("%s", e)}</li>`; + }, render_contents: function() { const container = document.querySelector(".prefs-plugin-list"); @@ -450,7 +457,7 @@ const Helpers = { xhr.json("backend.php", {op: "pref-prefs", method: "uninstallPlugin", plugin: plugin}, (reply) => { if (reply && reply.status == 1) - Helpers.Prefs.refresh(); + Helpers.Plugins.reload(); else { Notify.error("Plugin uninstallation failed."); } diff --git a/js/PrefUsers.js b/js/PrefUsers.js index 7ce3cae94..a6081f35f 100644 --- a/js/PrefUsers.js +++ b/js/PrefUsers.js @@ -75,7 +75,7 @@ const Users = { <fieldset> <label>${__('Access level: ')}</label> ${App.FormFields.select_hash("access_level", - user.access_level, reply.access_level_names, {disabled: admin_disabled.toString()})} + user.access_level, reply.access_level_names, {disabled: admin_disabled.toString()}, "", {numeric_sort: true})} ${admin_disabled ? App.FormFields.hidden_tag("access_level", user.access_level.toString()) : ''} diff --git a/js/common.js b/js/common.js index 1299a0c64..b972f2376 100755 --- a/js/common.js +++ b/js/common.js @@ -186,7 +186,7 @@ const xhr = { }, json: function(url, params = {}, complete = undefined, failed = undefined) { return new Promise((resolve, reject) => - this.post(url, params).then((data) => { + this.post(url, params, null, failed).then((data) => { let obj = null; try { @@ -198,6 +198,7 @@ const xhr = { failed(e); reject(e); + return; } console.log('xhr.json', '<<<', obj, (new Date().getTime() - xhr._ts) + " ms"); diff --git a/js/form/Select.js b/js/form/Select.js index 530880e2d..0c73cd52c 100755 --- a/js/form/Select.js +++ b/js/form/Select.js @@ -1,8 +1,66 @@ -/* global dijit, define */ -define(["dojo/_base/declare", "dijit/form/Select"], function (declare) { - return declare("fox.form.Select", dijit.form.Select, { +/* eslint-disable prefer-rest-params */ +/* global define */ +// FIXME: there probably is a better, more dojo-like notation for custom data- properties +define(["dojo/_base/declare", + "dijit/form/Select", + "dojo/_base/lang", // lang.hitch + "dijit/MenuItem", + "dijit/MenuSeparator", + "dojo/aspect", + ], function (declare, select, lang, MenuItem, MenuSeparator, aspect) { + return declare("fox.form.Select", select, { focus: function() { return; // Stop dijit.form.Select from keeping focus after closing the menu }, + startup: function() { + this.inherited(arguments); + + if (this.attr('data-dropdown-skip-first') == 'true') { + aspect.before(this, "_loadChildren", () => { + this.options = this.options.splice(1); + }); + } + }, + // hook invoked when dropdown MenuItem is clicked + onItemClick: function(/*item, menu*/) { + // + }, + _setValueAttr: function(/*anything*/ newValue, /*Boolean?*/ priorityChange){ + if (this.attr('data-prevent-value-change') == 'true' && newValue != '') + return; + + this.inherited(arguments); + }, + // the only difference from dijit/form/Select is _onItemClicked() handler + _getMenuItemForOption: function(/*_FormSelectWidget.__SelectOption*/ option){ + // summary: + // For the given option, return the menu item that should be + // used to display it. This can be overridden as needed + if (!option.value && !option.label){ + // We are a separator (no label set for it) + return new MenuSeparator({ownerDocument: this.ownerDocument}); + } else { + // Just a regular menu option + const click = lang.hitch(this, "_setValueAttr", option); + const item = new MenuItem({ + option: option, + label: (this.labelType === 'text' ? (option.label || '').toString() + .replace(/&/g, '&').replace(/</g, '<') : + option.label) || this.emptyLabel, + onClick: () => { + this.onItemClick(item, this.dropDown); + + click(); + }, + ownerDocument: this.ownerDocument, + dir: this.dir, + textDir: this.textDir, + disabled: option.disabled || false + }); + item.focusNode.setAttribute("role", "option"); + + return item; + } + }, }); }); |