summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Dolgov <[email protected]>2021-12-14 21:53:45 +0300
committerAndrew Dolgov <[email protected]>2021-12-14 21:53:45 +0300
commit720b31879634f21ea7db85f49d6f07ec7d7344bc (patch)
treea8657d8762cd2d5d8ea3118731cc9826e9678d05
parent8a645892a60bc3181f598d2192f01cc21f35b719 (diff)
* fox.form.Select: add several properties allowing it to better
imitate other controls like DropDownButton, etc. * rework several main toolbar items to use fox.form.Select instead of other controls * replace HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM with HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2 because of markup change (option instead of menuitem) * PluginHost: add some explicit typecasts to make intellephense shut up
-rwxr-xr-xclasses/feeds.php2
-rw-r--r--classes/plugin.php13
-rwxr-xr-xclasses/pluginhost.php28
-rw-r--r--index.php21
-rw-r--r--js/Feeds.js4
-rwxr-xr-xjs/Headlines.js94
-rwxr-xr-xjs/form/Select.js64
7 files changed, 179 insertions, 47 deletions
diff --git a/classes/feeds.php b/classes/feeds.php
index 62fd6a5b3..503108e41 100755
--- a/classes/feeds.php
+++ b/classes/feeds.php
@@ -133,7 +133,7 @@ class Feeds extends Handler_Protected {
$reply['vfeed_group_enabled'] = $vfeed_group_enabled;
$plugin_menu_items = "";
- PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM,
+ PluginHost::getInstance()->chain_hooks_callback(PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2,
function ($result) use (&$plugin_menu_items) {
$plugin_menu_items .= $result;
},
diff --git a/classes/plugin.php b/classes/plugin.php
index be8376925..3bced3b04 100644
--- a/classes/plugin.php
+++ b/classes/plugin.php
@@ -647,6 +647,7 @@ abstract class Plugin {
}
/** Allows adding custom elements to headlines Select... dropdown
+ * @deprecated removed, see Plugin::hook_headline_toolbar_select_menu_item2()
* @param int $feed_id
* @param int $is_cat
* @return string
@@ -658,6 +659,18 @@ abstract class Plugin {
return "";
}
+ /** Allows adding custom elements to headlines Select... select dropdown (<option> format)
+ * @param int $feed_id
+ * @param int $is_cat
+ * @return string
+ * @see PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2
+ */
+ function hook_headline_toolbar_select_menu_item2($feed_id, $is_cat) {
+ user_error("Dummy method invoked.", E_USER_ERROR);
+
+ return "";
+ }
+
/** Invoked when user tries to subscribe to feed, may override information (i.e. feed URL) used afterwards
* @param string $url
* @param string $auth_login
diff --git a/classes/pluginhost.php b/classes/pluginhost.php
index a3a389def..952d4df77 100755
--- a/classes/pluginhost.php
+++ b/classes/pluginhost.php
@@ -189,9 +189,14 @@ class PluginHost {
/** @see Plugin::hook_headlines_custom_sort_override() */
const HOOK_HEADLINES_CUSTOM_SORT_OVERRIDE = "hook_headlines_custom_sort_override";
- /** @see Plugin::hook_headline_toolbar_select_menu_item() */
+ /** @see Plugin::hook_headline_toolbar_select_menu_item()
+ * @deprecated removed, see PluginHost::HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2
+ */
const HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM = "hook_headline_toolbar_select_menu_item";
+ /** @see Plugin::hook_headline_toolbar_select_menu_item() */
+ const HOOK_HEADLINE_TOOLBAR_SELECT_MENU_ITEM2 = "hook_headline_toolbar_select_menu_item2";
+
/** @see Plugin::hook_pre_subscribe() */
const HOOK_PRE_SUBSCRIBE = "hook_pre_subscribe";
@@ -270,9 +275,10 @@ class PluginHost {
* @param mixed $args
*/
function run_hooks(string $hook, ...$args): void {
- $method = strtolower($hook);
- foreach ($this->get_hooks($hook) as $plugin) {
+ $method = strtolower((string)$hook);
+
+ foreach ($this->get_hooks((string)$hook) as $plugin) {
//Debug::log("invoking: " . get_class($plugin) . "->$hook()", Debug::$LOG_VERBOSE);
try {
@@ -291,9 +297,9 @@ class PluginHost {
* @param mixed $check
*/
function run_hooks_until(string $hook, $check, ...$args): bool {
- $method = strtolower($hook);
+ $method = strtolower((string)$hook);
- foreach ($this->get_hooks($hook) as $plugin) {
+ foreach ($this->get_hooks((string)$hook) as $plugin) {
try {
$result = $plugin->$method(...$args);
@@ -315,9 +321,9 @@ class PluginHost {
* @param mixed $args
*/
function run_hooks_callback(string $hook, Closure $callback, ...$args): void {
- $method = strtolower($hook);
+ $method = strtolower((string)$hook);
- foreach ($this->get_hooks($hook) as $plugin) {
+ foreach ($this->get_hooks((string)$hook) as $plugin) {
//Debug::log("invoking: " . get_class($plugin) . "->$hook()", Debug::$LOG_VERBOSE);
try {
@@ -336,9 +342,9 @@ class PluginHost {
* @param mixed $args
*/
function chain_hooks_callback(string $hook, Closure $callback, &...$args): void {
- $method = strtolower($hook);
+ $method = strtolower((string)$hook);
- foreach ($this->get_hooks($hook) as $plugin) {
+ foreach ($this->get_hooks((string)$hook) as $plugin) {
//Debug::log("invoking: " . get_class($plugin) . "->$hook()", Debug::$LOG_VERBOSE);
try {
@@ -358,7 +364,7 @@ class PluginHost {
function add_hook(string $type, Plugin $sender, int $priority = 50): void {
$priority = (int) $priority;
- if (!method_exists($sender, strtolower($type))) {
+ if (!method_exists($sender, strtolower((string)$type))) {
user_error(
sprintf("Plugin %s tried to register a hook without implementation: %s",
get_class($sender), $type),
@@ -422,7 +428,7 @@ class PluginHost {
asort($plugins);
- $this->load(join(",", $plugins), $kind, $owner_uid, $skip_init);
+ $this->load(join(",", $plugins), (int)$kind, $owner_uid, $skip_init);
}
/**
diff --git a/index.php b/index.php
index 538346549..2dbc078cd 100644
--- a/index.php
+++ b/index.php
@@ -215,20 +215,13 @@
?>
</select>
- <div class="catchup-button" dojoType="fox.form.ComboButton" onclick="Feeds.catchupCurrent()">
- <span><?= __('Mark as read') ?></span>
- <div dojoType="dijit.DropDownMenu">
- <div dojoType="dijit.MenuItem" onclick="Feeds.catchupCurrent('1day')">
- <?= __('Older than one day') ?>
- </div>
- <div dojoType="dijit.MenuItem" onclick="Feeds.catchupCurrent('1week')">
- <?= __('Older than one week') ?>
- </div>
- <div dojoType="dijit.MenuItem" onclick="Feeds.catchupCurrent('2week')">
- <?= __('Older than two weeks') ?>
- </div>
- </div>
- </div>
+ <select class="catchup-button" id="main-catchup-dropdown" dojoType="fox.form.Select"
+ data-prevent-value-change="true">
+ <option value=""><?= __('Mark as read') ?></option>
+ <option value="1day"><?= __('Older than one day') ?></option>
+ <option value="1week"><?= __('Older than one week') ?></option>
+ <option value="2week"><?= __('Older than two weeks') ?></option>
+ </select>
</form>
diff --git a/js/Feeds.js b/js/Feeds.js
index 5ef554af0..714eb77d2 100644
--- a/js/Feeds.js
+++ b/js/Feeds.js
@@ -282,6 +282,10 @@ const Feeds = {
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 7557676ee..b64ba6bca 100755
--- a/js/Headlines.js
+++ b/js/Headlines.js
@@ -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);
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, '&amp;').replace(/</g, '&lt;') :
+ 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;
+ }
+ },
});
});