Lorem ipsum dolor sit amet, consectetur adipiscing elit. diff --git a/extensions/amp-access/0.1/amp-access.js b/extensions/amp-access/0.1/amp-access.js index a9be4e7d1762..14124fd9db1f 100644 --- a/extensions/amp-access/0.1/amp-access.js +++ b/extensions/amp-access/0.1/amp-access.js @@ -14,6 +14,7 @@ * limitations under the License. */ +import {actionServiceFor} from '../../../src/action'; import {assertHttpsUrl} from '../../../src/url'; import {getService} from '../../../src/service'; import {installStyles} from '../../../src/styles'; @@ -122,6 +123,16 @@ export class AccessService { /** @private */ startInternal_() { + actionServiceFor(this.win).installActionHandler( + this.accessElement_, this.handleAction_.bind(this)); + } + + /** + * @param {!ActionInvocation} invocation + * @private + */ + handleAction_(invocation) { + log.fine(TAG, 'Invocation: ', invocation); } } diff --git a/src/custom-element.js b/src/custom-element.js index 83682f292be7..66f3d76e8ca9 100644 --- a/src/custom-element.js +++ b/src/custom-element.js @@ -789,6 +789,7 @@ export function createAmpElementProto(win, name, implementationClass) { const actionQueue = assert(this.actionQueue_); this.actionQueue_ = null; + // TODO(dvoytenko, #1260): dedupe actions. actionQueue.forEach(invocation => { this.executionAction_(invocation, true); }); diff --git a/src/service/action-impl.js b/src/service/action-impl.js index f05cd1ca8c08..15ef09d5b31a 100644 --- a/src/service/action-impl.js +++ b/src/service/action-impl.js @@ -14,8 +14,10 @@ * limitations under the License. */ +import {assert} from '../asserts'; import {getService} from '../service'; import {log} from '../log'; +import {timer} from '../timer'; /** @const {string} */ const TAG_ = 'Action'; @@ -23,6 +25,9 @@ const TAG_ = 'Action'; /** @const {string} */ const ACTION_MAP_ = '__AMP_ACTION_MAP__' + Math.random(); +/** @const {string} */ +const ACTION_QUEUE_ = '__AMP_ACTION_QUEUE__'; + /** @const {string} */ const DEFAULT_METHOD_ = 'activate'; @@ -120,6 +125,40 @@ export class ActionService { this.invoke_(target, method, source, event, null); } + /** + * Installs action handler for the specified element. + * @param {!Element} target + * @param {function(!ActionInvocation)} handler + */ + installActionHandler(target, handler) { + const debugid = target.tagName + '#' + target.id; + assert(target.id && target.id.substring(0, 4) == 'amp-', + 'AMP element is expected: %s', debugid); + + const currentQueue = target[ACTION_QUEUE_]; + if (currentQueue) { + assert(Object.prototype.toString.call(currentQueue) == '[object Array]', + 'Expected queue to be an array: %s', debugid); + } + + // Override queue with the handler. + target[ACTION_QUEUE_] = {'push': handler}; + + // Dequeue the current queue. + if (currentQueue) { + timer.delay(() => { + // TODO(dvoytenko, #1260): dedupe actions. + currentQueue.forEach(invocation => { + try { + handler(invocation); + } catch (e) { + log.error(TAG_, 'Action execution failed:', invocation, e); + } + }); + }, 1); + } + } + /** * @param {!Element} source * @param {string} actionEventType @@ -169,22 +208,32 @@ export class ActionService { // TODO(dvoytenko): implement common method handlers, e.g. "toggleClass" - // Only amp elements are allowed to proceed further. - if (target.tagName.toLowerCase().substring(0, 4) != 'amp-') { - this.actionInfoError_('Target must be an AMP element', actionInfo, - target); + // AMP elements. + if (target.tagName.toLowerCase().substring(0, 4) == 'amp-') { + if (target.enqueAction) { + target.enqueAction(invocation); + } else { + this.actionInfoError_('Unrecognized AMP element "' + + target.tagName.toLowerCase() + '". ' + + 'Did you forget to include it via