diff options
Diffstat (limited to 'toolkit/jetpack/sdk/context-menu/context.js')
-rw-r--r-- | toolkit/jetpack/sdk/context-menu/context.js | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/toolkit/jetpack/sdk/context-menu/context.js b/toolkit/jetpack/sdk/context-menu/context.js new file mode 100644 index 000000000..fc5aea500 --- /dev/null +++ b/toolkit/jetpack/sdk/context-menu/context.js @@ -0,0 +1,147 @@ +/* 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/. */ + +const { Class } = require("../core/heritage"); +const { extend } = require("../util/object"); +const { MatchPattern } = require("../util/match-pattern"); +const readers = require("./readers"); + +// Context class is required to implement a single `isCurrent(target)` method +// that must return boolean value indicating weather given target matches a +// context or not. Most context implementations below will have an associated +// reader that way context implementation can setup a reader to extract necessary +// information to make decision if target is matching a context. +const Context = Class({ + isRequired: false, + isCurrent(target) { + throw Error("Context class must implement isCurrent(target) method"); + }, + get required() { + Object.defineProperty(this, "required", { + value: Object.assign(Object.create(Object.getPrototypeOf(this)), + this, + {isRequired: true}) + }); + return this.required; + } +}); +Context.required = function(...params) { + return Object.assign(new this(...params), {isRequired: true}); +}; +exports.Context = Context; + + +// Next few context implementations use an associated reader to extract info +// from the context target and story it to a private symbol associtaed with +// a context implementation. That way name collisions are avoided while required +// information is still carried along. +const isPage = Symbol("context/page?") +const PageContext = Class({ + extends: Context, + read: {[isPage]: new readers.isPage()}, + isCurrent: target => target[isPage] +}); +exports.Page = PageContext; + +const isFrame = Symbol("context/frame?"); +const FrameContext = Class({ + extends: Context, + read: {[isFrame]: new readers.isFrame()}, + isCurrent: target => target[isFrame] +}); +exports.Frame = FrameContext; + +const selection = Symbol("context/selection") +const SelectionContext = Class({ + read: {[selection]: new readers.Selection()}, + isCurrent: target => !!target[selection] +}); +exports.Selection = SelectionContext; + +const link = Symbol("context/link"); +const LinkContext = Class({ + extends: Context, + read: {[link]: new readers.LinkURL()}, + isCurrent: target => !!target[link] +}); +exports.Link = LinkContext; + +const isEditable = Symbol("context/editable?") +const EditableContext = Class({ + extends: Context, + read: {[isEditable]: new readers.isEditable()}, + isCurrent: target => target[isEditable] +}); +exports.Editable = EditableContext; + + +const mediaType = Symbol("context/mediaType") + +const ImageContext = Class({ + extends: Context, + read: {[mediaType]: new readers.MediaType()}, + isCurrent: target => target[mediaType] === "image" +}); +exports.Image = ImageContext; + + +const VideoContext = Class({ + extends: Context, + read: {[mediaType]: new readers.MediaType()}, + isCurrent: target => target[mediaType] === "video" +}); +exports.Video = VideoContext; + + +const AudioContext = Class({ + extends: Context, + read: {[mediaType]: new readers.MediaType()}, + isCurrent: target => target[mediaType] === "audio" +}); +exports.Audio = AudioContext; + +const isSelectorMatch = Symbol("context/selector/mathches?") +const SelectorContext = Class({ + extends: Context, + initialize(selector) { + this.selector = selector; + // Each instance of selector context will need to store read + // data into different field, so that case with multilpe selector + // contexts won't cause a conflicts. + this[isSelectorMatch] = Symbol(selector); + this.read = {[this[isSelectorMatch]]: new readers.SelectorMatch(selector)}; + }, + isCurrent(target) { + return target[this[isSelectorMatch]]; + } +}); +exports.Selector = SelectorContext; + +const url = Symbol("context/url"); +const URLContext = Class({ + extends: Context, + initialize(pattern) { + this.pattern = new MatchPattern(pattern); + }, + read: {[url]: new readers.PageURL()}, + isCurrent(target) { + return this.pattern.test(target[url]); + } +}); +exports.URL = URLContext; + +var PredicateContext = Class({ + extends: Context, + initialize(isMatch) { + if (typeof(isMatch) !== "function") { + throw TypeError("Predicate context mus be passed a function"); + } + + this.isMatch = isMatch + }, + isCurrent(target) { + return this.isMatch(target); + } +}); +exports.Predicate = PredicateContext; |