mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
706 lines
24 KiB
XML
706 lines
24 KiB
XML
<?xml version="1.0"?>
|
|
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
|
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
|
|
|
|
|
<bindings
|
|
xmlns="http://www.mozilla.org/xbl"
|
|
xmlns:xbl="http://www.mozilla.org/xbl"
|
|
xmlns:html="http://www.w3.org/1999/xhtml"
|
|
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
|
|
|
<binding id="richgrid"
|
|
extends="chrome://global/content/bindings/general.xml#basecontrol">
|
|
|
|
<content>
|
|
<html:div id="grid-div" anonid="grid" class="richgrid-grid">
|
|
<children/>
|
|
</html:div>
|
|
</content>
|
|
|
|
<implementation implements="nsIDOMXULSelectControlElement">
|
|
<property name="_grid" readonly="true" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'grid');"/>
|
|
<field name="controller">null</field>
|
|
|
|
<!-- nsIDOMXULMultiSelectControlElement (not fully implemented) -->
|
|
|
|
<method name="clearSelection">
|
|
<body>
|
|
<![CDATA[
|
|
// 'selection' and 'selected' are confusingly overloaded here
|
|
// as richgrid is adopting multi-select behavior, but select/selected are already being
|
|
// used to describe triggering the default action of a tile
|
|
if (this._selectedItem){
|
|
this._selectedItem.removeAttribute("selected");
|
|
this._selectedItem = null;
|
|
}
|
|
|
|
for (let childItem of this.selectedItems) {
|
|
childItem.removeAttribute("selected");
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="toggleItemSelection">
|
|
<parameter name="anItem"/>
|
|
<body>
|
|
<![CDATA[
|
|
let wasSelected = anItem.selected;
|
|
if ("single" == this.getAttribute("seltype")) {
|
|
this.clearSelection();
|
|
}
|
|
this._selectedItem = wasSelected ? null : anItem;
|
|
anItem.selected = !wasSelected;
|
|
|
|
this._fireOnSelectionChange();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="selectItem">
|
|
<parameter name="anItem"/>
|
|
<body>
|
|
<![CDATA[
|
|
let wasSelected = anItem.selected,
|
|
isSingleMode = ("single" == this.getAttribute("seltype"));
|
|
if (isSingleMode) {
|
|
this.clearSelection();
|
|
}
|
|
this._selectedItem = anItem;
|
|
anItem.selected = true;
|
|
if (isSingleMode) {
|
|
if (!wasSelected) {
|
|
this._fireOnSelect();
|
|
}
|
|
} else {
|
|
this._fireOnSelectionChange();
|
|
}
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="handleItemClick">
|
|
<parameter name="aItem"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (this.controller)
|
|
this.controller.handleItemClick(aItem);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="handleItemContextMenu">
|
|
<parameter name="aItem"/>
|
|
<parameter name="aEvent"/>
|
|
<body>
|
|
<![CDATA[
|
|
// we'll republish this as a selectionchange event on the grid
|
|
aEvent.stopPropagation();
|
|
this.toggleItemSelection(aItem);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
<property name="contextActions">
|
|
<getter>
|
|
<![CDATA[
|
|
// return the subset of verbs that apply to all selected tiles
|
|
let tileNodes = this.selectedItems;
|
|
if (!tileNodes.length) {
|
|
return new Set();
|
|
}
|
|
|
|
// given one or more sets of values,
|
|
// return a set with only those values present in each
|
|
let initialItem = tileNodes[0];
|
|
|
|
let verbSet = new Set(initialItem.contextActions);
|
|
for (let i=1; i<tileNodes.length; i++){
|
|
let set = tileNodes[i].contextActions;
|
|
for (let item of verbSet) {
|
|
if (!set.has(item)){
|
|
verbSet.delete(item);
|
|
}
|
|
}
|
|
}
|
|
// add the clear-selection button if more than one tiles are selected
|
|
if (tileNodes.length > 1) {
|
|
verbSet.add('clear');
|
|
}
|
|
// returns Set
|
|
return verbSet;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<!-- nsIDOMXULSelectControlElement -->
|
|
|
|
<property name="itemCount" readonly="true" onget="return this.children.length;"/>
|
|
|
|
<field name="_selectedItem">null</field>
|
|
<property name="selectedItem" onget="return this._selectedItem;">
|
|
<setter>
|
|
<![CDATA[
|
|
this.selectItem(val);
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<!-- partial implementation of multiple selection interface -->
|
|
<property name="selectedItems">
|
|
<getter>
|
|
<![CDATA[
|
|
return this.querySelectorAll("richgriditem[selected='true']");
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<property name="selectedIndex">
|
|
<getter>
|
|
<![CDATA[
|
|
return this.getIndexOfItem(this._selectedItem);
|
|
]]>
|
|
</getter>
|
|
<setter>
|
|
<![CDATA[
|
|
if (val >= 0) {
|
|
let selected = this.getItemAtIndex(val);
|
|
this.selectItem(selected);
|
|
} else {
|
|
this.clearSelection();
|
|
}
|
|
]]>
|
|
</setter>
|
|
</property>
|
|
|
|
<method name="appendItem">
|
|
<parameter name="aLabel"/>
|
|
<parameter name="aValue"/>
|
|
<parameter name="aSkipArrange"/>
|
|
<body>
|
|
<![CDATA[
|
|
let addition = this._createItemElement(aLabel, aValue);
|
|
this.appendChild(addition);
|
|
if (!aSkipArrange)
|
|
this.arrangeItems();
|
|
return addition;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="clearAll">
|
|
<body>
|
|
<![CDATA[
|
|
while (this.firstChild) {
|
|
this.removeChild(this.firstChild);
|
|
}
|
|
this._grid.style.width = "0px";
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="insertItemAt">
|
|
<parameter name="anIndex"/>
|
|
<parameter name="aLabel"/>
|
|
<parameter name="aValue"/>
|
|
<parameter name="aSkipArrange"/>
|
|
<body>
|
|
<![CDATA[
|
|
let existing = this.getItemAtIndex(anIndex);
|
|
let addition = this._createItemElement(aLabel, aValue);
|
|
if (existing) {
|
|
this.insertBefore(addition, existing);
|
|
} else {
|
|
this.appendChild(addition);
|
|
}
|
|
if (!aSkipArrange)
|
|
this.arrangeItems();
|
|
return addition;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
<method name="removeItemAt">
|
|
<parameter name="anIndex"/>
|
|
<parameter name="aSkipArrange"/>
|
|
<body>
|
|
<![CDATA[
|
|
let removal = this.getItemAtIndex(anIndex);
|
|
this.removeChild(removal);
|
|
if (!aSkipArrange)
|
|
this.arrangeItems();
|
|
return removal;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="getIndexOfItem">
|
|
<parameter name="anItem"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!anItem)
|
|
return -1;
|
|
|
|
return Array.prototype.indexOf.call(this.children, anItem);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="getItemAtIndex">
|
|
<parameter name="anIndex"/>
|
|
<body>
|
|
<![CDATA[
|
|
if (!this._isIndexInBounds(anIndex))
|
|
return null;
|
|
return this.children.item(anIndex);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<!-- Interface for offsetting selection and checking bounds -->
|
|
|
|
<property name="isSelectionAtStart" readonly="true"
|
|
onget="return this.selectedIndex == 0;"/>
|
|
|
|
<property name="isSelectionAtEnd" readonly="true"
|
|
onget="return this.selectedIndex == (this.itemCount - 1);"/>
|
|
|
|
<property name="isSelectionInStartRow" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
return this.selectedIndex < this.columnCount;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<property name="isSelectionInEndRow" readonly="true">
|
|
<getter>
|
|
<![CDATA[
|
|
let lowerBound = (this.rowCount - 1) * this.columnCount;
|
|
let higherBound = this.rowCount * this.columnCount;
|
|
|
|
return this.selectedIndex >= lowerBound &&
|
|
this.selectedIndex < higherBound;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
|
|
<method name="offsetSelection">
|
|
<parameter name="aOffset"/>
|
|
<body>
|
|
<![CDATA[
|
|
let newIndex = this.selectedIndex + aOffset;
|
|
if (this._isIndexInBounds(newIndex))
|
|
this.selectedIndex = newIndex;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="offsetSelectionByRow">
|
|
<parameter name="aRowOffset"/>
|
|
<body>
|
|
<![CDATA[
|
|
let newIndex = this.selectedIndex + (this.columnCount * aRowOffset);
|
|
if (this._isIndexInBounds(newIndex))
|
|
this.selectedIndex -= this.columnCount;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<!-- Interface for grid layout management -->
|
|
|
|
<field name="_rowCount">0</field>
|
|
<property name="rowCount" readonly="true" onget="return this._rowCount;"/>
|
|
|
|
<field name="_columnCount">0</field>
|
|
<property name="columnCount" readonly="true" onget="return this._columnCount;"/>
|
|
|
|
<field name="_scheduledArrangeItemsTries">0</field>
|
|
|
|
<!-- define a height where we consider an item not yet rendered
|
|
10 is the height of the empty item (padding/border etc. only) -->
|
|
<field name="_itemHeightRenderThreshold">10</field>
|
|
|
|
<method name="arrangeItems">
|
|
<body>
|
|
<![CDATA[
|
|
if (this.itemCount <= 0) {
|
|
return;
|
|
}
|
|
let item = this.getItemAtIndex(0);
|
|
if (item == null) {
|
|
return;
|
|
}
|
|
let gridItemRect = item.getBoundingClientRect();
|
|
|
|
// cap the number of times we reschedule calling arrangeItems
|
|
let maxRetries = 5;
|
|
|
|
// delay as necessary until the item has a proper height
|
|
if (gridItemRect.height <= this._itemHeightRenderThreshold) {
|
|
if (this._scheduledArrangeItemsTimerId) {
|
|
// retry of arrangeItems already scheduled
|
|
return;
|
|
}
|
|
|
|
// track how many times we've attempted arrangeItems
|
|
this._scheduledArrangeItemsTries++;
|
|
|
|
if (maxRetries > this._scheduledArrangeItemsTries) {
|
|
// schedule re-try of arrangeItems at the next tick
|
|
this._scheduledArrangeItemsTimerId = setTimeout(this.arrangeItems.bind(this), 0);
|
|
return;
|
|
}
|
|
}
|
|
|
|
// items ready to arrange (or retries max exceeded)
|
|
// reset the flags
|
|
if (this._scheduledArrangeItemsTimerId) {
|
|
clearTimeout(this._scheduledArrangeItemsTimerId);
|
|
delete this._scheduledArrangeItemsTimerId;
|
|
}
|
|
if (this._scheduledArrangeItemsTries) {
|
|
this._scheduledArrangeItemsTries = 0;
|
|
}
|
|
|
|
// Autocomplete is a binding within a binding, so we have to step
|
|
// up an additional parentNode.
|
|
let container = null;
|
|
if (this.parentNode.id == "results-vbox" ||
|
|
this.parentNode.id == "searches-vbox")
|
|
container = this.parentNode.parentNode.getBoundingClientRect();
|
|
else
|
|
container = this.parentNode.getBoundingClientRect();
|
|
|
|
// If we don't have valid dimensions we can't arrange yet
|
|
if (!container.height || !gridItemRect.height) {
|
|
return;
|
|
}
|
|
|
|
// We favor overflowing horizontally, not vertically
|
|
let maxRowCount = Math.floor(container.height / gridItemRect.height) - 1;
|
|
|
|
this._rowCount = this.getAttribute("rows");
|
|
this._columnCount = this.getAttribute("columns");
|
|
|
|
if (!this._rowCount) {
|
|
this._rowCount = Math.min(this.itemCount, maxRowCount);
|
|
}
|
|
if (!this._columnCount){
|
|
this._columnCount = Math.ceil(this.itemCount / this._rowCount);
|
|
}
|
|
|
|
this._grid.style.width = (this._columnCount * gridItemRect.width) + "px";
|
|
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<!-- Inteface to suppress selection events -->
|
|
|
|
<field name="_suppressOnSelect"/>
|
|
<property name="suppressOnSelect"
|
|
onget="return this.getAttribute('suppressonselect') == 'true';"
|
|
onset="this.setAttribute('suppressonselect', val);"/>
|
|
<property name="crossSlideBoundary"
|
|
onget="return this.hasAttribute('crossslideboundary')? this.getAttribute('crossslideboundary') : Infinity;"/>
|
|
|
|
<!-- Internal methods -->
|
|
<field name="_xslideHandler"/>
|
|
<constructor>
|
|
<![CDATA[
|
|
if (this.controller && this.controller.gridBoundCallback != undefined)
|
|
this.controller.gridBoundCallback();
|
|
// set up cross-slide gesture handling for multiple-selection grids
|
|
if ("undefined" !== typeof CrossSlide && "multiple" == this.getAttribute("seltype")) {
|
|
this._xslideHandler = new CrossSlide.Handler(this, {
|
|
REARRANGESTART: this.crossSlideBoundary
|
|
});
|
|
this.addEventListener("touchstart", this._xslideHandler, false);
|
|
this.addEventListener("touchmove", this._xslideHandler, false);
|
|
this.addEventListener("touchend", this._xslideHandler, false);
|
|
}
|
|
// XXX This event was never actually implemented (bug 223411).
|
|
var event = document.createEvent("Events");
|
|
event.initEvent("contentgenerated", true, true);
|
|
this.dispatchEvent(event);
|
|
]]>
|
|
</constructor>
|
|
<destructor>
|
|
<![CDATA[
|
|
if (this._xslideHandler) {
|
|
this.removeEventListener("touchstart", this._xslideHandler);
|
|
this.removeEventListener("touchmove", this._xslideHandler);
|
|
this.removeEventListener("touchend", this._xslideHandler);
|
|
this._xslideHandler = null;
|
|
}
|
|
]]>
|
|
</destructor>
|
|
<method name="_isIndexInBounds">
|
|
<parameter name="anIndex"/>
|
|
<body>
|
|
<![CDATA[
|
|
return anIndex >= 0 && anIndex < this.itemCount;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_createItemElement">
|
|
<parameter name="aLabel"/>
|
|
<parameter name="aValue"/>
|
|
<body>
|
|
<![CDATA[
|
|
let item = this.ownerDocument.createElement("richgriditem");
|
|
item.control = this;
|
|
item.setAttribute("label", aLabel);
|
|
if (aValue)
|
|
item.setAttribute("value", aValue);
|
|
return item;
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<method name="_fireOnSelect">
|
|
<body>
|
|
<![CDATA[
|
|
if (this.suppressOnSelect || this._suppressOnSelect)
|
|
return;
|
|
|
|
var event = document.createEvent("Events");
|
|
event.initEvent("select", true, true);
|
|
this.dispatchEvent(event);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
<method name="_fireOnSelectionChange">
|
|
<body>
|
|
<![CDATA[
|
|
// flush out selection-related cached properties so they get recalc'd next time
|
|
// fire an event?
|
|
if (this.suppressOnSelect || this._suppressOnSelect)
|
|
return;
|
|
|
|
var event = document.createEvent("Events");
|
|
event.initEvent("selectionchange", true, true);
|
|
this.dispatchEvent(event);
|
|
]]>
|
|
</body>
|
|
</method>
|
|
</implementation>
|
|
<handlers>
|
|
<handler event="context-action">
|
|
<![CDATA[
|
|
// context-action is an event fired by the appbar typically
|
|
// which directs us to do something to the selected tiles
|
|
switch (event.action) {
|
|
case "clear":
|
|
this.clearSelection();
|
|
break;
|
|
default:
|
|
if (this.controller && this.controller.doActionOnSelectedTiles) {
|
|
this.controller.doActionOnSelectedTiles(event.action, event);
|
|
}
|
|
}
|
|
]]>
|
|
</handler>
|
|
<handler event="MozCrossSliding">
|
|
<![CDATA[
|
|
// MozCrossSliding is swipe gesture across a tile
|
|
// The tile should follow the drag to reinforce the gesture
|
|
// (with inertia/speedbump behavior)
|
|
let state = event.crossSlidingState;
|
|
let thresholds = this._xslideHandler.thresholds;
|
|
let transformValue;
|
|
switch(state) {
|
|
case "cancelled":
|
|
// hopefully nothing else is transform-ing the tile
|
|
event.target.removeAttribute('crosssliding');
|
|
event.target.style.removeProperty('transform');
|
|
break;
|
|
case "dragging":
|
|
case "selecting":
|
|
event.target.setAttribute("crosssliding", true);
|
|
// just track the mouse in the initial phases of the drag gesture
|
|
transformValue = (event.direction=='x') ?
|
|
'translateX('+event.delta+'px)' :
|
|
'translateY('+event.delta+'px)';
|
|
event.target.style.transform = transformValue;
|
|
break;
|
|
case "selectSpeedBumping":
|
|
case "speedBumping":
|
|
event.target.setAttribute('crosssliding', true);
|
|
// in speed-bump phase, we add inertia to the drag
|
|
let offset = CrossSlide.speedbump(
|
|
event.delta,
|
|
thresholds.SPEEDBUMPSTART,
|
|
thresholds.SPEEDBUMPEND
|
|
);
|
|
transformValue = (event.direction=='x') ?
|
|
'translateX('+offset+'px)' :
|
|
'translateY('+offset+'px)';
|
|
event.target.style.transform = transformValue;
|
|
break;
|
|
// "rearranging" case not used or implemented here
|
|
case "completed":
|
|
event.target.removeAttribute('crosssliding');
|
|
event.target.style.removeProperty('transform');
|
|
break;
|
|
}
|
|
]]>
|
|
</handler>
|
|
<handler event="MozCrossSlideSelect">
|
|
<![CDATA[
|
|
this.toggleItemSelection(event.target);
|
|
]]>
|
|
</handler>
|
|
</handlers>
|
|
</binding>
|
|
<binding id="richgrid-item">
|
|
<content>
|
|
<xul:vbox anonid="anon-richgrid-item" class="richgrid-item-content" xbl:inherits="customImage">
|
|
<xul:hbox class="richgrid-icon-container" xbl:inherits="customImage">
|
|
<xul:box class="richgrid-icon-box"><xul:image anonid="anon-richgrid-item-icon" xbl:inherits="src=iconURI"/></xul:box>
|
|
<xul:box flex="1" />
|
|
</xul:hbox>
|
|
<xul:description anonid="anon-richgrid-item-label" class="richgrid-item-desc" xbl:inherits="value=label" crop="end"/>
|
|
</xul:vbox>
|
|
</content>
|
|
|
|
<implementation>
|
|
<property name="_box" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-richgrid-item');"/>
|
|
<property name="_textbox" onget="return document.getAnonymousElementByAttribute(this, 'class', 'richgrid-item-desc');"/>
|
|
<property name="_icon" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-richgrid-item-icon');"/>
|
|
<property name="_label" onget="return document.getAnonymousElementByAttribute(this, 'anonid', 'anon-richgrid-item-label');"/>
|
|
<property name="iconSrc"
|
|
onset="this._icon.src = val; this.setAttribute('iconURI', val);"
|
|
onget="return this._icon.src;" />
|
|
|
|
<property name="selected"
|
|
onget="return this.hasAttribute('selected');"
|
|
onset="if (val) this.setAttribute('selected', val); else this.removeAttribute('selected');" />
|
|
<property name="url"
|
|
onget="return this.getAttribute('value')"
|
|
onset="this.setAttribute('value', val);"/>
|
|
<property name="label"
|
|
onget="return this._label.getAttribute('value')"
|
|
onset="this.setAttribute('label', val); this._label.setAttribute('value', val);"/>
|
|
<property name="pinned"
|
|
onget="return this.hasAttribute('pinned')"
|
|
onset="if (val) { this.setAttribute('pinned', val) } else this.removeAttribute('pinned');"/>
|
|
|
|
<constructor>
|
|
<![CDATA[
|
|
this.refresh();
|
|
]]>
|
|
</constructor>
|
|
<method name="refresh">
|
|
<body>
|
|
<![CDATA[
|
|
// Prevent an exception in case binding is not done yet.
|
|
if (!this._icon)
|
|
return;
|
|
|
|
// Seed the binding properties from bound-node attribute values
|
|
// Usage: node.refresh()
|
|
// - reinitializes all binding properties from their associated attributes
|
|
|
|
this.iconSrc = this.getAttribute('iconURI');
|
|
this.color = this.getAttribute("customColor");
|
|
this.label = this.getAttribute('label');
|
|
// url getter just looks directly at attribute
|
|
// selected getter just looks directly at attribute
|
|
// pinned getter just looks directly at attribute
|
|
// value getter just looks directly at attribute
|
|
this._contextActions = null;
|
|
this.refreshBackgroundImage();
|
|
]]>
|
|
</body>
|
|
</method>
|
|
|
|
<property name="control">
|
|
<getter><![CDATA[
|
|
var parent = this.parentNode;
|
|
while (parent) {
|
|
if (parent instanceof Components.interfaces.nsIDOMXULSelectControlElement)
|
|
return parent;
|
|
parent = parent.parentNode;
|
|
}
|
|
return null;
|
|
]]></getter>
|
|
</property>
|
|
|
|
<property name="color" onget="return this.getAttribute('customColor');">
|
|
<setter><![CDATA[
|
|
if (val) {
|
|
this.setAttribute("customColor", val);
|
|
this._box.style.backgroundColor = val;
|
|
this._textbox.style.backgroundColor = val;
|
|
} else {
|
|
this.removeAttribute("customColor");
|
|
this._box.style.removeProperty("background-color");
|
|
this._textbox.style.removeProperty("background-color");
|
|
}
|
|
]]></setter>
|
|
</property>
|
|
|
|
<property name="backgroundImage" onget="return this.getAttribute('customImage');">
|
|
<setter><![CDATA[
|
|
if (val) {
|
|
this.setAttribute("customImage", val);
|
|
this._box.style.backgroundImage = val;
|
|
} else {
|
|
this.removeAttribute("customImage");
|
|
this._box.style.removeProperty("background-image");
|
|
}
|
|
]]></setter>
|
|
</property>
|
|
|
|
<method name="refreshBackgroundImage">
|
|
<body><![CDATA[
|
|
if (this.backgroundImage) {
|
|
this._box.style.removeProperty("background-image");
|
|
this._box.style.setProperty("background-image", this.backgroundImage);
|
|
}
|
|
]]></body>
|
|
</method>
|
|
|
|
<field name="_contextActions">null</field>
|
|
<property name="contextActions">
|
|
<getter>
|
|
<![CDATA[
|
|
if(!this._contextActions) {
|
|
this._contextActions = new Set();
|
|
let actionSet = this._contextActions;
|
|
let actions = this.getAttribute("data-contextactions");
|
|
if (actions) {
|
|
actions.split(/[,\s]+/).forEach(function(verb){
|
|
actionSet.add(verb);
|
|
});
|
|
}
|
|
}
|
|
return this._contextActions;
|
|
]]>
|
|
</getter>
|
|
</property>
|
|
</implementation>
|
|
|
|
<handlers>
|
|
<handler event="click" button="0">
|
|
<![CDATA[
|
|
// left-click/touch handler
|
|
this.control.handleItemClick(this, event);
|
|
]]>
|
|
</handler>
|
|
<handler event="contextmenu">
|
|
<![CDATA[
|
|
// fires for right-click, long-click and (keyboard) contextmenu input
|
|
// TODO: handle cross-slide event when it becomes available,
|
|
// .. using contextmenu is a stop-gap measure to allow us to
|
|
// toggle the selected state of tiles in a grid
|
|
this.control.handleItemContextMenu(this, event);
|
|
]]>
|
|
</handler>
|
|
</handlers>
|
|
</binding>
|
|
</bindings>
|