Repository: medialize/jQuery-contextMenu Branch: master Commit: 39805525db8d Files: 150 Total size: 1.3 MB Directory structure: gitextract_y3pnf_da/ ├── .editorconfig ├── .gitignore ├── .jscsrc ├── .sauce.yml ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bower.json ├── contextMenu.jquery.json ├── dist/ │ ├── jquery.contextMenu.css │ ├── jquery.contextMenu.js │ └── jquery.ui.position.js ├── documentation/ │ ├── CONTRIBUTE.md │ ├── couscous.yml │ ├── demo/ │ │ ├── accesskeys.md │ │ ├── accesskeys_test.md │ │ ├── async-create.md │ │ ├── async-promise.md │ │ ├── callback.md │ │ ├── callback_test.md │ │ ├── custom-command.md │ │ ├── custom-command_test.md │ │ ├── disabled-callback.md │ │ ├── disabled-callback_test.md │ │ ├── disabled-changing.md │ │ ├── disabled-changing_test.md │ │ ├── disabled-menu.md │ │ ├── disabled.md │ │ ├── disabled_test.md │ │ ├── dynamic-create.md │ │ ├── dynamic.md │ │ ├── fontawesome-icons.md │ │ ├── html5-import.md │ │ ├── html5-polyfill-firefox8.md │ │ ├── html5-polyfill.md │ │ ├── input.md │ │ ├── keeping-contextmenu-open.md │ │ ├── menu-promise.md │ │ ├── menu-title.md │ │ ├── on-dom-element.md │ │ ├── sub-menus-promise.md │ │ ├── sub-menus.md │ │ ├── sub-menus_test.md │ │ ├── trigger-custom.md │ │ ├── trigger-hover-autohide.md │ │ ├── trigger-hover.md │ │ ├── trigger-left-click.md │ │ └── trigger-swipe.md │ ├── demo.md │ ├── docs/ │ │ ├── custom-command-types.md │ │ ├── customize.md │ │ ├── events.md │ │ ├── font-awesome.md │ │ ├── html5-polyfill.md │ │ ├── input-helpers.md │ │ ├── items.md │ │ ├── plugin-commands.md │ │ └── runtime-options.md │ ├── docs.md │ ├── index.md │ └── website/ │ ├── .gitignore │ ├── LICENSE │ ├── README.md │ ├── couscous.yml │ ├── css/ │ │ ├── screen.css │ │ ├── theme-fixes.css │ │ └── theme.css │ ├── default.twig │ ├── fonts/ │ │ └── FontAwesome.otf │ ├── js/ │ │ ├── main.js │ │ └── theme.js │ └── original-theme/ │ ├── bower.json │ ├── css/ │ │ └── badge_only.css │ └── sass/ │ ├── _theme_badge.sass │ ├── _theme_badge_fa.sass │ ├── _theme_breadcrumbs.sass │ ├── _theme_font_awesome_compatibility.sass │ ├── _theme_layout.sass │ ├── _theme_mathjax.sass │ ├── _theme_rst.sass │ ├── _theme_variables.sass │ ├── badge_only.sass │ └── theme.sass ├── gulpfile.js ├── karma-saucelabs.conf.js ├── karma.conf.js ├── package.json ├── src/ │ ├── .csscomb.json │ ├── .csslintrc │ ├── .jshintrc │ ├── jquery.contextMenu.js │ ├── jquery.ui.position.js │ └── sass/ │ ├── _icons.scss │ ├── _variables.scss │ ├── icons/ │ │ ├── _icon_classes.scss.tpl │ │ ├── _mixins.scss │ │ ├── _variables.scss │ │ └── _variables.scss.tpl │ └── jquery.contextMenu.scss ├── test/ │ ├── index.html │ ├── integration/ │ │ ├── custom-command.js │ │ ├── disabled-callback.js │ │ ├── disabled-changing.js │ │ ├── disabled-menu.js │ │ ├── disabled.js │ │ ├── dynamic-create.js │ │ ├── dynamic.js │ │ ├── html/ │ │ │ ├── accesskeys.html │ │ │ ├── accesskeys_test.html │ │ │ ├── async-create.html │ │ │ ├── callback.html │ │ │ ├── callback_test.html │ │ │ ├── custom-command.html │ │ │ ├── custom-command_test.html │ │ │ ├── disabled-callback.html │ │ │ ├── disabled-callback_test.html │ │ │ ├── disabled-changing.html │ │ │ ├── disabled-changing_test.html │ │ │ ├── disabled-menu.html │ │ │ ├── disabled.html │ │ │ ├── disabled_test.html │ │ │ ├── dynamic-create.html │ │ │ ├── dynamic.html │ │ │ ├── html5-import.html │ │ │ ├── html5-polyfill-firefox8.html │ │ │ ├── html5-polyfill.html │ │ │ ├── input.html │ │ │ ├── keeping-contextmenu-open.html │ │ │ ├── menu-title.html │ │ │ ├── on-dom-element.html │ │ │ ├── sub-menus.html │ │ │ ├── sub-menus_test.html │ │ │ ├── trigger-custom.html │ │ │ ├── trigger-hover-autohide.html │ │ │ ├── trigger-hover.html │ │ │ ├── trigger-left-click.html │ │ │ └── trigger-swipe.html │ │ ├── input.js │ │ ├── keeping-contextmenu-open.js │ │ ├── on-dom-element.js │ │ ├── trigger-custom.js │ │ ├── trigger-left-click.js │ │ └── trigger-right-click.js │ ├── specs/ │ │ ├── accesskeys.js │ │ ├── aync-create.js │ │ ├── callback.js │ │ └── submenu.js │ └── unit/ │ └── contextmenu.test.js └── wdio.conf.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ root = true [**] charset = utf-8 end_of_line = lf indent_size = 4 indent_style = space insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: .gitignore ================================================ .DS_Store node_modules/ bower_components/ ### Node template # Logs logs *.log npm-debug.log* # Runtime data pids *.pid *.seed # Directory for instrumented libs generated by jscoverage/JSCover lib-cov # Coverage directory used by tools like istanbul coverage # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) .grunt # node-waf configuration .lock-wscript # Compiled binary addons (http://nodejs.org/api/addons.html) build/Release # Dependency directory # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git node_modules ### JetBrains template # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio *.iml ## Directory-based project format: .idea/ # if you remove the above rule, at least ignore the following: # User-specific stuff: # .idea/workspace.xml # .idea/tasks.xml # .idea/dictionaries # Sensitive or high-churn files: # .idea/dataSources.ids # .idea/dataSources.xml # .idea/sqlDataSources.xml # .idea/dynamic.xml # .idea/uiDesigner.xml # Gradle: # .idea/gradle.xml # .idea/libraries # Mongo Explorer plugin: # .idea/mongoSettings.xml ## File-based project format: *.ipr *.iws ## Plugin-specific files: # IntelliJ /out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties ================================================ FILE: .jscsrc ================================================ { "requireCurlyBraces": [ "if", "else", "for", "while", "do", "try", "catch" ], "requireOperatorBeforeLineBreak": true, "requireParenthesesAroundIIFE": true, "requireCommaBeforeLineBreak": true, "requireCamelCaseOrUpperCaseIdentifiers": true, "requireDotNotation": true, "requireSpacesInForStatement": true, "requireSpaceBetweenArguments": true, "maximumLineLength": { "value": 100, "tabSize": 4, "allExcept": ["urlComments", "regex"] }, "validateQuoteMarks": { "mark": "\"", "escape": true }, "disallowMixedSpacesAndTabs": "smart", "disallowTrailingWhitespace": true, "disallowMultipleLineStrings": true, "disallowTrailingComma": true, "disallowSpaceBeforeComma": true, "requireSpaceAfterComma": true, "requireSpaceBeforeBlockStatements": true, "requireSpacesInFunctionExpression": { "beforeOpeningCurlyBrace": true }, "requireSpaceAfterKeywords": [ "if", "else", "for", "while", "do", "switch", "return", "try", "catch" ], "requireSpacesInsideObjectBrackets": "all", "requireSemicolons": true, "requireSpaceAfterBinaryOperators": true, "requireLineFeedAtFileEnd": true, "requireSpaceBeforeBinaryOperators": [ "=", "+=", "-=", "*=", "/=", "%=", "<<=", ">>=", ">>>=", "&=", "|=", "^=", "+=", "+", "-", "*", "/", "%", "<<", ">>", ">>>", "&", "|", "^", "&&", "||", "===", "==", ">=", "<=", "<", ">", "!=", "!==" ], "requireSpacesInAnonymousFunctionExpression": { "beforeOpeningCurlyBrace": true }, "requireSpacesInNamedFunctionExpression": { "beforeOpeningCurlyBrace": true }, "requirePaddingNewLinesBeforeLineComments": true, "validateLineBreaks": "LF", "disallowKeywords": [ "with" ], "disallowKeywordsOnNewLine": [ "else" ], "disallowSpacesInFunctionExpression": { "beforeOpeningRoundBrace": true }, "disallowSpacesInNamedFunctionExpression": { "beforeOpeningRoundBrace": true }, "disallowSpacesInAnonymousFunctionExpression": { "beforeOpeningRoundBrace": true }, "disallowSpacesInCallExpression": true, "disallowSpaceAfterObjectKeys": true, "disallowSpaceAfterPrefixUnaryOperators": true, "disallowSpaceBeforePostfixUnaryOperators": true, "disallowSpaceBeforeBinaryOperators": [ "," ], "disallowMultipleLineBreaks": true } ================================================ FILE: .sauce.yml ================================================ --- language: "javascript" framework: "webdriverio" configPath: "wdio.conf.js" ================================================ FILE: .travis.yml ================================================ sudo: false cache: directories: - node_modules env: global: - GIT_NAME: "'Couscous auto deploy'" - GIT_EMAIL: couscous@couscous.io - GH_REF: github.com/swisnl/jQuery-contextMenu - SAUCE_USERNAME: "bbrala-contextmenu" # GH_TOKEN - secure: gWIh9j5sUgVn9DmVDYnmS4OT2GhHmsndLTPKRLgAF8LM4sa65SkSCNB4Xu5drcb1a6e4egOe0bXNgXSl70ApBoQIxPvjvWxLBCjPQBPD6kJjo2ovOsfxLARMSGAqlGN7hiocLi+s8qa7RP3uuJ373z+Ge+bLIV6vFxEjK9U3Iz4= # SAUCE_ACCESS_KEY - secure: UOse3txRLxLQKsPVQf6OKZZP3c0nLaPJ+4G2vR/qJqBXCTTCQ84+9qx9ih/40FDFcjVXwabJsdn0EhkqDw4h50OGdc58V1UfSbk7g1RiuvvRakOPTK0J9h7bEkBPb7QQXCvAVfOZ81DN6l5lMjmH1tiC2T/h/MNOLHPXsbzXElg= matrix: include: - language: node_js node_js: "10" env: JQUERY=1 - language: node_js node_js: "10" env: JQUERY=2 - language: node_js node_js: "10" env: JQUERY=3 - language: php php: 7.4 env: DOCUMENTATION=1 allow_failures: - env: DOCUMENTATION=1 install: - if [ "$JQUERY" ] ; then yarn add jquery@$JQUERY ; fi - if [ "$DOCUMENTATION" ] ; then cd documentation && composer global require couscous/couscous ; fi script: - if [ "$JQUERY" ] ; then yarn run test ; else /home/travis/.config/composer/vendor/bin/couscous travis-auto-deploy --php-version=7.4 ; fi - if [ "$JQUERY" ] && [ "$SAUCE_USERNAME" ] ; then yarn run test-sauce ; fi deploy: provider: npm email: bjorn@swis.nl api_key: secure: YvSWphr8aTtwhvzO99jAVl4KoGHFEwwYVf1R7mklO3ZvU4yM1AYQ7m/gwpgkO3vBl0U6C1ixoE5VZzZHHDx3U1UAWeKktVdTvCp3uBDlnRVJdPIQ2gR5hT8X6j6LLTBc1iN/gaf5rT8xTeeeJp/M9gK6f757k88ZLm1DP/mdU3w= on: tags: true repo: swisnl/jQuery-contextMenu branch: master condition: $JQUERY = 3 ================================================ FILE: CHANGELOG.md ================================================ ## Changelog ## ### Unreleased * Context menu no longer jumps to the top of the screen #749 ### 2.9.2 * Fix reflow when adding click layer to page (fixed #721, thanks @Rhain) ### 2.9.1 * Fix error when closing the menu by clicking on the page without any element under that click point. (fixes #717) * Upgrades dependencies ### 2.9.0 #### Added * Added `dataAttr` option to add arbitrary data attributes to menu items. #### Changed * Updated dev dependencies. ### 2.8.1 #### Fixed * Added FontAwesome `fab` class to known classes. #### Documentation * Updated documentation for `callback` (thanks @arashdalir) ### 2.8.0 #### Added * Added support for `events.preShow` so you can enable default browser menu if needed (thanks @terwarf) ### 2.7.1 #### Fixed * A context menu appears outside the screen Under certain conditions (thanks @so-susa) * No font-awesome icons visible in submenu ([Issue #659](https://github.com/swisnl/jQuery-contextMenu/issues/659)) thanks @betafritz and @klues ### 2.7.0 #### Documentation * Add `getting started` to the documentation. * Fixed typo in documentation which breaks the `callback` demo. * Fixed typo `promis` => `promise` ([Issue #633](https://github.com/swisnl/jQuery-contextMenu/issues/633)). * Fixed arguments for callback option in documentation ([Issue #571](https://github.com/swisnl/jQuery-contextMenu/issues/571)). #### Added * Added support for Font Awesome 5 ([Issue #593](https://github.com/swisnl/jQuery-contextMenu/issues/593)), ([Issue #593](https://github.com/swisnl/jQuery-contextMenu/issues/593)) ### 2.6.4 #### Fixed * `events.activated` is called without `options`as argument ([Issue #580](https://github.com/swisnl/jQuery-contextMenu/issues/580)). * LayerClick sometimes breaks when the source is not a mouseevent ([Issue #132](https://github.com/swisnl/jQuery-contextMenu/issues/132)). * The contextmenu now checks `visible` on items once instead of twice. Fixes [issue 612](https://github.com/swisnl/jQuery-contextMenu/issues/612). * Font awesome li height is now consistent again ([Issue #610](https://github.com/swisnl/jQuery-contextMenu/issues/610)). ### 2.6.3 #### Fixed * Broke build script after 2.5.0 which ment no updates to dist folder ([Issue #578](https://github.com/swisnl/jQuery-contextMenu/issues/578)). ### 2.6.2 #### Fixed * Dev dependency ended up in normal dependencies. ### 2.6.1 #### Added * Ability to define touchstart as trigger (thanks @npuser) * Extra event `activated` that triggers after the menu is activated (thanks @AliShahrivarian) * Flag denoting if a second trigger should close the menu (thanks @OliverColeman) * Added update call to update visibility, disabled, icon and form value stats for items. Fixes issue ([Issue #555](https://github.com/swisnl/jQuery-contextMenu/issues/555)). ```javascript $('.context-menu-one').contextMenu('update'); // update single menu $.contextMenu('update') // update all open menus ``` #### Fixed * Fix for out of bounds problem on window edges (thanks @AliShahrivarian) ### 2.5.0 #### Added * Callback function now supplies original event ([Issue #211](https://github.com/swisnl/jQuery-contextMenu/issues/211)) thanks @wizzard0 ### 2.4.5 #### Fixed * ContextMenu appears with wrong position ([Issue #502](https://github.com/swisnl/jQuery-contextMenu/issues/502) thanks @apptaro * Check if given selected value is a 0, if it is a zero so return it as is. Thanks @Falseee * Events are never trigger when opening a contextMenu right after the other ([Issue #454](https://github.com/swisnl/jQuery-contextMenu/issues/454) thanks @kagant15 * Accesskey jQuery Modal Dialog not working ([Issue #506](https://github.com/swisnl/jQuery-contextMenu/issues/506) thanks @CiTRO33 * Fix submenu hover not always staying active if hovering over a submenu item. ([Issue #523](https://github.com/swisnl/jQuery-contextMenu/issues/523) thanks @tim-nz * Change $node.click() to $node.get(0).click() to allow native event in HTML5 ([Issue #517](https://github.com/swisnl/jQuery-contextMenu/issues/517) ### 2.4.4 #### Fixed * trigger is sometimes called on undefined objects because of typecheck on null. thanks @andreasrosdal ### 2.4.3 #### Changed * The inline style causes a Content Security Policy violation if style-src 'unsafe-inline' is not defined in the policy. [PR 498](https://github.com/swisnl/jQuery-contextMenu/pull/498) thanks @StealthDuck * Removed GPL license from the comment in the plugin. Was already removed everywhere else. Only MIT applies now. #### Added * Added SauceLabs tests for common browsers. ### 2.4.2 ### ### Fixed * Focus not set on content editable element when right clicking the second time ([Issue #482](https://github.com/swisnl/jQuery-contextMenu/issues/482)) * `selectableSubMenu` broke disabling click menu (fixes ([Issue #493](https://github.com/swisnl/jQuery-contextMenu/issues/493)) ### 2.4.1 ### #### Fixed * Quick fix for error in visible check ([Issue #484](https://github.com/swisnl/jQuery-contextMenu/issues/484)) #### Updated * Tweaked positioning of submenu ([Issue #387](https://github.com/swisnl/jQuery-contextMenu/issues/387)) ### 2.4.0 ### #### Added * Selectable Sub Menus ([Issue #483](https://github.com/swisnl/jQuery-contextMenu/issues/483)) thanks @zyuhel #### Fixed * The contextmenu shows even if all items are set to visible:false ([Issue #473](https://github.com/swisnlhttps://github.com/swisnl/jQuery-contextMenu/issues/482/jQuery-contextMenu/issues/473)) #### Documentation * Update documentation to include demo for async promise fixes ([Issue #470](https://github.com/swisnl/jQuery-contextMenu/issues/470)) ### 2.3.0 ### #### Added * Asynchronous promise support for submenu's ([Issue #429](https://github.com/swisnl/jQuery-contextMenu/issues/429)) thanks @Ruud-cb for the hard work. * Include dist and src in package.json to easily use SCSS files ([PR #467](https://github.com/swisnl/jQuery-contextMenu/pull/467)) thanks @RoachMech #### Fixed * Font family when using font awesome ([Issue #433](https://github.com/swisnl/jQuery-contextMenu/issues/433)) * Add check for `opt.$menu` is null when handling callbacks. ([Issue #462](https://github.com/swisnl/jQuery-contextMenu/issues/462)) thanks @andreasrosdal #### Changed * Make `` and `` tags xhtml compatible ([Issue #451](https://github.com/swisnl/jQuery-contextMenu/issues/451)) thanks @andreasplesch * Update jQuery UI position to 1.12.1 #### Documentation * Fix demo for custom-command. ([Issue #294](https://github.com/swisnl/jQuery-contextMenu/issues/294)) * Fix broken link and demo title ([Issue #458](https://github.com/swisnl/jQuery-contextMenu/issues/458)) ### 2.2.4 ### #### Fixed * Error on try to recreate menu after destroy ([Issue #397](https://github.com/swisnl/jQuery-contextMenu/issues/397)) ### 2.2.3 ### #### Fixed * Callbacks are now called from the scope of the menu the item is in (like a submenu). For now they overwrite root callbacks only if the item is not in a submenu, this so the callbacks are always correct. Unfortunately this will also mean the callbacks option is still not complete if you use the same key for an item in any place. Cant fix that easily. Issue #413. ### 2.2.1 ### #### Added * Alias for 'cm_seperator' type: 'cm_separator' (thanks @nelson6e65) #### Changed * Removed old integration tests, framework on which they were built is abandoned. * Enable jQuery 3 tests in TravisCI #### Fixed * jQuery 3 support was fixed again, was a result of jQuery UI (Fixes #407) * Add checks for null before using opt.$menu and root.$menu. Fixes #352 (thanks @andreasrosdal) * Small fix for color or ``input`` option on hover #### Documentation * Documentation added for cm_seperator (thanks @nelson6e65) * Fix typo in items options documentation (thanks @nelson6e65) * Fix typo in animation: fadeOut (thanks @avi-meslati-sp) * Fix typo in docs code: `show` is used twice (thanks @kgeorgiou) * Fix in async documentation. ### 2.2.0 ### #### Added * Add option to show item title as HTML (thanks @brassard) * Full Font Awesome support #### Changed * Use relative units for css fixes ([Issue #386](https://github.com/swisnl/jQuery-contextMenu/issues/386)) (thanks @RoachMech) * Change unicode characters in CSS to readable strings. * Improved item styles (thanks @anseki) #### Fixed * Force woff2 font creation for Windows some machines. * Fix so that disabled items can't get focus anymore (thanks @anseki) * Fix so menu size is calculated better no items will take up 2 lines again (thanks @anseki) * Fix bower.json (thanks @nelson6e65) * Fix typo in documentation for "position" and "build" callback (thanks @mmcev106) ### 2.1.1 ### * Fixed a problem when using the open function with custom arguments (thanks @RareDevil) * `width` is increased when repoening menu. Fixed by using outerwidth to calculate width. Fixes #360 (thanks @anseki) * Submenus are not collapsed when the menu is closed fixes #358 (thanks @anseki) * Small delay in checking for autohide to fix missing the menu by a pixel or two. Fixes #347 (thanks @Risord) * Check if an item is not hidden in any way when scrolling through items with the keyboard. Fixes #348 * Change links and base url of documentation to https as mentioned by @OmgImAlexis in PR#345 ### 2.1.0 ### * Added support for providing a function as zIndex value in options object (thanks @eivindga) * Fixed a switch to use the correct type for separators (thanks @RareDevil) * Fixed the problem with submenus size wrongly ([Issue #308](https://github.com/swisnl/jQuery-contextMenu/issues/308)) (thanks @RareDevil) * Incorrect entry on package.json ([Issue #336](https://github.com/swisnl/jQuery-contextMenu/issues/336)) (thanks @Dijir) * Gray out disabled icons as well as text ([Issue #337](https://github.com/swisnl/jQuery-contextMenu/issues/337)) (thanks @r02b) * Optimized generated CSS so that ``context-menu-icon`` class can be used to overwrite icon CSS. * Positioning of contextmenu when using appendTo (thanks @mrMarco) * Check to see if target have a higher zIndex than the contextmenu in the key event handler (thanks @RareDevil) ### 2.0.1 (December 3rd 2015) ### * Remove executable bit from jquery.contextMenu.js (thanks @jacknagel) * Fixed a problem there was when using a function for icons (thanks @RareDevil) * Fixed a problem where submenus resized wrong (thanks @RareDevil) * Fixed a problem where the contextmenu would open another menu (thanks @RareDevil) - ([Issue #252](https://github.com/swisnl/jQuery-contextMenu/issues/252) and [Issue #293](https://github.com/swisnl/jQuery-contextMenu/issues/293)) * Fixed regression of node name's not being appended to the label of input elements. (thanks @RareDevil) * Add check that root.$layer exists, to prevent calling hide() on an defined object. (thanks @andreasrosdal) ### 2.0.0 (October 28th 2015) ### * __This version changes the default names of the icon classes in order to stop CSS conflicts with frameworks which define the class 'icon'.__ In order to keep the icon names the same as before this change you can change the defaults on the classnames for the icons ([docs on classNames option](http://swisnl.github.io/jQuery-contextMenu/docs.html#options-classNames)). The classnames will probably be "context-menu-icon-*" as proposed earlier by @rodneyrehm. * You can not use SASS to customize your contextmenu. The gulp command build-icons takes all the SVG icons from src/icons and builds them into a font. In order to this we needed to break backwards compatibility. This does mean the new CSS does not have the old .icon class defined which makes it a lot more stable within CSS frameworks. The first revision of the documentation is found [here](documentation/docs/customize.md). * The 1.x branch will be maintained for a while with bugfixes. But support for 1.x will be dropped in the coming months. * Reverted the change from 1.7.0: .html() changed back to .text() since it is an security issue (thanks @arai-a) ### 1.10.1 (October 25th 2015) ### * Added gulp command (integration-test-paths) to change the paths in the integration tests to the correct path after they are overwritten by the documentation generator. * Make sure the contextmenu is not outside the client area by (thanks to @arai-a) * Update jQuery dependecy so that it will not result in double installation of jQuery when using npm (thanks to @fredericlb) ### 1.9.1 (October 11th 2015) ### * Fixed a bug where the classNames options would fail on a submenu. * New documentation site and generation using [couscous](https://github.com/CouscousPHP/Couscous) ### 1.9.0 (October 1st 2015) ### * Make classes configurable for those that can easily conflict. See the [docs on classNames option](http://swisnl.github.io/jQuery-contextMenu/docs.html#options-classNames). This also prepares to change classnames to non conflicting defaults so the hassle with frameworks as bootstrap will stop. * Fix for handling of seperator string. It threw an error on the protected property of String.$node * Fix for opening the contextmenu at coordinate 0,0 (by [Andreme](https://github.com/andreme)) * Fixed check for jQuery UI ([Issue #182](https://github.com/swisnl/jQuery-contextMenu/issues/182)) * Updated doc for function argument for icon ### 1.8.1 (September 14th 2015) ### * Updated readme. * Updated dist files ### 1.8.0 (September 14th 2015) - dist files not updated! ### * Added dist folder with compiled JS and CSS, added these files to package and bower configuration. * Fixed doc link for jQuery UI position ([Issue #274](https://github.com/swisnl/jQuery-contextMenu/issues/274)) * Item icon can now be a callback to dynamically decide on icon class. - ([Issue #158](https://github.com/swisnl/jQuery-contextMenu/issues/158), [Issue #129](https://github.com/swisnl/jQuery-contextMenu/issues/129), [Issue #151](https://github.com/swisnl/jQuery-contextMenu/issues/151), [Issue #249](https://github.com/swisnl/jQuery-contextMenu/issues/249)) * Small fix to calculating width and height on screen edges when padding is present. ### 1.7.0 (August 29th 2015) ### * Touch support optimisations (by kccarter76) * changed .text to .html so there are no extra span's fixed - ([Issue #252](https://github.com/swisnl/jQuery-contextMenu/issues/252)) * added visibility callback to item definition * copy the HTML5 icon attribute when creating from HTML5 elements * growing menu when opening multiple times fixed - ([Issue #197](https://github.com/swisnl/jQuery-contextMenu/issues/197)) * fixed failure to run tests ### 1.6.8 (August 18th 2015) ### * changes for new maintainer ### 1.6.7 (May 21st 2015) ### * looking for maintainer note * publish to npm ### 1.6.6 (July 12th 2014) ### * fixing bower manifest ### 1.6.5 (January 20th 2013) ### * fixing "opening a second menu can break the layer" - ([Issue #105](https://github.com/swisnl/jQuery-contextMenu/issues/105)) ### 1.6.4 (January 19th 2013) ### * fixing [jQuery plugin manifest](https://github.com/swisnl/jQuery-contextMenu/commit/413b1ecaba0aeb4e50f97cee35f7c367435e7830#commitcomment-2465216), again. yep. I'm that kind of a guy. :( ### 1.6.3 (January 19th 2013) ### * fixing [jQuery plugin manifest](https://github.com/swisnl/jQuery-contextMenu/commit/413b1ecaba0aeb4e50f97cee35f7c367435e7830#commitcomment-2465216) ### 1.6.2 (January 19th 2013) ### * fixing "menu won't close" regression introduced by 1.6.1 ### 1.6.1 (January 19th 2013) ### * fixing potential html parsing problem * upgrading to jQuery UI position v1.10.0 * replaced `CRLF` by `LF` (no idea how this happened in the first place...) * adding `options.reposition` to dis/allow simply relocating a menu instead of rebuilding it ([Issue #104](https://github.com/swisnl/jQuery-contextMenu/issues/104)) ### 1.6.0 (December 29th 2012) ### * adding [DOM Element bound context menus](http://swisnl.github.io/jQuery-contextMenu/demo/on-dom-element.html) - ([Issue 88](https://github.com/swisnl/jQuery-contextMenu/issues/88)) * adding class `context-menu-active` to define state on active trigger element - ([Issue 92](https://github.com/swisnl/jQuery-contextMenu/issues/92)) * adding [demo for TouchSwipe](http://swisnl.github.io/jQuery-contextMenu/demo/trigger-swipe.html) activation * adding export of internal functions and event handlers - ([Issue 101](https://github.com/swisnl/jQuery-contextMenu/issues/101)) * fixing key "watch" might translate to Object.prototype.watch in callbacks map - ([Issue 93](https://github.com/swisnl/jQuery-contextMenu/issues/93)) * fixing menu and submenu width calculation - ([Issue 18](https://github.com/swisnl/jQuery-contextMenu/issues/18)) * fixing unused variables - ([Issue 100](https://github.com/swisnl/jQuery-contextMenu/issues/100)) * fixing iOS "click" compatibility problem - ([Issue 83](https://github.com/swisnl/jQuery-contextMenu/issues/83)) * fixing separators to not be clickable - ([Issue 85](https://github.com/swisnl/jQuery-contextMenu/issues/85)) * fixing issues with fixed positioned triggers ([Issue 95](https://github.com/swisnl/jQuery-contextMenu/issues/95)) * fixing word break problem - ([Issue 80](https://github.com/swisnl/jQuery-contextMenu/issues/80)) ### 1.5.25 (October 8th 2012) ### * upgrading to jQuery 1.8.2 ([Issue 78](https://github.com/swisnl/jQuery-contextMenu/issues/78)) * upgrading to jQuery UI position 1.9.0 RC1 ([Issue 78](https://github.com/swisnl/jQuery-contextMenu/issues/78)) ### 1.5.24 (August 30th 2012) ### * adding context menu options to input command events ([Issue 72](https://github.com/swisnl/jQuery-contextMenu/issues/72), dtex) * code cosmetics for JSLint ### 1.5.23 (August 22nd 2012) ### * fixing reposition/close issue on scrolled documents ([Issue 69](https://github.com/swisnl/jQuery-contextMenu/issues/69)) * fixing jQuery reference ([Issue 68](https://github.com/swisnl/jQuery-contextMenu/issues/68)) ### 1.5.22 (July 16th 2012) ### * fixing issue with animation and remove on hide (Issue #64) ### 1.5.21 (July 14th 2012) ### * fixing backdrop would not remove on destroy (Issue #63) ### 1.5.20 (June 26th 2012) ### Note: git tag of version is `v1.6.20`?! * fixing backdrop would not position properly in IE6 (Issue #59) * fixing nested input elements not accessible in Chrome / Safari (Issue #58) ### 1.5.19 ### Note: git tag of version is missing...?! * fixing sub-menu positioning when `$.ui.position` is not available (Issue #56) ### 1.5.18 ### Note: git tag of version is missing...?! * fixing html5 `` import (Issue #53) ### 1.5.17 (June 4th 2012) ### * fixing `options` to default to `options.trigger = "right"` * fixing variable name typo (Within Issue #51) * fixing menu not closing while opening other menu (Within Issue #51) * adding workaround for `contextmenu`-bug in Firefox 12 (Within Issue #51) ### 1.5.16 (May 29th 2012) ### * added vendor-prefixed user-select to CSS * fixed issue with z-indexing when `` is used as a trigger (Issue #49) ### 1.5.15 (May 26th 2012) ### * allowing to directly open another element's menu while a menu is shown (Issue #48) * fixing autohide option that would not properly hide the menu ### 1.5.14 (May 22nd 2012) ### * options.build() would break default options (Issue #47) * $.contextMenu('destroy') would not remove backdrop ### 1.5.13 (May 4th 2012) ### * exposing $trigger to dynamically built custom menu-item types (Issue #42) * fixing repositioning of open menu (formerly accidental re-open) * adding asynchronous example * dropping ignoreRightClick in favor of proper event-type detection ### 1.5.12 (May 2nd 2012) ### * prevent invoking callback of first item of a sub-menu when clicking on the sub-menu-item (Issue #41) ### 1.5.11 (April 27th 2012) ### * providing `opt.$trigger` to show event (Issue #39) ### 1.5.10 (April 21st 2012) ### * ignoreRightClick would not prevent right click when menu is already open (Issue #38) ### 1.5.9 (March 10th 2012) ### * If build() did not return any items, an empty menu was shown (Issue #33) ### 1.5.8 (January 28th 2012) ### * Capturing Page Up and Page Down keys to ignore like space (Issue #30) * Added Home / End keys to jump to first / last command of menu (Issue #29) * Bug hitting enter in an <input> would yield an error (Issue #28) ### 1.5.7 (January 21st 2012) ### * Non-ASCII character in jquery.contextMenu.js caused compatibility issues in Rails (Issue #27) ### 1.5.6 (January 8th 2012) ### * Bug contextmenu event was not passed to build() callback (Issue #24) * Bug sub-menu markers would not display properly in Safari and Chrome (Issue #25) ### 1.5.5 (January 6th 2012) ### * Bug Internet Explorer would not close menu when giving input elements focus (Issue #23) ### 1.5.4 (January 5th 2012) ## * Bug not set z-index of sub-menus might not overlap the main menu correctly (Issue #22) ### 1.5.3 (January 1st 2012) ### * Bug `console.log is undefined` ### 1.5.2 (December 25th 2012) ### * Bug sub-menus would not properly update their disabled states (Issue #16) [again…] * Bug sub-menus would not properly adjust width accoring to min-width and max-width (Issue #18) ### 1.5.1 (December 18th 2011) ### * Bug sub-menus would not properly update their disabled states (Issue #16) ### 1.5 (December 13th 2011) ### * Added [dynamic menu creation](http://swisnl.github.io/jQuery-contextMenu/demo/dynamic-create.html) (Issue #15) ### 1.4.4 (December 12th 2011) ### * Bug positioning <menu> when trigger element is `position:fixed` (Issue #14) ### 1.4.3 (December 11th 2011) ### * Bug key handler would caputure all key strokes while menu was visible (essentially disabling F5 and co.) ### 1.4.2 (December 6th 2011) ### * Bug opt.$trigger was not available to disabled callbacks * jQuery bumped to 1.7.1 ### 1.4.1 (November 9th 2011) ### * Bug where <menu> imports would not pass action (click event) properly ### 1.4 (November 7th 2011) ### * Upgraded to jQuery 1.7 (changed dependecy!) * Added internal events `contextmenu:focus`, `contextmenu:blur` and `contextmenu:hide` * Added custom <command> types * Bug where `className` wasn't properly set on <menu> ### 1.3 (September 5th 2011) ### * Added support for accesskeys * Bug where two sub-menus could be open simultaneously ### 1.2.2 (August 24th 2011) ### * Bug in HTML5 import ### 1.2.1 (August 24th 2011) ### * Bug in HTML5 detection ### 1.2 (August 24th 2011) ### * Added compatibility to <menuitem> for Firefox 8 * Upgraded to jQuery 1.6.2 ### 1.1 (August 11th 2011) ### * Bug #1 TypeError on HTML5 action passthru * Bug #2 disbaled callback not invoked properly * Feature #3 auto-hide option for hover trigger * Feature #4 option to use a single callback for all commands, rather than registering the same function for each item * Option to ignore right-click (original "contextmenu" event trigger) for non-right-click triggers ### 1.0 (July 7th 2011) ### * Initial $.contextMenu handler ================================================ FILE: LICENSE ================================================ The MIT License Copyright (c) 2010-2016 SWIS BV Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # jQuery contextMenu plugin & polyfill # [](https://greenkeeper.io/) [](https://app.travis-ci.com/github/swisnl/jQuery-contextMenu) [](https://www.npmjs.com/package/jquery-contextmenu) [](https://www.npmjs.com/package/jquery-contextmenu) [](https://cdnjs.com/libraries/jquery-contextmenu) []() [](https://plant.treeware.earth/swisnl/jQuery-contextMenu) `$.contextMenu` is a management facility for - you guessed it - context menus. It was designed for an application where there are hundreds of elements that may show a context menu - so intialization speed and memory usage are kept fairly small. It also allows to register context menus without providing actual markup, as `$.contextMenu` generates DOMElements as needed. [features](http://swisnl.github.io/jQuery-contextMenu/index.html) - [demo](http://swisnl.github.io/jQuery-contextMenu/demo.html) - [documentation](http://swisnl.github.io/jQuery-contextMenu/docs.html) [](https://saucelabs.com/u/bbrala-contextmenu) ## Dependencies ## * jQuery >=1.8.2 * jQuery UI position (optional but recommended) ## Usage ## register contextMenu from javascript: ```javascript $.contextMenu({ // define which elements trigger this menu selector: ".with-cool-menu", // define the elements of the menu items: { foo: {name: "Foo", callback: function(key, opt){ alert("Foo!"); }}, bar: {name: "Bar", callback: function(key, opt){ alert("Bar!") }} } // there's more, have a look at the demos and docs... }); ``` have a look at the [demos](http://swisnl.github.io/jQuery-contextMenu/demo.html). ## Version 3.0 beta Version 3.0 is a restructure of the javascript into something more sane written in ES6. It consolidates all API's so callbacks are better documented and more concise. The basics are still the same, but all callbacks are structured differently. The goal of this refactor was mostly to make the ContextMenu easier to maintain, and make the API's more consise. It also adds JSdoc comments so the API documentation is generated from the code and it enables code completion. Check out the [3.x branch](https://github.com/swisnl/jQuery-contextMenu/tree/3.x), or install with `npm install jquery-contextmenu@next`. ## HTML5 Compatibility ## Firefox 8 implemented contextmenu using the <menuitem> tags for menu-structure. The specs however state that <command> tags should be used for this purpose. $.contextMenu accepts both. Firefox 8 does not yet fully implement the contextmenu specification ([Ticket #617528](https://bugzilla.mozilla.org/show_bug.cgi?id=617528)). The elements [a](http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command), [button](http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command), [input](http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-input-element-to-define-a-command) and [option](http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-option-element-to-define-a-command) usable as commands are being ignored altogether. It also doesn't (optically) distinguish between checkbox/radio and regular commands ([Bug #705292](https://bugzilla.mozilla.org/show_bug.cgi?id=705292)). * [contextmenu specs](http://www.w3.org/TR/html5/interactive-elements.html#context-menus) * [command specs](http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html) * [Browser support according to caniuse.com](http://caniuse.com/#search=context%20menu) Note: While the specs note <option>s to be rendered as regular commands, $.contextMenu will render an actual <select>. import contextMenu from HTML5 <menu>: ```javascript $.contextMenu("html5"); ``` ## Interaction Principles You're (obviously) able to use the context menu with your mouse. Once it is opened, you can also use the keyboard to (fully) navigate it. * ↑ (up) previous item in list, will skip disabled elements and wrap around * ↓ (down) next item in, will skip disabled elements and wrap around * → (right) dive into sub-menu * ← (left) rise from sub-menu * ↵ (return) invoke command * ⇥ (tab) next item or input element, will skip disabled elements and wrap around * ⇪ ⇥ (shift tab) previous item or input element, will skip disabled elements and wrap around * ⎋ (escape) close menu * ⌴ (space) captured and ignore to avoid page scrolling (for consistency with native menus) * ⇞ (page up) captured and ignore to avoid page scrolling (for consistency with native menus) * ⇟ (page down) captured and ignore to avoid page scrolling (for consistency with native menus) * ↖ (home) first item in list, will skip disabled elements * ↘ (end) last item in list, will skip disabled elements Besides the obvious, browser also react to alphanumeric key strokes. Hitting r in a context menu will make Firefox (8) reload the page immediately. Chrome selects the option to see infos on the page, Safari selects the option to print the document. Awesome, right? Until trying the same on Windows I did not realize that the browsers were using the access-key for this. I would've preferred typing the first character of something, say "s" for "save" and then iterate through all the commands beginning with s. But that's me - what do I know about UX? Anyways, $.contextMenu now also supports accesskey handling. ## Authors * [Björn Brala](https://github.com/swisnl) * [Rodney Rehm](https://github.com/rodneyrehm) (original creator) * [Christiaan Baartse](https://github.com/christiaan) (single callback per menu) * [Addy Osmani](https://github.com/addyosmani) (compatibility with native context menu in Firefox 8) ## License `$.contextMenu` is published under the [MIT license](http://www.opensource.org/licenses/mit-license) This package is [Treeware](https://treeware.earth). If you use it in production, then we ask that you [**buy the world a tree**](https://plant.treeware.earth/swisnl/jQuery-contextMenu) to thank us for our work. By contributing to the Treeware forest you’ll be creating employment for local families and restoring wildlife habitats. ## Special thanks Font-Awesome icons used from [encharm/Font-Awesome-SVG-PNG](https://github.com/encharm/Font-Awesome-SVG-PNG). ## SWIS :heart: Open Source [SWIS][link-swis] is a web agency from Leiden, the Netherlands. We love working with open source software. [link-swis]: https://www.swis.nl ================================================ FILE: bower.json ================================================ { "name": "jQuery-contextMenu", "main": [ "dist/jquery.contextMenu.js", "dist/jquery.contextMenu.css" ], "homepage": "http://swisnl.github.io/jQuery-contextMenu/", "authors": [ "Björn Brala (http://www.swis.nl)", "Rodney Rehm (http://rodneyrehm.de/en)" ], "description": "Full featured context menu handler capable of handling thousands of elements", "keywords": ["contextmenu", "context-menu", "right-click-menu", "right-click", "navigation", "menu"], "license": "MIT", "dependencies": { "jquery": ">=1.8.2" }, "ignore": [ "demo/", "prettify/", "screenshots/", "**/.*", "*.md", "*.html", "/*.css", "/*.js" ] } ================================================ FILE: contextMenu.jquery.json ================================================ { "name": "contextMenu", "title": "jQuery contextMenu", "description": "full featured context menu handler capable of handling thousands of elements", "keywords": [ "contextmenu", "context-menu", "right-click-menu", "right-click", "navigation", "menu" ], "version": "git-master", "author": { "name": "Björn Brala (SWIS.nl)", "url": "http://www.swis.nl" }, "licenses": [ { "type": "MIT", "url": "https://github.com/jquery/jquery-color/blob/2.1.2/MIT-LICENSE.txt" } ], "bugs": "https://github.com/swisnl/jQuery-contextMenu/issues", "homepage": "http://swisnl.github.io/jQuery-contextMenu/", "docs": "http://swisnl.github.io/jQuery-contextMenu/docs.html", "dependencies": { "jquery": ">=1.8.2" } } ================================================ FILE: dist/jquery.contextMenu.css ================================================ @charset "UTF-8"; /*! * jQuery contextMenu - Plugin for simple contextMenu handling * * Version: v2.9.2 * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * * Copyright (c) 2011-2025 SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * * Date: 2025-11-04T11:31:40.817Z */ @-webkit-keyframes cm-spin { 0% { -webkit-transform: translateY(-50%) rotate(0deg); transform: translateY(-50%) rotate(0deg); } 100% { -webkit-transform: translateY(-50%) rotate(359deg); transform: translateY(-50%) rotate(359deg); } } @-o-keyframes cm-spin { 0% { -webkit-transform: translateY(-50%) rotate(0deg); -o-transform: translateY(-50%) rotate(0deg); transform: translateY(-50%) rotate(0deg); } 100% { -webkit-transform: translateY(-50%) rotate(359deg); -o-transform: translateY(-50%) rotate(359deg); transform: translateY(-50%) rotate(359deg); } } @keyframes cm-spin { 0% { -webkit-transform: translateY(-50%) rotate(0deg); -o-transform: translateY(-50%) rotate(0deg); transform: translateY(-50%) rotate(0deg); } 100% { -webkit-transform: translateY(-50%) rotate(359deg); -o-transform: translateY(-50%) rotate(359deg); transform: translateY(-50%) rotate(359deg); } } @font-face { font-family: "context-menu-icons"; font-style: normal; font-weight: normal; src: url("font/context-menu-icons.eot?2dq4x"); src: url("font/context-menu-icons.eot?2dq4x#iefix") format("embedded-opentype"), url("font/context-menu-icons.woff2?2dq4x") format("woff2"), url("font/context-menu-icons.woff?2dq4x") format("woff"), url("font/context-menu-icons.ttf?2dq4x") format("truetype"); } .context-menu-icon-add:before { content: "\EA01"; } .context-menu-icon-copy:before { content: "\EA02"; } .context-menu-icon-cut:before { content: "\EA03"; } .context-menu-icon-delete:before { content: "\EA04"; } .context-menu-icon-edit:before { content: "\EA05"; } .context-menu-icon-loading:before { content: "\EA06"; } .context-menu-icon-paste:before { content: "\EA07"; } .context-menu-icon-quit:before { content: "\EA08"; } .context-menu-icon::before { position: absolute; top: 50%; left: 0; width: 2em; font-family: "context-menu-icons"; font-size: 1em; font-style: normal; font-weight: normal; line-height: 1; color: #2980b9; text-align: center; -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); -o-transform: translateY(-50%); transform: translateY(-50%); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .context-menu-icon.context-menu-hover:before { color: #fff; } .context-menu-icon.context-menu-disabled::before { color: #bbb; } .context-menu-icon.context-menu-icon-loading:before { -webkit-animation: cm-spin 2s infinite; -o-animation: cm-spin 2s infinite; animation: cm-spin 2s infinite; } .context-menu-icon.context-menu-icon--fa { display: list-item; font-family: inherit; line-height: inherit; } .context-menu-icon.context-menu-icon--fa::before { position: absolute; top: 50%; left: 0; width: 2em; font-family: FontAwesome; font-size: 1em; font-style: normal; font-weight: normal; line-height: 1; color: #2980b9; text-align: center; -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); -o-transform: translateY(-50%); transform: translateY(-50%); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } .context-menu-icon.context-menu-icon--fa.context-menu-hover:before { color: #fff; } .context-menu-icon.context-menu-icon--fa.context-menu-disabled::before { color: #bbb; } .context-menu-icon.context-menu-icon--fa5 { display: list-item; font-family: inherit; line-height: inherit; } .context-menu-icon.context-menu-icon--fa5 i, .context-menu-icon.context-menu-icon--fa5 svg { position: absolute; top: .3em; left: .5em; color: #2980b9; } .context-menu-icon.context-menu-icon--fa5.context-menu-hover > i, .context-menu-icon.context-menu-icon--fa5.context-menu-hover > svg { color: #fff; } .context-menu-icon.context-menu-icon--fa5.context-menu-disabled i, .context-menu-icon.context-menu-icon--fa5.context-menu-disabled svg { color: #bbb; } .context-menu-list { position: absolute; display: inline-block; min-width: 13em; max-width: 26em; padding: .25em 0; margin: .3em; font-family: inherit; font-size: inherit; list-style-type: none; background: #fff; border: 1px solid #bebebe; border-radius: .2em; -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, .5); box-shadow: 0 2px 5px rgba(0, 0, 0, .5); } .context-menu-item { position: relative; -webkit-box-sizing: content-box; -moz-box-sizing: content-box; box-sizing: content-box; padding: .2em 2em; color: #2f2f2f; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; background-color: #fff; } .context-menu-separator { padding: 0; margin: .35em 0; border-bottom: 1px solid #e6e6e6; } .context-menu-item > label > input, .context-menu-item > label > textarea { -webkit-user-select: text; -moz-user-select: text; -ms-user-select: text; user-select: text; } .context-menu-item.context-menu-hover { color: #fff; cursor: pointer; background-color: #2980b9; } .context-menu-item.context-menu-disabled { color: #bbb; cursor: default; background-color: #fff; } .context-menu-input.context-menu-hover { color: #2f2f2f; cursor: default; } .context-menu-submenu:after { position: absolute; top: 50%; right: .5em; z-index: 1; width: 0; height: 0; content: ''; border-color: transparent transparent transparent #2f2f2f; border-style: solid; border-width: .25em 0 .25em .25em; -webkit-transform: translateY(-50%); -ms-transform: translateY(-50%); -o-transform: translateY(-50%); transform: translateY(-50%); } /** * Inputs */ .context-menu-item.context-menu-input { padding: .3em .6em; } /* vertically align inside labels */ .context-menu-input > label > * { vertical-align: top; } /* position checkboxes and radios as icons */ .context-menu-input > label > input[type="checkbox"], .context-menu-input > label > input[type="radio"] { position: relative; top: .12em; margin-right: .4em; } .context-menu-input > label { margin: 0; } .context-menu-input > label, .context-menu-input > label > input[type="text"], .context-menu-input > label > textarea, .context-menu-input > label > select { display: block; width: 100%; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; } .context-menu-input > label > textarea { height: 7em; } .context-menu-item > .context-menu-list { top: .3em; /* re-positioned by js */ right: -.3em; display: none; } .context-menu-item.context-menu-visible > .context-menu-list { display: block; } .context-menu-accesskey { text-decoration: underline; } ================================================ FILE: dist/jquery.contextMenu.js ================================================ /** * jQuery contextMenu v2.9.2 - Plugin for simple contextMenu handling * * Version: v2.9.2 * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * * Copyright (c) 2011-2025 SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * * Date: 2025-11-04T11:31:41.320Z */ // jscs:disable /* jshint ignore:start */ (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as anonymous module. define(['jquery'], factory); } else if (typeof exports === 'object') { // Node / CommonJS factory(require('jquery')); } else { // Browser globals. factory(jQuery); } })(function ($) { 'use strict'; // helper function to check for rapid interactions after menu display var isInteractionTooFast = function($element) { if (!('ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0)) { return false; } var interactionTime = Date.now(); var $liItem = $element.is('input, textarea, select') ? $element.closest('.context-menu-item') : $element; if (!$liItem || !$liItem.length) { return false; } var $parentMenu = $liItem.parent(); if (!$parentMenu || !$parentMenu.length) { return false; } // only apply the check for items within submenus if ($parentMenu.hasClass('context-menu-root')) { return false; } var showTimestamp = $parentMenu.data('_showTimestamp'); var timeDifference = showTimestamp ? interactionTime - showTimestamp : Infinity; // threshold for fast interaction (e.g., mobile tap) var threshold = 50; // ms return timeDifference < threshold; }; // TODO: - // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio // create structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative // determine html5 compatibility $.support.htmlMenuitem = ('HTMLMenuItemElement' in window); $.support.htmlCommand = ('HTMLCommandElement' in window); $.support.eventSelectstart = ('onselectstart' in document.documentElement); /* // should the need arise, test for css user-select $.support.cssUserSelect = (function(){ var t = false, e = document.createElement('div'); $.each('Moz|Webkit|Khtml|O|ms|Icab|'.split('|'), function(i, prefix) { var propCC = prefix + (prefix ? 'U' : 'u') + 'serSelect', prop = (prefix ? ('-' + prefix.toLowerCase() + '-') : '') + 'user-select'; e.style.cssText = prop + ': text;'; if (e.style[propCC] == 'text') { t = true; return false; } return true; }); return t; })(); */ if (!$.ui || !$.widget) { // duck punch $.cleanData like jQueryUI does to get that remove event $.cleanData = (function (orig) { return function (elems) { var events, elem, i; for (i = 0; elems[i] != null; i++) { elem = elems[i]; try { // Only trigger remove when necessary to save time events = $._data(elem, 'events'); if (events && events.remove) { $(elem).triggerHandler('remove'); } // Http://bugs.jquery.com/ticket/8235 } catch (e) { } } orig(elems); }; })($.cleanData); } /* jshint ignore:end */ // jscs:enable var // currently active contextMenu trigger $currentTrigger = null, // is contextMenu initialized with at least one menu? initialized = false, // window handle $win = $(window), // number of registered menus counter = 0, // mapping selector to namespace namespaces = {}, // mapping namespace to options menus = {}, // custom command type handlers types = {}, // default values defaults = { // selector of contextMenu trigger selector: null, // where to append the menu to appendTo: null, // method to trigger context menu ["right", "left", "hover"] trigger: 'right', // hide menu when mouse leaves trigger / menu elements autoHide: false, // ms to wait before showing a hover-triggered context menu delay: 200, // flag denoting if a second trigger should simply move (true) or rebuild (false) an open menu // as long as the trigger happened on one of the trigger-element's child nodes reposition: true, // Flag denoting if a second trigger should close the menu, as long as // the trigger happened on one of the trigger-element's child nodes. // This overrides the reposition option. hideOnSecondTrigger: false, // use a modal layer for closing the menu rather than a captured event on document useModal: true, //ability to select submenu selectableSubMenu: false, // Default classname configuration to be able avoid conflicts in frameworks classNames: { hover: 'context-menu-hover', // Item hover disabled: 'context-menu-disabled', // Item disabled visible: 'context-menu-visible', // Item visible notSelectable: 'context-menu-not-selectable', // Item not selectable icon: 'context-menu-icon', iconEdit: 'context-menu-icon-edit', iconCut: 'context-menu-icon-cut', iconCopy: 'context-menu-icon-copy', iconPaste: 'context-menu-icon-paste', iconDelete: 'context-menu-icon-delete', iconAdd: 'context-menu-icon-add', iconQuit: 'context-menu-icon-quit', iconLoadingClass: 'context-menu-icon-loading' }, // determine position to show menu at determinePosition: function ($menu) { // position to the lower middle of the trigger element if ($.ui && $.ui.position) { // .position() is provided as a jQuery UI utility // (...and it won't work on hidden elements) $menu.css('display', 'block').position({ my: 'center top', at: 'center bottom', of: this, offset: '0 5', collision: 'fit' }).css('display', 'none'); } else { // determine contextMenu position var offset = this.offset(); offset.top += this.outerHeight(); offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2; $menu.css(offset); } }, // position menu position: function (opt, x, y) { var offset; // determine contextMenu position if (!x && !y) { opt.determinePosition.call(this, opt.$menu); return; } else if (x === 'maintain' && y === 'maintain') { // x and y must not be changed (after re-show on command click) offset = opt.$menu.position(); } else { // x and y are given (by mouse event) var offsetParentOffset = opt.$menu.offsetParent().offset(); offset = {top: y - offsetParentOffset.top, left: x -offsetParentOffset.left}; } // correct offset if viewport demands it var bottom = $win.scrollTop() + $win.height(), right = $win.scrollLeft() + $win.width(), height = opt.$menu.outerHeight(), width = opt.$menu.outerWidth(); if (offset.top + height > bottom) { offset.top -= height; } if (offset.top < 0) { offset.top = 0; } if (offset.left + width > right) { offset.left -= width; } if (offset.left < 0) { offset.left = 0; } opt.$menu.css(offset); }, // position the sub-menu positionSubmenu: function ($menu) { if (typeof $menu === 'undefined') { // When user hovers over item (which has sub items) handle.focusItem will call this. // but the submenu does not exist yet if opt.items is a promise. just return, will // call positionSubmenu after promise is completed. return; } if ($.ui && $.ui.position) { // .position() is provided as a jQuery UI utility // (...and it won't work on hidden elements) $menu.css('display', 'block').position({ my: 'left top-5', at: 'right top', of: this, collision: 'flipfit fit' }).css('display', ''); } else { // determine contextMenu position var offset = { top: -9, left: this.outerWidth() - 5 }; $menu.css(offset); } }, // offset to add to zIndex zIndex: 1, // show hide animation settings animation: { duration: 50, show: 'slideDown', hide: 'slideUp' }, // events events: { preShow: $.noop, show: $.noop, hide: $.noop, activated: $.noop }, // default callback callback: null, // list of contextMenu items items: {} }, // mouse position for hover activation hoveract = { timer: null, pageX: null, pageY: null }, // determine zIndex zindex = function ($t) { var zin = 0, $tt = $t; while (true) { zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0); $tt = $tt.parent(); if (!$tt || !$tt.length || 'html body'.indexOf($tt.prop('nodeName').toLowerCase()) > -1) { break; } } return zin; }, // event handlers handle = { // abort anything abortevent: function (e) { e.preventDefault(); e.stopImmediatePropagation(); }, // contextmenu show dispatcher contextmenu: function (e) { var $this = $(this); //Show browser context-menu when preShow returns false if (e.data.events.preShow($this,e) === false) { return; } // disable actual context-menu if we are using the right mouse button as the trigger if (e.data.trigger === 'right') { e.preventDefault(); e.stopImmediatePropagation(); } // abort native-triggered events unless we're triggering on right click if ((e.data.trigger !== 'right' && e.data.trigger !== 'demand') && e.originalEvent) { return; } // Let the current contextmenu decide if it should show or not based on its own trigger settings if (typeof e.mouseButton !== 'undefined' && e.data) { if (!(e.data.trigger === 'left' && e.mouseButton === 0) && !(e.data.trigger === 'right' && e.mouseButton === 2)) { // Mouse click is not valid. return; } } // abort event if menu is visible for this trigger if ($this.hasClass('context-menu-active')) { return; } if (!$this.hasClass('context-menu-disabled')) { // theoretically need to fire a show event at // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this }); // e.data.$menu.trigger(evt); $currentTrigger = $this; if (e.data.build) { var built = e.data.build($currentTrigger, e); // abort if build() returned false if (built === false) { return; } // dynamically build menu on invocation e.data = $.extend(true, {}, defaults, e.data, built || {}); // abort if there are no items to display if (!e.data.items || $.isEmptyObject(e.data.items)) { // Note: jQuery captures and ignores errors from event handlers if (window.console) { (console.error || console.log).call(console, 'No items specified to show in contextMenu'); } throw new Error('No Items specified'); } // backreference for custom command type creation e.data.$trigger = $currentTrigger; op.create(e.data); } op.show.call($this, e.data, e.pageX, e.pageY); } }, // contextMenu left-click trigger click: function (e) { e.preventDefault(); e.stopImmediatePropagation(); $(this).trigger($.Event('contextmenu', {data: e.data, pageX: e.pageX, pageY: e.pageY})); }, // contextMenu right-click trigger mousedown: function (e) { // register mouse down var $this = $(this); // hide any previous menus if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) { $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide'); } // activate on right click if (e.button === 2) { $currentTrigger = $this.data('contextMenuActive', true); } }, // contextMenu right-click trigger mouseup: function (e) { // show menu var $this = $(this); if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) { e.preventDefault(); e.stopImmediatePropagation(); $currentTrigger = $this; $this.trigger($.Event('contextmenu', {data: e.data, pageX: e.pageX, pageY: e.pageY})); } $this.removeData('contextMenuActive'); }, // contextMenu hover trigger mouseenter: function (e) { var $this = $(this), $related = $(e.relatedTarget), $document = $(document); // abort if we're coming from a menu if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { return; } // abort if a menu is shown if ($currentTrigger && $currentTrigger.length) { return; } hoveract.pageX = e.pageX; hoveract.pageY = e.pageY; hoveract.data = e.data; $document.on('mousemove.contextMenuShow', handle.mousemove); hoveract.timer = setTimeout(function () { hoveract.timer = null; $document.off('mousemove.contextMenuShow'); $currentTrigger = $this; $this.trigger($.Event('contextmenu', { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY })); }, e.data.delay); }, // contextMenu hover trigger mousemove: function (e) { hoveract.pageX = e.pageX; hoveract.pageY = e.pageY; }, // contextMenu hover trigger mouseleave: function (e) { // abort if we're leaving for a menu var $related = $(e.relatedTarget); if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { return; } try { clearTimeout(hoveract.timer); } catch (e) { } hoveract.timer = null; }, // click on layer to hide contextMenu layerClick: function (e, opt, onhide) { var $this = $(this), root = (opt !== undefined) ? opt : $this.data('contextMenuRoot'), button = e.button, x = e.pageX, y = e.pageY, fakeClick = x === undefined, target, offset; // If the click is not real, things break: https://github.com/swisnl/jQuery-contextMenu/issues/132 if(fakeClick){ if (root !== null && typeof root !== 'undefined' && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); } return; } // if the click closing is done through windwow event listener rather than a transparent layer if (!root.$layer) { target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop()); if (root.$menu === null || typeof root.$menu === 'undefined' || !root.$menu[0].contains(target)) { root.$menu.trigger('contextmenu:hide'); if (typeof onhide !== 'undefined') onhide(); } return; } e.preventDefault(); setTimeout(function () { var $window; var triggerAction = ((root.trigger === 'left' && button === 0) || (root.trigger === 'right' && button === 2)); // find the element that would've been clicked, wasn't the layer in the way if (document.elementFromPoint && root.$layer) { root.$layer.hide(); target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop()); // also need to try and focus this element if we're in a contenteditable area, // as the layer will prevent the browser mouse action we want if (target !== null && target.isContentEditable) { var range = document.createRange(), sel = window.getSelection(); range.selectNode(target); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } $(target).trigger(e); root.$layer.show(); } if (root.hideOnSecondTrigger && triggerAction && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); return; } if (root.reposition && triggerAction) { if (document.elementFromPoint) { if (root.$trigger.is(target)) { root.position.call(root.$trigger, root, x, y); return; } } else { offset = root.$trigger.offset(); $window = $(window); // while this looks kinda awful, it's the best way to avoid // unnecessarily calculating any positions offset.top += $window.scrollTop(); if (offset.top <= e.pageY) { offset.left += $window.scrollLeft(); if (offset.left <= e.pageX) { offset.bottom = offset.top + root.$trigger.outerHeight(); if (offset.bottom >= e.pageY) { offset.right = offset.left + root.$trigger.outerWidth(); if (offset.right >= e.pageX) { // reposition root.position.call(root.$trigger, root, x, y); return; } } } } } } if (target && triggerAction) { root.$trigger.one('contextmenu:hidden', function () { $(target).contextMenu({x: x, y: y, button: button}); }); } if (root !== null && typeof root !== 'undefined' && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); } }, 50); }, // key handled :hover keyStop: function (e, opt) { if (!opt.isInput) { e.preventDefault(); } e.stopPropagation(); }, key: function (e) { var opt = {}; // Only get the data from $currentTrigger if it exists if ($currentTrigger) { opt = $currentTrigger.data('contextMenu') || {}; } // If the trigger happen on a element that are above the contextmenu do this if (typeof opt.zIndex === 'undefined') { opt.zIndex = 0; } var targetZIndex = 0; var getZIndexOfTriggerTarget = function (target) { if (target.style.zIndex !== '') { targetZIndex = target.style.zIndex; } else { if (target.offsetParent !== null && typeof target.offsetParent !== 'undefined') { getZIndexOfTriggerTarget(target.offsetParent); } else if (target.parentElement !== null && typeof target.parentElement !== 'undefined') { getZIndexOfTriggerTarget(target.parentElement); } } }; getZIndexOfTriggerTarget(e.target); // If targetZIndex is heigher then opt.zIndex dont progress any futher. // This is used to make sure that if you are using a dialog with a input / textarea / contenteditable div // and its above the contextmenu it wont steal keys events if (opt.$menu && parseInt(targetZIndex,10) > parseInt(opt.$menu.css("zIndex"),10)) { return; } switch (e.keyCode) { case 9: case 38: // up handle.keyStop(e, opt); // if keyCode is [38 (up)] or [9 (tab) with shift] if (opt.isInput) { if (e.keyCode === 9 && e.shiftKey) { e.preventDefault(); if (opt.$selected) { opt.$selected.find('input, textarea, select').blur(); } if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('prevcommand'); } return; } else if (e.keyCode === 38 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { // checkboxes don't capture this key e.preventDefault(); return; } } else if (e.keyCode !== 9 || e.shiftKey) { if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('prevcommand'); } return; } break; // omitting break; // case 9: // tab - reached through omitted break; case 40: // down handle.keyStop(e, opt); if (opt.isInput) { if (e.keyCode === 9) { e.preventDefault(); if (opt.$selected) { opt.$selected.find('input, textarea, select').blur(); } if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('nextcommand'); } return; } else if (e.keyCode === 40 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { // checkboxes don't capture this key e.preventDefault(); return; } } else { if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('nextcommand'); } return; } break; case 37: // left handle.keyStop(e, opt); if (opt.isInput || !opt.$selected || !opt.$selected.length) { break; } if (!opt.$selected.parent().hasClass('context-menu-root')) { var $parent = opt.$selected.parent().parent(); opt.$selected.trigger('contextmenu:blur'); opt.$selected = $parent; return; } break; case 39: // right handle.keyStop(e, opt); if (opt.isInput || !opt.$selected || !opt.$selected.length) { break; } var itemdata = opt.$selected.data('contextMenu') || {}; if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) { opt.$selected = null; itemdata.$selected = null; itemdata.$menu.trigger('nextcommand'); return; } break; case 35: // end case 36: // home if (opt.$selected && opt.$selected.find('input, textarea, select').length) { return; } else { (opt.$selected && opt.$selected.parent() || opt.$menu) .children(':not(.' + opt.classNames.disabled + ', .' + opt.classNames.notSelectable + ')')[e.keyCode === 36 ? 'first' : 'last']() .trigger('contextmenu:focus'); e.preventDefault(); return; } break; case 13: // enter handle.keyStop(e, opt); if (opt.isInput) { if (opt.$selected && !opt.$selected.is('textarea, select')) { e.preventDefault(); return; } break; } if (typeof opt.$selected !== 'undefined' && opt.$selected !== null) { opt.$selected.trigger('mouseup'); } return; case 32: // space case 33: // page up case 34: // page down // prevent browser from scrolling down while menu is visible handle.keyStop(e, opt); return; case 27: // esc handle.keyStop(e, opt); if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('contextmenu:hide'); } return; default: // 0-9, a-z var k = (String.fromCharCode(e.keyCode)).toUpperCase(); if (opt.accesskeys && opt.accesskeys[k]) { // according to the specs accesskeys must be invoked immediately opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu ? 'contextmenu:focus' : 'mouseup'); return; } break; } // pass event to selected item, // stop propagation to avoid endless recursion e.stopPropagation(); if (typeof opt.$selected !== 'undefined' && opt.$selected !== null) { opt.$selected.trigger(e); } }, // select previous possible command in menu prevItem: function (e) { e.stopPropagation(); var opt = $(this).data('contextMenu') || {}; var root = $(this).data('contextMenuRoot') || {}; // obtain currently selected menu if (opt.$selected) { var $s = opt.$selected; opt = opt.$selected.parent().data('contextMenu') || {}; opt.$selected = $s; } var $children = opt.$menu.children(), $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(), $round = $prev; // skip disabled or hidden elements while ($prev.hasClass(root.classNames.disabled) || $prev.hasClass(root.classNames.notSelectable) || $prev.is(':hidden')) { if ($prev.prev().length) { $prev = $prev.prev(); } else { $prev = $children.last(); } if ($prev.is($round)) { // break endless loop return; } } // leave current if (opt.$selected) { handle.itemMouseleave.call(opt.$selected.get(0), e); } // activate next handle.itemMouseenter.call($prev.get(0), e); // focus input var $input = $prev.find('input, textarea, select'); if ($input.length) { $input.focus(); } }, // select next possible command in menu nextItem: function (e) { e.stopPropagation(); var opt = $(this).data('contextMenu') || {}; var root = $(this).data('contextMenuRoot') || {}; // obtain currently selected menu if (opt.$selected) { var $s = opt.$selected; opt = opt.$selected.parent().data('contextMenu') || {}; opt.$selected = $s; } var $children = opt.$menu.children(), $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(), $round = $next; // skip disabled while ($next.hasClass(root.classNames.disabled) || $next.hasClass(root.classNames.notSelectable) || $next.is(':hidden')) { if ($next.next().length) { $next = $next.next(); } else { $next = $children.first(); } if ($next.is($round)) { // break endless loop return; } } // leave current if (opt.$selected) { handle.itemMouseleave.call(opt.$selected.get(0), e); } // activate next handle.itemMouseenter.call($next.get(0), e); // focus input var $input = $next.find('input, textarea, select'); if ($input.length) { $input.focus(); } }, // flag that we're inside an input so the key handler can act accordingly focusInput: function () { var $this = $(this).closest('.context-menu-item'), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; root.$selected = opt.$selected = $this; root.isInput = opt.isInput = true; }, // flag that we're inside an input so the key handler can act accordingly blurInput: function () { var $this = $(this).closest('.context-menu-item'), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; root.isInput = opt.isInput = false; }, // :hover on menu menuMouseenter: function () { var root = $(this).data().contextMenuRoot; root.hovering = true; }, // :hover on menu menuMouseleave: function (e) { var root = $(this).data().contextMenuRoot; if (root.$layer && root.$layer.is(e.relatedTarget)) { root.hovering = false; } }, // :hover done manually so key handling is possible itemMouseenter: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; // prevent fast hover on mobile tap-through if (isInteractionTooFast($this)) { return; } root.hovering = true; // abort if we're re-entering if (e && root.$layer && root.$layer.is(e.relatedTarget)) { e.preventDefault(); e.stopImmediatePropagation(); } // make sure only one item is selected (opt.$menu ? opt : root).$menu .children('.' + root.classNames.hover).trigger('contextmenu:blur') .children('.hover').trigger('contextmenu:blur'); if ($this.hasClass(root.classNames.disabled) || $this.hasClass(root.classNames.notSelectable)) { opt.$selected = null; return; } $this.trigger('contextmenu:focus'); }, // :hover done manually so key handling is possible itemMouseleave: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) { if (typeof root.$selected !== 'undefined' && root.$selected !== null) { root.$selected.trigger('contextmenu:blur'); } e.preventDefault(); e.stopImmediatePropagation(); root.$selected = opt.$selected = opt.$node; return; } if(opt && opt.$menu && opt.$menu.hasClass('context-menu-visible')){ return; } $this.trigger('contextmenu:blur'); }, // contextMenu item click itemClick: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot, key = data.contextMenuKey, callback; // prevent fast click-through on mobile taps if (isInteractionTooFast($this)) { e.preventDefault(); e.stopImmediatePropagation(); return; } // abort if the key is unknown or disabled or is a menu // explicitly handle non-selectable submenu clicks first to stop propagation if ($this.is('.context-menu-submenu') && root.selectableSubMenu === false) { e.preventDefault(); e.stopImmediatePropagation(); // Stop event here for non-selectable submenus return; } // original check for other non-clickable/disabled items if (!opt.items[key] || $this.is('.' + root.classNames.disabled + ', .context-menu-separator, .' + root.classNames.notSelectable)) { return; } // if it wasn't a non-selectable submenu or other disabled item, prevent default and stop propagation before callback e.preventDefault(); e.stopImmediatePropagation(); if ($.isFunction(opt.callbacks[key]) && Object.prototype.hasOwnProperty.call(opt.callbacks, key)) { // item-specific callback callback = opt.callbacks[key]; } else if ($.isFunction(root.callback)) { // default callback callback = root.callback; } else { // no callback, no action return; } // hide menu if callback doesn't stop that if (callback.call(root.$trigger, key, root, e) !== false) { root.$menu.trigger('contextmenu:hide'); } else if (root.$menu.parent().length) { op.update.call(root.$trigger, root); } }, // ignore click events on input elements inputClick: function (e) { e.stopImmediatePropagation(); }, // hide hideMenu: function (e, data) { var root = $(this).data('contextMenuRoot'); op.hide.call(root.$trigger, root, data && data.force); }, // focus focusItem: function (e) { e.stopPropagation(); var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if ($this.hasClass(root.classNames.disabled) || $this.hasClass(root.classNames.notSelectable)) { return; } $this .addClass([root.classNames.hover, root.classNames.visible].join(' ')) // select other items and included items .parent().find('.context-menu-item').not($this) .removeClass(root.classNames.visible) .filter('.' + root.classNames.hover) .trigger('contextmenu:blur'); // remember selected opt.$selected = root.$selected = $this; if(opt && opt.$node && opt.$node.hasClass('context-menu-submenu')){ opt.$node.addClass(root.classNames.hover); } // position sub-menu - do after show so dumb $.ui.position can keep up if (opt.$node) { root.positionSubmenu.call(opt.$node, opt.$menu); if (opt.$menu) { var focusShowTimestamp = Date.now(); opt.$menu.data('_showTimestamp', focusShowTimestamp); } } }, // blur blurItem: function (e) { e.stopPropagation(); var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if (opt.autoHide) { // for tablets and touch screens this needs to remain $this.removeClass(root.classNames.visible); } $this.removeClass(root.classNames.hover); opt.$selected = null; } }, // operations op = { show: function (opt, x, y) { var $trigger = $(this), css = {}; // hide any open menus if ($('#context-menu-layer').length > 0) $('#context-menu-layer').trigger('mousedown'); else $(document).trigger('contextmenu:hide'); // backreference for callbacks opt.$trigger = $trigger; // show event if (opt.events.show.call($trigger, opt) === false) { $currentTrigger = null; return; } // create or update context menu var hasVisibleItems = op.update.call($trigger, opt); if (hasVisibleItems === false) { $currentTrigger = null; return; } // position menu opt.position.call($trigger, opt, x, y); // make sure we're in front if (opt.zIndex) { var additionalZValue = opt.zIndex; // If opt.zIndex is a function, call the function to get the right zIndex. if (typeof opt.zIndex === 'function') { additionalZValue = opt.zIndex.call($trigger, opt); } css.zIndex = zindex($trigger) + additionalZValue; } // add layer op.layer.call(opt.$menu, opt, css.zIndex); // adjust sub-menu zIndexes opt.$menu.find('ul').css('zIndex', css.zIndex + 1); // position and show context menu opt.$menu.css(css)[opt.animation.show](opt.animation.duration, function () { $trigger.trigger('contextmenu:visible'); var rootShowTimestamp = Date.now(); opt.$menu.data('_showTimestamp', rootShowTimestamp); op.activated(opt); opt.events.activated(opt); }); // make options available and set state $trigger .data('contextMenu', opt) .addClass('context-menu-active'); // register key handler $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key); // register autoHide handler if (opt.autoHide) { // mouse position handler $(document).on('mousemove.contextMenuAutoHide', function (e) { // need to capture the offset on mousemove, // since the page might've been scrolled since activation var pos = $trigger.offset(); pos.right = pos.left + $trigger.outerWidth(); pos.bottom = pos.top + $trigger.outerHeight(); if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) { /* Additional hover check after short time, you might just miss the edge of the menu */ setTimeout(function () { if (!opt.hovering && opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('contextmenu:hide'); } }, 50); } }); } }, hide: function (opt, force) { var $trigger = $(this); if (!opt) { opt = $trigger.data('contextMenu') || {}; } // hide event if (!force && opt.events && opt.events.hide.call($trigger, opt) === false) { return; } // remove options and revert state $trigger .removeData('contextMenu') .removeClass('context-menu-active'); if (opt.$layer) { // keep layer for a bit so the contextmenu event can be aborted properly by opera setTimeout((function ($layer) { return function () { $layer.remove(); }; })(opt.$layer), 10); try { delete opt.$layer; } catch (e) { opt.$layer = null; } } // remove handle $currentTrigger = null; // remove selected opt.$menu.find('.' + opt.classNames.hover).trigger('contextmenu:blur'); opt.$selected = null; // collapse all submenus opt.$menu.find('.' + opt.classNames.visible).removeClass(opt.classNames.visible); // unregister key and mouse handlers // $(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705 $(document).off('.contextMenuAutoHide').off('keydown.contextMenu'); // hide menu if (opt.$menu) { opt.$menu[opt.animation.hide](opt.animation.duration, function () { // tear down dynamically built menu after animation is completed. if (opt.build) { opt.$menu.remove(); $.each(opt, function (key) { switch (key) { case 'ns': case 'selector': case 'build': case 'trigger': return true; default: opt[key] = undefined; try { delete opt[key]; } catch (e) { } return true; } }); } setTimeout(function () { $trigger.trigger('contextmenu:hidden'); }, 10); }); } }, create: function (opt, root) { if (typeof root === 'undefined') { root = opt; } // define handler for fast input clicks var handleFastInputClick = function(e) { var $inputClicked = $(this); if (isInteractionTooFast($inputClicked)) { e.preventDefault(); e.stopImmediatePropagation(); return false; } }; // create contextMenu opt.$menu = $('').addClass(opt.className || '').data({ 'contextMenu': opt, 'contextMenuRoot': root }); if(opt.dataAttr){ $.each(opt.dataAttr, function (key, item) { opt.$menu.attr('data-' + opt.key, item); }); } $.each(['callbacks', 'commands', 'inputs'], function (i, k) { opt[k] = {}; if (!root[k]) { root[k] = {}; } }); if (!root.accesskeys) { root.accesskeys = {}; } function createNameNode(item) { var $name = $(''); if (item._accesskey) { if (item._beforeAccesskey) { $name.append(document.createTextNode(item._beforeAccesskey)); } $('') .addClass('context-menu-accesskey') .text(item._accesskey) .appendTo($name); if (item._afterAccesskey) { $name.append(document.createTextNode(item._afterAccesskey)); } } else { if (item.isHtmlName) { // restrict use with access keys if (typeof item.accesskey !== 'undefined') { throw new Error('accesskeys are not compatible with HTML names and cannot be used together in the same item'); } $name.html(item.name); } else { $name.text(item.name); } } return $name; } // create contextMenu items $.each(opt.items, function (key, item) { var $t = $('').addClass(item.className || ''), $label = null, $input = null; // iOS needs to see a click-event bound to an element to actually // have the TouchEvents infrastructure trigger the click event $t.on('click', $.noop); // Make old school string seperator a real item so checks wont be // akward later. // And normalize 'cm_separator' into 'cm_seperator'. if (typeof item === 'string' || item.type === 'cm_separator') { item = {type: 'cm_seperator'}; } item.$node = $t.data({ 'contextMenu': opt, 'contextMenuRoot': root, 'contextMenuKey': key }); // register accesskey // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that if (typeof item.accesskey !== 'undefined') { var aks = splitAccesskey(item.accesskey); for (var i = 0, ak; ak = aks[i]; i++) { if (!root.accesskeys[ak]) { root.accesskeys[ak] = item; var matched = item.name.match(new RegExp('^(.*?)(' + ak + ')(.*)$', 'i')); if (matched) { item._beforeAccesskey = matched[1]; item._accesskey = matched[2]; item._afterAccesskey = matched[3]; } break; } } } if (item.type && types[item.type]) { // run custom type handler types[item.type].call($t, item, opt, root); // register commands $.each([opt, root], function (i, k) { k.commands[key] = item; // Overwrite only if undefined or the item is appended to the root. This so it // doesn't overwrite callbacks of root elements if the name is the same. if ($.isFunction(item.callback) && (typeof k.callbacks[key] === 'undefined' || typeof opt.type === 'undefined')) { k.callbacks[key] = item.callback; } }); } else { // add label for input if (item.type === 'cm_seperator') { $t.addClass('context-menu-separator ' + root.classNames.notSelectable); } else if (item.type === 'html') { $t.addClass('context-menu-html ' + root.classNames.notSelectable); } else if (item.type !== 'sub' && item.type) { $label = $('').appendTo($t); createNameNode(item).appendTo($label); $t.addClass('context-menu-input'); opt.hasTypes = true; $.each([opt, root], function (i, k) { k.commands[key] = item; k.inputs[key] = item; }); } else if (item.items) { item.type = 'sub'; } switch (item.type) { case 'cm_seperator': break; case 'text': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .appendTo($label); break; case 'textarea': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .appendTo($label); if (item.height) { $input.height(item.height); } break; case 'checkbox': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .prop('checked', !!item.selected) .prependTo($label); // prevent checkbox default action on fast click-through $input.on('click', handleFastInputClick); break; case 'radio': $input = $('') .attr('name', 'context-menu-input-' + item.radio) .val(item.value || '') .prop('checked', !!item.selected) .prependTo($label); // prevent radio default action on fast click-through $input.on('click', handleFastInputClick); break; case 'select': $input = $('') .attr('name', 'context-menu-input-' + key) .appendTo($label); if (item.options) { $.each(item.options, function (value, text) { $('').val(value).text(text).appendTo($input); }); $input.val(item.selected); } break; case 'sub': createNameNode(item).appendTo($t); item.appendTo = item.$node; $t.data('contextMenu', item).addClass('context-menu-submenu'); item.callback = null; // If item contains items, and this is a promise, we should create it later // check if subitems is of type promise. If it is a promise we need to create // it later, after promise has been resolved. if ('function' === typeof item.items.then) { // probably a promise, process it, when completed it will create the sub menu's. op.processPromises(item, root, item.items); } else { // normal submenu. op.create(item, root); } break; case 'html': $(item.html).appendTo($t); break; default: $.each([opt, root], function (i, k) { k.commands[key] = item; // Overwrite only if undefined or the item is appended to the root. This so it // doesn't overwrite callbacks of root elements if the name is the same. if ($.isFunction(item.callback) && (typeof k.callbacks[key] === 'undefined' || typeof opt.type === 'undefined')) { k.callbacks[key] = item.callback; } }); createNameNode(item).appendTo($t); break; } // disable key listener in if (item.type && item.type !== 'sub' && item.type !== 'html' && item.type !== 'cm_seperator') { $input .on('focus', handle.focusInput) .on('blur', handle.blurInput); if (item.events) { $input.on(item.events, opt); } } // add icons if (item.icon) { if ($.isFunction(item.icon)) { item._icon = item.icon.call(this, this, $t, key, item); } else { if (typeof(item.icon) === 'string' && ( item.icon.substring(0, 4) === 'fab ' || item.icon.substring(0, 4) === 'fas ' || item.icon.substring(0, 4) === 'fad ' || item.icon.substring(0, 4) === 'far ' || item.icon.substring(0, 4) === 'fal ') ) { // to enable font awesome $t.addClass(root.classNames.icon + ' ' + root.classNames.icon + '--fa5'); item._icon = $(''); } else if (typeof(item.icon) === 'string' && item.icon.substring(0, 3) === 'fa-') { item._icon = root.classNames.icon + ' ' + root.classNames.icon + '--fa fa ' + item.icon; } else { item._icon = root.classNames.icon + ' ' + root.classNames.icon + '-' + item.icon; } } if(typeof(item._icon) === "string"){ $t.addClass(item._icon); } else { $t.prepend(item._icon); } } } // cache contained elements item.$input = $input; item.$label = $label; // attach item to menu $t.appendTo(opt.$menu); // Disable text selection if (!opt.hasTypes && $.support.eventSelectstart) { // browsers support user-select: none, // IE has a special event for text-selection // browsers supporting neither will not be preventing text-selection $t.on('selectstart.disableTextSelect', handle.abortevent); } }); // attach contextMenu to (to bypass any possible overflow:hidden issues on parents of the trigger element) if (!opt.$node) { opt.$menu.css('display', 'none').addClass('context-menu-root'); } opt.$menu.appendTo(opt.appendTo || document.body); }, resize: function ($menu, nested) { var domMenu; // determine widths of submenus, as CSS won't grow them automatically // position:absolute within position:absolute; min-width:100; max-width:200; results in width: 100; // kinda sucks hard... // determine width of absolutely positioned element $menu.css({position: 'absolute', display: 'block'}); // don't apply yet, because that would break nested elements' widths $menu.data('width', (domMenu = $menu.get(0)).getBoundingClientRect ? Math.ceil(domMenu.getBoundingClientRect().width) : $menu.outerWidth() + 1); // outerWidth() returns rounded pixels // reset styles so they allow nested elements to grow/shrink naturally $menu.css({ position: 'static', minWidth: '0px', maxWidth: '100000px' }); // identify width of nested menus $menu.find('> li > ul').each(function () { op.resize($(this), true); }); // reset and apply changes in the end because nested // elements' widths wouldn't be calculatable otherwise if (!nested) { $menu.find('ul').addBack().css({ position: '', display: '', minWidth: '', maxWidth: '' }).outerWidth(function () { return $(this).data('width'); }); } }, update: function (opt, root) { var $trigger = this; if (typeof root === 'undefined') { root = opt; op.resize(opt.$menu); } var hasVisibleItems = false; // re-check disabled for each item opt.$menu.children().each(function () { var $item = $(this), key = $item.data('contextMenuKey'), item = opt.items[key], disabled = ($.isFunction(item.disabled) && item.disabled.call($trigger, key, root)) || item.disabled === true, visible; if ($.isFunction(item.visible)) { visible = item.visible.call($trigger, key, root); } else if (typeof item.visible !== 'undefined') { visible = item.visible === true; } else { visible = true; } if (visible) { hasVisibleItems = true; } $item[visible ? 'show' : 'hide'](); // dis- / enable item $item[disabled ? 'addClass' : 'removeClass'](root.classNames.disabled); if ($.isFunction(item.icon)) { $item.removeClass(item._icon); var iconResult = item.icon.call(this, $trigger, $item, key, item); if(typeof(iconResult) === "string"){ $item.addClass(iconResult); } else { $item.prepend(iconResult); } } if (item.type) { // dis- / enable input elements $item.find('input, select, textarea').prop('disabled', disabled); // update input states switch (item.type) { case 'text': case 'textarea': item.$input.val(item.value || ''); break; case 'checkbox': case 'radio': item.$input.val(item.value || '').prop('checked', !!item.selected); break; case 'select': item.$input.val((item.selected === 0 ? "0" : item.selected) || ''); break; } } if (item.$menu) { // update sub-menu var subMenuHasVisibleItems = op.update.call($trigger, item, root); if (subMenuHasVisibleItems) { hasVisibleItems = true; } } }); return hasVisibleItems; }, layer: function (opt, zIndex) { if (!opt.useModal) { var listener = function (ev) { handle.layerClick(ev, opt, function() { document.removeEventListener('mousedown', listener, true); }); }; document.addEventListener('mousedown', listener, true); return; } // add transparent layer for click area // filter and background for Internet Explorer, Issue #23 var $layer = opt.$layer = $('') .css({ height: $win.height(), width: $win.width(), display: 'block', position: 'fixed', 'z-index': zIndex - 1, top: 0, left: 0, opacity: 0, filter: 'alpha(opacity=0)', 'background-color': '#000' }) .data('contextMenuRoot', opt) .appendTo(document.body) .on('contextmenu', handle.abortevent) .on('mousedown', handle.layerClick); // IE6 doesn't know position:fixed; if (typeof document.body.style.maxWidth === 'undefined') { // IE6 doesn't support maxWidth $layer.css({ 'position': 'absolute', 'height': $(document).height() }); } return $layer; }, processPromises: function (opt, root, promise) { // Start opt.$node.addClass(root.classNames.iconLoadingClass); function completedPromise(opt, root, items) { // Completed promise (dev called promise.resolve). We now have a list of items which can // be used to create the rest of the context menu. if (typeof items === 'undefined') { // Null result, dev should have checked errorPromise(undefined);//own error object } finishPromiseProcess(opt, root, items); } function errorPromise(opt, root, errorItem) { // User called promise.reject() with an error item, if not, provide own error item. if (typeof errorItem === 'undefined') { errorItem = { "error": { name: "No items and no error item", icon: "context-menu-icon context-menu-icon-quit" } }; if (window.console) { (console.error || console.log).call(console, 'When you reject a promise, provide an "items" object, equal to normal sub-menu items'); } } else if (typeof errorItem === 'string') { errorItem = {"error": {name: errorItem}}; } finishPromiseProcess(opt, root, errorItem); } function finishPromiseProcess(opt, root, items) { if (typeof root.$menu === 'undefined' || !root.$menu.is(':visible')) { return; } opt.$node.removeClass(root.classNames.iconLoadingClass); opt.items = items; op.create(opt, root, true); // Create submenu op.update(opt, root); // Correctly update position if user is already hovered over menu item root.positionSubmenu.call(opt.$node, opt.$menu); // positionSubmenu, will only do anything if user already hovered over menu item that just got new subitems. } // Wait for promise completion. .then(success, error, notify) (we don't track notify). Bind the opt // and root to avoid scope problems promise.then(completedPromise.bind(this, opt, root), errorPromise.bind(this, opt, root)); }, // operation that will run after contextMenu showed on screen activated: function(opt){ var $menu = opt.$menu; var $menuOffset = $menu.offset(); var winHeight = $(window).height(); var winWidth = $(window).width(); var winScrollTop = $(window).scrollTop(); var winScrollLeft = $(window).scrollLeft(); var menuHeight = $menu.height(); var outerHeight = $menu.outerHeight(); var outerWidth = $menu.outerWidth(); if(menuHeight > winHeight){ $menu.css({ 'height' : winHeight + 'px', 'overflow-x': 'hidden', 'overflow-y': 'auto', 'top': winScrollTop + 'px' }); } else if($menuOffset.top < winScrollTop){ $menu.css({ 'top': winScrollTop + 'px' }); } else if($menuOffset.top + outerHeight > winScrollTop + winHeight){ $menu.css({ 'top': $menuOffset.top - (($menuOffset.top + outerHeight) - (winScrollTop + winHeight)) + "px" }); } if($menuOffset.left + outerWidth > winScrollLeft + winWidth){ $menu.css({ 'left': $menuOffset.left - (($menuOffset.left + outerWidth) - (winScrollLeft + winWidth)) + "px" }); } } }; // split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key function splitAccesskey(val) { var t = val.split(/\s+/); var keys = []; for (var i = 0, k; k = t[i]; i++) { k = k.charAt(0).toUpperCase(); // first character only // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it. // a map to look up already used access keys would be nice keys.push(k); } return keys; } // handle contextMenu triggers $.fn.contextMenu = function (operation) { var $t = this, $o = operation; if (this.length > 0) { // this is not a build on demand menu if (typeof operation === 'undefined') { this.first().trigger('contextmenu'); } else if (typeof operation.x !== 'undefined' && typeof operation.y !== 'undefined') { this.first().trigger($.Event('contextmenu', { pageX: operation.x, pageY: operation.y, mouseButton: operation.button })); } else if (operation === 'hide') { var $menu = this.first().data('contextMenu') ? this.first().data('contextMenu').$menu : null; if ($menu) { $menu.trigger('contextmenu:hide'); } } else if (operation === 'destroy') { $.contextMenu('destroy', {context: this}); } else if ($.isPlainObject(operation)) { operation.context = this; $.contextMenu('create', operation); } else if (operation) { this.removeClass('context-menu-disabled'); } else if (!operation) { this.addClass('context-menu-disabled'); } } else { $.each(menus, function () { if (this.selector === $t.selector) { $o.data = this; $.extend($o.data, {trigger: 'demand'}); } }); handle.contextmenu.call($o.target, $o); } return this; }; // manage contextMenu instances $.contextMenu = function (operation, options) { if (typeof operation !== 'string') { options = operation; operation = 'create'; } if (typeof options === 'string') { options = {selector: options}; } else if (typeof options === 'undefined') { options = {}; } // merge with default options var o = $.extend(true, {}, defaults, options || {}); var $document = $(document); var $context = $document; var _hasContext = false; if (!o.context || !o.context.length) { o.context = document; } else { // you never know what they throw at you... $context = $(o.context).first(); o.context = $context.get(0); _hasContext = !$(o.context).is(document); } switch (operation) { case 'update': // Updates visibility and such if(_hasContext){ op.update($context); } else { for(var menu in menus){ if(menus.hasOwnProperty(menu)){ op.update(menus[menu]); } } } break; case 'create': // no selector no joy if (!o.selector) { throw new Error('No selector specified'); } // make sure internal classes are not bound to if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) { throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className'); } if (!o.build && (!o.items || $.isEmptyObject(o.items))) { throw new Error('No Items specified'); } counter++; o.ns = '.contextMenu' + counter; if (!_hasContext) { namespaces[o.selector] = o.ns; } menus[o.ns] = o; // default to right click if (!o.trigger) { o.trigger = 'right'; } if (!initialized) { var itemClick = o.itemClickEvent === 'click' ? 'click.contextMenu' : 'mouseup.contextMenu'; var contextMenuItemObj = { // 'mouseup.contextMenu': handle.itemClick, // 'click.contextMenu': handle.itemClick, 'contextmenu:focus.contextMenu': handle.focusItem, 'contextmenu:blur.contextMenu': handle.blurItem, 'contextmenu.contextMenu': handle.abortevent, 'mouseenter.contextMenu': handle.itemMouseenter, 'mouseleave.contextMenu': handle.itemMouseleave }; contextMenuItemObj[itemClick] = handle.itemClick; // make sure item click is registered first $document .on({ 'contextmenu:hide.contextMenu': handle.hideMenu, 'prevcommand.contextMenu': handle.prevItem, 'nextcommand.contextMenu': handle.nextItem, 'contextmenu.contextMenu': handle.abortevent, 'mouseenter.contextMenu': handle.menuMouseenter, 'mouseleave.contextMenu': handle.menuMouseleave }, '.context-menu-list') .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick) .on(contextMenuItemObj, '.context-menu-item'); initialized = true; } // engage native contextmenu event $context .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu); if (_hasContext) { // add remove hook, just in case $context.on('remove' + o.ns, function () { $(this).contextMenu('destroy'); }); } switch (o.trigger) { case 'hover': $context .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter) .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave); break; case 'left': $context.on('click' + o.ns, o.selector, o, handle.click); break; case 'touchstart': $context.on('touchstart' + o.ns, o.selector, o, handle.click); break; /* default: // http://www.quirksmode.org/dom/events/contextmenu.html $document .on('mousedown' + o.ns, o.selector, o, handle.mousedown) .on('mouseup' + o.ns, o.selector, o, handle.mouseup); break; */ } // create menu if (!o.build) { op.create(o); } break; case 'destroy': var $visibleMenu; if (_hasContext) { // get proper options var context = o.context; $.each(menus, function (ns, o) { if (!o) { return true; } // Is this menu equest to the context called from if (!$(context).is(o.selector)) { return true; } $visibleMenu = $('.context-menu-list').filter(':visible'); if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is($(o.context).find(o.selector))) { $visibleMenu.trigger('contextmenu:hide', {force: true}); } try { if (menus[o.ns].$menu) { menus[o.ns].$menu.remove(); } delete menus[o.ns]; } catch (e) { menus[o.ns] = null; } $(o.context).off(o.ns); return true; }); } else if (!o.selector) { $document.off('.contextMenu .contextMenuAutoHide'); $.each(menus, function (ns, o) { $(o.context).off(o.ns); }); namespaces = {}; menus = {}; counter = 0; initialized = false; $('#context-menu-layer, .context-menu-list').remove(); } else if (namespaces[o.selector]) { $visibleMenu = $('.context-menu-list').filter(':visible'); if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is(o.selector)) { $visibleMenu.trigger('contextmenu:hide', {force: true}); } try { if (menus[namespaces[o.selector]].$menu) { menus[namespaces[o.selector]].$menu.remove(); } delete menus[namespaces[o.selector]]; } catch (e) { menus[namespaces[o.selector]] = null; } $document.off(namespaces[o.selector]); } break; case 'html5': // if and are not handled by the browser, // or options was a bool true, // initialize $.contextMenu for them if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options === 'boolean' && options)) { $('menu[type="context"]').each(function () { if (this.id) { $.contextMenu({ selector: '[contextmenu=' + this.id + ']', items: $.contextMenu.fromMenu(this) }); } }).css('display', 'none'); } break; default: throw new Error('Unknown operation "' + operation + '"'); } return this; }; // import values into commands $.contextMenu.setInputValues = function (opt, data) { if (typeof data === 'undefined') { data = {}; } $.each(opt.inputs, function (key, item) { switch (item.type) { case 'text': case 'textarea': item.value = data[key] || ''; break; case 'checkbox': item.selected = data[key] ? true : false; break; case 'radio': item.selected = (data[item.radio] || '') === item.value; break; case 'select': item.selected = data[key] || ''; break; } }); }; // export values from commands $.contextMenu.getInputValues = function (opt, data) { if (typeof data === 'undefined') { data = {}; } $.each(opt.inputs, function (key, item) { switch (item.type) { case 'text': case 'textarea': case 'select': data[key] = item.$input.val(); break; case 'checkbox': data[key] = item.$input.prop('checked'); break; case 'radio': if (item.$input.prop('checked')) { data[item.radio] = item.value; } break; } }); return data; }; // find function inputLabel(node) { return (node.id && $('label[for="' + node.id + '"]').val()) || node.name; } // convert to items object function menuChildren(items, $children, counter) { if (!counter) { counter = 0; } $children.each(function () { var $node = $(this), node = this, nodeName = this.nodeName.toLowerCase(), label, item; // extract if (nodeName === 'label' && $node.find('input, textarea, select').length) { label = $node.text(); $node = $node.children().first(); node = $node.get(0); nodeName = node.nodeName.toLowerCase(); } /* * accepts flow-content as children. that means , and such are valid menu items. * Not being the sadistic kind, $.contextMenu only accepts: * , , , , , , and of course . * Everything else will be imported as an html node, which is not interfaced with contextMenu. */ // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command switch (nodeName) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element case 'menu': item = {name: $node.attr('label'), items: {}}; counter = menuChildren(item.items, $node.children(), counter); break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command case 'a': // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command case 'button': item = { name: $node.text(), disabled: !!$node.attr('disabled'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command case 'menuitem': case 'command': switch ($node.attr('type')) { case undefined: case 'command': case 'menuitem': item = { name: $node.attr('label'), disabled: !!$node.attr('disabled'), icon: $node.attr('icon'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; case 'checkbox': item = { type: 'checkbox', disabled: !!$node.attr('disabled'), name: $node.attr('label'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', disabled: !!$node.attr('disabled'), name: $node.attr('label'), radio: $node.attr('radiogroup'), value: $node.attr('id'), selected: !!$node.attr('checked') }; break; default: item = undefined; } break; case 'hr': item = '-------'; break; case 'input': switch ($node.attr('type')) { case 'text': item = { type: 'text', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'checkbox': item = { type: 'checkbox', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), radio: !!$node.attr('name'), value: $node.val(), selected: !!$node.attr('checked') }; break; default: item = undefined; break; } break; case 'select': item = { type: 'select', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: $node.val(), options: {} }; $node.children().each(function () { item.options[this.value] = $(this).text(); }); break; case 'textarea': item = { type: 'textarea', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'label': break; default: item = {type: 'html', html: $node.clone(true)}; break; } if (item) { counter++; items['key' + counter] = item; } }); return counter; } // convert html5 menu $.contextMenu.fromMenu = function (element) { var $this = $(element), items = {}; menuChildren(items, $this.children()); return items; }; // make defaults accessible $.contextMenu.defaults = defaults; $.contextMenu.types = types; // export internal functions - undocumented, for hacking only! $.contextMenu.handle = handle; $.contextMenu.op = op; $.contextMenu.menus = menus; }); ================================================ FILE: dist/jquery.ui.position.js ================================================ /*! jQuery UI - v1.12.1 - 2016-09-16 * http://jqueryui.com * Includes: position.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ (function( factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define([ "jquery" ], factory ); } else { // Browser globals factory( jQuery ); } }(function( $ ) { $.ui = $.ui || {}; var version = $.ui.version = "1.12.1"; /*! * jQuery UI Position 1.12.1 * http://jqueryui.com * * Copyright jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/position/ */ //>>label: Position //>>group: Core //>>description: Positions elements relative to other elements. //>>docs: http://api.jqueryui.com/position/ //>>demos: http://jqueryui.com/position/ ( function() { var cachedScrollbarWidth, max = Math.max, abs = Math.abs, rhorizontal = /left|center|right/, rvertical = /top|center|bottom/, roffset = /[\+\-]\d+(\.[\d]+)?%?/, rposition = /^\w+/, rpercent = /%$/, _position = $.fn.position; function getOffsets( offsets, width, height ) { return [ parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) ]; } function parseCss( element, property ) { return parseInt( $.css( element, property ), 10 ) || 0; } function getDimensions( elem ) { var raw = elem[ 0 ]; if ( raw.nodeType === 9 ) { return { width: elem.width(), height: elem.height(), offset: { top: 0, left: 0 } }; } if ( $.isWindow( raw ) ) { return { width: elem.width(), height: elem.height(), offset: { top: elem.scrollTop(), left: elem.scrollLeft() } }; } if ( raw.preventDefault ) { return { width: 0, height: 0, offset: { top: raw.pageY, left: raw.pageX } }; } return { width: elem.outerWidth(), height: elem.outerHeight(), offset: elem.offset() }; } $.position = { scrollbarWidth: function() { if ( cachedScrollbarWidth !== undefined ) { return cachedScrollbarWidth; } var w1, w2, div = $( "" + "" ), innerDiv = div.children()[ 0 ]; $( "body" ).append( div ); w1 = innerDiv.offsetWidth; div.css( "overflow", "scroll" ); w2 = innerDiv.offsetWidth; if ( w1 === w2 ) { w2 = div[ 0 ].clientWidth; } div.remove(); return ( cachedScrollbarWidth = w1 - w2 ); }, getScrollInfo: function( within ) { var overflowX = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-x" ), overflowY = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-y" ), hasOverflowX = overflowX === "scroll" || ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), hasOverflowY = overflowY === "scroll" || ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); return { width: hasOverflowY ? $.position.scrollbarWidth() : 0, height: hasOverflowX ? $.position.scrollbarWidth() : 0 }; }, getWithinInfo: function( element ) { var withinElement = $( element || window ), isWindow = $.isWindow( withinElement[ 0 ] ), isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, hasOffset = !isWindow && !isDocument; return { element: withinElement, isWindow: isWindow, isDocument: isDocument, offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, scrollLeft: withinElement.scrollLeft(), scrollTop: withinElement.scrollTop(), width: withinElement.outerWidth(), height: withinElement.outerHeight() }; } }; $.fn.position = function( options ) { if ( !options || !options.of ) { return _position.apply( this, arguments ); } // Make a copy, we don't want to modify arguments options = $.extend( {}, options ); var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, target = $( options.of ), within = $.position.getWithinInfo( options.within ), scrollInfo = $.position.getScrollInfo( within ), collision = ( options.collision || "flip" ).split( " " ), offsets = {}; dimensions = getDimensions( target ); if ( target[ 0 ].preventDefault ) { // Force left top to allow flipping options.at = "left top"; } targetWidth = dimensions.width; targetHeight = dimensions.height; targetOffset = dimensions.offset; // Clone to reuse original targetOffset later basePosition = $.extend( {}, targetOffset ); // Force my and at to have valid horizontal and vertical positions // if a value is missing or invalid, it will be converted to center $.each( [ "my", "at" ], function() { var pos = ( options[ this ] || "" ).split( " " ), horizontalOffset, verticalOffset; if ( pos.length === 1 ) { pos = rhorizontal.test( pos[ 0 ] ) ? pos.concat( [ "center" ] ) : rvertical.test( pos[ 0 ] ) ? [ "center" ].concat( pos ) : [ "center", "center" ]; } pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; // Calculate offsets horizontalOffset = roffset.exec( pos[ 0 ] ); verticalOffset = roffset.exec( pos[ 1 ] ); offsets[ this ] = [ horizontalOffset ? horizontalOffset[ 0 ] : 0, verticalOffset ? verticalOffset[ 0 ] : 0 ]; // Reduce to just the positions without the offsets options[ this ] = [ rposition.exec( pos[ 0 ] )[ 0 ], rposition.exec( pos[ 1 ] )[ 0 ] ]; } ); // Normalize collision option if ( collision.length === 1 ) { collision[ 1 ] = collision[ 0 ]; } if ( options.at[ 0 ] === "right" ) { basePosition.left += targetWidth; } else if ( options.at[ 0 ] === "center" ) { basePosition.left += targetWidth / 2; } if ( options.at[ 1 ] === "bottom" ) { basePosition.top += targetHeight; } else if ( options.at[ 1 ] === "center" ) { basePosition.top += targetHeight / 2; } atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); basePosition.left += atOffset[ 0 ]; basePosition.top += atOffset[ 1 ]; return this.each( function() { var collisionPosition, using, elem = $( this ), elemWidth = elem.outerWidth(), elemHeight = elem.outerHeight(), marginLeft = parseCss( this, "marginLeft" ), marginTop = parseCss( this, "marginTop" ), collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, position = $.extend( {}, basePosition ), myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); if ( options.my[ 0 ] === "right" ) { position.left -= elemWidth; } else if ( options.my[ 0 ] === "center" ) { position.left -= elemWidth / 2; } if ( options.my[ 1 ] === "bottom" ) { position.top -= elemHeight; } else if ( options.my[ 1 ] === "center" ) { position.top -= elemHeight / 2; } position.left += myOffset[ 0 ]; position.top += myOffset[ 1 ]; collisionPosition = { marginLeft: marginLeft, marginTop: marginTop }; $.each( [ "left", "top" ], function( i, dir ) { if ( $.ui.position[ collision[ i ] ] ) { $.ui.position[ collision[ i ] ][ dir ]( position, { targetWidth: targetWidth, targetHeight: targetHeight, elemWidth: elemWidth, elemHeight: elemHeight, collisionPosition: collisionPosition, collisionWidth: collisionWidth, collisionHeight: collisionHeight, offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], my: options.my, at: options.at, within: within, elem: elem } ); } } ); if ( options.using ) { // Adds feedback as second argument to using callback, if present using = function( props ) { var left = targetOffset.left - position.left, right = left + targetWidth - elemWidth, top = targetOffset.top - position.top, bottom = top + targetHeight - elemHeight, feedback = { target: { element: target, left: targetOffset.left, top: targetOffset.top, width: targetWidth, height: targetHeight }, element: { element: elem, left: position.left, top: position.top, width: elemWidth, height: elemHeight }, horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" }; if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { feedback.horizontal = "center"; } if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { feedback.vertical = "middle"; } if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { feedback.important = "horizontal"; } else { feedback.important = "vertical"; } options.using.call( this, props, feedback ); }; } elem.offset( $.extend( position, { using: using } ) ); } ); }; $.ui.position = { fit: { left: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, outerWidth = within.width, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = withinOffset - collisionPosLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, newOverRight; // Element is wider than within if ( data.collisionWidth > outerWidth ) { // Element is initially over the left side of within if ( overLeft > 0 && overRight <= 0 ) { newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; position.left += overLeft - newOverRight; // Element is initially over right side of within } else if ( overRight > 0 && overLeft <= 0 ) { position.left = withinOffset; // Element is initially over both left and right sides of within } else { if ( overLeft > overRight ) { position.left = withinOffset + outerWidth - data.collisionWidth; } else { position.left = withinOffset; } } // Too far left -> align with left edge } else if ( overLeft > 0 ) { position.left += overLeft; // Too far right -> align with right edge } else if ( overRight > 0 ) { position.left -= overRight; // Adjust based on position and margin } else { position.left = max( position.left - collisionPosLeft, position.left ); } }, top: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollTop : within.offset.top, outerHeight = data.within.height, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = withinOffset - collisionPosTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, newOverBottom; // Element is taller than within if ( data.collisionHeight > outerHeight ) { // Element is initially over the top of within if ( overTop > 0 && overBottom <= 0 ) { newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; position.top += overTop - newOverBottom; // Element is initially over bottom of within } else if ( overBottom > 0 && overTop <= 0 ) { position.top = withinOffset; // Element is initially over both top and bottom of within } else { if ( overTop > overBottom ) { position.top = withinOffset + outerHeight - data.collisionHeight; } else { position.top = withinOffset; } } // Too far up -> align with top } else if ( overTop > 0 ) { position.top += overTop; // Too far down -> align with bottom edge } else if ( overBottom > 0 ) { position.top -= overBottom; // Adjust based on position and margin } else { position.top = max( position.top - collisionPosTop, position.top ); } } }, flip: { left: function( position, data ) { var within = data.within, withinOffset = within.offset.left + within.scrollLeft, outerWidth = within.width, offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = collisionPosLeft - offsetLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, myOffset = data.my[ 0 ] === "left" ? -data.elemWidth : data.my[ 0 ] === "right" ? data.elemWidth : 0, atOffset = data.at[ 0 ] === "left" ? data.targetWidth : data.at[ 0 ] === "right" ? -data.targetWidth : 0, offset = -2 * data.offset[ 0 ], newOverRight, newOverLeft; if ( overLeft < 0 ) { newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { position.left += myOffset + atOffset + offset; } } else if ( overRight > 0 ) { newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { position.left += myOffset + atOffset + offset; } } }, top: function( position, data ) { var within = data.within, withinOffset = within.offset.top + within.scrollTop, outerHeight = within.height, offsetTop = within.isWindow ? within.scrollTop : within.offset.top, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = collisionPosTop - offsetTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, top = data.my[ 1 ] === "top", myOffset = top ? -data.elemHeight : data.my[ 1 ] === "bottom" ? data.elemHeight : 0, atOffset = data.at[ 1 ] === "top" ? data.targetHeight : data.at[ 1 ] === "bottom" ? -data.targetHeight : 0, offset = -2 * data.offset[ 1 ], newOverTop, newOverBottom; if ( overTop < 0 ) { newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { position.top += myOffset + atOffset + offset; } } else if ( overBottom > 0 ) { newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { position.top += myOffset + atOffset + offset; } } } }, flipfit: { left: function() { $.ui.position.flip.left.apply( this, arguments ); $.ui.position.fit.left.apply( this, arguments ); }, top: function() { $.ui.position.flip.top.apply( this, arguments ); $.ui.position.fit.top.apply( this, arguments ); } } }; } )(); var position = $.ui.position; })); ================================================ FILE: documentation/CONTRIBUTE.md ================================================ To generate documentation install couscous and run in this folder. Use ```couscous preview``` to run local server with the documentation. http://couscous.io/docs/getting-started.html The generated html will also be copied to the tests/integration/html folder for integration tests. ================================================ FILE: documentation/couscous.yml ================================================ title: jQuery contextMenu (2.x) subTitle: Management facility for context menus. Developed for a large number of triggering objects. HTML5 Polyfill. #template: # url: https://github.com/CouscousPHP/Template-ReadTheDocs exclude: - website - website-dark baseUrl: https://swisnl.github.io/jQuery-contextMenu scripts: before: - mkdir website/dist - cp -R ../dist/* website/dist/ - git clone -b gh-pages https://github.com/swisnl/jQuery-contextMenu docs-3x - cp -R docs-3x/3.x website/ after: - rm -rf website/dist/ - rm -rf docs-3x/ - cp .couscous/generated/demo/* ../test/integration/html github: user: swisnl repo: jQuery-contextMenu # The left menu bar menu: items: introduction: text: Introduction relativeUrl: / items: author: text: Author absoluteUrl: https://www.swis.nl/over-ons/bjorn-brala demo: text: Demo # You can use relative urls relativeUrl: demo.html documentation: text: Documentation relativeUrl: docs.html items: options: text: Options relativeUrl: docs.html items: text: Defining menu items relativeUrl: docs/items.html plugin-commands: text: Plugin commands relativeUrl: docs/plugin-commands.html custom-icons: text: Customize icons relativeUrl: docs/customize.html fontawesome-icons: text: FontAwesome icons relativeUrl: demo/fontawesome-icons.html font-awesome: text: Font Awesome support relativeUrl: docs/font-awesome.html runtime-options: text: Runtime options relativeUrl: docs/runtime-options.html custom-command-types: text: Custom command types relativeUrl: docs/custom-command-types.html events: text: Events relativeUrl: docs/events.html html5-polyfill-docs: text: HTML5 polyfill relativeUrl: docs/html5-polyfill.html demo-gallery: text: Demo gallery relativeUrl: demo.html items: simple-context-menu: text: Simple Context Menu relativeUrl: demo.html fontawesome-icons: text: FontAwesome icons relativeUrl: demo/fontawesome-icons.html accesskeys: text: Accesskeys relativeUrl: demo/accesskeys.html async-create: text: Create Context Menu (asynchronous) relativeUrl: demo/async-create.html async-promise: text: Create Context Menu (promise) relativeUrl: demo/async-promise.html callback: text: Command's action (callbacks) relativeUrl: demo/callback.html custom-command: text: Custom Command Types relativeUrl: demo/custom-command.html disabled: text: Disabled Command relativeUrl: demo/disabled.html disabled-callback: text: Disabled Callback Command relativeUrl: demo/disabled-callback.html disabled-changing: text: Changing Command's disabled status relativeUrl: demo/disabled-changing.html disabled-menu: text: Disabled Menu relativeUrl: demo/disabled-menu.html dynamic: text: Adding new Context Menu Triggers relativeUrl: demo/dynamic.html dynamic-create: text: Create Context Menu on demand relativeUrl: demo/dynamic-create.html html5-import: text: Importing HTML5 menu relativeUrl: demo/html5-import.html html5-polyfill: text: HTML5 polyfill relativeUrl: demo/html5-polyfill.html html5-polyfill-firefox8: text: HTML5 polyfill (Firefox) relativeUrl: demo/html5-polyfill-firefox8.html input: text: Input Commands relativeUrl: demo/input.html keeping-contextmenu-open: text: Keeping the context menu open relativeUrl: demo/keeping-contextmenu-open.html menu-title: text: Menus with titles relativeUrl: demo/menu-title.html on-dom-element: text: Context Menu on DOM Element relativeUrl: demo/on-dom-element.html sub-menus: text: Submenus relativeUrl: demo/sub-menus.html trigger-custom: text: Custom Activated Context Menu relativeUrl: demo/trigger-custom.html trigger-hover: text: Hover Activated Context Menu relativeUrl: demo/trigger-hover.html trigger-hover-autohide: text: Hover Activated Context Menu With Autohide relativeUrl: demo/trigger-hover-autohide.html trigger-left-click: text: Left-Click Trigger relativeUrl: demo/trigger-left-click.html trigger-swipe: text: Swipe Trigger relativeUrl: demo/trigger-swipe.html ================================================ FILE: documentation/demo/accesskeys.md ================================================ --- currentMenu: accesskeys --- # Demo: Accesskeys - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/accesskeys_test.md ================================================ - [Demo: Accesskeys](#demo-accesskeys) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Accesskeys right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/async-create.md ================================================ --- currentMenu: async-create --- # Demo: Create Context Menu (asynchronous) - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/async-promise.md ================================================ --- currentMenu: async-promise --- # Demo: Submenu through promise (asynchronous) - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/callback.md ================================================ --- currentMenu: callback --- # Demo: Callback - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/callback_test.md ================================================ - [Demo: Callback](#demo-callback) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Callback right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/custom-command.md ================================================ --- currentMenu: custom-command --- # Demo: Custom command - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/custom-command_test.md ================================================ - [Demo: Custom command](#demo-custom-command) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Custom command right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-callback.md ================================================ --- currentMenu: disabled-callback --- # Demo: Disabled Callback - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-callback_test.md ================================================ - [Demo: Disabled Callback](#demo-disabled-callback) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Disabled Callback right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-changing.md ================================================ --- currentMenu: disabled-changing --- # Demo: Disabled changing - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-changing_test.md ================================================ - [Demo: Disabled changing](#demo-disabled-changing) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Disabled changing right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-menu.md ================================================ --- currentMenu: disabled-menu --- # Demo: Disabled menu - [Example code](#example-code) - [Example HTML](#example-html) right click me Enable Menu ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled.md ================================================ --- currentMenu: disabled --- # Demo: Disabled - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled_test.md ================================================ - [Demo: Disabled](#demo-disabled) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Disabled right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/dynamic-create.md ================================================ --- currentMenu: dynamic-create --- # Demo: Create Context Menu on Demand - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/dynamic.md ================================================ --- currentMenu: dynamic --- # Demo: Adding new Context Menu Triggers - [Example code](#example-code) - [Example HTML](#example-html) `jQuery.contextMenu` allows you to define a <menu> before the trigger elements are available. Button ## Example code ## Example HTML ================================================ FILE: documentation/demo/fontawesome-icons.md ================================================ --- currentMenu: fontawesome-icons --- # Demo: FontAwesome icons - [Example code](#example-code) - [Example HTML](#example-html) The menu allows you to use [FontAwesome](http://fontawesome.io/) [icons](http://fontawesome.io/icons/) in your menu. Just include the CSS for FontAwesome and you are ready to go. right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/html5-import.md ================================================ --- currentMenu: html5-import --- # Demo: Importing HTML5 `` - [Example code](#example-code) - [Example HTML](#example-html) `jQuery.contextMenu` allows you to import HTML5's `` structures to use in older browsers. right click me ## Example code ## Example HTML ```html ``` ================================================ FILE: documentation/demo/html5-polyfill-firefox8.md ================================================ --- currentMenu: html5-polyfill-firefox8 --- # Demo: HTML5 Polyfill (Firefox) - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ```html ``` ================================================ FILE: documentation/demo/html5-polyfill.md ================================================ --- currentMenu: html5-polyfill --- # Demo: HTML5 Polyfill - [Example code](#example-code) - [Example HTML](#example-html) `jQuery.contextMenu` allows you to import HTML5's <menu> structures to use in older browsers. right click me ## Example code ## Example HTML ```html foo bar ``` foo bar ================================================ FILE: documentation/demo/input.md ================================================ --- currentMenu: input --- # Demo: Input Commands - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/keeping-contextmenu-open.md ================================================ --- currentMenu: keeping-contextmenu-open --- # Demo: Keeping the Menu visible - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/menu-promise.md ================================================ --- currentMenu: menu-promise --- # Demo: Submenu through promise (asynchronous) - [Example code](#example-code) - [Example HTML](#example-html) right-clickable context menus immediate: async: ## Example code ## Example HTML ================================================ FILE: documentation/demo/menu-title.md ================================================ --- currentMenu: menu-title --- # Demo: Menu Title - [Example CSS](#example-css) - [Example code](#example-code) - [Example HTML](#example-html) right click me right click me right click me ## Example CSS ## Example code ## Example HTML ```html right click me right click me right click me ``` ================================================ FILE: documentation/demo/on-dom-element.md ================================================ --- currentMenu: on-dom-element --- # Demo: Context Menu on DOM Element - [Example code](#example-code) - [Example HTML](#example-html) right click me 1 right click me 2 right click me 3 right click me 4 ## Example code ## Example HTML ```html right click me 1 right click me 2 right click me 3 right click me 4 ``` ================================================ FILE: documentation/demo/sub-menus-promise.md ================================================ --- currentMenu: sub-menus-promise --- # Demo: Submenus with promise - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/sub-menus.md ================================================ --- currentMenu: sub-menus --- # Demo: Submenus - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/sub-menus_test.md ================================================ - [Demo: Submenus](#demo-submenus) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Submenus right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-custom.md ================================================ --- currentMenu: trigger-custom --- # Demo: Custom Activated Context Menu - [Example code](#example-code) - [Example HTML](#example-html) press that button Button ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-hover-autohide.md ================================================ --- currentMenu: trigger-hover-autohide --- # Demo: Hover Activated Context Menu With Autohide - [Example code](#example-code) - [Example HTML](#example-html) hover over me ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-hover.md ================================================ --- currentMenu: trigger-hover --- # Demo: Hover Activated Context Menu - [Example code](#example-code) - [Example HTML](#example-html) hover over me ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-left-click.md ================================================ --- currentMenu: trigger-left-click --- # Demo: Left-Click Trigger - [Example code](#example-code) - [Example HTML](#example-html) left click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-swipe.md ================================================ --- currentMenu: trigger-swipe --- # Demo: Swipe Trigger - [Example code](#example-code) - [Example HTML](#example-html) This demo uses the (third party) [TouchSwipe](https://github.com/mattbryson/TouchSwipe-Jquery-Plugin) plugin. swype right ## Example code ## Example HTML ================================================ FILE: documentation/demo.md ================================================ --- currentMenu: simple-context-menu --- # Demo: Simple Context Menu - [Example code: Simple Context Menu](#example-code-simple-context-menu) - [Example HTML: Simple Context Menu](#example-html-simple-context-menu) - [jQuery Context Menu Demo Gallery](#jquery-context-menu-demo-gallery) right click me ## Example code: Simple Context Menu ## Example HTML: Simple Context Menu ## jQuery Context Menu Demo Gallery * [Simple Context Menu](demo.html) * [Context Menu on DOM Element](demo/on-dom-element.html) * [Adding new Context Menu Triggers](demo/dynamic.html) * [Create Context Menu on demand](demo/dynamic-create.html) * [Create Context Menu (asynchronous)](demo/async-create.html) * [Keeping the context menu open](demo/keeping-contextmenu-open.html) * [Command's action (callbacks)](demo/callback.html) * [Left-Click Trigger](demo/trigger-left-click.html) * [Swipe Trigger](demo/trigger-swipe.html) * [Hover Activated Context Menu](demo/trigger-hover.html) * [Hover Activated Context Menu With Autohide](demo/trigger-hover-autohide.html) * [Custom Activated Context Menu](demo/trigger-custom.html) * [Disabled Menu](demo/disabled-menu.html) * [Disabled Command](demo/disabled.html) * [Disabled Callback Command](demo/disabled-callback.html) * [Changing Command's disabled status](demo/disabled-changing.html) * [Accesskeys](demo/accesskeys.html) * [Submenus](demo/sub-menus.html) * [Input Commands](demo/input.html) * [Custom Command Types](demo/custom-command.html) * [Menus with titles](demo/menu-title.html) * [Importing HTML5 ](demo/html5-import.html) * [HTML5 Polyfill](demo/html5-polyfill.html) * [HTML5 Polyfill (Firefox)](demo/html5-polyfill-firefox8.html) ================================================ FILE: documentation/docs/custom-command-types.md ================================================ --- currentMenu: custom-command-types --- # Custom Command Types Besides the built-in command types custom handlers can be defined. The command generator must be placed in `$.contextMenu.types`. It is identified by the key given in that object. The generator function is executed in the context of the new command's `` within the menu. item is the object passed at creation. Use this to pass values from your definition to the generator. `opt` is the current menu level, `root` is the menu's root-level `opt` (relevant for sub-menus only). A custom command type can be whatever you like it to be, it can behave how ever you want it to behave. Besides the keyboard interaction paradigm (`up`, `down`, `tab`, `escape`) key-events are passed on to the `` which can be accessed via `$(this).on('keydown', …);` Note that you'll probably want to disable default action handling (click, pressing enter) in favor of the custom command's behavior. ```javascript $.contextMenu.types.myType = function(item, opt, root) { $('' + item.customName + '').appendTo(this); this.on('contextmenu:focus', function(e) { // setup some awesome stuff }).on('contextmenu:blur', function(e) { // tear down whatever you did }).on('keydown', function(e) { // some funky key handling, maybe? }); }; $.contextMenu({ selector: '.context-menu-custom', items: { label: {type: "myType", customName: "Foo Bar"} } }); ``` ================================================ FILE: documentation/docs/customize.md ================================================ --- currentMenu: custom-icons --- ## Customize icons You can add icons to src/icons and run ``gulp build-icons``. This will make the icons available for use in the contextmenu using the icon property. So for example the file checkmark.svg wil result in the CSS context-menu-icon-checkmark which you can use by using the [icon option](items#icon) when defining a menu item. Is is also possible to just use FontAwesome icons, see the [demo of FontAwesome](https://swisnl.github.io/jQuery-contextMenu/demo/fontawesome-icons). ### Example ```javascript var items = { firstCommand: { name: "Paste", icon: "checkmark" // Class context-menu-icon-checkmark is used on the menu item. This is generated from checkmark.svg } } ``` Font-Awesome icons used from [encharm/Font-Awesome-SVG-PNG](https://github.com/encharm/Font-Awesome-SVG-PNG). You can download more there if you like. Finally, you will need to re-build the CSS using [`sass`](http://sass-lang.com), otherwise you may see mismatchings between the icon references. Use the command ``gulp css`` to re-build the CSS in the dist directory. The new CSS files will contain the icons you added. ## Customize CSS You can use the _variables.scss to adjust variables on pretty much everything you want to change. ================================================ FILE: documentation/docs/events.md ================================================ --- currentMenu: events --- # Events - [contextmenu](#contextmenu) - [prevcommand](#prevcommand) - [nextcommand](#nextcommand) - [contextmenu:hide](#contextmenuhide) - [contextmenu:focus](#contextmenufocus) - [contextmenu:blur](#contextmenublur) - [keydown](#keydown) List of events that are triggered on the menu. You can manually trigger some events to control the menu. ## contextmenu `contextmenu` : Trigger context menu to be shown for a trigger object. Available on trigger object. The Event must be supplied with coordinates for the menu: `{pageX: 123, pageY:123}` ``` $('.context-menu-one').first().trigger( $.Event('contextmenu', {pageX: 123, pageY: 123}) ); $('.context-menu-one').first().trigger("contextmenu"); ``` will invoke `determinePosition` to position the menu. ## prevcommand `prevcommand` : Select / highlight the previous possible command Available on context menu. ``` opt.$menu.trigger("prevcommand"); ``` ## nextcommand `nextcommand` : Select / highlight the next possible command Available on context menu. ``` opt.$menu.trigger("nextcommand"); ``` ## contextmenu:hide `contextmenu:hide` : Hide the menu Available on context menu. ``` opt.$menu.trigger("contextmenu:hide"); ``` ## contextmenu:focus `contextmenu:focus` : React to a command item being focused Triggered on context menu item when mouse or keyboard interaction lead to a "hover state" for that command item. ``` $(document.body).on("contextmenu:focus", ".context-menu-item", function(e){ console.log("focus:", this); } ); ``` ## contextmenu:blur `contextmenu:blur` : Available on each context menu item. Triggered on context menu item when mouse or keyboard interaction lead from a "hover state" to "default state" for that command item. ``` $(document.body).on("contextmenu:blur", ".context-menu-item", function(e){ console.log("blur:", this); } ); ``` ## keydown `keydown` : Available on each context menu item. Triggered on context menu item when keyboard interaction could not be handled by jQuery.contextMenu. ``` $(document.body).on("keydown", ".context-menu-item", function(e){ console.log("key:", e.keyCode); } ); ``` ================================================ FILE: documentation/docs/font-awesome.md ================================================ --- currentMenu: font-awesome ------------------------- ## Customize icons It is possible to use font-awesome icons if you like. You need to include the [Font Awesome CSS](https://www.bootstrapcdn.com/fontawesome/) in your application. That will enable you to use the icon classes to use those icons. Check out the [demo](https://swisnl.github.io/jQuery-contextMenu/demo/fontawesome-icons) ## Bring your own icons It is also possible to use your own SVG icons if you like, you can [customize](customize) this by using the SASS files. ## Recent Font Awesome support The contextmenu supports the new Font Awesome by including the correct font-awesome CSS and adding the new classes as icon for a menu item. When using for example `fas fa-beer` it will crete the `i` tag for the icon in the menu and adjust CSS accordingly. ================================================ FILE: documentation/docs/html5-polyfill.md ================================================ --- currentMenu: html5-polyfill --- # HTML5 `` shiv/polyfill - [HTML5 `` import](#html5-menu-import) - [HTML5 `` shiv/polyfill](#html5-menu-shivpolyfill) ## HTML5 `` import considering the following HTML `$.contextMenu.fromMenu($('#html5menu'))` will return a proper items object. ``` ``` `$.contextMenu.fromMenu()` will properly import (and thus handle) the following elements. Everything else is imported as `{type: "html"}` ``` (W3C Specification) (Firefox) the text ``` The `` must be hidden but not removed, as all command events (clicks) are passed-thru to the original command element! Note: While the specs note ``s to be rendered as regular commands, `$.contextMenu` will render an actual ``. ## HTML5 `` shiv/polyfill Engaging the HTML5 polyfill (ignoring `$.contextMenu` if context menus are available natively): ``` $(function(){ $.contextMenu("html5"); }); ``` Engaging the HTML5 polyfill (ignoring browser native implementation): ``` $(function(){ $.contextMenu("html5", true); }); ``` ================================================ FILE: documentation/docs/input-helpers.md ================================================ --- currentMenu: input-helpers --- # Helpers - [Import values for ``](#import-values-for-input) - [Export values from ``](#export-values-from-input) ## Import values for `` To fill input commands with values from a map: ``` {events: { hide: function(opt){ $.contextMenu.getInputValues(opt, {command1: "foo", command2: "bar"}); } } } ``` To fill input commands with values from data-attributes: ``` {events: { hide: function(opt){ $.contextMenu.getInputValues(opt, this.data()); } } } ``` ## Export values from `` To fetch values from input commands: ``` {events: { hide: function(opt){ var values = $.contextMenu.setInputValues(opt} } } ``` To save values from input commands to data-attributes: ``` {events: { hide: function(opt){ $.contextMenu.setInputValues(opt, this.data()); } } } ``` ================================================ FILE: documentation/docs/items.md ================================================ --- currentMenu: items --- # Items - [options.items](#optionsitems) - [name](#name) - [callback](#callback) - [className](#classname) - [icon](#icon) - [disabled](#disabled) - [visible](#visible) - [type](#type) - [events](#events) - [value](#value) - [selected](#selected) - [radio](#radio) - [options](#options) - [height](#height) - [items](#items) - [accesskey](#accesskey) The items map contains the commands to list in the menu. Each command has a unique key identifying an item object. The value may either be an item (properties explained below), or a string (which will insert a separator, disregarding the string's content). It is also possible to define a seperator the same as an item, and use the `type`:`cm_separator` to define it. ```javascript var items = { firstCommand: itemOptions, separator1: "-----", separator2: { "type": "cm_separator" }, command2: itemOptions } ``` Since 2.3 it is also possible to use a promise as item, so you can build submenu's based on a snynchronous promis. Check out the [demo using a promise](demo/async-promise.md) for an example how to use this. The example uses jQuery deferred, but any promise should do. Promised can only be used in combination with the [build option](docs#build). ## options.items ### name Specify the human readable name of the command in the menu. This is used as the label for the option. `name`: `string` #### Example ```javascript var items = { firstCommand: { name: "Copy" } } ``` ### isHtmlName When truthy, the defined `name` value is HTML. The value will be rendered using `$.html()` instead of `$.text()`. __Note: Cannot be used with the [accesskey](#accesskey) option in the same item.__ `isHtmlName`: `boolean` #### Example ```javascript var items = { firstCommand: { name: "Copy Text", isHtmlName: true } } ``` ### callback Specifies the callback to execute if clicked on The Callback is executed in the context of the triggering object. The first argument is the key of the command. The second argument is the options object. The Callback may return false to prevent the menu from being hidden. If no callback and no default callback is specified, the item will not have an action `callback`: `function(itemKey, opt, originalEvent)` #### Example ```javascript var items = { firstCommand: { name: "Copy", callback: function(itemKey, opt, e){ // Alert the key of the item and the trigger element's id. alert("Clicked on " + itemKey + " on element " + opt.$trigger.id); // Do not close the menu after clicking an item return false; } } } ``` ### className Specifies additional classNames to add to the menu item. Seperate multiple classes by using spaces. `className`: `string` #### Example ```javascript var items = { firstCommand: { name: "Copy", className: 'contextmenu-item-custom contextmenu-item-custom__highlight' } } ``` ### icon Specifies the icon class to set for the item. When using a string icons must be defined in CSS with selectors like `.context-menu-item.context-menu-icon-edit`, where `edit` is the icon class specified. When using a callback you can return a class string to use that as the class on the item. You can also modify the element by using the `$itemElement` argument. `icon`: `string` or `function(opt, $itemElement, itemKey, item)` #### Example ```javascript var items = { firstCommand: { name: "Copy", icon: function(opt, $itemElement, itemKey, item){ // Set the content to the menu trigger selector and add an bootstrap icon to the item. $itemElement.html(' ' + opt.selector); // Add the context-menu-icon-updated class to the item return 'context-menu-icon-updated'; } }, secondCommand: { name: "Paste", icon: "paste" // Class context-menu-icon-paste is used on the menu item. } } ``` ### disabled Specifies if the command is disabled (`true`) or enabled (`false`). May be a callback returning a `boolean`. The callback is executed in the context of the triggering object (so this inside the function refers to the element the context menu was shown for). The first argument is the `key` of the command. The second argument is the `options object`. `disabled`: `boolean` or `function(itemKey, opt)` #### Example ```javascript var items = { firstCommand: { name: "Copy", disabled: function(key, opt){ // Disable this item if the menu was triggered on a div if(opt.$trigger.nodeName === 'div'){ return true; } } } } ``` ### visible Specifies if the command is visible (`true`) or not (`false`). May be a callback returning a boolean. The callback is executed in the context of the triggering object (so this inside the function refers to the element the context menu was shown for). The first argument is the key of the command. The second argument is the `options object`. `visible`: `boolean` or `function(itemKey, opt)` #### Example ```javascript var items = { firstCommand: { name: "Copy", visible: function(key, opt){ // Hide this item if the menu was triggered on a div if(opt.$trigger.nodeName === 'div'){ return false; } } } } ``` ### type Specifies the type of the command. `type`: `null`, `undefined`, `text`, `textarea`, `checkbox`, `radio`, `select`, `html` default: `null` Value | Description ---- | ---- `null`, `undefined` , `""` | The command is a simple clickable item. `"text"` | Makes the command an `` of type `text`.The name followed by the `` are encapsulated in a ``. `"textarea"` | Makes the command a ``. The name followed by the `` are encapsulated in a ``. `"checkbox"` | Makes the command an `` of type checkbox. The name preceeded by the `` are encapsulated in a ``. The checkbox-element is moved to the icon space `"radio"` | Makes the command an `` of type radio. The name preceeded by the `` are encapsulated in a ``. The radio-element is moved to the icon space `"select"` | Makes the command a ``. The name followed by the `` are encapsulated in a ``. `"html"` | Makes an non-command element. When you select `type: 'html'` add the html to the `html` property. So: `{ item: { type: 'html', html: 'html!' } }`. You can also just use the item name with the [`isHtmlName`](isHtmlName) property. #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { name: { name: "Text", type: 'text', value: "Hello World", events: { keyup: function(e) { // add some fancy key handling here? window.console && console.log('key: '+ e.keyCode); } } }, sep1: "---------", // yesno: { name: "Boolean", type: 'checkbox', selected: true }, sep2: "---------", // radio1: { name: "Radio1", type: 'radio', radio: 'radio', value: '1' }, radio2: { name: "Radio2", type: 'radio', radio: 'radio', value: '2', selected: true }, sep3: "---------", // select: { name: "Select", type: 'select', options: {1: 'one', 2: 'two', 3: 'three'}, selected: 2 }, // area1: { name: "Textarea with height", type: 'textarea', value: "Hello World", height: 40 }, area2: { name: "Textarea", type: 'textarea', value: "Hello World" }, sep4: "---------", key: { name: "Something Clickable", callback: $.noop } } }); ``` ### events Events to register on `` elements. The contents of the options object are passed to jQuery event.data. __Only used with [types](#type) `text`, `textarea`, `radio`, `checkbox` and `select`.__ `events`: `object` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', events: { command1: { name: "Foobar", type: "text", events: { keyup: function(e){ alert(e.keyCode); alert(e.data.$trigger.attr("id")); } } } } }); ``` ### value The value of the `` element. __Only used with [types](#type) `text`, `textarea`, `radio`.__ `value`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', command1: { name: "Foobar", type: "text", value: "default value" } }); ``` ### selected The selected option of a `select` element and the checked property for `checkbox` and `radio` types. __Only used with [types](#type) `select`, `checkbox`, `radio`.__ `selected`: `string` or `boolean` Value | Description ---- | ---- `boolean` | Use with `checkbox` and `radio` to check. `string` | Use with `select` to select that option. #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // select: { name: "Select", type: 'select', options: {1: 'one', 2: 'two', 3: 'three'}, selected: "2" } } }); ``` ### radio Specifies the group of the radio elements. __Only used with [type](#type) `radio`.__ `radio`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // radio1: { name: "Radio1", type: 'radio', radio: 'radio', value: '1' }, radio2: { name: "Radio2", type: 'radio', radio: 'radio', value: '2', selected: true } } }); ``` ### options Specifies the `` elements for the `` element. __Only used with [type](#type) `select`.__ `options`: `object` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // select: { name: "Select", type: 'select', options: {1: 'one', 2: 'two', 3: 'three'}, selected: "2" } } }); ``` ### height The height in pixel `` element. If not specified, the height is defined by CSS. __Only used with [type](#type) `textarea`.__ `height`: `int` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // myTextarea: { name: "Textarea", type: 'textarea', height: 200 } } }); ``` ### items Commands to show in a sub-menu. You can nest as many as you like. `items`: `object` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // myItemWithSubmenu: { name: "Textarea", { items { mySubmenu { name: "Command 1" callback: function(key, opt){ alert("Clicked on " + key); } } } } } } }); ``` ### accesskey Character(s) to be used as accesskey. Considering `a b c` $.contextMenu will first try to use »a« as the accesskey, if already taken, it'll fall through to »b«. Words are reduced to the first character, so »hello world« is treated as »h w«. Note: Accesskeys are treated unique throughout one menu. This means an item in a sub-menu can't occupy the same accesskey as an item in the main menu. `accesskey`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', accesskey: 'a' callback: function(itemKey, opt){ alert('I pressed a!'); } }); ``` ### dataAttr Allows to pass data attributes (`data-*`) that get applied to the menu item. It should be passed as an object with key and a value. #### Example ```javascript var items = { firstCommand: { name: "Copy", dataAttr: { menuTitle: "My custom title" } } } ``` ================================================ FILE: documentation/docs/plugin-commands.md ================================================ --- currentMenu: plugin-commands --- # Plugin commands - [Disable a contextMenu trigger](#disable-a-contextmenu-trigger) - [Enable a contextMenu trigger](#enable-a-contextmenu-trigger) - [Manually show a contextMenu](#manually-show-a-contextmenu) - [Manually hide a contextMenu](#manually-hide-a-contextmenu) - [Unregister all contextMenus](#unregister-all-contextmenus) ## Disable a contextMenu trigger disable contextMenu to be shown on specified trigger elements ``` $(".some-selector").contextMenu(false); ``` ## Enable a contextMenu trigger enable contextMenu to be shown on specified trigger elements ``` $(".some-selector").contextMenu(true); ``` ## Manually show a contextMenu show the contextMenu of the first element of the selector (position determined by determinePosition): ``` $(".some-selector").contextMenu(); $(".some-selector").contextMenu({x: 123, y: 123}); ``` ## Manually hide a contextMenu hide the contextMenu of the first element of the selector: ``` $(".some-selector").contextMenu("hide"); Unregister contextMenu ``` ## Unregister a specific contextMenu To unregister / destroy a specific contextMenu: ``` $.contextMenu( 'destroy', selector ); ``` selector expects the (string) selector that the contextMenu was registered to ## Unregister all contextMenus To unregister / destroy all contextMenus: ``` $.contextMenu( 'destroy' ); ``` ================================================ FILE: documentation/docs/runtime-options.md ================================================ --- currentMenu: runtime-options --- # Runtime options (opt) - [$node](#node) - [$input](#input) - [$label](#label) - [$menu](#menu) - [$trigger](#trigger) - [callbacks](#callbacks) - [commands](#commands) - [inputs](#inputs) - [hasTypes](#hastypes) - [ns](#ns) The runtime options are passed to most callbacks on registration. This gives you the ability to access DOM elemnts and configuration dynamicly. One way of using these in in the general [callback](#callback) when an item is clicked. #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items : { name: "textfield", type: "text", value: "welcome!" }, callback: function(itemKey, opt){ // Alert the classes on the item that was clicked. alert(opt.$node.attr('class')); // Alert "welcome!" alert(opt.inputs[itemsKey].$input.val()); } }); ``` ### $selected Reference to the `` command element. `$selected`: `jQuery element` ### $input Reference to the `` or `` of the command element. __Only available with [type](#type) "text", "textarea", "checkbox", "radio" and "select".__ `$input`: `jQuery element` ### $label Reference to the `` of the command element. __Only available with [type](#type) "text", "textarea", "checkbox", "radio" and "select".__ `$label`: `jQuery element` ### $menu Or the menu element of the contextmenu or the `` sub-menu element when called inside a submenu. `$node`: `jQuery element` ### $trigger The element triggering the menu. `$trigger`: `jQuery element` ### callbacks Registered [callbacks](#callback) of all commands (including those of sub-menus). Warning: If you use the same keys for an item in any place, it will overwrite that callback here. `callbacks`: `array` ### commands Registered commands (including those of sub-menus). Warning: If you use the same keys for an item in any place, it will overwrite that command here. `commands`: `array` ### inputs Registered commands of input-type (including those of sub-menus). Warning: If you use the same keys for an item in any place, it will overwrite that command here. Access a specific ``: `opt.inputs[key].$input` `inputs`: `jQuery element` ### hasTypes flag denoting if the menu contains input elements. `hasTypes`: `jQuery element` ### ns The namespace (including leading dot) all events for this contextMenu instance were registered under. `ns`: `string` ================================================ FILE: documentation/docs.md ================================================ --- currentMenu: options --- # Documentation - [Register new contextMenu](#register-new-contextmenu) - [Update contextMenu state](#update-contextmenu-state) - [Options (at registration)](#options-at-registration) - [selector](#selector) - [items](#items) - [appendTo](#appendto) - [trigger](#trigger) - [reposition](#reposition) - [delay](#delay) - [autoHide](#autohide) - [zIndex](#zindex) - [className](#classname) - [classNames](#classnames) - [animation](#animation) - [events](#events) - [position](#position) - [determinePosition](#determineposition) - [callback](#callback) - [build](#build) ## Register new contextMenu To register a new contextMenu: * Note: For SVG support use jQuery >= 1.12|2.2 ```javascript $.contextMenu( options ); ``` ## Update contextMenu state It is possible to refresh the state of the contextmenu [disabled](https://swisnl.github.io/jQuery-contextMenu/docs/items.html#disabled), [visibility](https://swisnl.github.io/jQuery-contextMenu/docs/items.html#visible), [icons](https://swisnl.github.io/jQuery-contextMenu/docs/items.html#icon) and [input values](https://swisnl.github.io/jQuery-contextMenu/docs/items.html#type) through the `update` command. This will reevaluate any custom callbacks. ```javascript $('.context-menu-one').contextMenu('update'); // update single menu $.contextMenu('update') // update all open menus ``` ## Options (at registration) ### selector The jQuery selector matching the elements to trigger on. This option is mandatory. `selector`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu' }); ``` ### items Object with [items](docs/items.html) to be listed in contextMenu. See [items](docs/items.html) for a full documentation on how to build your menu items. `items`: `object` Object containing [items](docs/items.html) objects. #### Example ```javascript $.contextMenu({ selector: '.context-menu', items: { copy: { name: "Copy", callback: function(key, opt){ alert("Clicked on " + key); } } } }); ``` ### appendTo Specifies the selector string or DOMElement the generated menu is to be appended to. `appendTo`: `string` or `DOMElement` default: `document.body` #### Example ```javascript // select the container with a selector $.contextMenu({ selector: 'span.context-menu', appendTo: 'div#context-menus-container' }); // select the container with a dom element var element = document.getElementById('context-menus-container'); $.contextMenu({ selector: 'span.context-menu', appendTo: element }); ``` ### trigger Specifies what event on the element specified in the [selector](#selector) triggers the contextmenu. `trigger`: `string` default: `'right'` Value | Description ---- | ---- `right` | Right mouse button `left` | Left mouse button `hover` | Hover the element `touchstart` | Touchstart only `none` | No trigger #### Example ```javascript // trigger with left mouse button $.contextMenu({ selector: 'span.context-menu', trigger: 'left' }); // trigger on hover $.contextMenu({ selector: 'span.context-menu', trigger: 'hover' }); ``` ### hideOnSecondTrigger Flag denoting if a second trigger should close the menu, as long as the trigger happened on one of the trigger-element's child nodes. This overrides the reposition option. `hideOnSecondTrigger`: `boolean` default: `false` ### selectableSubMenu Specifies if menu items containing submenus should be clickable or not. `selectableSubMenu`: `boolean` default: `false` Value | Description ---- | ---- `true` | All Enabled menu items, even containing others are clickable `false` | Items containing subitems are not clickable #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', selectableSubMenu: true }); ``` ### reposition Specifies if a menu should be repositioned (`true`) or rebuilt (`false`) if a second [trigger](#trigger) event (like a right click) is performed on the same element (or its children) while the menu is still visible. `reposition`: `boolean` default: `true` Value | Description ---- | ---- `true` | Reposition menu when triggered `false` | Rebuild menu when triggered #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', reposition: false }); ``` ### delay Specifies the time in milliseconds to wait before showing the menu. Only applies to [trigger](#trigger): "hover" `delay`: `int` default: `200` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', delay: 500 }); ``` ### autoHide Specifies if the menu must be hidden when the mouse pointer is moved out of the [trigger](#trigger) and [menu items](#items). `autoHide`: `boolean` default: `false` Value | Description ---- | ---- `true` | Hide the menu on mouseout `false` | Do not hide the menu on mouseout #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', autoHide: true }); ``` ### zIndex Specifies the offset to add to the calculated zIndex of the [trigger](#trigger) element. Set to `0` to prevent zIndex manipulation. Can be a function that returns an int to calculate the zIndex on build. `zIndex`: `int`|`function` default: `1` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', zIndex: 10 }); $.contextMenu({ selector: 'span.context-menu', zIndex: function($trigger, opt){ return 120; }); ``` ### className Specifies additional classNames to add to the menu element. Seperate multiple classes by using spaces. `className`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', className: 'contextmenu-custom contextmenu-custom__highlight' }); ``` ### classNames Specifies the base class names of the contextmenu elements. This can be used to change the class names of some classes that might conflict with frameworks like Bootstrap. `classNames`: `object` ```javascript // Classname configuration to be able avoid conflicts in frameworks var options = { classNames : { hover: 'hover', // Item hover disabled: 'disabled', // Item disabled visible: 'visible', // Item visible notSelectable: 'not-selectable', // Item not selectable icon: 'context-menu-icon', // Base icon class iconEdit: 'context-menu-icon-edit', iconCut: 'context-menu-icon-cut', iconCopy: 'context-menu-icon-copy', iconPaste: 'context-menu-icon-paste', iconDelete: 'context-menu-icon-delete', iconAdd: 'context-menu-icon-add', iconQuit: 'context-menu-icon-quit', iconLoadingClass: 'context-menu-icon-loading' } } ``` ### animation Animation properties take effect on showing and hiding the menu. Duration specifies the duration of the animation in milliseconds. `show` and `hide` specify [jQuery methods](http://api.jquery.com/category/effects/) to show and hide elements. `animation`: `object` default: `{duration: 500, show: 'slideDown', hide: 'slideUp'}` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', animation: `{duration: 250, show: 'fadeIn', hide: 'fadeOut'}` }); ``` ### events The `show` and `hide` events are triggered *before* the menu is shown or hidden. The event handlers are executed in the context of the triggering object. This will thus reference the jQuery handle of the [trigger](#trigger) object. A reference to the current options object is passed, the options object is a collection of current options and references to the DOM nodes of the menu. The event handlers may return `false` to prevent the `show` or `hide` process. `events`: `object` Value | Description ---- | ---- `events.preShow` | Called before show of the contextmenu, when returning false default browser context menu is shown `events.show` | Called on show of the contextmenu `events.hide` | Called before hide of the contextmenu `events.activated` | Called after activation of the contextmenu #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', events: { show : function(options){ // Add class to the menu this.addClass('currently-showing-menu'); // Show an alert with the selector of the menu if( confirm('Open menu with selector ' + options.selector + '?') === true ){ return true; } else { // Prevent the menu to be shown. return false; } }, hide : function(options){ if( confirm('Hide menu with selector ' + options.selector + '?') === true ){ return true; } else { // Prevent the menu to be hidden. return false; } }, activated : function(options){ if( confirm('Hide menu with selector ' + options.selector + '?') === true ){ console.log('Menu Activated'); } } }); ``` ### position Callback to override the position of the context menu. The function is executed in the context of the trigger object. The first argument is the `$menu` jQuery object, which is the menu element. The second and third arguments are `x` and `y` coordinates provided by the `show` event. The `x` and `y` may either be integers denoting the offset from the top left corner, `undefined`, or the string `"maintain"`. If the string `"maintain"` is provided, the current position of the `$menu` must be used. If the coordinates are `undefined`, appropriate coordinates must be determined. An example of how this can be achieved is provided with [determinePosition](#determinePosition). `position`: `function(opt.$menu, x, y)` Value `x` or `y` | Description ---- | ---- `int` | Offset in pixels from top-left of trigger element. `"maintain"` | Maintain current `x` or `y` coordinate `undefined` | Unknown, [determinePosition](#determinePosition) is called. #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', position: function(opt, x, y){ opt.$menu.css({top: 123, left: 123}); } }); ``` ### determinePosition Determine the position of the menu in respect to the given [trigger](#trigger) object, this function is called when there is no `x` and `y` set on the [position](#position) call. `determinePosition`: `function(opt.$menu)` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', determinePosition: function($menu){ // Position using jQuery.ui.position // http://api.jqueryui.com/position/ $menu.css('display', 'block') .position({ my: "center top", at: "center bottom", of: this, offset: "0 5"}) .css('display', 'none'); } }); ``` ### callback Specifies the default callback to be used in case an [item](#items) does not expose its own callback. The default callback behaves just like item.callback. `callback`: `function(itemKey, opt)` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', callback: function(itemKey, opt){ // Alert the key of the item and the trigger element's id. alert("Clicked on " + itemKey + " on element " + opt.$trigger.attr("id")); // Do not close the menu after clicking an item return false; } }); ``` ### build The callback is executed with two arguments given: the jQuery reference to the triggering element and the original contextmenu event. It is executed without context (so this won't refer to anything useful). If the build callback is found at registration, the menu is not built right away. The menu creation is delayed to the point where the menu is actually called to show. Dynamic menus don't stay in the DOM. After a menu created with build is hidden, its DOM-footprint is destroyed. With build, only the options [selector](#selector) and [trigger](#trigger) may be specified in the [options](#options-at-registration) object. All other options need to be returned from the build callback. the build callback may return a boolean false to signal contextMenu to not display a context menu `build`: `function($triggerElement, event)` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', build: function($triggerElement, e){ return { callback: function(){}, items: { menuItem: {name: "My on demand menu item"} } }; } }); ``` ### itemClickEvent Allows the selection of the `click` event instead of the `mouseup` event to handle the user mouse interaction with the contexMenu. The default event is `mouseup`. Set the option to `"click"` to change to the `click` event. `itemClickEvent`: `"click"` This option is global: the first contexMenu registered sets it. To change it afterwards all the contextMenu have to be unregistered with `$.contextMenu( 'destroy' );` before the change has effect again. ================================================ FILE: documentation/index.md ================================================ --- currentMenu: introduction --- # [jQuery contextMenu](https://github.com/swisnl/jQuery-contextMenu) ## Contextmenu plugin & polyfill - [Features](#features) - [Authors](#authors) - [License](#license) The contextMenu Plugin was designed for web applications in need of menus on a possibly large amount of objects. Unlike implementations as [a beautiful site's](http://abeautifulsite.net/blog/2008/09/jquery-context-menu-plugin/) or [trendskitchens'](http://www.trendskitchens.co.nz/jquery/contextmenu/) this contextMenu treats the menu as the primary object. That means, that a single menu is defined that can be used by multiple objects. Unlike the mentioned plugins, contextMenu doesn't need to bind itself to triggering objects. This allows injecting and removing triggers without having to re-initialize or update contextMenu.  contextMenu can provide a simple list of clickable commands, or offer an in-menu form. This makes very simple attribute modification possible. See the [input example](demo/input.html). Once a menu is registered, it cannot be altered. That means no commands can be added or removed from the menu. This allows contextMenu to keep a single definition in memory, which enables it to work with hundreds of trigger objects. contextMenu knows the two callbacks _show_ and _hide_ which can be used to update the state of commands within the menu. This allows en/disabling commands, changing icons or updating the values of contained `` elements. As of version 1.5 context menus can be created dynamically. That means the described behavior (once created, cannot be altered) still applies - but can be circumvented. Menus can be created on demand and they can be different depending on the triggering element. ## Getting started If you use NPM, install the `jquery-contextmenu` and include it in your build process. If you just want to load the library into your website you can either donwload the contents of the `dist` folder or use [cdnjs.com](https://cdnjs.com/libraries/jquery-contextmenu). ``` ``` 1. Download contents of the `dist` folder of this library to you project site, for example to `scripts/contextmenu` 2. `` 3. `` 4. `` 5. `` ## Features * trigger contextMenu with right-click, [left-click](demo/trigger-left-click.html), [hover](demo/trigger-hover.html) or own [custom trigger](demo/trigger-custom.html) events * delegated event handling removing the need for re-initialization when trigger objects are [added / removed](demo/dynamic.html) * dynamic [on-demand](demo/dynamic-create.html) menu creation * optional icons for commands * [input elements](demo/input.html) (text, textarea, checkbox, radio, select) within the menu * custom html elements (command free) * show/hide callbacks to update the state of commands * small memory footprint even with hundreds of trigger objects * adjust position of menu to fit in viewport * [enable / disable](demo/disabled-changing.html) commands * nested [sub-menus](demo/sub-menus.html) * full keyboard interaction * [HTML5 ``](demo/html5-import.html) support * CSS is for styling, javascript is not... ## Authors * [Björn Brala (SWIS)](http://www.swis.nl/over-ons/bjorn-brala) * [Rodney Rehm](http://rodneyrehm.de/en/) * [Christian Baartse](https://github.com/christiaan) (single callback per menu) * [Addy Osmani](https://github.com/addyosmani) (compatibility with native context menu in Firefox 8) ## License $.contextMenu is published under the [MIT license](http://www.opensource.org/licenses/mit-license). ================================================ FILE: documentation/website/.gitignore ================================================ /.couscous/ /bower_components/ ================================================ FILE: documentation/website/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013 Dave Snider Copyright (c) 2015 Matthieu Napoli This project was forked of the work by Dave Sniper at https://github.com/snide/sphinx_rtd_theme Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: documentation/website/README.md ================================================ - [](#) - [currentMenu: home ](#currentmenu-home) - [Read The Docs template for Couscous](#read-the-docs-template-for-couscous) - [Usage](#usage) - [Configuration](#configuration) - [Menu](#menu) - [```markdown ](#markdown) - [currentMenu: home ](#currentmenu-home-1) - [TODO](#todo) --- currentMenu: home --- # Read The Docs template for Couscous [Read The Docs](https://readthedocs.org/) builds websites using a Sphinx theme. [That theme](https://github.com/snide/sphinx_rtd_theme) is open source and released under the MIT license. This project is a port of the Sphinx theme to Couscous.  ## Usage To use the template, set it up in your `couscous.yml` configuration file: ```yaml template: url: https://github.com/CouscousPHP/Template-ReadTheDocs ``` ## Configuration Here are all the variables you can set in your `couscous.yml`: ```yaml # Base URL of the published website baseUrl: http://username.github.io/project # Used to link to the GitHub project github: user: myself repo: my-project title: My project subTitle: This is a great project. # The left menu bar menu: items: home: text: FAQ # You can use relative urls relativeUrl: doc/faq.html foo: text: Another link # Or absolute urls absoluteUrl: https://example.com ``` Note that the menu items can also contain HTML: ```yaml home: text: " FAQ" relativeUrl: doc/faq.html ``` ## Menu To set the current menu item (i.e. highlighted menu item), set the `currentMenu` key in the Markdown files: ```markdown --- currentMenu: home --- # Welcome ``` ## TODO Want to help? The original template came with a lot of SASS files and features. Some features are not ported to this Couscous template (e.g. the search). What would be good to do: - find a way to cleanly handle the SASS files and the `theme.css` (which contains the generated/minified CSS) which right now are duplicates. Relying on SASS wouldn't be a good idea though since it would be end-users have to install SASS. - clean up unused CSS and SASS style (e.g. search bar, …) In the meantime, the original minified `theme.css` is used and it works just great. ================================================ FILE: documentation/website/couscous.yml ================================================ template: directory: . github: user: CouscousPHP repo: Template-ReadTheDocs title: ReadTheDocs template subTitle: A template based on readthedocs.org # The left menu bar menu: items: home: text: Home page # You can use relative urls relativeUrl: getting-started: text: Getting Started relativeUrl: faq: text: FAQ relativeUrl: couscous: text: Couscous.io # Or absolute urls absoluteUrl: http://couscous.io ================================================ FILE: documentation/website/css/screen.css ================================================ html .icon { display:block; } .rst-content ul { margin-bottom:24px; } .rst-content ul li { line-height:24px; } h3 { color:#2980B9; } h4,h5,h6 { color:#195176; } .maintained-by-swis { float:right; margin-right:0; } .edit-on-github { float:right; /*margin-right:20px;*/ } body .wy-side-nav-search > a { padding-left:0; } body .wy-side-nav-search { background-color: #1C7F99; text-align: left; padding:0.4045em 1.618em; } .clear { clear:both; } .wy-table-responsive table.docutils { max-width: 695px; } .swis-branding { display: block; position: fixed; left:0; bottom:0; color:#b3b3b3; font-size:14px; width:300px; padding:10px; background:#343131; } div.swis-branding a:hover { background:none; text-decoration: underline; } div.swis-branding a { display: block; line-height: 46px; margin: 5px 0 -10px; text-align: left; padding:0; } div.swis-branding .swis-logo { width:70px; float:left; clear:left; margin-right:10px; } html body { padding-bottom:150px; } /***************** ****************** * HTML Element stijlen * * Vergeet deze niet over te nemen in editor.css (body ==> #tinymce) *****************/ @font-face { /* font-family: 'proxima_nova_rgregular';*/ font-family: 'Proxima Nova'; src: url('../fonts/ProximaNova-Reg-webfont.eot'); src: local('☺'), url('../fonts/ProximaNova-Reg-webfont.svg#proxima_nova_rgregular') format('svg'), url('../fonts/ProximaNova-Reg-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/ProximaNova-Reg-webfont.woff') format('woff'), url('../fonts/ProximaNova-Reg-webfont.ttf') format('truetype'); font-weight: normal; font-style: normal; } @font-face { /*font-family: 'proxima_nova_ltsemibold';*/ font-family: 'Proxima Nova'; src: url('../fonts/ProximaNova-Sbold-webfont.eot'); src: local('☺'), url('../fonts/ProximaNova-Sbold-webfont.svg#proxima_nova_ltsemibold') format('svg'), url('../fonts/ProximaNova-Sbold-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/ProximaNova-Sbold-webfont.woff') format('woff'), url('../fonts/ProximaNova-Sbold-webfont.ttf') format('truetype'); font-weight: bold; font-style: normal; } @font-face { font-family: 'Proxima Nova Bold'; src: url('../fonts/oud/proximanova-bold.eot.eot'); src: local('☺'), url('../fonts/oud/proximanova-bold.svg#proxima_nova_ltbold') format('svg'), url('../fonts/oud/proximanova-bold.eot?#iefix') format('embedded-opentype'), url('../fonts/oud/proximanova-bold.woff') format('woff'), url('../fonts/oud/proximanova-bold.ttf.ttf') format('truetype'); font-weight: bold; font-style: normal; } /** * @license * MyFonts Webfont Build ID 2970176, 2015-02-11T06:20:04-0500 * * The fonts listed in this notice are subject to the End User License * Agreement(s) entered into by the website owner. All other parties are * explicitly restricted from using the Licensed Webfonts(s). * * You may obtain a valid license at the URLs below. * * Webfont: ProximaNovaA-Light by Mark Simonson * URL: http://www.myfonts.com/fonts/marksimonson/proxima-nova/a-light/ * * Webfont: ProximaNovaS-Light by Mark Simonson * URL: http://www.myfonts.com/fonts/marksimonson/proxima-nova/s-light/ * * Webfont: ProximaNova-Light by Mark Simonson * URL: http://www.myfonts.com/fonts/marksimonson/proxima-nova/light/ * * * License: http://www.myfonts.com/viewlicense?type=web&buildid=2970176 * Licensed pageviews: 1,000,000 * Webfonts copyright: Copyright (c) Mark Simonson, 2005. All rights reserved. * * © 2015 MyFonts Inc */ /* @import must be at top of file, otherwise CSS will not work */ @import url("//hello.myfonts.net/count/2d5240"); @font-face { font-family: 'ProximaNova-Light'; src: url('../fonts/2D5240_2_0.eot'); src: url('../fonts/2D5240_2_0.eot?#iefix') format('embedded-opentype'), url('../fonts/2D5240_2_0.woff2') format('woff2'), url('../fonts/2D5240_2_0.woff') format('woff'), url('../fonts/2D5240_2_0.ttf') format('truetype'); font-weight: 100; font-style: normal; } ================================================ FILE: documentation/website/css/theme-fixes.css ================================================ .wy-side-nav-search>a, .wy-side-nav-search .wy-dropdown>a { margin-bottom: 0; } /* Code blocks */ pre > code { display: block; font-size: 12px; white-space: inherit; max-width: inherit; background: transparent; border: none; overflow-x: auto; padding: 0.5em; color: #333; } pre > code.hljs { background: transparent; } pre { border: 1px solid #e1e4e5; background: #fff; margin: 1px 0 24px 0; } ================================================ FILE: documentation/website/css/theme.css ================================================ * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box } article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block } audio, canvas, video { display: inline-block; *display: inline; *zoom: 1 } audio:not([controls]) { display: none } [hidden] { display: none } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box } html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100% } body { margin: 0 } a:hover, a:active { outline: 0 } abbr[title] { border-bottom: 1px dotted } b, strong { font-weight: bold } blockquote { margin: 0 } dfn { font-style: italic } ins { background: #ff9; color: #000; text-decoration: none } mark { background: #ff0; color: #000; font-style: italic; font-weight: bold } pre, code, .rst-content tt, kbd, samp { font-family: monospace, serif; _font-family: "courier new", monospace; font-size: 1em } pre { white-space: pre } q { quotes: none } q:before, q:after { content: ""; content: none } small { font-size: 85% } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline } sup { top: -0.5em } sub { bottom: -0.25em } nav ul, nav ol, nav dl { margin: 0; padding: 0; list-style: none; list-style-image: none } nav li { list-style: none } dd { margin: 0 } img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; max-width: 100% } svg:not(:root) { overflow: hidden } figure { margin: 0 } form { margin: 0 } fieldset { border: 0; margin: 0; padding: 0 } label { cursor: pointer } legend { border: 0; *margin-left: -7px; padding: 0; white-space: normal } button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle } button, input { line-height: normal } button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; *overflow: visible } button[disabled], input[disabled] { cursor: default } input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; *width: 13px; *height: 13px } input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box } input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0 } textarea { overflow: auto; vertical-align: top; resize: vertical } table { border-collapse: collapse; border-spacing: 0 } td { vertical-align: top } .chromeframe { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0 } .ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; *line-height: 0 } .ir br { display: none } .hidden { display: none !important; visibility: hidden } .visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px } .visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto } .invisible { visibility: hidden } .relative { position: relative } big, small { font-size: 100% } @media print { html, body, section { background: none !important } * { box-shadow: none !important; text-shadow: none !important; filter: none !important; -ms-filter: none !important } a, a:visited { text-decoration: underline } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: "" } pre, blockquote { page-break-inside: avoid } thead { display: table-header-group } tr, img { page-break-inside: avoid } img { max-width: 100% !important } @page { margin: 0.5cm } p, h2, h3 { orphans: 3; widows: 3 } h2, h3 { page-break-after: avoid } } .fa:before, .rst-content .admonition-title:before, .rst-content h1 .headerlink:before, .rst-content h2 .headerlink:before, .rst-content h3 .headerlink:before, .rst-content h4 .headerlink:before, .rst-content h5 .headerlink:before, .rst-content h6 .headerlink:before, .rst-content dl dt .headerlink:before, .icon:before, .wy-dropdown .caret:before, .wy-inline-validate.wy-inline-validate-success .wy-input-context:before, .wy-inline-validate.wy-inline-validate-danger .wy-input-context:before, .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, .wy-inline-validate.wy-inline-validate-info .wy-input-context:before, .wy-alert, .rst-content .note, .rst-content .attention, .rst-content .caution, .rst-content .danger, .rst-content .error, .rst-content .hint, .rst-content .important, .rst-content .tip, .rst-content .warning, .rst-content .seealso, .rst-content .admonition-todo, .btn, input[type="text"], input[type="password"], input[type="email"], input[type="url"], input[type="date"], input[type="month"], input[type="time"], input[type="datetime"], input[type="datetime-local"], input[type="week"], input[type="number"], input[type="search"], input[type="tel"], input[type="color"], select, textarea, .wy-menu-vertical li.on a, .wy-menu-vertical li.current > a, .wy-side-nav-search > a, .wy-side-nav-search .wy-dropdown > a, .wy-nav-top a { -webkit-font-smoothing: antialiased } .clearfix { *zoom: 1 } .clearfix:before, .clearfix:after { display: table; content: "" } .clearfix:after { clear: both } /*! * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ @font-face { font-family: 'FontAwesome'; src: url("../fonts/fontawesome-webfont.eot?v=4.1.0"); src: url("../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0") format("embedded-opentype"), url("../fonts/fontawesome-webfont.woff?v=4.1.0") format("woff"), url("../fonts/fontawesome-webfont.ttf?v=4.1.0") format("truetype"), url("../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular") format("svg"); font-weight: normal; font-style: normal } .fa, .rst-content .admonition-title, .rst-content h1 .headerlink, .rst-content h2 .headerlink, .rst-content h3 .headerlink, .rst-content h4 .headerlink, .rst-content h5 .headerlink, .rst-content h6 .headerlink, .rst-content dl dt .headerlink, .icon { display: inline-block; font-family: FontAwesome; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale } .fa-lg { font-size: 1.33333em; line-height: 0.75em; vertical-align: -15% } .fa-2x { font-size: 2em } .fa-3x { font-size: 3em } .fa-4x { font-size: 4em } .fa-5x { font-size: 5em } .fa-fw { width: 1.28571em; text-align: center } .fa-ul { padding-left: 0; margin-left: 2.14286em; list-style-type: none } .fa-ul > li { position: relative } .fa-li { position: absolute; left: -2.14286em; width: 2.14286em; top: 0.14286em; text-align: center } .fa-li.fa-lg { left: -1.85714em } .fa-border { padding: .2em .25em .15em; border: solid 0.08em #eee; border-radius: .1em } .pull-right { float: right } .pull-left { float: left } .fa.pull-left, .rst-content .pull-left.admonition-title, .rst-content h1 .pull-left.headerlink, .rst-content h2 .pull-left.headerlink, .rst-content h3 .pull-left.headerlink, .rst-content h4 .pull-left.headerlink, .rst-content h5 .pull-left.headerlink, .rst-content h6 .pull-left.headerlink, .rst-content dl dt .pull-left.headerlink, .pull-left.icon { margin-right: .3em } .fa.pull-right, .rst-content .pull-right.admonition-title, .rst-content h1 .pull-right.headerlink, .rst-content h2 .pull-right.headerlink, .rst-content h3 .pull-right.headerlink, .rst-content h4 .pull-right.headerlink, .rst-content h5 .pull-right.headerlink, .rst-content h6 .pull-right.headerlink, .rst-content dl dt .pull-right.headerlink, .pull-right.icon { margin-left: .3em } .fa-spin { -webkit-animation: spin 2s infinite linear; -moz-animation: spin 2s infinite linear; -o-animation: spin 2s infinite linear; animation: spin 2s infinite linear } @-moz-keyframes spin { 0% { -moz-transform: rotate(0deg) } 100% { -moz-transform: rotate(359deg) } } @-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg) } 100% { -webkit-transform: rotate(359deg) } } @-o-keyframes spin { 0% { -o-transform: rotate(0deg) } 100% { -o-transform: rotate(359deg) } } @keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg) } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg) } } .fa-rotate-90 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: rotate(90deg) } .fa-rotate-180 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg) } .fa-rotate-270 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -ms-transform: rotate(270deg); -o-transform: rotate(270deg); transform: rotate(270deg) } .fa-flip-horizontal { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0); -webkit-transform: scale(-1, 1); -moz-transform: scale(-1, 1); -ms-transform: scale(-1, 1); -o-transform: scale(-1, 1); transform: scale(-1, 1) } .fa-flip-vertical { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); -webkit-transform: scale(1, -1); -moz-transform: scale(1, -1); -ms-transform: scale(1, -1); -o-transform: scale(1, -1); transform: scale(1, -1) } .fa-stack { position: relative; display: inline-block; width: 2em; height: 2em; line-height: 2em; vertical-align: middle } .fa-stack-1x, .fa-stack-2x { position: absolute; left: 0; width: 100%; text-align: center } .fa-stack-1x { line-height: inherit } .fa-stack-2x { font-size: 2em } .fa-inverse { color: #fff } .fa-glass:before { content: "" } .fa-music:before { content: "" } .fa-search:before, .icon-search:before { content: "" } .fa-envelope-o:before { content: "" } .fa-heart:before { content: "" } .fa-star:before { content: "" } .fa-star-o:before { content: "" } .fa-user:before { content: "" } .fa-film:before { content: "" } .fa-th-large:before { content: "" } .fa-th:before { content: "" } .fa-th-list:before { content: "" } .fa-check:before { content: "" } .fa-times:before { content: "" } .fa-search-plus:before { content: "" } .fa-search-minus:before { content: "" } .fa-power-off:before { content: "" } .fa-signal:before { content: "" } .fa-gear:before, .fa-cog:before { content: "" } .fa-trash-o:before { content: "" } .fa-home:before, .icon-home:before { content: "" } .fa-file-o:before { content: "" } .fa-clock-o:before { content: "" } .fa-road:before { content: "" } .fa-download:before { content: "" } .fa-arrow-circle-o-down:before { content: "" } .fa-arrow-circle-o-up:before { content: "" } .fa-inbox:before { content: "" } .fa-play-circle-o:before { content: "" } .fa-rotate-right:before, .fa-repeat:before { content: "" } .fa-refresh:before { content: "" } .fa-list-alt:before { content: "" } .fa-lock:before { content: "" } .fa-flag:before { content: "" } .fa-headphones:before { content: "" } .fa-volume-off:before { content: "" } .fa-volume-down:before { content: "" } .fa-volume-up:before { content: "" } .fa-qrcode:before { content: "" } .fa-barcode:before { content: "" } .fa-tag:before { content: "" } .fa-tags:before { content: "" } .fa-book:before, .icon-book:before { content: "" } .fa-bookmark:before { content: "" } .fa-print:before { content: "" } .fa-camera:before { content: "" } .fa-font:before { content: "" } .fa-bold:before { content: "" } .fa-italic:before { content: "" } .fa-text-height:before { content: "" } .fa-text-width:before { content: "" } .fa-align-left:before { content: "" } .fa-align-center:before { content: "" } .fa-align-right:before { content: "" } .fa-align-justify:before { content: "" } .fa-list:before { content: "" } .fa-dedent:before, .fa-outdent:before { content: "" } .fa-indent:before { content: "" } .fa-video-camera:before { content: "" } .fa-photo:before, .fa-image:before, .fa-picture-o:before { content: "" } .fa-pencil:before { content: "" } .fa-map-marker:before { content: "" } .fa-adjust:before { content: "" } .fa-tint:before { content: "" } .fa-edit:before, .fa-pencil-square-o:before { content: "" } .fa-share-square-o:before { content: "" } .fa-check-square-o:before { content: "" } .fa-arrows:before { content: "" } .fa-step-backward:before { content: "" } .fa-fast-backward:before { content: "" } .fa-backward:before { content: "" } .fa-play:before { content: "" } .fa-pause:before { content: "" } .fa-stop:before { content: "" } .fa-forward:before { content: "" } .fa-fast-forward:before { content: "" } .fa-step-forward:before { content: "" } .fa-eject:before { content: "" } .fa-chevron-left:before { content: "" } .fa-chevron-right:before { content: "" } .fa-plus-circle:before { content: "" } .fa-minus-circle:before { content: "" } .fa-times-circle:before, .wy-inline-validate.wy-inline-validate-danger .wy-input-context:before { content: "" } .fa-check-circle:before, .wy-inline-validate.wy-inline-validate-success .wy-input-context:before { content: "" } .fa-question-circle:before { content: "" } .fa-info-circle:before { content: "" } .fa-crosshairs:before { content: "" } .fa-times-circle-o:before { content: "" } .fa-check-circle-o:before { content: "" } .fa-ban:before { content: "" } .fa-arrow-left:before { content: "" } .fa-arrow-right:before { content: "" } .fa-arrow-up:before { content: "" } .fa-arrow-down:before { content: "" } .fa-mail-forward:before, .fa-share:before { content: "" } .fa-expand:before { content: "" } .fa-compress:before { content: "" } .fa-plus:before { content: "" } .fa-minus:before { content: "" } .fa-asterisk:before { content: "" } .fa-exclamation-circle:before, .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, .wy-inline-validate.wy-inline-validate-info .wy-input-context:before, .rst-content .admonition-title:before { content: "" } .fa-gift:before { content: "" } .fa-leaf:before { content: "" } .fa-fire:before, .icon-fire:before { content: "" } .fa-eye:before { content: "" } .fa-eye-slash:before { content: "" } .fa-warning:before, .fa-exclamation-triangle:before { content: "" } .fa-plane:before { content: "" } .fa-calendar:before { content: "" } .fa-random:before { content: "" } .fa-comment:before { content: "" } .fa-magnet:before { content: "" } .fa-chevron-up:before { content: "" } .fa-chevron-down:before { content: "" } .fa-retweet:before { content: "" } .fa-shopping-cart:before { content: "" } .fa-folder:before { content: "" } .fa-folder-open:before { content: "" } .fa-arrows-v:before { content: "" } .fa-arrows-h:before { content: "" } .fa-bar-chart-o:before { content: "" } .fa-twitter-square:before { content: "" } .fa-facebook-square:before { content: "" } .fa-camera-retro:before { content: "" } .fa-key:before { content: "" } .fa-gears:before, .fa-cogs:before { content: "" } .fa-comments:before { content: "" } .fa-thumbs-o-up:before { content: "" } .fa-thumbs-o-down:before { content: "" } .fa-star-half:before { content: "" } .fa-heart-o:before { content: "" } .fa-sign-out:before { content: "" } .fa-linkedin-square:before { content: "" } .fa-thumb-tack:before { content: "" } .fa-external-link:before { content: "" } .fa-sign-in:before { content: "" } .fa-trophy:before { content: "" } .fa-github-square:before { content: "" } .fa-upload:before { content: "" } .fa-lemon-o:before { content: "" } .fa-phone:before { content: "" } .fa-square-o:before { content: "" } .fa-bookmark-o:before { content: "" } .fa-phone-square:before { content: "" } .fa-twitter:before { content: "" } .fa-facebook:before { content: "" } .fa-github:before, .icon-github:before { content: "" } .fa-unlock:before { content: "" } .fa-credit-card:before { content: "" } .fa-rss:before { content: "" } .fa-hdd-o:before { content: "" } .fa-bullhorn:before { content: "" } .fa-bell:before { content: "" } .fa-certificate:before { content: "" } .fa-hand-o-right:before { content: "" } .fa-hand-o-left:before { content: "" } .fa-hand-o-up:before { content: "" } .fa-hand-o-down:before { content: "" } .fa-arrow-circle-left:before, .icon-circle-arrow-left:before { content: "" } .fa-arrow-circle-right:before, .icon-circle-arrow-right:before { content: "" } .fa-arrow-circle-up:before { content: "" } .fa-arrow-circle-down:before { content: "" } .fa-globe:before { content: "" } .fa-wrench:before { content: "" } .fa-tasks:before { content: "" } .fa-filter:before { content: "" } .fa-briefcase:before { content: "" } .fa-arrows-alt:before { content: "" } .fa-group:before, .fa-users:before { content: "" } .fa-chain:before, .fa-link:before, .icon-link:before { content: "" } .fa-cloud:before { content: "" } .fa-flask:before { content: "" } .fa-cut:before, .fa-scissors:before { content: "" } .fa-copy:before, .fa-files-o:before { content: "" } .fa-paperclip:before { content: "" } .fa-save:before, .fa-floppy-o:before { content: "" } .fa-square:before { content: "" } .fa-navicon:before, .fa-reorder:before, .fa-bars:before { content: "" } .fa-list-ul:before { content: "" } .fa-list-ol:before { content: "" } .fa-strikethrough:before { content: "" } .fa-underline:before { content: "" } .fa-table:before { content: "" } .fa-magic:before { content: "" } .fa-truck:before { content: "" } .fa-pinterest:before { content: "" } .fa-pinterest-square:before { content: "" } .fa-google-plus-square:before { content: "" } .fa-google-plus:before { content: "" } .fa-money:before { content: "" } .fa-caret-down:before, .wy-dropdown .caret:before, .icon-caret-down:before { content: "" } .fa-caret-up:before { content: "" } .fa-caret-left:before { content: "" } .fa-caret-right:before { content: "" } .fa-columns:before { content: "" } .fa-unsorted:before, .fa-sort:before { content: "" } .fa-sort-down:before, .fa-sort-desc:before { content: "" } .fa-sort-up:before, .fa-sort-asc:before { content: "" } .fa-envelope:before { content: "" } .fa-linkedin:before { content: "" } .fa-rotate-left:before, .fa-undo:before { content: "" } .fa-legal:before, .fa-gavel:before { content: "" } .fa-dashboard:before, .fa-tachometer:before { content: "" } .fa-comment-o:before { content: "" } .fa-comments-o:before { content: "" } .fa-flash:before, .fa-bolt:before { content: "" } .fa-sitemap:before { content: "" } .fa-umbrella:before { content: "" } .fa-paste:before, .fa-clipboard:before { content: "" } .fa-lightbulb-o:before { content: "" } .fa-exchange:before { content: "" } .fa-cloud-download:before { content: "" } .fa-cloud-upload:before { content: "" } .fa-user-md:before { content: "" } .fa-stethoscope:before { content: "" } .fa-suitcase:before { content: "" } .fa-bell-o:before { content: "" } .fa-coffee:before { content: "" } .fa-cutlery:before { content: "" } .fa-file-text-o:before { content: "" } .fa-building-o:before { content: "" } .fa-hospital-o:before { content: "" } .fa-ambulance:before { content: "" } .fa-medkit:before { content: "" } .fa-fighter-jet:before { content: "" } .fa-beer:before { content: "" } .fa-h-square:before { content: "" } .fa-plus-square:before { content: "" } .fa-angle-double-left:before { content: "" } .fa-angle-double-right:before { content: "" } .fa-angle-double-up:before { content: "" } .fa-angle-double-down:before { content: "" } .fa-angle-left:before { content: "" } .fa-angle-right:before { content: "" } .fa-angle-up:before { content: "" } .fa-angle-down:before { content: "" } .fa-desktop:before { content: "" } .fa-laptop:before { content: "" } .fa-tablet:before { content: "" } .fa-mobile-phone:before, .fa-mobile:before { content: "" } .fa-circle-o:before { content: "" } .fa-quote-left:before { content: "" } .fa-quote-right:before { content: "" } .fa-spinner:before { content: "" } .fa-circle:before { content: "" } .fa-mail-reply:before, .fa-reply:before { content: "" } .fa-github-alt:before { content: "" } .fa-folder-o:before { content: "" } .fa-folder-open-o:before { content: "" } .fa-smile-o:before { content: "" } .fa-frown-o:before { content: "" } .fa-meh-o:before { content: "" } .fa-gamepad:before { content: "" } .fa-keyboard-o:before { content: "" } .fa-flag-o:before { content: "" } .fa-flag-checkered:before { content: "" } .fa-terminal:before { content: "" } .fa-code:before { content: "" } .fa-mail-reply-all:before, .fa-reply-all:before { content: "" } .fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { content: "" } .fa-location-arrow:before { content: "" } .fa-crop:before { content: "" } .fa-code-fork:before { content: "" } .fa-unlink:before, .fa-chain-broken:before { content: "" } .fa-question:before { content: "" } .fa-info:before { content: "" } .fa-exclamation:before { content: "" } .fa-superscript:before { content: "" } .fa-subscript:before { content: "" } .fa-eraser:before { content: "" } .fa-puzzle-piece:before { content: "" } .fa-microphone:before { content: "" } .fa-microphone-slash:before { content: "" } .fa-shield:before { content: "" } .fa-calendar-o:before { content: "" } .fa-fire-extinguisher:before { content: "" } .fa-rocket:before { content: "" } .fa-maxcdn:before { content: "" } .fa-chevron-circle-left:before { content: "" } .fa-chevron-circle-right:before { content: "" } .fa-chevron-circle-up:before { content: "" } .fa-chevron-circle-down:before { content: "" } .fa-html5:before { content: "" } .fa-css3:before { content: "" } .fa-anchor:before { content: "" } .fa-unlock-alt:before { content: "" } .fa-bullseye:before { content: "" } .fa-ellipsis-h:before { content: "" } .fa-ellipsis-v:before { content: "" } .fa-rss-square:before { content: "" } .fa-play-circle:before { content: "" } .fa-ticket:before { content: "" } .fa-minus-square:before { content: "" } .fa-minus-square-o:before { content: "" } .fa-level-up:before { content: "" } .fa-level-down:before { content: "" } .fa-check-square:before { content: "" } .fa-pencil-square:before { content: "" } .fa-external-link-square:before { content: "" } .fa-share-square:before { content: "" } .fa-compass:before { content: "" } .fa-toggle-down:before, .fa-caret-square-o-down:before { content: "" } .fa-toggle-up:before, .fa-caret-square-o-up:before { content: "" } .fa-toggle-right:before, .fa-caret-square-o-right:before { content: "" } .fa-euro:before, .fa-eur:before { content: "" } .fa-gbp:before { content: "" } .fa-dollar:before, .fa-usd:before { content: "" } .fa-rupee:before, .fa-inr:before { content: "" } .fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { content: "" } .fa-ruble:before, .fa-rouble:before, .fa-rub:before { content: "" } .fa-won:before, .fa-krw:before { content: "" } .fa-bitcoin:before, .fa-btc:before { content: "" } .fa-file:before { content: "" } .fa-file-text:before { content: "" } .fa-sort-alpha-asc:before { content: "" } .fa-sort-alpha-desc:before { content: "" } .fa-sort-amount-asc:before { content: "" } .fa-sort-amount-desc:before { content: "" } .fa-sort-numeric-asc:before { content: "" } .fa-sort-numeric-desc:before { content: "" } .fa-thumbs-up:before { content: "" } .fa-thumbs-down:before { content: "" } .fa-youtube-square:before { content: "" } .fa-youtube:before { content: "" } .fa-xing:before { content: "" } .fa-xing-square:before { content: "" } .fa-youtube-play:before { content: "" } .fa-dropbox:before { content: "" } .fa-stack-overflow:before { content: "" } .fa-instagram:before { content: "" } .fa-flickr:before { content: "" } .fa-adn:before { content: "" } .fa-bitbucket:before, .icon-bitbucket:before { content: "" } .fa-bitbucket-square:before { content: "" } .fa-tumblr:before { content: "" } .fa-tumblr-square:before { content: "" } .fa-long-arrow-down:before { content: "" } .fa-long-arrow-up:before { content: "" } .fa-long-arrow-left:before { content: "" } .fa-long-arrow-right:before { content: "" } .fa-apple:before { content: "" } .fa-windows:before { content: "" } .fa-android:before { content: "" } .fa-linux:before { content: "" } .fa-dribbble:before { content: "" } .fa-skype:before { content: "" } .fa-foursquare:before { content: "" } .fa-trello:before { content: "" } .fa-female:before { content: "" } .fa-male:before { content: "" } .fa-gittip:before { content: "" } .fa-sun-o:before { content: "" } .fa-moon-o:before { content: "" } .fa-archive:before { content: "" } .fa-bug:before { content: "" } .fa-vk:before { content: "" } .fa-weibo:before { content: "" } .fa-renren:before { content: "" } .fa-pagelines:before { content: "" } .fa-stack-exchange:before { content: "" } .fa-arrow-circle-o-right:before { content: "" } .fa-arrow-circle-o-left:before { content: "" } .fa-toggle-left:before, .fa-caret-square-o-left:before { content: "" } .fa-dot-circle-o:before { content: "" } .fa-wheelchair:before { content: "" } .fa-vimeo-square:before { content: "" } .fa-turkish-lira:before, .fa-try:before { content: "" } .fa-plus-square-o:before { content: "" } .fa-space-shuttle:before { content: "" } .fa-slack:before { content: "" } .fa-envelope-square:before { content: "" } .fa-wordpress:before { content: "" } .fa-openid:before { content: "" } .fa-institution:before, .fa-bank:before, .fa-university:before { content: "" } .fa-mortar-board:before, .fa-graduation-cap:before { content: "" } .fa-yahoo:before { content: "" } .fa-google:before { content: "" } .fa-reddit:before { content: "" } .fa-reddit-square:before { content: "" } .fa-stumbleupon-circle:before { content: "" } .fa-stumbleupon:before { content: "" } .fa-delicious:before { content: "" } .fa-digg:before { content: "" } .fa-pied-piper-square:before, .fa-pied-piper:before { content: "" } .fa-pied-piper-alt:before { content: "" } .fa-drupal:before { content: "" } .fa-joomla:before { content: "" } .fa-language:before { content: "" } .fa-fax:before { content: "" } .fa-building:before { content: "" } .fa-child:before { content: "" } .fa-paw:before { content: "" } .fa-spoon:before { content: "" } .fa-cube:before { content: "" } .fa-cubes:before { content: "" } .fa-behance:before { content: "" } .fa-behance-square:before { content: "" } .fa-steam:before { content: "" } .fa-steam-square:before { content: "" } .fa-recycle:before { content: "" } .fa-automobile:before, .fa-car:before { content: "" } .fa-cab:before, .fa-taxi:before { content: "" } .fa-tree:before { content: "" } .fa-spotify:before { content: "" } .fa-deviantart:before { content: "" } .fa-soundcloud:before { content: "" } .fa-database:before { content: "" } .fa-file-pdf-o:before { content: "" } .fa-file-word-o:before { content: "" } .fa-file-excel-o:before { content: "" } .fa-file-powerpoint-o:before { content: "" } .fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before { content: "" } .fa-file-zip-o:before, .fa-file-archive-o:before { content: "" } .fa-file-sound-o:before, .fa-file-audio-o:before { content: "" } .fa-file-movie-o:before, .fa-file-video-o:before { content: "" } .fa-file-code-o:before { content: "" } .fa-vine:before { content: "" } .fa-codepen:before { content: "" } .fa-jsfiddle:before { content: "" } .fa-life-bouy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before { content: "" } .fa-circle-o-notch:before { content: "" } .fa-ra:before, .fa-rebel:before { content: "" } .fa-ge:before, .fa-empire:before { content: "" } .fa-git-square:before { content: "" } .fa-git:before { content: "" } .fa-hacker-news:before { content: "" } .fa-tencent-weibo:before { content: "" } .fa-qq:before { content: "" } .fa-wechat:before, .fa-weixin:before { content: "" } .fa-send:before, .fa-paper-plane:before { content: "" } .fa-send-o:before, .fa-paper-plane-o:before { content: "" } .fa-history:before { content: "" } .fa-circle-thin:before { content: "" } .fa-header:before { content: "" } .fa-paragraph:before { content: "" } .fa-sliders:before { content: "" } .fa-share-alt:before { content: "" } .fa-share-alt-square:before { content: "" } .fa-bomb:before { content: "" } .fa, .rst-content .admonition-title, .rst-content h1 .headerlink, .rst-content h2 .headerlink, .rst-content h3 .headerlink, .rst-content h4 .headerlink, .rst-content h5 .headerlink, .rst-content h6 .headerlink, .rst-content dl dt .headerlink, .icon, .wy-dropdown .caret, .wy-inline-validate.wy-inline-validate-success .wy-input-context, .wy-inline-validate.wy-inline-validate-danger .wy-input-context, .wy-inline-validate.wy-inline-validate-warning .wy-input-context, .wy-inline-validate.wy-inline-validate-info .wy-input-context { font-family: inherit } .fa:before, .rst-content .admonition-title:before, .rst-content h1 .headerlink:before, .rst-content h2 .headerlink:before, .rst-content h3 .headerlink:before, .rst-content h4 .headerlink:before, .rst-content h5 .headerlink:before, .rst-content h6 .headerlink:before, .rst-content dl dt .headerlink:before, .icon:before, .wy-dropdown .caret:before, .wy-inline-validate.wy-inline-validate-success .wy-input-context:before, .wy-inline-validate.wy-inline-validate-danger .wy-input-context:before, .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, .wy-inline-validate.wy-inline-validate-info .wy-input-context:before { font-family: "FontAwesome"; display: inline-block; font-style: normal; font-weight: normal; line-height: 1; text-decoration: inherit } a .fa, a .rst-content .admonition-title, .rst-content a .admonition-title, a .rst-content h1 .headerlink, .rst-content h1 a .headerlink, a .rst-content h2 .headerlink, .rst-content h2 a .headerlink, a .rst-content h3 .headerlink, .rst-content h3 a .headerlink, a .rst-content h4 .headerlink, .rst-content h4 a .headerlink, a .rst-content h5 .headerlink, .rst-content h5 a .headerlink, a .rst-content h6 .headerlink, .rst-content h6 a .headerlink, a .rst-content dl dt .headerlink, .rst-content dl dt a .headerlink, a .icon { display: inline-block; text-decoration: inherit } .btn .fa, .btn .rst-content .admonition-title, .rst-content .btn .admonition-title, .btn .rst-content h1 .headerlink, .rst-content h1 .btn .headerlink, .btn .rst-content h2 .headerlink, .rst-content h2 .btn .headerlink, .btn .rst-content h3 .headerlink, .rst-content h3 .btn .headerlink, .btn .rst-content h4 .headerlink, .rst-content h4 .btn .headerlink, .btn .rst-content h5 .headerlink, .rst-content h5 .btn .headerlink, .btn .rst-content h6 .headerlink, .rst-content h6 .btn .headerlink, .btn .rst-content dl dt .headerlink, .rst-content dl dt .btn .headerlink, .btn .icon, .nav .fa, .nav .rst-content .admonition-title, .rst-content .nav .admonition-title, .nav .rst-content h1 .headerlink, .rst-content h1 .nav .headerlink, .nav .rst-content h2 .headerlink, .rst-content h2 .nav .headerlink, .nav .rst-content h3 .headerlink, .rst-content h3 .nav .headerlink, .nav .rst-content h4 .headerlink, .rst-content h4 .nav .headerlink, .nav .rst-content h5 .headerlink, .rst-content h5 .nav .headerlink, .nav .rst-content h6 .headerlink, .rst-content h6 .nav .headerlink, .nav .rst-content dl dt .headerlink, .rst-content dl dt .nav .headerlink, .nav .icon { display: inline } .btn .fa.fa-large, .btn .rst-content .fa-large.admonition-title, .rst-content .btn .fa-large.admonition-title, .btn .rst-content h1 .fa-large.headerlink, .rst-content h1 .btn .fa-large.headerlink, .btn .rst-content h2 .fa-large.headerlink, .rst-content h2 .btn .fa-large.headerlink, .btn .rst-content h3 .fa-large.headerlink, .rst-content h3 .btn .fa-large.headerlink, .btn .rst-content h4 .fa-large.headerlink, .rst-content h4 .btn .fa-large.headerlink, .btn .rst-content h5 .fa-large.headerlink, .rst-content h5 .btn .fa-large.headerlink, .btn .rst-content h6 .fa-large.headerlink, .rst-content h6 .btn .fa-large.headerlink, .btn .rst-content dl dt .fa-large.headerlink, .rst-content dl dt .btn .fa-large.headerlink, .btn .fa-large.icon, .nav .fa.fa-large, .nav .rst-content .fa-large.admonition-title, .rst-content .nav .fa-large.admonition-title, .nav .rst-content h1 .fa-large.headerlink, .rst-content h1 .nav .fa-large.headerlink, .nav .rst-content h2 .fa-large.headerlink, .rst-content h2 .nav .fa-large.headerlink, .nav .rst-content h3 .fa-large.headerlink, .rst-content h3 .nav .fa-large.headerlink, .nav .rst-content h4 .fa-large.headerlink, .rst-content h4 .nav .fa-large.headerlink, .nav .rst-content h5 .fa-large.headerlink, .rst-content h5 .nav .fa-large.headerlink, .nav .rst-content h6 .fa-large.headerlink, .rst-content h6 .nav .fa-large.headerlink, .nav .rst-content dl dt .fa-large.headerlink, .rst-content dl dt .nav .fa-large.headerlink, .nav .fa-large.icon { line-height: 0.9em } .btn .fa.fa-spin, .btn .rst-content .fa-spin.admonition-title, .rst-content .btn .fa-spin.admonition-title, .btn .rst-content h1 .fa-spin.headerlink, .rst-content h1 .btn .fa-spin.headerlink, .btn .rst-content h2 .fa-spin.headerlink, .rst-content h2 .btn .fa-spin.headerlink, .btn .rst-content h3 .fa-spin.headerlink, .rst-content h3 .btn .fa-spin.headerlink, .btn .rst-content h4 .fa-spin.headerlink, .rst-content h4 .btn .fa-spin.headerlink, .btn .rst-content h5 .fa-spin.headerlink, .rst-content h5 .btn .fa-spin.headerlink, .btn .rst-content h6 .fa-spin.headerlink, .rst-content h6 .btn .fa-spin.headerlink, .btn .rst-content dl dt .fa-spin.headerlink, .rst-content dl dt .btn .fa-spin.headerlink, .btn .fa-spin.icon, .nav .fa.fa-spin, .nav .rst-content .fa-spin.admonition-title, .rst-content .nav .fa-spin.admonition-title, .nav .rst-content h1 .fa-spin.headerlink, .rst-content h1 .nav .fa-spin.headerlink, .nav .rst-content h2 .fa-spin.headerlink, .rst-content h2 .nav .fa-spin.headerlink, .nav .rst-content h3 .fa-spin.headerlink, .rst-content h3 .nav .fa-spin.headerlink, .nav .rst-content h4 .fa-spin.headerlink, .rst-content h4 .nav .fa-spin.headerlink, .nav .rst-content h5 .fa-spin.headerlink, .rst-content h5 .nav .fa-spin.headerlink, .nav .rst-content h6 .fa-spin.headerlink, .rst-content h6 .nav .fa-spin.headerlink, .nav .rst-content dl dt .fa-spin.headerlink, .rst-content dl dt .nav .fa-spin.headerlink, .nav .fa-spin.icon { display: inline-block } .btn.fa:before, .rst-content .btn.admonition-title:before, .rst-content h1 .btn.headerlink:before, .rst-content h2 .btn.headerlink:before, .rst-content h3 .btn.headerlink:before, .rst-content h4 .btn.headerlink:before, .rst-content h5 .btn.headerlink:before, .rst-content h6 .btn.headerlink:before, .rst-content dl dt .btn.headerlink:before, .btn.icon:before { opacity: 0.5; -webkit-transition: opacity 0.05s ease-in; -moz-transition: opacity 0.05s ease-in; transition: opacity 0.05s ease-in } .btn.fa:hover:before, .rst-content .btn.admonition-title:hover:before, .rst-content h1 .btn.headerlink:hover:before, .rst-content h2 .btn.headerlink:hover:before, .rst-content h3 .btn.headerlink:hover:before, .rst-content h4 .btn.headerlink:hover:before, .rst-content h5 .btn.headerlink:hover:before, .rst-content h6 .btn.headerlink:hover:before, .rst-content dl dt .btn.headerlink:hover:before, .btn.icon:hover:before { opacity: 1 } .btn-mini .fa:before, .btn-mini .rst-content .admonition-title:before, .rst-content .btn-mini .admonition-title:before, .btn-mini .rst-content h1 .headerlink:before, .rst-content h1 .btn-mini .headerlink:before, .btn-mini .rst-content h2 .headerlink:before, .rst-content h2 .btn-mini .headerlink:before, .btn-mini .rst-content h3 .headerlink:before, .rst-content h3 .btn-mini .headerlink:before, .btn-mini .rst-content h4 .headerlink:before, .rst-content h4 .btn-mini .headerlink:before, .btn-mini .rst-content h5 .headerlink:before, .rst-content h5 .btn-mini .headerlink:before, .btn-mini .rst-content h6 .headerlink:before, .rst-content h6 .btn-mini .headerlink:before, .btn-mini .rst-content dl dt .headerlink:before, .rst-content dl dt .btn-mini .headerlink:before, .btn-mini .icon:before { font-size: 14px; vertical-align: -15% } .wy-alert, .rst-content .note, .rst-content .attention, .rst-content .caution, .rst-content .danger, .rst-content .error, .rst-content .hint, .rst-content .important, .rst-content .tip, .rst-content .warning, .rst-content .seealso, .rst-content .admonition-todo { padding: 12px; line-height: 24px; margin-bottom: 24px; background: #e7f2fa } .wy-alert-title, .rst-content .admonition-title { color: #fff; font-weight: bold; display: block; color: #fff; background: #6ab0de; margin: -12px; padding: 6px 12px; margin-bottom: 12px } .wy-alert.wy-alert-danger, .rst-content .wy-alert-danger.note, .rst-content .wy-alert-danger.attention, .rst-content .wy-alert-danger.caution, .rst-content .danger, .rst-content .error, .rst-content .wy-alert-danger.hint, .rst-content .wy-alert-danger.important, .rst-content .wy-alert-danger.tip, .rst-content .wy-alert-danger.warning, .rst-content .wy-alert-danger.seealso, .rst-content .wy-alert-danger.admonition-todo { background: #fdf3f2 } .wy-alert.wy-alert-danger .wy-alert-title, .rst-content .wy-alert-danger.note .wy-alert-title, .rst-content .wy-alert-danger.attention .wy-alert-title, .rst-content .wy-alert-danger.caution .wy-alert-title, .rst-content .danger .wy-alert-title, .rst-content .error .wy-alert-title, .rst-content .wy-alert-danger.hint .wy-alert-title, .rst-content .wy-alert-danger.important .wy-alert-title, .rst-content .wy-alert-danger.tip .wy-alert-title, .rst-content .wy-alert-danger.warning .wy-alert-title, .rst-content .wy-alert-danger.seealso .wy-alert-title, .rst-content .wy-alert-danger.admonition-todo .wy-alert-title, .wy-alert.wy-alert-danger .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-danger .admonition-title, .rst-content .wy-alert-danger.note .admonition-title, .rst-content .wy-alert-danger.attention .admonition-title, .rst-content .wy-alert-danger.caution .admonition-title, .rst-content .danger .admonition-title, .rst-content .error .admonition-title, .rst-content .wy-alert-danger.hint .admonition-title, .rst-content .wy-alert-danger.important .admonition-title, .rst-content .wy-alert-danger.tip .admonition-title, .rst-content .wy-alert-danger.warning .admonition-title, .rst-content .wy-alert-danger.seealso .admonition-title, .rst-content .wy-alert-danger.admonition-todo .admonition-title { background: #f29f97 } .wy-alert.wy-alert-warning, .rst-content .wy-alert-warning.note, .rst-content .attention, .rst-content .caution, .rst-content .wy-alert-warning.danger, .rst-content .wy-alert-warning.error, .rst-content .wy-alert-warning.hint, .rst-content .wy-alert-warning.important, .rst-content .wy-alert-warning.tip, .rst-content .warning, .rst-content .wy-alert-warning.seealso, .rst-content .admonition-todo { background: #ffedcc } .wy-alert.wy-alert-warning .wy-alert-title, .rst-content .wy-alert-warning.note .wy-alert-title, .rst-content .attention .wy-alert-title, .rst-content .caution .wy-alert-title, .rst-content .wy-alert-warning.danger .wy-alert-title, .rst-content .wy-alert-warning.error .wy-alert-title, .rst-content .wy-alert-warning.hint .wy-alert-title, .rst-content .wy-alert-warning.important .wy-alert-title, .rst-content .wy-alert-warning.tip .wy-alert-title, .rst-content .warning .wy-alert-title, .rst-content .wy-alert-warning.seealso .wy-alert-title, .rst-content .admonition-todo .wy-alert-title, .wy-alert.wy-alert-warning .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-warning .admonition-title, .rst-content .wy-alert-warning.note .admonition-title, .rst-content .attention .admonition-title, .rst-content .caution .admonition-title, .rst-content .wy-alert-warning.danger .admonition-title, .rst-content .wy-alert-warning.error .admonition-title, .rst-content .wy-alert-warning.hint .admonition-title, .rst-content .wy-alert-warning.important .admonition-title, .rst-content .wy-alert-warning.tip .admonition-title, .rst-content .warning .admonition-title, .rst-content .wy-alert-warning.seealso .admonition-title, .rst-content .admonition-todo .admonition-title { background: #f0b37e } .wy-alert.wy-alert-info, .rst-content .note, .rst-content .wy-alert-info.attention, .rst-content .wy-alert-info.caution, .rst-content .wy-alert-info.danger, .rst-content .wy-alert-info.error, .rst-content .wy-alert-info.hint, .rst-content .wy-alert-info.important, .rst-content .wy-alert-info.tip, .rst-content .wy-alert-info.warning, .rst-content .seealso, .rst-content .wy-alert-info.admonition-todo { background: #e7f2fa } .wy-alert.wy-alert-info .wy-alert-title, .rst-content .note .wy-alert-title, .rst-content .wy-alert-info.attention .wy-alert-title, .rst-content .wy-alert-info.caution .wy-alert-title, .rst-content .wy-alert-info.danger .wy-alert-title, .rst-content .wy-alert-info.error .wy-alert-title, .rst-content .wy-alert-info.hint .wy-alert-title, .rst-content .wy-alert-info.important .wy-alert-title, .rst-content .wy-alert-info.tip .wy-alert-title, .rst-content .wy-alert-info.warning .wy-alert-title, .rst-content .seealso .wy-alert-title, .rst-content .wy-alert-info.admonition-todo .wy-alert-title, .wy-alert.wy-alert-info .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-info .admonition-title, .rst-content .note .admonition-title, .rst-content .wy-alert-info.attention .admonition-title, .rst-content .wy-alert-info.caution .admonition-title, .rst-content .wy-alert-info.danger .admonition-title, .rst-content .wy-alert-info.error .admonition-title, .rst-content .wy-alert-info.hint .admonition-title, .rst-content .wy-alert-info.important .admonition-title, .rst-content .wy-alert-info.tip .admonition-title, .rst-content .wy-alert-info.warning .admonition-title, .rst-content .seealso .admonition-title, .rst-content .wy-alert-info.admonition-todo .admonition-title { background: #6ab0de } .wy-alert.wy-alert-success, .rst-content .wy-alert-success.note, .rst-content .wy-alert-success.attention, .rst-content .wy-alert-success.caution, .rst-content .wy-alert-success.danger, .rst-content .wy-alert-success.error, .rst-content .hint, .rst-content .important, .rst-content .tip, .rst-content .wy-alert-success.warning, .rst-content .wy-alert-success.seealso, .rst-content .wy-alert-success.admonition-todo { background: #dbfaf4 } .wy-alert.wy-alert-success .wy-alert-title, .rst-content .wy-alert-success.note .wy-alert-title, .rst-content .wy-alert-success.attention .wy-alert-title, .rst-content .wy-alert-success.caution .wy-alert-title, .rst-content .wy-alert-success.danger .wy-alert-title, .rst-content .wy-alert-success.error .wy-alert-title, .rst-content .hint .wy-alert-title, .rst-content .important .wy-alert-title, .rst-content .tip .wy-alert-title, .rst-content .wy-alert-success.warning .wy-alert-title, .rst-content .wy-alert-success.seealso .wy-alert-title, .rst-content .wy-alert-success.admonition-todo .wy-alert-title, .wy-alert.wy-alert-success .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-success .admonition-title, .rst-content .wy-alert-success.note .admonition-title, .rst-content .wy-alert-success.attention .admonition-title, .rst-content .wy-alert-success.caution .admonition-title, .rst-content .wy-alert-success.danger .admonition-title, .rst-content .wy-alert-success.error .admonition-title, .rst-content .hint .admonition-title, .rst-content .important .admonition-title, .rst-content .tip .admonition-title, .rst-content .wy-alert-success.warning .admonition-title, .rst-content .wy-alert-success.seealso .admonition-title, .rst-content .wy-alert-success.admonition-todo .admonition-title { background: #1abc9c } .wy-alert.wy-alert-neutral, .rst-content .wy-alert-neutral.note, .rst-content .wy-alert-neutral.attention, .rst-content .wy-alert-neutral.caution, .rst-content .wy-alert-neutral.danger, .rst-content .wy-alert-neutral.error, .rst-content .wy-alert-neutral.hint, .rst-content .wy-alert-neutral.important, .rst-content .wy-alert-neutral.tip, .rst-content .wy-alert-neutral.warning, .rst-content .wy-alert-neutral.seealso, .rst-content .wy-alert-neutral.admonition-todo { background: #f3f6f6 } .wy-alert.wy-alert-neutral .wy-alert-title, .rst-content .wy-alert-neutral.note .wy-alert-title, .rst-content .wy-alert-neutral.attention .wy-alert-title, .rst-content .wy-alert-neutral.caution .wy-alert-title, .rst-content .wy-alert-neutral.danger .wy-alert-title, .rst-content .wy-alert-neutral.error .wy-alert-title, .rst-content .wy-alert-neutral.hint .wy-alert-title, .rst-content .wy-alert-neutral.important .wy-alert-title, .rst-content .wy-alert-neutral.tip .wy-alert-title, .rst-content .wy-alert-neutral.warning .wy-alert-title, .rst-content .wy-alert-neutral.seealso .wy-alert-title, .rst-content .wy-alert-neutral.admonition-todo .wy-alert-title, .wy-alert.wy-alert-neutral .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-neutral .admonition-title, .rst-content .wy-alert-neutral.note .admonition-title, .rst-content .wy-alert-neutral.attention .admonition-title, .rst-content .wy-alert-neutral.caution .admonition-title, .rst-content .wy-alert-neutral.danger .admonition-title, .rst-content .wy-alert-neutral.error .admonition-title, .rst-content .wy-alert-neutral.hint .admonition-title, .rst-content .wy-alert-neutral.important .admonition-title, .rst-content .wy-alert-neutral.tip .admonition-title, .rst-content .wy-alert-neutral.warning .admonition-title, .rst-content .wy-alert-neutral.seealso .admonition-title, .rst-content .wy-alert-neutral.admonition-todo .admonition-title { color: #404040; background: #e1e4e5 } .wy-alert.wy-alert-neutral a, .rst-content .wy-alert-neutral.note a, .rst-content .wy-alert-neutral.attention a, .rst-content .wy-alert-neutral.caution a, .rst-content .wy-alert-neutral.danger a, .rst-content .wy-alert-neutral.error a, .rst-content .wy-alert-neutral.hint a, .rst-content .wy-alert-neutral.important a, .rst-content .wy-alert-neutral.tip a, .rst-content .wy-alert-neutral.warning a, .rst-content .wy-alert-neutral.seealso a, .rst-content .wy-alert-neutral.admonition-todo a { color: #2980B9 } .wy-alert p:last-child, .rst-content .note p:last-child, .rst-content .attention p:last-child, .rst-content .caution p:last-child, .rst-content .danger p:last-child, .rst-content .error p:last-child, .rst-content .hint p:last-child, .rst-content .important p:last-child, .rst-content .tip p:last-child, .rst-content .warning p:last-child, .rst-content .seealso p:last-child, .rst-content .admonition-todo p:last-child { margin-bottom: 0 } .wy-tray-container { position: fixed; bottom: 0px; left: 0; z-index: 600 } .wy-tray-container li { display: block; width: 300px; background: transparent; color: #fff; text-align: center; box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.1); padding: 0 24px; min-width: 20%; opacity: 0; height: 0; line-height: 56px; overflow: hidden; -webkit-transition: all 0.3s ease-in; -moz-transition: all 0.3s ease-in; transition: all 0.3s ease-in } .wy-tray-container li.wy-tray-item-success { background: #27AE60 } .wy-tray-container li.wy-tray-item-info { background: #2980B9 } .wy-tray-container li.wy-tray-item-warning { background: #E67E22 } .wy-tray-container li.wy-tray-item-danger { background: #E74C3C } .wy-tray-container li.on { opacity: 1; height: 56px } @media screen and (max-width: 768px) { .wy-tray-container { bottom: auto; top: 0; width: 100% } .wy-tray-container li { width: 100% } } button { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; cursor: pointer; line-height: normal; -webkit-appearance: button; *overflow: visible } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0 } button[disabled] { cursor: default } .btn { display: inline-block; border-radius: 2px; line-height: normal; white-space: nowrap; text-align: center; cursor: pointer; font-size: 100%; padding: 6px 12px 8px 12px; color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); background-color: #27AE60; text-decoration: none; font-weight: normal; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; box-shadow: 0px 1px 2px -1px rgba(255, 255, 255, 0.5) inset, 0px -2px 0px 0px rgba(0, 0, 0, 0.1) inset; outline-none: false; vertical-align: middle; *display: inline; zoom: 1; -webkit-user-drag: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-transition: all 0.1s linear; -moz-transition: all 0.1s linear; transition: all 0.1s linear } .btn-hover { background: #2e8ece; color: #fff } .btn:hover { background: #2cc36b; color: #fff } .btn:focus { background: #2cc36b; outline: 0 } .btn:active { box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.05) inset, 0px 2px 0px 0px rgba(0, 0, 0, 0.1) inset; padding: 8px 12px 6px 12px } .btn:visited { color: #fff } .btn:disabled { background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); filter: alpha(opacity=40); opacity: 0.4; cursor: not-allowed; box-shadow: none } .btn-disabled { background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); filter: alpha(opacity=40); opacity: 0.4; cursor: not-allowed; box-shadow: none } .btn-disabled:hover, .btn-disabled:focus, .btn-disabled:active { background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); filter: alpha(opacity=40); opacity: 0.4; cursor: not-allowed; box-shadow: none } .btn::-moz-focus-inner { padding: 0; border: 0 } .btn-small { font-size: 80% } .btn-info { background-color: #2980B9 !important } .btn-info:hover { background-color: #2e8ece !important } .btn-neutral { background-color: #f3f6f6 !important; color: #404040 !important } .btn-neutral:hover { background-color: #e5ebeb !important; color: #404040 } .btn-neutral:visited { color: #404040 !important } .btn-success { background-color: #27AE60 !important } .btn-success:hover { background-color: #295 !important } .btn-danger { background-color: #E74C3C !important } .btn-danger:hover { background-color: #ea6153 !important } .btn-warning { background-color: #E67E22 !important } .btn-warning:hover { background-color: #e98b39 !important } .btn-invert { background-color: #222 } .btn-invert:hover { background-color: #2f2f2f !important } .btn-link { background-color: transparent !important; color: #2980B9; box-shadow: none; border-color: transparent !important } .btn-link:hover { background-color: transparent !important; color: #409ad5 !important; box-shadow: none } .btn-link:active { background-color: transparent !important; color: #409ad5 !important; box-shadow: none } .btn-link:visited { color: #9B59B6 } .wy-btn-group .btn, .wy-control .btn { vertical-align: middle } .wy-btn-group { margin-bottom: 24px; *zoom: 1 } .wy-btn-group:before, .wy-btn-group:after { display: table; content: "" } .wy-btn-group:after { clear: both } .wy-dropdown { position: relative; display: inline-block } .wy-dropdown-active .wy-dropdown-menu { display: block } .wy-dropdown-menu { position: absolute; left: 0; display: none; float: left; top: 100%; min-width: 100%; background: #fcfcfc; z-index: 100; border: solid 1px #cfd7dd; box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1); padding: 12px } .wy-dropdown-menu > dd > a { display: block; clear: both; color: #404040; white-space: nowrap; font-size: 90%; padding: 0 12px; cursor: pointer } .wy-dropdown-menu > dd > a:hover { background: #2980B9; color: #fff } .wy-dropdown-menu > dd.divider { border-top: solid 1px #cfd7dd; margin: 6px 0 } .wy-dropdown-menu > dd.search { padding-bottom: 12px } .wy-dropdown-menu > dd.search input[type="search"] { width: 100% } .wy-dropdown-menu > dd.call-to-action { background: #e3e3e3; text-transform: uppercase; font-weight: 500; font-size: 80% } .wy-dropdown-menu > dd.call-to-action:hover { background: #e3e3e3 } .wy-dropdown-menu > dd.call-to-action .btn { color: #fff } .wy-dropdown.wy-dropdown-up .wy-dropdown-menu { bottom: 100%; top: auto; left: auto; right: 0 } .wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu { background: #fcfcfc; margin-top: 2px } .wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a { padding: 6px 12px } .wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover { background: #2980B9; color: #fff } .wy-dropdown.wy-dropdown-left .wy-dropdown-menu { right: 0; text-align: right } .wy-dropdown-arrow:before { content: " "; border-bottom: 5px solid #f5f5f5; border-left: 5px solid transparent; border-right: 5px solid transparent; position: absolute; display: block; top: -4px; left: 50%; margin-left: -3px } .wy-dropdown-arrow.wy-dropdown-arrow-left:before { left: 11px } .wy-form-stacked select { display: block } .wy-form-aligned input, .wy-form-aligned textarea, .wy-form-aligned select, .wy-form-aligned .wy-help-inline, .wy-form-aligned label { display: inline-block; *display: inline; *zoom: 1; vertical-align: middle } .wy-form-aligned .wy-control-group > label { display: inline-block; vertical-align: middle; width: 10em; margin: 6px 12px 0 0; float: left } .wy-form-aligned .wy-control { float: left } .wy-form-aligned .wy-control label { display: block } .wy-form-aligned .wy-control select { margin-top: 6px } fieldset { border: 0; margin: 0; padding: 0 } legend { display: block; width: 100%; border: 0; padding: 0; white-space: normal; margin-bottom: 24px; font-size: 150%; *margin-left: -7px } label { display: block; margin: 0 0 0.3125em 0; color: #333; font-size: 90% } input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle } .wy-control-group { margin-bottom: 24px; *zoom: 1; max-width: 68em; margin-left: auto; margin-right: auto; *zoom: 1 } .wy-control-group:before, .wy-control-group:after { display: table; content: "" } .wy-control-group:after { clear: both } .wy-control-group:before, .wy-control-group:after { display: table; content: "" } .wy-control-group:after { clear: both } .wy-control-group.wy-control-group-required > label:after { content: " *"; color: #E74C3C } .wy-control-group .wy-form-full, .wy-control-group .wy-form-halves, .wy-control-group .wy-form-thirds { padding-bottom: 12px } .wy-control-group .wy-form-full select, .wy-control-group .wy-form-halves select, .wy-control-group .wy-form-thirds select { width: 100% } .wy-control-group .wy-form-full input[type="text"], .wy-control-group .wy-form-full input[type="password"], .wy-control-group .wy-form-full input[type="email"], .wy-control-group .wy-form-full input[type="url"], .wy-control-group .wy-form-full input[type="date"], .wy-control-group .wy-form-full input[type="month"], .wy-control-group .wy-form-full input[type="time"], .wy-control-group .wy-form-full input[type="datetime"], .wy-control-group .wy-form-full input[type="datetime-local"], .wy-control-group .wy-form-full input[type="week"], .wy-control-group .wy-form-full input[type="number"], .wy-control-group .wy-form-full input[type="search"], .wy-control-group .wy-form-full input[type="tel"], .wy-control-group .wy-form-full input[type="color"], .wy-control-group .wy-form-halves input[type="text"], .wy-control-group .wy-form-halves input[type="password"], .wy-control-group .wy-form-halves input[type="email"], .wy-control-group .wy-form-halves input[type="url"], .wy-control-group .wy-form-halves input[type="date"], .wy-control-group .wy-form-halves input[type="month"], .wy-control-group .wy-form-halves input[type="time"], .wy-control-group .wy-form-halves input[type="datetime"], .wy-control-group .wy-form-halves input[type="datetime-local"], .wy-control-group .wy-form-halves input[type="week"], .wy-control-group .wy-form-halves input[type="number"], .wy-control-group .wy-form-halves input[type="search"], .wy-control-group .wy-form-halves input[type="tel"], .wy-control-group .wy-form-halves input[type="color"], .wy-control-group .wy-form-thirds input[type="text"], .wy-control-group .wy-form-thirds input[type="password"], .wy-control-group .wy-form-thirds input[type="email"], .wy-control-group .wy-form-thirds input[type="url"], .wy-control-group .wy-form-thirds input[type="date"], .wy-control-group .wy-form-thirds input[type="month"], .wy-control-group .wy-form-thirds input[type="time"], .wy-control-group .wy-form-thirds input[type="datetime"], .wy-control-group .wy-form-thirds input[type="datetime-local"], .wy-control-group .wy-form-thirds input[type="week"], .wy-control-group .wy-form-thirds input[type="number"], .wy-control-group .wy-form-thirds input[type="search"], .wy-control-group .wy-form-thirds input[type="tel"], .wy-control-group .wy-form-thirds input[type="color"] { width: 100% } .wy-control-group .wy-form-full { float: left; display: block; margin-right: 2.35765%; width: 100%; margin-right: 0 } .wy-control-group .wy-form-full:last-child { margin-right: 0 } .wy-control-group .wy-form-halves { float: left; display: block; margin-right: 2.35765%; width: 48.82117% } .wy-control-group .wy-form-halves:last-child { margin-right: 0 } .wy-control-group .wy-form-halves:nth-of-type(2n) { margin-right: 0 } .wy-control-group .wy-form-halves:nth-of-type(2n+1) { clear: left } .wy-control-group .wy-form-thirds { float: left; display: block; margin-right: 2.35765%; width: 31.76157% } .wy-control-group .wy-form-thirds:last-child { margin-right: 0 } .wy-control-group .wy-form-thirds:nth-of-type(3n) { margin-right: 0 } .wy-control-group .wy-form-thirds:nth-of-type(3n+1) { clear: left } .wy-control-group.wy-control-group-no-input .wy-control { margin: 6px 0 0 0; font-size: 90% } .wy-control-no-input { display: inline-block; margin: 6px 0 0 0; font-size: 90% } .wy-control-group.fluid-input input[type="text"], .wy-control-group.fluid-input input[type="password"], .wy-control-group.fluid-input input[type="email"], .wy-control-group.fluid-input input[type="url"], .wy-control-group.fluid-input input[type="date"], .wy-control-group.fluid-input input[type="month"], .wy-control-group.fluid-input input[type="time"], .wy-control-group.fluid-input input[type="datetime"], .wy-control-group.fluid-input input[type="datetime-local"], .wy-control-group.fluid-input input[type="week"], .wy-control-group.fluid-input input[type="number"], .wy-control-group.fluid-input input[type="search"], .wy-control-group.fluid-input input[type="tel"], .wy-control-group.fluid-input input[type="color"] { width: 100% } .wy-form-message-inline { display: inline-block; padding-left: 0.3em; color: #666; vertical-align: middle; font-size: 90% } .wy-form-message { display: block; color: #999; font-size: 70%; margin-top: 0.3125em; font-style: italic } input { line-height: normal } input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; *overflow: visible } input[type="text"], input[type="password"], input[type="email"], input[type="url"], input[type="date"], input[type="month"], input[type="time"], input[type="datetime"], input[type="datetime-local"], input[type="week"], input[type="number"], input[type="search"], input[type="tel"], input[type="color"] { -webkit-appearance: none; padding: 6px; display: inline-block; border: 1px solid #ccc; font-size: 80%; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; box-shadow: inset 0 1px 3px #ddd; border-radius: 0; -webkit-transition: border 0.3s linear; -moz-transition: border 0.3s linear; transition: border 0.3s linear } input[type="datetime-local"] { padding: 0.34375em 0.625em } input[disabled] { cursor: default } input[type="checkbox"], input[type="radio"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 0; margin-right: 0.3125em; *height: 13px; *width: 13px } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none } input[type="text"]:focus, input[type="password"]:focus, input[type="email"]:focus, input[type="url"]:focus, input[type="date"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="week"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="color"]:focus { outline: 0; outline: thin dotted \9; border-color: #333 } input.no-focus:focus { border-color: #ccc !important } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted #333; outline: 1px auto #129FEA } input[type="text"][disabled], input[type="password"][disabled], input[type="email"][disabled], input[type="url"][disabled], input[type="date"][disabled], input[type="month"][disabled], input[type="time"][disabled], input[type="datetime"][disabled], input[type="datetime-local"][disabled], input[type="week"][disabled], input[type="number"][disabled], input[type="search"][disabled], input[type="tel"][disabled], input[type="color"][disabled] { cursor: not-allowed; background-color: #f3f6f6; color: #cad2d3 } input:focus:invalid, textarea:focus:invalid, select:focus:invalid { color: #E74C3C; border: 1px solid #E74C3C } input:focus:invalid:focus, textarea:focus:invalid:focus, select:focus:invalid:focus { border-color: #E74C3C } input[type="file"]:focus:invalid:focus, input[type="radio"]:focus:invalid:focus, input[type="checkbox"]:focus:invalid:focus { outline-color: #E74C3C } input.wy-input-large { padding: 12px; font-size: 100% } textarea { overflow: auto; vertical-align: top; width: 100%; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif } select, textarea { padding: 0.5em 0.625em; display: inline-block; border: 1px solid #ccc; font-size: 80%; box-shadow: inset 0 1px 3px #ddd; -webkit-transition: border 0.3s linear; -moz-transition: border 0.3s linear; transition: border 0.3s linear } select { border: 1px solid #ccc; background-color: #fff } select[multiple] { height: auto } select:focus, textarea:focus { outline: 0 } select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly] { cursor: not-allowed; background-color: #fff; color: #cad2d3; border-color: transparent } .wy-checkbox, .wy-radio { margin: 6px 0; color: #404040; display: block } .wy-checkbox input, .wy-radio input { vertical-align: baseline } .wy-form-message-inline { display: inline-block; *display: inline; *zoom: 1; vertical-align: middle } .wy-input-prefix, .wy-input-suffix { white-space: nowrap; padding: 6px } .wy-input-prefix .wy-input-context, .wy-input-suffix .wy-input-context { line-height: 27px; padding: 0 8px; display: inline-block; font-size: 80%; background-color: #f3f6f6; border: solid 1px #ccc; color: #999 } .wy-input-suffix .wy-input-context { border-left: 0 } .wy-input-prefix .wy-input-context { border-right: 0 } .wy-control-group.wy-control-group-error .wy-form-message, .wy-control-group.wy-control-group-error > label { color: #E74C3C } .wy-control-group.wy-control-group-error input[type="text"], .wy-control-group.wy-control-group-error input[type="password"], .wy-control-group.wy-control-group-error input[type="email"], .wy-control-group.wy-control-group-error input[type="url"], .wy-control-group.wy-control-group-error input[type="date"], .wy-control-group.wy-control-group-error input[type="month"], .wy-control-group.wy-control-group-error input[type="time"], .wy-control-group.wy-control-group-error input[type="datetime"], .wy-control-group.wy-control-group-error input[type="datetime-local"], .wy-control-group.wy-control-group-error input[type="week"], .wy-control-group.wy-control-group-error input[type="number"], .wy-control-group.wy-control-group-error input[type="search"], .wy-control-group.wy-control-group-error input[type="tel"], .wy-control-group.wy-control-group-error input[type="color"] { border: solid 1px #E74C3C } .wy-control-group.wy-control-group-error textarea { border: solid 1px #E74C3C } .wy-inline-validate { white-space: nowrap } .wy-inline-validate .wy-input-context { padding: 0.5em 0.625em; display: inline-block; font-size: 80% } .wy-inline-validate.wy-inline-validate-success .wy-input-context { color: #27AE60 } .wy-inline-validate.wy-inline-validate-danger .wy-input-context { color: #E74C3C } .wy-inline-validate.wy-inline-validate-warning .wy-input-context { color: #E67E22 } .wy-inline-validate.wy-inline-validate-info .wy-input-context { color: #2980B9 } .rotate-90 { -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: rotate(90deg) } .rotate-180 { -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg) } .rotate-270 { -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -ms-transform: rotate(270deg); -o-transform: rotate(270deg); transform: rotate(270deg) } .mirror { -webkit-transform: scaleX(-1); -moz-transform: scaleX(-1); -ms-transform: scaleX(-1); -o-transform: scaleX(-1); transform: scaleX(-1) } .mirror.rotate-90 { -webkit-transform: scaleX(-1) rotate(90deg); -moz-transform: scaleX(-1) rotate(90deg); -ms-transform: scaleX(-1) rotate(90deg); -o-transform: scaleX(-1) rotate(90deg); transform: scaleX(-1) rotate(90deg) } .mirror.rotate-180 { -webkit-transform: scaleX(-1) rotate(180deg); -moz-transform: scaleX(-1) rotate(180deg); -ms-transform: scaleX(-1) rotate(180deg); -o-transform: scaleX(-1) rotate(180deg); transform: scaleX(-1) rotate(180deg) } .mirror.rotate-270 { -webkit-transform: scaleX(-1) rotate(270deg); -moz-transform: scaleX(-1) rotate(270deg); -ms-transform: scaleX(-1) rotate(270deg); -o-transform: scaleX(-1) rotate(270deg); transform: scaleX(-1) rotate(270deg) } @media only screen and (max-width: 480px) { .wy-form button[type="submit"] { margin: 0.7em 0 0 } .wy-form input[type="text"], .wy-form input[type="password"], .wy-form input[type="email"], .wy-form input[type="url"], .wy-form input[type="date"], .wy-form input[type="month"], .wy-form input[type="time"], .wy-form input[type="datetime"], .wy-form input[type="datetime-local"], .wy-form input[type="week"], .wy-form input[type="number"], .wy-form input[type="search"], .wy-form input[type="tel"], .wy-form input[type="color"] { margin-bottom: 0.3em; display: block } .wy-form label { margin-bottom: 0.3em; display: block } .wy-form input[type="password"], .wy-form input[type="email"], .wy-form input[type="url"], .wy-form input[type="date"], .wy-form input[type="month"], .wy-form input[type="time"], .wy-form input[type="datetime"], .wy-form input[type="datetime-local"], .wy-form input[type="week"], .wy-form input[type="number"], .wy-form input[type="search"], .wy-form input[type="tel"], .wy-form input[type="color"] { margin-bottom: 0 } .wy-form-aligned .wy-control-group label { margin-bottom: 0.3em; text-align: left; display: block; width: 100% } .wy-form-aligned .wy-control { margin: 1.5em 0 0 0 } .wy-form .wy-help-inline, .wy-form-message-inline, .wy-form-message { display: block; font-size: 80%; padding: 6px 0 } } @media screen and (max-width: 768px) { .tablet-hide { display: none } } @media screen and (max-width: 480px) { .mobile-hide { display: none } } .float-left { float: left } .float-right { float: right } .full-width { width: 100% } .wy-table, .rst-content table.docutils, .rst-content table.field-list { border-collapse: collapse; border-spacing: 0; empty-cells: show; margin-bottom: 24px } .wy-table caption, .rst-content table.docutils caption, .rst-content table.field-list caption { color: #000; font: italic 85%/1 arial, sans-serif; padding: 1em 0; text-align: center } .wy-table td, .rst-content table.docutils td, .rst-content table.field-list td, .wy-table th, .rst-content table.docutils th, .rst-content table.field-list th { font-size: 90%; margin: 0; overflow: visible; padding: 8px 16px } .wy-table td:first-child, .rst-content table.docutils td:first-child, .rst-content table.field-list td:first-child, .wy-table th:first-child, .rst-content table.docutils th:first-child, .rst-content table.field-list th:first-child { border-left-width: 0 } .wy-table thead, .rst-content table.docutils thead, .rst-content table.field-list thead { color: #000; text-align: left; vertical-align: bottom; white-space: nowrap } .wy-table thead th, .rst-content table.docutils thead th, .rst-content table.field-list thead th { font-weight: bold; border-bottom: solid 2px #e1e4e5 } .wy-table td, .rst-content table.docutils td, .rst-content table.field-list td { background-color: transparent; vertical-align: middle } .wy-table td p, .rst-content table.docutils td p, .rst-content table.field-list td p { line-height: 18px } .wy-table td p:last-child, .rst-content table.docutils td p:last-child, .rst-content table.field-list td p:last-child { margin-bottom: 0 } .wy-table .wy-table-cell-min, .rst-content table.docutils .wy-table-cell-min, .rst-content table.field-list .wy-table-cell-min { width: 1%; padding-right: 0 } .wy-table .wy-table-cell-min input[type=checkbox], .rst-content table.docutils .wy-table-cell-min input[type=checkbox], .rst-content table.field-list .wy-table-cell-min input[type=checkbox], .wy-table .wy-table-cell-min input[type=checkbox], .rst-content table.docutils .wy-table-cell-min input[type=checkbox], .rst-content table.field-list .wy-table-cell-min input[type=checkbox] { margin: 0 } .wy-table-secondary { color: gray; font-size: 90% } .wy-table-tertiary { color: gray; font-size: 80% } .wy-table-odd td, .wy-table-striped tr:nth-child(2n-1) td, .rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td { background-color: #f3f6f6 } .wy-table-backed { background-color: #f3f6f6 } .wy-table-bordered-all, .rst-content table.docutils { border: 1px solid #e1e4e5 } .wy-table-bordered-all td, .rst-content table.docutils td { border-bottom: 1px solid #e1e4e5; border-left: 1px solid #e1e4e5 } .wy-table-bordered-all tbody > tr:last-child td, .rst-content table.docutils tbody > tr:last-child td { border-bottom-width: 0 } .wy-table-bordered { border: 1px solid #e1e4e5 } .wy-table-bordered-rows td { border-bottom: 1px solid #e1e4e5 } .wy-table-bordered-rows tbody > tr:last-child td { border-bottom-width: 0 } .wy-table-horizontal tbody > tr:last-child td { border-bottom-width: 0 } .wy-table-horizontal td, .wy-table-horizontal th { border-width: 0 0 1px 0; border-bottom: 1px solid #e1e4e5 } .wy-table-horizontal tbody > tr:last-child td { border-bottom-width: 0 } .wy-table-responsive { margin-bottom: 24px; max-width: 100%; overflow: auto } .wy-table-responsive table { margin-bottom: 0 !important } .wy-table-responsive table td, .wy-table-responsive table th { white-space: nowrap } a { color: #2980B9; text-decoration: none; cursor: pointer } a:hover { color: #3091d1 } a:visited { color: #9B59B6 } html { height: 100%; overflow-x: hidden } body { font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; font-weight: normal; color: #404040; min-height: 100%; overflow-x: hidden; background: #edf0f2 } .wy-text-left { text-align: left } .wy-text-center { text-align: center } .wy-text-right { text-align: right } .wy-text-large { font-size: 120% } .wy-text-normal { font-size: 100% } .wy-text-small, small { font-size: 80% } .wy-text-strike { text-decoration: line-through } .wy-text-warning { color: #E67E22 !important } a.wy-text-warning:hover { color: #eb9950 !important } .wy-text-info { color: #2980B9 !important } a.wy-text-info:hover { color: #409ad5 !important } .wy-text-success { color: #27AE60 !important } a.wy-text-success:hover { color: #36d278 !important } .wy-text-danger { color: #E74C3C !important } a.wy-text-danger:hover { color: #ed7669 !important } .wy-text-neutral { color: #404040 !important } a.wy-text-neutral:hover { color: #595959 !important } h1, h2, h3, h4, h5, h6, legend { margin-top: 0; font-weight: 700; font-family: "Roboto Slab", "ff-tisa-web-pro", "Georgia", Arial, sans-serif } p { line-height: 24px; margin: 0; font-size: 16px; margin-bottom: 24px } h1 { font-size: 175% } h2 { font-size: 150% } h3 { font-size: 125% } h4 { font-size: 115% } h5 { font-size: 110% } h6 { font-size: 100% } hr { display: block; height: 1px; border: 0; border-top: 1px solid #e1e4e5; margin: 24px 0; padding: 0 } code, .rst-content tt { white-space: nowrap; max-width: 100%; background: #fff; border: solid 1px #e1e4e5; font-size: 75%; padding: 0 5px; font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; color: #E74C3C; overflow-x: auto } code.code-large, .rst-content tt.code-large { font-size: 90% } .wy-plain-list-disc, .rst-content .section ul, .rst-content .toctree-wrapper ul, article ul { list-style: disc; line-height: 24px; margin-bottom: 24px } .wy-plain-list-disc li, .rst-content .section ul li, .rst-content .toctree-wrapper ul li, article ul li { list-style: disc; margin-left: 24px } .wy-plain-list-disc li p:last-child, .rst-content .section ul li p:last-child, .rst-content .toctree-wrapper ul li p:last-child, article ul li p:last-child { margin-bottom: 0 } .wy-plain-list-disc li ul, .rst-content .section ul li ul, .rst-content .toctree-wrapper ul li ul, article ul li ul { margin-bottom: 0 } .wy-plain-list-disc li li, .rst-content .section ul li li, .rst-content .toctree-wrapper ul li li, article ul li li { list-style: circle } .wy-plain-list-disc li li li, .rst-content .section ul li li li, .rst-content .toctree-wrapper ul li li li, article ul li li li { list-style: square } .wy-plain-list-disc li ol li, .rst-content .section ul li ol li, .rst-content .toctree-wrapper ul li ol li, article ul li ol li { list-style: decimal } .wy-plain-list-decimal, .rst-content .section ol, .rst-content ol.arabic, article ol { list-style: decimal; line-height: 24px; margin-bottom: 24px } .wy-plain-list-decimal li, .rst-content .section ol li, .rst-content ol.arabic li, article ol li { list-style: decimal; margin-left: 24px } .wy-plain-list-decimal li p:last-child, .rst-content .section ol li p:last-child, .rst-content ol.arabic li p:last-child, article ol li p:last-child { margin-bottom: 0 } .wy-plain-list-decimal li ul, .rst-content .section ol li ul, .rst-content ol.arabic li ul, article ol li ul { margin-bottom: 0 } .wy-plain-list-decimal li ul li, .rst-content .section ol li ul li, .rst-content ol.arabic li ul li, article ol li ul li { list-style: disc } .codeblock-example { border: 1px solid #e1e4e5; border-bottom: none; padding: 24px; padding-top: 48px; font-weight: 500; background: #fff; position: relative } .codeblock-example:after { content: "Example"; position: absolute; top: 0px; left: 0px; background: #9B59B6; color: #fff; padding: 6px 12px } .codeblock-example.prettyprint-example-only { border: 1px solid #e1e4e5; margin-bottom: 24px } .codeblock, pre.literal-block, .rst-content .literal-block, .rst-content pre.literal-block, div[class^='highlight'] { border: 1px solid #e1e4e5; padding: 0px; overflow-x: auto; background: #fff; margin: 1px 0 24px 0 } .codeblock div[class^='highlight'], pre.literal-block div[class^='highlight'], .rst-content .literal-block div[class^='highlight'], div[class^='highlight'] div[class^='highlight'] { border: none; background: none; margin: 0 } div[class^='highlight'] td.code { width: 100% } .linenodiv pre { border-right: solid 1px #e6e9ea; margin: 0; padding: 12px 12px; font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; font-size: 12px; line-height: 1.5; color: #d9d9d9 } div[class^='highlight'] pre { white-space: pre; margin: 0; padding: 12px 12px; font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; font-size: 12px; line-height: 1.5; display: block; overflow: auto; color: #404040 } @media print { .codeblock, pre.literal-block, .rst-content .literal-block, .rst-content pre.literal-block, div[class^='highlight'], div[class^='highlight'] pre { white-space: pre-wrap } } .hll { background-color: #ffc; margin: 0 -12px; padding: 0 12px; display: block } .c { color: #998; font-style: italic } .err { color: #a61717; background-color: #e3d2d2 } .k { font-weight: bold } .o { font-weight: bold } .cm { color: #998; font-style: italic } .cp { color: #999; font-weight: bold } .c1 { color: #998; font-style: italic } .cs { color: #999; font-weight: bold; font-style: italic } .gd { color: #000; background-color: #fdd } .gd .x { color: #000; background-color: #faa } .ge { font-style: italic } .gr { color: #a00 } .gh { color: #999 } .gi { color: #000; background-color: #dfd } .gi .x { color: #000; background-color: #afa } .go { color: #888 } .gp { color: #555 } .gs { font-weight: bold } .gu { color: purple; font-weight: bold } .gt { color: #a00 } .kc { font-weight: bold } .kd { font-weight: bold } .kn { font-weight: bold } .kp { font-weight: bold } .kr { font-weight: bold } .kt { color: #458; font-weight: bold } .m { color: #099 } .s { color: #d14 } .n { color: #333 } .na { color: teal } .nb { color: #0086b3 } .nc { color: #458; font-weight: bold } .no { color: teal } .ni { color: purple } .ne { color: #900; font-weight: bold } .nf { color: #900; font-weight: bold } .nn { color: #555 } .nt { color: navy } .nv { color: teal } .ow { font-weight: bold } .w { color: #bbb } .mf { color: #099 } .mh { color: #099 } .mi { color: #099 } .mo { color: #099 } .sb { color: #d14 } .sc { color: #d14 } .sd { color: #d14 } .s2 { color: #d14 } .se { color: #d14 } .sh { color: #d14 } .si { color: #d14 } .sx { color: #d14 } .sr { color: #009926 } .s1 { color: #d14 } .ss { color: #990073 } .bp { color: #999 } .vc { color: teal } .vg { color: teal } .vi { color: teal } .il { color: #099 } .gc { color: #999; background-color: #EAF2F5 } .wy-breadcrumbs li { display: inline-block } .wy-breadcrumbs li.wy-breadcrumbs-aside { float: right } .wy-breadcrumbs li a { display: inline-block; padding: 5px } .wy-breadcrumbs li a:first-child { padding-left: 0 } .wy-breadcrumbs-extra { margin-bottom: 0; color: #b3b3b3; font-size: 80%; display: inline-block } @media screen and (max-width: 480px) { .wy-breadcrumbs-extra { display: none } .wy-breadcrumbs li.wy-breadcrumbs-aside { display: none } } @media print { .wy-breadcrumbs li.wy-breadcrumbs-aside { display: none } } .wy-affix { position: fixed; top: 1.618em } .wy-menu a:hover { text-decoration: none } .wy-menu-horiz { *zoom: 1 } .wy-menu-horiz:before, .wy-menu-horiz:after { display: table; content: "" } .wy-menu-horiz:after { clear: both } .wy-menu-horiz ul, .wy-menu-horiz li { display: inline-block } .wy-menu-horiz li:hover { background: rgba(255, 255, 255, 0.1) } .wy-menu-horiz li.divide-left { border-left: solid 1px #404040 } .wy-menu-horiz li.divide-right { border-right: solid 1px #404040 } .wy-menu-horiz a { height: 32px; display: inline-block; line-height: 32px; padding: 0 16px } .wy-menu-vertical header { height: 32px; display: inline-block; line-height: 32px; padding: 0 1.618em; display: block; font-weight: bold; text-transform: uppercase; font-size: 80%; color: #2980B9; white-space: nowrap } .wy-menu-vertical ul { margin-bottom: 0 } .wy-menu-vertical li.divide-top { border-top: solid 1px #404040 } .wy-menu-vertical li.divide-bottom { border-bottom: solid 1px #404040 } .wy-menu-vertical li.current { background: #e3e3e3 } .wy-menu-vertical li.current a { color: gray; border-right: solid 1px #c9c9c9; padding: 0.4045em 2.427em } .wy-menu-vertical li.current a:hover { background: #d6d6d6 } .wy-menu-vertical li.on a, .wy-menu-vertical li.current > a { color: #404040; padding: 0.4045em 1.618em; font-weight: bold; position: relative; background: #fcfcfc; border: none; border-bottom: solid 1px #c9c9c9; border-top: solid 1px #c9c9c9; padding-left: 1.618em -4px } .wy-menu-vertical li.on a:hover, .wy-menu-vertical li.current > a:hover { background: #fcfcfc } .wy-menu-vertical li.toctree-l2.current > a { background: #c9c9c9; padding: 0.4045em 2.427em } .wy-menu-vertical li.current ul { display: block } .wy-menu-vertical li ul { margin-bottom: 0; display: none } .wy-menu-vertical .local-toc li ul { display: block } .wy-menu-vertical li ul li a { margin-bottom: 0; color: #b3b3b3; font-weight: normal } .wy-menu-vertical a { display: inline-block; line-height: 18px; padding: 0.4045em 1.618em; display: block; position: relative; font-size: 90%; color: #b3b3b3 } .wy-menu-vertical a:hover { background-color: #4e4a4a; cursor: pointer } .wy-menu-vertical a:active { background-color: #2980B9; cursor: pointer; color: #fff } .wy-side-nav-search { z-index: 200; background-color: #2980B9; text-align: center; padding: 0.809em; display: block; color: #fcfcfc; margin-bottom: 0.809em } .wy-side-nav-search input[type=text] { width: 100%; border-radius: 50px; padding: 6px 12px; border-color: #2472a4 } .wy-side-nav-search img { display: block; margin: auto auto 0.809em auto; height: 45px; width: 45px; background-color: #2980B9; padding: 5px; border-radius: 100% } .wy-side-nav-search > a, .wy-side-nav-search .wy-dropdown > a { color: #fcfcfc; font-size: 100%; font-weight: bold; display: inline-block; padding: 4px 6px; margin-bottom: 0.809em } .wy-side-nav-search > a:hover, .wy-side-nav-search .wy-dropdown > a:hover { background: rgba(255, 255, 255, 0.1) } .wy-nav .wy-menu-vertical header { color: #2980B9 } .wy-nav .wy-menu-vertical a { color: #b3b3b3 } .wy-nav .wy-menu-vertical a:hover { background-color: #2980B9; color: #fff } [data-menu-wrap] { -webkit-transition: all 0.2s ease-in; -moz-transition: all 0.2s ease-in; transition: all 0.2s ease-in; position: absolute; opacity: 1; width: 100%; opacity: 0 } [data-menu-wrap].move-center { left: 0; right: auto; opacity: 1 } [data-menu-wrap].move-left { right: auto; left: -100%; opacity: 0 } [data-menu-wrap].move-right { right: -100%; left: auto; opacity: 0 } .wy-body-for-nav { background: left repeat-y #fcfcfc; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC); background-size: 300px 1px } .wy-grid-for-nav { position: absolute; width: 100%; height: 100% } .wy-nav-side { position: absolute; top: 0; left: 0; width: 300px; overflow: hidden; min-height: 100%; background: #343131; z-index: 200 } .wy-nav-top { display: none; background: #2980B9; color: #fff; padding: 0.4045em 0.809em; position: relative; line-height: 50px; text-align: center; font-size: 100%; *zoom: 1 } .wy-nav-top:before, .wy-nav-top:after { display: table; content: "" } .wy-nav-top:after { clear: both } .wy-nav-top a { color: #fff; font-weight: bold } .wy-nav-top img { margin-right: 12px; height: 45px; width: 45px; background-color: #2980B9; padding: 5px; border-radius: 100% } .wy-nav-top i { font-size: 30px; float: left; cursor: pointer } .wy-nav-content-wrap { margin-left: 300px; background: #fcfcfc; min-height: 100% } .wy-nav-content { padding: 1.618em 3.236em; height: 100%; max-width: 800px; margin: auto } .wy-body-mask { position: fixed; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.2); display: none; z-index: 499 } .wy-body-mask.on { display: block } footer { color: #999 } footer p { margin-bottom: 12px } .rst-footer-buttons { *zoom: 1 } .rst-footer-buttons:before, .rst-footer-buttons:after { display: table; content: "" } .rst-footer-buttons:after { clear: both } #search-results .search li { margin-bottom: 24px; border-bottom: solid 1px #e1e4e5; padding-bottom: 24px } #search-results .search li:first-child { border-top: solid 1px #e1e4e5; padding-top: 24px } #search-results .search li a { font-size: 120%; margin-bottom: 12px; display: inline-block } #search-results .context { color: gray; font-size: 90% } @media screen and (max-width: 768px) { .wy-body-for-nav { background: #fcfcfc } .wy-nav-top { display: block } .wy-nav-side { left: -300px } .wy-nav-side.shift { width: 85%; left: 0 } .wy-nav-content-wrap { margin-left: 0 } .wy-nav-content-wrap .wy-nav-content { padding: 1.618em } .wy-nav-content-wrap.shift { position: fixed; min-width: 100%; left: 85%; top: 0; height: 100%; overflow: hidden } } @media screen and (min-width: 1400px) { .wy-nav-content-wrap { background: rgba(0, 0, 0, 0.05) } .wy-nav-content { margin: 0; background: #fcfcfc } } @media print { .rst-versions, footer, .wy-nav-side { display: none } .wy-nav-content-wrap { margin-left: 0 } } nav.stickynav { position: fixed; top: 0 } .rst-versions { position: fixed; bottom: 0; left: 0; width: 300px; color: #fcfcfc; background: #1f1d1d; border-top: solid 10px #343131; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; z-index: 400 } .rst-versions a { color: #2980B9; text-decoration: none } .rst-versions .rst-badge-small { display: none } .rst-versions .rst-current-version { padding: 12px; background-color: #272525; display: block; text-align: right; font-size: 90%; cursor: pointer; color: #27AE60; *zoom: 1 } .rst-versions .rst-current-version:before, .rst-versions .rst-current-version:after { display: table; content: "" } .rst-versions .rst-current-version:after { clear: both } .rst-versions .rst-current-version .fa, .rst-versions .rst-current-version .rst-content .admonition-title, .rst-content .rst-versions .rst-current-version .admonition-title, .rst-versions .rst-current-version .rst-content h1 .headerlink, .rst-content h1 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h2 .headerlink, .rst-content h2 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h3 .headerlink, .rst-content h3 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h4 .headerlink, .rst-content h4 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h5 .headerlink, .rst-content h5 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h6 .headerlink, .rst-content h6 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content dl dt .headerlink, .rst-content dl dt .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .icon { color: #fcfcfc } .rst-versions .rst-current-version .fa-book, .rst-versions .rst-current-version .icon-book { float: left } .rst-versions .rst-current-version .icon-book { float: left } .rst-versions .rst-current-version.rst-out-of-date { background-color: #E74C3C; color: #fff } .rst-versions .rst-current-version.rst-active-old-version { background-color: #F1C40F; color: #000 } .rst-versions.shift-up .rst-other-versions { display: block } .rst-versions .rst-other-versions { font-size: 90%; padding: 12px; color: gray; display: none } .rst-versions .rst-other-versions hr { display: block; height: 1px; border: 0; margin: 20px 0; padding: 0; border-top: solid 1px #413d3d } .rst-versions .rst-other-versions dd { display: inline-block; margin: 0 } .rst-versions .rst-other-versions dd a { display: inline-block; padding: 6px; color: #fcfcfc } .rst-versions.rst-badge { width: auto; bottom: 20px; right: 20px; left: auto; border: none; max-width: 300px } .rst-versions.rst-badge .icon-book { float: none } .rst-versions.rst-badge .fa-book, .rst-versions.rst-badge .icon-book { float: none } .rst-versions.rst-badge.shift-up .rst-current-version { text-align: right } .rst-versions.rst-badge.shift-up .rst-current-version .fa-book, .rst-versions.rst-badge.shift-up .rst-current-version .icon-book { float: left } .rst-versions.rst-badge.shift-up .rst-current-version .icon-book { float: left } .rst-versions.rst-badge .rst-current-version { width: auto; height: 30px; line-height: 30px; padding: 0 6px; display: block; text-align: center } @media screen and (max-width: 768px) { .rst-versions { width: 85%; display: none } .rst-versions.shift { display: block } img { width: 100%; height: auto } } .rst-content img { max-width: 100%; height: auto !important } .rst-content div.figure { margin-bottom: 24px } .rst-content div.figure.align-center { text-align: center } .rst-content .section > img, .rst-content .section > a > img { margin-bottom: 24px } .rst-content blockquote { margin-left: 24px; line-height: 24px; margin-bottom: 24px } .rst-content .note .last, .rst-content .attention .last, .rst-content .caution .last, .rst-content .danger .last, .rst-content .error .last, .rst-content .hint .last, .rst-content .important .last, .rst-content .tip .last, .rst-content .warning .last, .rst-content .seealso .last, .rst-content .admonition-todo .last { margin-bottom: 0 } .rst-content .admonition-title:before { margin-right: 4px } .rst-content .admonition table { border-color: rgba(0, 0, 0, 0.1) } .rst-content .admonition table td, .rst-content .admonition table th { background: transparent !important; border-color: rgba(0, 0, 0, 0.1) !important } .rst-content .section ol.loweralpha, .rst-content .section ol.loweralpha li { list-style: lower-alpha } .rst-content .section ol.upperalpha, .rst-content .section ol.upperalpha li { list-style: upper-alpha } .rst-content .section ol p, .rst-content .section ul p { margin-bottom: 12px } .rst-content .line-block { margin-left: 24px } .rst-content .topic-title { font-weight: bold; margin-bottom: 12px } .rst-content .toc-backref { color: #404040 } .rst-content .align-right { float: right; margin: 0px 0px 24px 24px } .rst-content .align-left { float: left; margin: 0px 24px 24px 0px } .rst-content .align-center { margin: auto; display: block } .rst-content h1 .headerlink, .rst-content h2 .headerlink, .rst-content h3 .headerlink, .rst-content h4 .headerlink, .rst-content h5 .headerlink, .rst-content h6 .headerlink, .rst-content dl dt .headerlink { display: none; visibility: hidden; font-size: 14px } .rst-content h1 .headerlink:after, .rst-content h2 .headerlink:after, .rst-content h3 .headerlink:after, .rst-content h4 .headerlink:after, .rst-content h5 .headerlink:after, .rst-content h6 .headerlink:after, .rst-content dl dt .headerlink:after { visibility: visible; content: ""; font-family: FontAwesome; display: inline-block } .rst-content h1:hover .headerlink, .rst-content h2:hover .headerlink, .rst-content h3:hover .headerlink, .rst-content h4:hover .headerlink, .rst-content h5:hover .headerlink, .rst-content h6:hover .headerlink, .rst-content dl dt:hover .headerlink { display: inline-block } .rst-content .sidebar { float: right; width: 40%; display: block; margin: 0 0 24px 24px; padding: 24px; background: #f3f6f6; border: solid 1px #e1e4e5 } .rst-content .sidebar p, .rst-content .sidebar ul, .rst-content .sidebar dl { font-size: 90% } .rst-content .sidebar .last { margin-bottom: 0 } .rst-content .sidebar .sidebar-title { display: block; font-family: "Roboto Slab", "ff-tisa-web-pro", "Georgia", Arial, sans-serif; font-weight: bold; background: #e1e4e5; padding: 6px 12px; margin: -24px; margin-bottom: 24px; font-size: 100% } .rst-content .highlighted { background: #F1C40F; display: inline-block; font-weight: bold; padding: 0 6px } .rst-content .footnote-reference, .rst-content .citation-reference { vertical-align: super; font-size: 90% } .rst-content table.docutils.citation, .rst-content table.docutils.footnote { background: none; border: none; color: #999 } .rst-content table.docutils.citation td, .rst-content table.docutils.citation tr, .rst-content table.docutils.footnote td, .rst-content table.docutils.footnote tr { border: none; background-color: transparent !important; white-space: normal } .rst-content table.docutils.citation td.label, .rst-content table.docutils.footnote td.label { padding-left: 0; padding-right: 0; vertical-align: top } .rst-content table.field-list { border: none } .rst-content table.field-list td { border: none; padding-top: 5px } .rst-content table.field-list td > strong { display: inline-block; margin-top: 3px } .rst-content table.field-list .field-name { padding-right: 10px; text-align: left; white-space: nowrap } .rst-content table.field-list .field-body { text-align: left; padding-left: 0 } .rst-content tt { color: #000 } .rst-content tt big, .rst-content tt em { font-size: 100% !important; line-height: normal } .rst-content tt .xref, a .rst-content tt { font-weight: bold } .rst-content a tt { color: #2980B9 } .rst-content dl { margin-bottom: 24px } .rst-content dl dt { font-weight: bold } .rst-content dl p, .rst-content dl table, .rst-content dl ul, .rst-content dl ol { margin-bottom: 12px !important } .rst-content dl dd { margin: 0 0 12px 24px } .rst-content dl:not(.docutils) { margin-bottom: 24px } .rst-content dl:not(.docutils) dt { display: inline-block; margin: 6px 0; font-size: 90%; line-height: normal; background: #e7f2fa; color: #2980B9; border-top: solid 3px #6ab0de; padding: 6px; position: relative } .rst-content dl:not(.docutils) dt:before { color: #6ab0de } .rst-content dl:not(.docutils) dt .headerlink { color: #404040; font-size: 100% !important } .rst-content dl:not(.docutils) dl dt { margin-bottom: 6px; border: none; border-left: solid 3px #ccc; background: #f0f0f0; color: gray } .rst-content dl:not(.docutils) dl dt .headerlink { color: #404040; font-size: 100% !important } .rst-content dl:not(.docutils) dt:first-child { margin-top: 0 } .rst-content dl:not(.docutils) tt { font-weight: bold } .rst-content dl:not(.docutils) tt.descname, .rst-content dl:not(.docutils) tt.descclassname { background-color: transparent; border: none; padding: 0; font-size: 100% !important } .rst-content dl:not(.docutils) tt.descname { font-weight: bold } .rst-content dl:not(.docutils) .optional { display: inline-block; padding: 0 4px; color: #000; font-weight: bold } .rst-content dl:not(.docutils) .property { display: inline-block; padding-right: 8px } .rst-content .viewcode-link, .rst-content .viewcode-back { display: inline-block; color: #27AE60; font-size: 80%; padding-left: 24px } .rst-content .viewcode-back { display: block; float: right } .rst-content p.rubric { margin-bottom: 12px; font-weight: bold } @media screen and (max-width: 480px) { .rst-content .sidebar { width: 100% } } span[id*='MathJax-Span'] { color: #404040 } .math { text-align: center } /*# sourceMappingURL=theme.css.map */ ================================================ FILE: documentation/website/default.twig ================================================ {{ title }} {{ title|default('The title') }} {% for itemId, item in menu.items %} {{ item.text|raw }} {% if item.items is defined %} {% for subItemId, subItem in item.items %} {{ subItem.text|raw }} {% endfor %} {% endif %} {% endfor %} Maintained by: Creative Digital Agency {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} {{ title|default('The title') }} {# PAGE CONTENT #} {# Maintained by SWIS#} Fork on GitHub {{ content|raw }} ================================================ FILE: documentation/website/js/main.js ================================================ (function($, undefined){ $(function() { $('.document table').addClass('docutils'); $('.showcase').each(function(){ var $this = $(that || this), text, nodeName, lang, that; if ($this.data('showcaseImport')) { $this = $($this.data('showcaseImport')); that = $this.get(0); } nodeName = (that || this).nodeName.toLowerCase(); lang = nodeName == 'script' ? 'js' : (nodeName == 'style' ? 'css' : 'html'); if (lang == 'html') { text = $('').append($this.clone()).html(); } else { text = $this.text(); } var newNode = $('') .append($('').text(text)) .insertBefore(this); that && $(this).remove(); }); }); })(jQuery); ================================================ FILE: documentation/website/js/theme.js ================================================ $( document ).ready(function() { // Shift nav in mobile when clicking the menu. $(document).on('click', "[data-toggle='wy-nav-top']", function() { $("[data-toggle='wy-nav-shift']").toggleClass("shift"); $("[data-toggle='rst-versions']").toggleClass("shift"); }); // Close menu when you click a link. $(document).on('click', ".wy-menu-vertical .current ul li a", function() { $("[data-toggle='wy-nav-shift']").removeClass("shift"); $("[data-toggle='rst-versions']").toggleClass("shift"); }); $(document).on('click', "[data-toggle='rst-current-version']", function() { $("[data-toggle='rst-versions']").toggleClass("shift-up"); }); // Make tables responsive $("table.docutils:not(.field-list)").wrap(""); }); window.SphinxRtdTheme = (function (jquery) { var stickyNav = (function () { var navBar, win, stickyNavCssClass = 'stickynav', applyStickNav = function () { if (navBar.height() <= win.height()) { navBar.addClass(stickyNavCssClass); } else { navBar.removeClass(stickyNavCssClass); } }, enable = function () { applyStickNav(); win.on('resize', applyStickNav); }, init = function () { navBar = jquery('nav.wy-nav-side:first'); win = jquery(window); }; jquery(init); return { enable : enable }; }()); return { StickyNav : stickyNav }; }($)); ================================================ FILE: documentation/website/original-theme/bower.json ================================================ { "name": "couscous-readthedocs-template", "dependencies": { "wyrm": "~0.0.x" } } ================================================ FILE: documentation/website/original-theme/css/badge_only.css ================================================ .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} /*# sourceMappingURL=badge_only.css.map */ ================================================ FILE: documentation/website/original-theme/sass/_theme_badge.sass ================================================ .rst-versions position: fixed bottom: 0 left: 0 width: $nav-desktop-width color: $section-background-color background: darken($menu-background-color, 8%) border-top: solid 10px $menu-background-color font-family: $base-font-family z-index: $z-index-tray a color: $link_color text-decoration: none .rst-badge-small display: none .rst-current-version padding: $base-line-height / 2 background-color: darken($menu-background-color, 5%) display: block text-align: right font-size: 90% cursor: pointer color: $green +clearfix .fa color: $section-background-color .fa-book float: left .icon-book float: left &.rst-out-of-date background-color: $red color: $white &.rst-active-old-version background-color: $yellow color: $black &.shift-up .rst-other-versions display: block .rst-other-versions font-size: 90% padding: $base-line-height / 2 color: $text-medium display: none hr display: block height: 1px border: 0 margin: 20px 0 padding: 0 border-top: solid 1px lighten($menu-background-color, 5%) dd display: inline-block margin: 0 a display: inline-block padding: $base-line-height / 4 color: $section-background-color &.rst-badge width: auto bottom: 20px right: 20px left: auto border: none max-width: $nav-desktop-width .icon-book float: none .fa-book float: none &.shift-up .rst-current-version text-align: right .fa-book float: left .icon-book float: left .rst-current-version width: auto height: 30px line-height: 30px padding: 0 $base-line-height / 4 display: block text-align: center +media($tablet) .rst-versions width: 85% display: none &.shift display: block img width: 100% height: auto ================================================ FILE: documentation/website/original-theme/sass/_theme_badge_fa.sass ================================================ // Slimmer version of FA for use on the badge_only.sass file. +font-face(FontAwesome, '#{$font-awesome-dir}fontawesome_webfont') .fa:before display: inline-block font-family: FontAwesome font-style: normal font-weight: normal line-height: 1 text-decoration: inherit +font-smooth a .fa display: inline-block text-decoration: inherit li .fa display: inline-block .fa-large:before, .fa-large:before /* 1.5 increased font size for fa-large * 1.25 width width: 1.5 * 1.25em ul.fas list-style-type: none margin-left: 2em text-indent: -0.8em li .fa width: .8em .fa-large:before, .fa-large:before /* 1.5 increased font size for fa-large * 1.25 width vertical-align: baseline // width: 1.5*1.25em .fa-book:before content: "\f02d" .icon-book:before content: "\f02d" .fa-caret-down:before content: "\f0d7" .icon-caret-down:before content: "\f0d7" .fa-caret-up:before content: "\f0d8" .icon-caret-up:before content: "\f0d8" .fa-caret-left:before content: "\f0d9" .icon-caret-left:before content: "\f0d9" .fa-caret-right:before content: "\f0da" .icon-caret-right:before content: "\f0da" ================================================ FILE: documentation/website/original-theme/sass/_theme_breadcrumbs.sass ================================================ .wy-breadcrumbs li display: inline-block &.wy-breadcrumbs-aside float: right a display: inline-block padding: 5px &:first-child padding-left: 0 .wy-breadcrumbs-extra margin-bottom: 0 color: $text-light font-size: 80% display: inline-block +media($mobile) .wy-breadcrumbs-extra display: none .wy-breadcrumbs li.wy-breadcrumbs-aside display: none @media print .wy-breadcrumbs li.wy-breadcrumbs-aside display: none ================================================ FILE: documentation/website/original-theme/sass/_theme_font_awesome_compatibility.sass ================================================ .icon @extend .fa .icon-home @extend .fa-home .icon-search @extend .fa-search .icon-book @extend .fa-book .icon-caret-down @extend .fa-caret-down .icon-github @extend .fa-github .icon-bitbucket @extend .fa-bitbucket .icon-fire @extend .fa-fire .icon-circle-arrow-right @extend .fa-arrow-circle-right .icon-circle-arrow-left @extend .fa-arrow-circle-left .icon-link @extend .fa-link ================================================ FILE: documentation/website/original-theme/sass/_theme_layout.sass ================================================ .wy-affix position: fixed top: $gutter .wy-menu a:hover text-decoration: none .wy-menu-horiz +clearfix ul, li display: inline-block li:hover background: rgba(255,255,255,.1) li &.divide-left border-left: solid 1px hsl(0, 0%, 25%) &.divide-right border-right: solid 1px hsl(0, 0%, 25%) a height: $base-font-size * 2 display: inline-block line-height: $base-font-size * 2 padding: 0 $base-font-size .wy-menu-vertical header height: $base-font-size * 2 display: inline-block line-height: $base-font-size * 2 padding: 0 $gutter display: block font-weight: bold text-transform: uppercase font-size: 80% color: $menu-logo-color white-space: nowrap ul margin-bottom: 0 li &.divide-top border-top: solid 1px hsl(0, 0%, 25%) &.divide-bottom border-bottom: solid 1px hsl(0, 0%, 25%) &.current background: darken($section-background-color, 10%) a color: $text-medium border-right: solid 1px darken($section-background-color, 20%) padding: $gutter / 4 $gutter * 1.5 &:hover background: darken($section-background-color, 15%) // On state for the first level li.on a, li.current > a color: $text-color padding: $gutter / 4 $gutter font-weight: bold position: relative background: $section-background-color border: none border-bottom: solid 1px darken($section-background-color, 20%) border-top: solid 1px darken($section-background-color, 20%) padding-left: $gutter -4px +font-smooth &:hover background: $section-background-color // This is the on state for pages beyond second level li.toctree-l2.current > a background: darken($section-background-color, 20%) padding: $gutter / 4 $gutter * 1.5 li.current ul display: block li ul margin-bottom: 0 display: none .local-toc li ul display: block li ul li a margin-bottom: 0 color: $text-light font-weight: normal a display: inline-block line-height: 18px padding: $gutter / 4 $gutter display: block position: relative font-size: 90% color: $text-light &:hover background-color: lighten($menu-background-color, 10%) cursor: pointer &:active background-color: $menu-logo-color cursor: pointer color: $white .wy-side-nav-search z-index: $z-index-popover background-color: $link-color text-align: center padding: $gutter / 2 display: block color: $section-background-color margin-bottom: $gutter / 2 input[type=text] width: 100% border-radius: 50px padding: 6px 12px border-color: darken($link-color, 5%) img display: block margin: auto auto $gutter / 2 auto height: 45px width: 45px background-color: $menu-logo-color padding: 5px border-radius: 100% > a, .wy-dropdown > a color: $section-background-color font-size: 100% font-weight: bold display: inline-block padding: $base-line-height / 6 $base-line-height / 4 margin-bottom: $gutter / 2 +font-smooth &:hover background: rgba(255,255,255,.1) .wy-nav .wy-menu-vertical header color: $link-color a color: $text-light &:hover background-color: $link-color color: $white [data-menu-wrap] +transition(all .2s ease-in) position: absolute opacity: 1 width: 100% opacity: 0 &.move-center left: 0 right: auto opacity: 1 &.move-left right: auto left: -100% opacity: 0 &.move-right right: -100% left: auto opacity: 0 .wy-body-for-nav background: left repeat-y $section-background-color background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC) background-size: $nav-desktop-width 1px .wy-grid-for-nav position: absolute width: 100% height: 100% .wy-nav-side position: absolute top: 0 left: 0 width: $nav-desktop-width overflow: hidden min-height: 100% background: $menu-background-color z-index: $z-index-popover .wy-nav-top display: none background: $link-color color: $white padding: $gutter / 4 $gutter / 2 position: relative line-height: 50px text-align: center font-size: 100% +clearfix a color: $white font-weight: bold +font-smooth img margin-right: $base-line-height / 2 height: 45px width: 45px background-color: $menu-logo-color padding: 5px border-radius: 100% i font-size: 30px float: left cursor: pointer .wy-nav-content-wrap margin-left: $nav-desktop-width background: $section-background-color min-height: 100% .wy-nav-content padding: $gutter $gutter * 2 height: 100% max-width: 800px margin: auto .wy-body-mask position: fixed width: 100% height: 100% background: rgba(0,0,0,.2) display: none z-index: $z-index-modal - 1 &.on display: block footer color: $gray-light p margin-bottom: $base-line-height / 2 .rst-footer-buttons +clearfix #search-results .search li margin-bottom: $base-line-height border-bottom: solid 1px $table_border_color padding-bottom: $base-line-height .search li:first-child border-top: solid 1px $table_border_color padding-top: $base-line-height .search li a font-size: 120% margin-bottom: $base-line-height / 2 display: inline-block .context color: $text-medium font-size: 90% +media($tablet) .wy-body-for-nav background: $section-background-color .wy-nav-top display: block .wy-nav-side @if $nav-desktop-position == left left: -$nav-desktop-width @else right: -$nav-desktop-width &.shift width: 85% left: 0 .wy-nav-content-wrap margin-left: 0 .wy-nav-content padding: $gutter &.shift position: fixed min-width: 100% left: 85% top: 0 height: 100% overflow: hidden +media($desktop-wider) .wy-nav-content-wrap background: rgba(0,0,0,.05) .wy-nav-content margin: 0 background: $section-background-color @media print .rst-versions, footer, .wy-nav-side display: none .wy-nav-content-wrap margin-left: 0 nav.stickynav position: fixed top: 0 ================================================ FILE: documentation/website/original-theme/sass/_theme_mathjax.sass ================================================ span[id*='MathJax-Span'] color: $mathjax-color .math text-align: center ================================================ FILE: documentation/website/original-theme/sass/_theme_rst.sass ================================================ // ------------------------------------------------------------------------------------------------------------------- // CONTRIBUTORS, PLEASE READ THIS! // ------------------------------------------------------------------------------------------------------------------- // Couple things... // 1. Lots of this @extends from wyrm_core/_type.sass (http://www.github.com/snide/wyrm/. // * Try not to replace any @extends code. It's pretty generic stuff meant to work together. // * That said, know that I'm very unlikely to accept PRs from wyrm just to change style here. // 2. I plan to remove the !importants in here. Part of it is due to lazyness, part to sphinx's fondness for nesting. // 3. Try to use variables from wyrm_core/wy_variables.sass. Notable are... // * $base-line-height // All margins, padding and line-height should use this in .25 increments. // * $text-color, $text-light, $text-dark...etc // * $base-font-family, $custom-font-family, $code-font-family // 4. If you have changes for mobile/tablet, put them at the bottom of the sass file. // -------------------------------------------------------------------------------------------------------------------- .rst-content // Sphinx by default applies HxW style attributes to images. This fixes that oversite. img max-width: 100% height: auto !important div.figure margin-bottom: $base-line-height div.figure.align-center text-align: center // Usually it's a good idea to give images some space. .section > img, .section > a > img margin-bottom: $base-line-height // Questionable whether this is nice or not. It styles eternal links, but comes with some baggage. // a.reference.external:after // font-family: FontAwesome // content: " \f08e " // color: $text-light // vertical-align: super // font-size: 60% // For the most part, its safe to assume that sphinx wants you to use a blockquote as an indent. It gets // used in many different ways, so don't assume you can apply some fancy style, just leave it be. blockquote margin-left: $base-line-height line-height: $base-line-height margin-bottom: $base-line-height .literal-block, pre.literal-block @extend .codeblock // These are the various note pullouts that sphinx applies .note, .attention, .caution, .danger, .error, .hint, .important, .tip, .warning, .seealso, .admonition-todo @extend .wy-alert .last margin-bottom: 0 .admonition-title @extend .wy-alert-title @extend .fa @extend .fa-exclamation-circle &:before margin-right: 4px .note, .seealso @extend .wy-alert.wy-alert-info .hint, .tip, .important @extend .wy-alert.wy-alert-success .error, .danger @extend .wy-alert.wy-alert-danger .warning, .caution, .attention, .admonition-todo @extend .wy-alert.wy-alert-warning // Some people put tables in notes. Let's give them very basic support. .admonition table border-color: rgba(0,0,0,.1) td, th background: transparent !important border-color: rgba(0,0,0,.1) !important .section ul, .toctree-wrapper ul @extend .wy-plain-list-disc .section ol.loweralpha, .section ol.loweralpha li list-style: lower-alpha .section ol.upperalpha, .section ol.upperalpha li list-style: upper-alpha .section ol, ol.arabic @extend .wy-plain-list-decimal .section ol p, .section ul p margin-bottom: $base-line-height / 2 .line-block margin-left: $base-line-height // Generics handling of headings and toc stuff. .topic-title font-weight: bold margin-bottom: $base-line-height / 2 .toc-backref color: $text-color .align-right float: right margin: 0px 0px $base-line-height $base-line-height .align-left float: left margin: 0px $base-line-height $base-line-height 0px .align-center margin: auto display: block // This is the #href that shows up on hover. Sphinx's is terrible so I hack it away. h1, h2, h3, h4, h5, h6, dl dt .headerlink display: none visibility: hidden font-size: 14px @extend .fa &:after visibility: visible content: "\f0c1" font-family: FontAwesome display: inline-block &:hover .headerlink display: inline-block // Sidebar content. You'll see at the bottom of this file I change it in mobile. .sidebar float: right width: 40% display: block margin: 0 0 $base-line-height $base-line-height padding: $base-line-height background: $table-stripe-color border: solid 1px $table-border-color // Sidebar content is usually less relevant, so adjust the margins and sizes. p, ul, dl font-size: 90% .last margin-bottom: 0 .sidebar-title display: block font-family: $custom-font-family font-weight: bold background: $table-border-color padding: $base-line-height / 4 $base-line-height / 2 margin: -$base-line-height margin-bottom: $base-line-height font-size: 100% // Sphinx can highlight searched text with ?highlighted=searchterm .highlighted background: $yellow display: inline-block font-weight: bold padding: 0 $base-line-height / 4 // These are the little citation links [1] that show up within paragraphs. .footnote-reference, .citation-reference vertical-align: super font-size: 90% // Tables! Sphinx LOVES TABLES. Most of wyrm assumes you're only going to use a table as a table // so I have to write a bunch of unique stuff for Sphinx to style them up differently like it's 2003. table.docutils.citation, table.docutils.footnote background: none border: none color: $gray-light td, tr border: none background-color: transparent !important white-space: normal td.label padding-left: 0 padding-right: 0 vertical-align: top table.docutils @extend .wy-table @extend .wy-table-bordered-all &:not(.field-list) @extend .wy-table-striped // This table is what gets spit out for auto-generated API stuff. I style it smaller bits of padding. table.field-list @extend .wy-table border: none td border: none padding-top: 5px td > strong display: inline-block margin-top: 3px .field-name padding-right: 10px text-align: left white-space: nowrap .field-body text-align: left padding-left: 0 // These are the "literals" that get spit out when you mark stuff as ``code`` as your write. tt @extend code color: $black big, em font-size: 100% !important line-height: normal .xref, a & font-weight: bold // If the literal is inside an a tag, let's color it like a link a tt color: $link-color dl margin-bottom: $base-line-height dt font-weight: bold // Most of the content within these dls are one liners, so I halve the normal margins. p, table, ul, ol margin-bottom: $base-line-height / 2 !important // rST seems to want dds to be treated as the browser would, indented. dd margin: 0 0 $base-line-height / 2 $base-line-height // This is what Sphinx spits out for it's autodocs. Depending upon what language the person is referencing // these things usually have a class of "method" or "class" or something similar, but really who knows. // Sphinx doesn't give me a generic class on these, so unfortunately I have to apply it to the root dl. // This makes me terribly unhappy and makes this code very nesty. Unfortunately I've seen hand-written docs // that output similar, but not quite the same nesting so this is really the best we can do. dl:not(.docutils) margin-bottom: $base-line-height // This would be the equivilant of a .. class:: dt display: inline-block margin: $base-line-height / 4 0 font-size: 90% line-height: normal background: lighten($blue, 50%) color: $blue border-top: solid 3px lighten($blue, 20%) padding: $base-line-height / 4 position: relative &:before color: lighten($blue, 20%) .headerlink color: $text-color font-size: 100% !important // And this would be the .. method:: dl dt margin-bottom: $base-line-height / 4 border: none border-left: solid 3px hsl(0,0%,80%) background: hsl(0,0%,94%) color: $text-medium .headerlink color: $text-color font-size: 100% !important dt:first-child margin-top: 0 // Since dts get the callout style, we treat this less as callouts. tt font-weight: bold &.descname, &.descclassname background-color: transparent border: none padding: 0 font-size: 100% !important &.descname font-weight: bold // This is for more advanced parameter control .optional display: inline-block padding: 0 4px color: $black font-weight: bold .property display: inline-block padding-right: 8px // Doc links to sourcecode .viewcode-link, .viewcode-back display: inline-block color: $green font-size: 80% padding-left: $base-line-height .viewcode-back display: block float: right p.rubric margin-bottom: 12px font-weight: bold // Mobile specific +media($mobile) .rst-content .sidebar width: 100% ================================================ FILE: documentation/website/original-theme/sass/_theme_variables.sass ================================================ // In here are varibles used for sphinx_rtd_theme, they either add to or overwrite the default ones // that are set in wyrm_core/wy_variables.sass. You'll find wyrm in bower_components if you're looking // for a reference. $font-awesome-dir: "../font/" $static-img: "../img/" $mathjax-color: $text-color $base-font-family: "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif $custom-font-family: "Roboto Slab", "ff-tisa-web-pro", "Georgia", Arial, sans-serif $custom-font-family2: Georgia, serif $code-font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace ================================================ FILE: documentation/website/original-theme/sass/badge_only.sass ================================================ // ------------------------------------------------------------ // CONTRIBUTORS, PLEASE READ THIS! // ------------------------------------------------------------ // This generates the RTD sticky badge for non RTD themes. As // always, only files labeled "theme_*.sass should be edited". // ------------------------------------------------------------ $border-box-sizing: false !default @import wyrm_core/wy_variables @import "theme_variables" @import bourbon @import neat @import wyrm_core/mixin @import wyrm_core/grid_settings @import "theme_badge_fa" @import "theme_badge" ================================================ FILE: documentation/website/original-theme/sass/theme.sass ================================================ // ------------------------------------------------------------ // CONTRIBUTORS, PLEASE READ THIS! // ------------------------------------------------------------ // This theme pulls from other frontend projects. The only // things you should edit are the sass files that start with // "theme_*.sass". All other files are loaded through bower. // ------------------------------------------------------------ // Variable defaults set by Wyrm @import wyrm_core/wy_variables // Variable overrides that change coloring and fonts for this theme. @import "theme_variables" // bourbon.io framework @import bourbon // Bourbon.io/neat framework, with some default media queries @import wyrm_core/grid_settings @import neat // Some corrections for neat @import wyrm_core/neat_extra // Custom reset @import wyrm_core/reset // Wyrm mixins @import wyrm_core/mixin // Font Awesome 4.0 with wyrm extras @import font-awesome @import wyrm_core/font_icon_defaults // Wyrm core styles used in this theme @import wyrm_core/alert @import wyrm_core/button @import wyrm_core/dropdown @import wyrm_core/form @import wyrm_core/generic @import wyrm_core/table @import wyrm_core/type // Pygments styling @import wyrm_addons/pygments/pygments @import wyrm_addons/pygments/pygments_light // Theme specific styles. These are likely the files you want to edit. @import "theme_breadcrumbs" @import "theme_layout" @import "theme_badge" @import "theme_rst" @import "theme_mathjax" @import "theme_font_awesome_compatibility" ================================================ FILE: gulpfile.js ================================================ 'use strict'; var gulp = require('gulp'); var plugins = require('gulp-load-plugins')(); var sass = require('gulp-sass'); var pkg = require('./package'); var pump = require('pump'); var scripts = { name: 'jquery.contextMenu.js', min: 'jquery.contextMenu.min.js', all: [ 'gulpfile.js', 'src/jquery.contextMenu.js', 'dist/jquery.contextMenu.js' ], main: 'dist/jquery.contextMenu.js', src: [ 'src/jquery.contextMenu.js' ], dest: 'dist', libs: [ ] }; var styles = { name: 'jquery.contextMenu.css', min: 'jquery.contextMenu.min.css', all: [ 'src/sass/**/*.scss' ], main: 'dist/jquery.contextMenu.css', src: 'src/sass/jquery.contextMenu.scss', dest: 'dist' }; var icons = { src: 'src/icons/*.svg', templateFileFont: 'src/sass/icons/_variables.scss.tpl', templateFileIconClasses: 'src/sass/icons/_icon_classes.scss.tpl', fontOutputPath: 'dist/font', scssOutputPath: 'src/sass/icons/' }; var replacement = { regexp: /@\w+/g, filter: function (placeholder) { switch (placeholder) { case '@VERSION': placeholder = pkg.version; break; case '@YEAR': placeholder = (new Date()).getFullYear(); break; case '@DATE': placeholder = (new Date()).toISOString(); break; } return placeholder; } }; gulp.task('jshint', function (cb) { pump([ gulp.src(scripts.src), plugins.jshint('src/.jshintrc'), plugins.jshint.reporter('default') ],cb); }); gulp.task('jsdist', function (cb) { pump([ gulp.src(scripts.src), plugins.sourcemaps.init(), plugins.replace(replacement.regexp, replacement.filter), gulp.dest(scripts.dest), plugins.rename(scripts.min), plugins.uglify(), plugins.sourcemaps.write('.'), gulp.dest(scripts.dest) ], cb); }); gulp.task('jslibs', function (cb){ pump([ gulp.src(scripts.libs), plugins.rename({prefix: 'jquery.ui.'}), gulp.dest('src'), gulp.dest('dist'), plugins.rename({extname: '.min.js'}), gulp.dest('dist'), plugins.uglify(), plugins.sourcemaps.write('.'), gulp.dest(scripts.dest) ], cb); }); gulp.task('css', function (cb) { return pump([ gulp.src(styles.src), sass(), plugins.csslint('src/.csslintrc'), plugins.csslint.formatter(), plugins.sourcemaps.init(), plugins.replace(replacement.regexp, replacement.filter), plugins.autoprefixer(), plugins.csscomb('src/.csscomb.json'), plugins.rename(styles.name), gulp.dest(styles.dest), plugins.rename(styles.min), plugins.cleanCss(), plugins.sourcemaps.write('.'), gulp.dest(styles.dest) ], cb); }); gulp.task('build-icons', function (done) { var iconfont = require('gulp-iconfont'); var consolidate = require('gulp-consolidate'); return gulp.src(icons.src) .pipe(iconfont({ fontName: 'context-menu-icons', fontHeight: 1024, descent: 64, normalize: true, appendCodepoints: false, startCodepoint: 0xE001, formats: ['ttf', 'eot', 'woff', 'woff2'] })) .on('glyphs', function (glyphs) { var options = { glyphs: glyphs, className: 'context-menu-icon', mixinName: 'context-menu-item-icon' }; gulp.src(icons.templateFileFont) .pipe(consolidate('lodash', options)) .pipe(plugins.rename({basename: '_variables', extname: '.scss'})) .pipe(gulp.dest(icons.scssOutputPath)); gulp.src(icons.templateFileIconClasses) .pipe(consolidate('lodash', options)) .pipe(plugins.rename('_icons.scss')) .pipe(gulp.dest('src/sass')); // set path to export your sample HTML }) .pipe(gulp.dest(icons.fontOutputPath)); }); /** * Update paths in integration tests that are generated by the documentation * generator so they use the local source. */ gulp.task('integration-test-paths', function(){ return gulp.src('test/integration/html/*.html'). pipe(plugins.replace('https\:\/\/swisnl\.github\.io\/jQuery-contextMenu\/dist\/jquery\.ui\.position\.min\.js', '\.\.\/\.\.\/\.\.\/dist\/jquery\.ui\.position\.min\.js')). pipe(plugins.replace('https\:\/\/swisnl\.github\.io\/jQuery\-contextMenu\/dist\/', '\.\.\/\.\.\/\.\.\/src\/')). pipe(plugins.replace('\/src\/jquery.contextMenu.css', '\/dist\/jquery.contextMenu.css')). pipe(gulp.dest('test/integration/html/')); }); gulp.task('js', gulp.series('jshint', 'jsdist', (done) => { done(); })); gulp.task('watch', gulp.parallel('js', 'css', function (done) { gulp.watch(scripts.src,gulp.series('js')); gulp.watch(styles.all, gulp.series('css')); done(); })); gulp.task('build', gulp.series('build-icons', 'css', 'js', 'integration-test-paths', (done) => { done(); })); gulp.task('default', gulp.series('watch', (done) => { done(); })); ================================================ FILE: karma-saucelabs.conf.js ================================================ module.exports = function (config) { if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) { console.log('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are set.') process.exit(1) } var testedCapabilities = {}; var browsers = []; var capabilities = { 'Windows 7': { 'internet explorer': ['11', '10', '9'], }, 'Windows 10': { 'firefox': ['latest', 'latest-1'], 'chrome': ['latest', 'latest-1'], 'MicrosoftEdge': ['latest'] }, 'macOS 10.12': { 'firefox': ['latest'], 'chrome': ['latest'], 'safari': ['latest'] } }; var buildDate = new Date().toISOString(); for (var osVersion in capabilities) { for (var browserKey in capabilities[osVersion]) { for(var i=0; i< capabilities[osVersion][browserKey].length; i++){ var browserVersion = capabilities[osVersion][browserKey][i]; testedCapabilities[osVersion + ' ' + browserKey + ' ' + browserVersion] = { base: 'SauceLabs', platform: osVersion, browserName: browserKey, version: browserVersion, name: osVersion + ' ' + browserKey + ' ' + browserVersion, build: buildDate }; } if(browsers.indexOf(browserKey) == -1){ browsers.push(browsers); } } } config.set({ basePath: '', // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['qunit'], // list of files / patterns to load in the browser files: [ // dependencies { pattern: 'node_modules/jquery/dist/jquery.js', watched: false, served: true, included: true }, { pattern: 'src/jquery.ui.position.js', watched: false, served: true, included: true }, { pattern: 'src/jquery.contextMenu.js', watched: false, served: true, included: true }, // test modules 'test/unit/*.js' ], reporters: ['dots', 'saucelabs'], port: 9876, colors: true, sauceLabs: { testName: 'jQuery contextMenu saucelabs', recordScreenshots: false, public: 'public' }, // Increase timeout in case connection in CI is slow captureTimeout: 600000, customLaunchers: testedCapabilities, browsers: Object.keys(testedCapabilities), singleRun: true }) } ================================================ FILE: karma.conf.js ================================================ // Karma configuration // Generated on Wed Oct 29 2014 01:56:16 GMT+0100 (CET) module.exports = function(config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '', // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['qunit'], // list of files / patterns to load in the browser files: [ // dependencies { pattern: 'node_modules/jquery/dist/jquery.js', watched: false, served: true, included: true }, { pattern: 'src/jquery.ui.position.js', watched: false, served: true, included: true }, { pattern: 'src/jquery.contextMenu.js', watched: false, served: true, included: true }, // test modules 'test/unit/*.js' ], // list of files to exclude exclude: [ ], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { }, // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter reporters: ['dots'], // web server port port: 9876, // enable / disable colors in the output (reporters and logs) colors: true, // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes autoWatch: false, // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher browsers: ['PhantomJS'], plugins : [ 'karma-phantomjs-launcher', 'karma-qunit' ], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits singleRun: true }); }; ================================================ FILE: package.json ================================================ { "name": "jquery-contextmenu", "title": "jQuery.contextMenu()", "version": "2.10.1", "author": { "name": "Björn Brala", "url": "http://www.swis.nl" }, "license": "MIT", "peerDependencies": { "jquery": ">=1.8.2" }, "devDependencies": { "doctoc": "^1.3.1", "gulp": "^4.0.2", "gulp-autoprefixer": "^7.0.1", "gulp-clean-css": "^4.2.0", "gulp-concat": "~2.6.0", "gulp-consolidate": "^0.2.0", "gulp-csscomb": "^3.0.8", "gulp-csslint": "^1.0.1", "gulp-debug": "^4.0.0", "gulp-dest": "^0.2.3", "gulp-htmlcomb": "^0.2.0", "gulp-iconfont": "^10.0.1", "gulp-jscs": "^4.1.0", "gulp-jshint": "^2.1.0", "gulp-load-plugins": "^2.0.1", "gulp-qunit": "^2.0.0", "gulp-rename": "^2.0.0", "gulp-replace": "^1.0.0", "gulp-sass": "^4.0.1", "gulp-sourcemaps": "^2.6.4", "gulp-uglify": "^3.0.0", "jshint": "^2.9.4", "karma": "^5.0.2", "karma-chrome-launcher": "^3.1.0", "karma-phantomjs-launcher": "^1.0.2", "karma-qunit": "^4.0.0", "karma-sauce-launcher": "4.1.3", "lodash": "^4.17.5", "pump": "^3.0.0", "qunit": "^2.0.1", "qunitjs": "^2.0.1", "wdio-dot-reporter": "^0.0.10", "wdio-mocha-framework": "^0.6.2", "wdio-sauce-service": "^0.4.8", "wdio-selenium-standalone-service": "^0.0.12", "webdriverio": "^6.1.3" }, "description": "Management facility for context menus. Developed for a large number of triggering objects. HTML5 Polyfill", "keywords": [ "contextmenu", "context menu", "context-menu", "menu", "html5 menu", "html5 contextmenu", "html5 context menu", "html5 context-menu", "jquery-plugin", "ecosystem:jquery" ], "homepage": "http://swisnl.github.io/jQuery-contextMenu/", "contributors": [ { "name": "Rodney Rehm", "url": "http://rodneyrehm.de" }, { "name": "Addy Osmani", "url": "https://github.com/addyosmani" }, { "name": "Christiaan Baartse", "url": "https://github.com/christiaan" } ], "repository": { "type": "git", "url": "git://github.com/swisnl/jQuery-contextMenu.git" }, "main": "dist/jquery.contextMenu.js", "files": [ "dist", "src" ], "scripts": { "test": "npm run test-unit", "test-unit": "./node_modules/karma/bin/karma start", "test-sauce": "./node_modules/karma/bin/karma start karma-saucelabs.conf.js", "test-accept": "./node_modules/.bin/wdio wdio.conf.js" }, "browserslist": [ "Android 2.3", "Android >= 4", "Chrome >= 20", "Firefox >= 24", "Explorer >= 8", "iOS >= 6", "Opera >= 12", "Safari >= 6" ], "dependencies": { "jquery": "^3.5.0" } } ================================================ FILE: src/.csscomb.json ================================================ { "always-semicolon": true, "block-indent": 2, "color-case": "lower", "color-shorthand": true, "element-case": "lower", "eof-newline": true, "leading-zero": false, "remove-empty-rulesets": true, "space-after-colon": 1, "space-after-combinator": 1, "space-before-selector-delimiter": 0, "space-between-declarations": "\n", "space-after-opening-brace": "\n", "space-before-closing-brace": "\n", "space-before-colon": 0, "space-before-combinator": 1, "space-before-opening-brace": 1, "strip-spaces": true, "unitless-zero": true, "vendor-prefix-align": true, "sort-order": [ [ "position", "top", "right", "bottom", "left", "z-index", "display", "float", "width", "min-width", "max-width", "height", "min-height", "max-height", "-webkit-box-sizing", "-moz-box-sizing", "box-sizing", "-webkit-appearance", "padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "overflow", "overflow-x", "overflow-y", "-webkit-overflow-scrolling", "-ms-overflow-x", "-ms-overflow-y", "-ms-overflow-style", "clip", "clear", "font", "font-family", "font-size", "font-style", "font-weight", "font-variant", "font-size-adjust", "font-stretch", "font-effect", "font-emphasize", "font-emphasize-position", "font-emphasize-style", "font-smooth", "-webkit-hyphens", "-moz-hyphens", "hyphens", "line-height", "color", "text-align", "-webkit-text-align-last", "-moz-text-align-last", "-ms-text-align-last", "text-align-last", "text-emphasis", "text-emphasis-color", "text-emphasis-style", "text-emphasis-position", "text-decoration", "text-indent", "text-justify", "text-outline", "-ms-text-overflow", "text-overflow", "text-overflow-ellipsis", "text-overflow-mode", "text-shadow", "text-transform", "text-wrap", "-webkit-text-size-adjust", "-ms-text-size-adjust", "letter-spacing", "-ms-word-break", "word-break", "word-spacing", "-ms-word-wrap", "word-wrap", "-moz-tab-size", "-o-tab-size", "tab-size", "white-space", "vertical-align", "list-style", "list-style-position", "list-style-type", "list-style-image", "pointer-events", "-ms-touch-action", "touch-action", "cursor", "visibility", "zoom", "flex-direction", "flex-order", "flex-pack", "flex-align", "table-layout", "empty-cells", "caption-side", "border-spacing", "border-collapse", "content", "quotes", "counter-reset", "counter-increment", "resize", "-webkit-user-select", "-moz-user-select", "-ms-user-select", "-o-user-select", "user-select", "nav-index", "nav-up", "nav-right", "nav-down", "nav-left", "background", "background-color", "background-image", "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", "filter:progid:DXImageTransform.Microsoft.gradient", "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", "filter", "background-repeat", "background-attachment", "background-position", "background-position-x", "background-position-y", "-webkit-background-clip", "-moz-background-clip", "background-clip", "background-origin", "-webkit-background-size", "-moz-background-size", "-o-background-size", "background-size", "border", "border-color", "border-style", "border-width", "border-top", "border-top-color", "border-top-style", "border-top-width", "border-right", "border-right-color", "border-right-style", "border-right-width", "border-bottom", "border-bottom-color", "border-bottom-style", "border-bottom-width", "border-left", "border-left-color", "border-left-style", "border-left-width", "border-radius", "border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius", "-webkit-border-image", "-moz-border-image", "-o-border-image", "border-image", "-webkit-border-image-source", "-moz-border-image-source", "-o-border-image-source", "border-image-source", "-webkit-border-image-slice", "-moz-border-image-slice", "-o-border-image-slice", "border-image-slice", "-webkit-border-image-width", "-moz-border-image-width", "-o-border-image-width", "border-image-width", "-webkit-border-image-outset", "-moz-border-image-outset", "-o-border-image-outset", "border-image-outset", "-webkit-border-image-repeat", "-moz-border-image-repeat", "-o-border-image-repeat", "border-image-repeat", "outline", "outline-width", "outline-style", "outline-color", "outline-offset", "-webkit-box-shadow", "-moz-box-shadow", "box-shadow", "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", "opacity", "-ms-interpolation-mode", "-webkit-transition", "-moz-transition", "-ms-transition", "-o-transition", "transition", "-webkit-transition-delay", "-moz-transition-delay", "-ms-transition-delay", "-o-transition-delay", "transition-delay", "-webkit-transition-timing-function", "-moz-transition-timing-function", "-ms-transition-timing-function", "-o-transition-timing-function", "transition-timing-function", "-webkit-transition-duration", "-moz-transition-duration", "-ms-transition-duration", "-o-transition-duration", "transition-duration", "-webkit-transition-property", "-moz-transition-property", "-ms-transition-property", "-o-transition-property", "transition-property", "-webkit-transform", "-moz-transform", "-ms-transform", "-o-transform", "transform", "-webkit-transform-origin", "-moz-transform-origin", "-ms-transform-origin", "-o-transform-origin", "transform-origin", "-webkit-animation", "-moz-animation", "-ms-animation", "-o-animation", "animation", "-webkit-animation-name", "-moz-animation-name", "-ms-animation-name", "-o-animation-name", "animation-name", "-webkit-animation-duration", "-moz-animation-duration", "-ms-animation-duration", "-o-animation-duration", "animation-duration", "-webkit-animation-play-state", "-moz-animation-play-state", "-ms-animation-play-state", "-o-animation-play-state", "animation-play-state", "-webkit-animation-timing-function", "-moz-animation-timing-function", "-ms-animation-timing-function", "-o-animation-timing-function", "animation-timing-function", "-webkit-animation-delay", "-moz-animation-delay", "-ms-animation-delay", "-o-animation-delay", "animation-delay", "-webkit-animation-iteration-count", "-moz-animation-iteration-count", "-ms-animation-iteration-count", "-o-animation-iteration-count", "animation-iteration-count", "-webkit-animation-direction", "-moz-animation-direction", "-ms-animation-direction", "-o-animation-direction", "animation-direction" ] ] } ================================================ FILE: src/.csslintrc ================================================ { "adjoining-classes": false, "box-sizing": false, "box-model": false, "compatible-vendor-prefixes": false, "floats": false, "font-sizes": false, "gradients": false, "important": false, "known-properties": false, "outline-none": false, "qualified-headings": false, "regex-selectors": false, "shorthand": false, "text-indent": false, "zero-units": false, "unique-headings": false, "universal-selector": false, "unqualified-attributes": false } ================================================ FILE: src/.jshintrc ================================================ { "bitwise": true, "curly": true, "eqeqeq": true, "forin": true, "freeze": true, "funcscope": true, "iterator": true, "latedef": true, "noarg": true, "nocomma": false, "noempty": true, "nonbsp": true, "nonew": true, "undef": true, "unused": true, "strict": false, "node": true, "browser": true, "jquery": true, "qunit": true, "globals": { "define": true } } ================================================ FILE: src/jquery.contextMenu.js ================================================ /** * jQuery contextMenu v@VERSION - Plugin for simple contextMenu handling * * Version: v@VERSION * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * * Copyright (c) 2011-@YEAR SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * * Date: @DATE */ // jscs:disable /* jshint ignore:start */ (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as anonymous module. define(['jquery'], factory); } else if (typeof exports === 'object') { // Node / CommonJS factory(require('jquery')); } else { // Browser globals. factory(jQuery); } })(function ($) { 'use strict'; // helper function to check for rapid interactions after menu display var isInteractionTooFast = function($element) { if (!('ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0)) { return false; } var interactionTime = Date.now(); var $liItem = $element.is('input, textarea, select') ? $element.closest('.context-menu-item') : $element; if (!$liItem || !$liItem.length) { return false; } var $parentMenu = $liItem.parent(); if (!$parentMenu || !$parentMenu.length) { return false; } // only apply the check for items within submenus if ($parentMenu.hasClass('context-menu-root')) { return false; } var showTimestamp = $parentMenu.data('_showTimestamp'); var timeDifference = showTimestamp ? interactionTime - showTimestamp : Infinity; // threshold for fast interaction (e.g., mobile tap) var threshold = 50; // ms return timeDifference < threshold; }; // TODO: - // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio // create structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative // determine html5 compatibility $.support.htmlMenuitem = ('HTMLMenuItemElement' in window); $.support.htmlCommand = ('HTMLCommandElement' in window); $.support.eventSelectstart = ('onselectstart' in document.documentElement); /* // should the need arise, test for css user-select $.support.cssUserSelect = (function(){ var t = false, e = document.createElement('div'); $.each('Moz|Webkit|Khtml|O|ms|Icab|'.split('|'), function(i, prefix) { var propCC = prefix + (prefix ? 'U' : 'u') + 'serSelect', prop = (prefix ? ('-' + prefix.toLowerCase() + '-') : '') + 'user-select'; e.style.cssText = prop + ': text;'; if (e.style[propCC] == 'text') { t = true; return false; } return true; }); return t; })(); */ if (!$.ui || !$.widget) { // duck punch $.cleanData like jQueryUI does to get that remove event $.cleanData = (function (orig) { return function (elems) { var events, elem, i; for (i = 0; elems[i] != null; i++) { elem = elems[i]; try { // Only trigger remove when necessary to save time events = $._data(elem, 'events'); if (events && events.remove) { $(elem).triggerHandler('remove'); } // Http://bugs.jquery.com/ticket/8235 } catch (e) { } } orig(elems); }; })($.cleanData); } /* jshint ignore:end */ // jscs:enable var // currently active contextMenu trigger $currentTrigger = null, // is contextMenu initialized with at least one menu? initialized = false, // window handle $win = $(window), // number of registered menus counter = 0, // mapping selector to namespace namespaces = {}, // mapping namespace to options menus = {}, // custom command type handlers types = {}, // default values defaults = { // selector of contextMenu trigger selector: null, // where to append the menu to appendTo: null, // method to trigger context menu ["right", "left", "hover"] trigger: 'right', // hide menu when mouse leaves trigger / menu elements autoHide: false, // ms to wait before showing a hover-triggered context menu delay: 200, // flag denoting if a second trigger should simply move (true) or rebuild (false) an open menu // as long as the trigger happened on one of the trigger-element's child nodes reposition: true, // Flag denoting if a second trigger should close the menu, as long as // the trigger happened on one of the trigger-element's child nodes. // This overrides the reposition option. hideOnSecondTrigger: false, // use a modal layer for closing the menu rather than a captured event on document useModal: true, //ability to select submenu selectableSubMenu: false, // Default classname configuration to be able avoid conflicts in frameworks classNames: { hover: 'context-menu-hover', // Item hover disabled: 'context-menu-disabled', // Item disabled visible: 'context-menu-visible', // Item visible notSelectable: 'context-menu-not-selectable', // Item not selectable icon: 'context-menu-icon', iconEdit: 'context-menu-icon-edit', iconCut: 'context-menu-icon-cut', iconCopy: 'context-menu-icon-copy', iconPaste: 'context-menu-icon-paste', iconDelete: 'context-menu-icon-delete', iconAdd: 'context-menu-icon-add', iconQuit: 'context-menu-icon-quit', iconLoadingClass: 'context-menu-icon-loading' }, // determine position to show menu at determinePosition: function ($menu) { // position to the lower middle of the trigger element if ($.ui && $.ui.position) { // .position() is provided as a jQuery UI utility // (...and it won't work on hidden elements) $menu.css('display', 'block').position({ my: 'center top', at: 'center bottom', of: this, offset: '0 5', collision: 'fit' }).css('display', 'none'); } else { // determine contextMenu position var offset = this.offset(); offset.top += this.outerHeight(); offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2; $menu.css(offset); } }, // position menu position: function (opt, x, y) { var offset; // determine contextMenu position if (!x && !y) { opt.determinePosition.call(this, opt.$menu); return; } else if (x === 'maintain' && y === 'maintain') { // x and y must not be changed (after re-show on command click) offset = opt.$menu.position(); } else { // x and y are given (by mouse event) var offsetParentOffset = opt.$menu.offsetParent().offset(); offset = {top: y - offsetParentOffset.top, left: x -offsetParentOffset.left}; } // correct offset if viewport demands it var bottom = $win.scrollTop() + $win.height(), right = $win.scrollLeft() + $win.width(), height = opt.$menu.outerHeight(), width = opt.$menu.outerWidth(); if (offset.top + height > bottom) { offset.top -= height; } if (offset.top < 0) { offset.top = 0; } if (offset.left + width > right) { offset.left -= width; } if (offset.left < 0) { offset.left = 0; } opt.$menu.css(offset); }, // position the sub-menu positionSubmenu: function ($menu) { if (typeof $menu === 'undefined') { // When user hovers over item (which has sub items) handle.focusItem will call this. // but the submenu does not exist yet if opt.items is a promise. just return, will // call positionSubmenu after promise is completed. return; } if ($.ui && $.ui.position) { // .position() is provided as a jQuery UI utility // (...and it won't work on hidden elements) $menu.css('display', 'block').position({ my: 'left top-5', at: 'right top', of: this, collision: 'flipfit fit' }).css('display', ''); } else { // determine contextMenu position var offset = { top: -9, left: this.outerWidth() - 5 }; $menu.css(offset); } }, // offset to add to zIndex zIndex: 1, // show hide animation settings animation: { duration: 50, show: 'slideDown', hide: 'slideUp' }, // events events: { preShow: $.noop, show: $.noop, hide: $.noop, activated: $.noop }, // default callback callback: null, // list of contextMenu items items: {} }, // mouse position for hover activation hoveract = { timer: null, pageX: null, pageY: null }, // determine zIndex zindex = function ($t) { var zin = 0, $tt = $t; while (true) { zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0); $tt = $tt.parent(); if (!$tt || !$tt.length || 'html body'.indexOf($tt.prop('nodeName').toLowerCase()) > -1) { break; } } return zin; }, // event handlers handle = { // abort anything abortevent: function (e) { e.preventDefault(); e.stopImmediatePropagation(); }, // contextmenu show dispatcher contextmenu: function (e) { var $this = $(this); //Show browser context-menu when preShow returns false if (e.data.events.preShow($this,e) === false) { return; } // disable actual context-menu if we are using the right mouse button as the trigger if (e.data.trigger === 'right') { e.preventDefault(); e.stopImmediatePropagation(); } // abort native-triggered events unless we're triggering on right click if ((e.data.trigger !== 'right' && e.data.trigger !== 'demand') && e.originalEvent) { return; } // Let the current contextmenu decide if it should show or not based on its own trigger settings if (typeof e.mouseButton !== 'undefined' && e.data) { if (!(e.data.trigger === 'left' && e.mouseButton === 0) && !(e.data.trigger === 'right' && e.mouseButton === 2)) { // Mouse click is not valid. return; } } // abort event if menu is visible for this trigger if ($this.hasClass('context-menu-active')) { return; } if (!$this.hasClass('context-menu-disabled')) { // theoretically need to fire a show event at // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this }); // e.data.$menu.trigger(evt); $currentTrigger = $this; if (e.data.build) { var built = e.data.build($currentTrigger, e); // abort if build() returned false if (built === false) { return; } // dynamically build menu on invocation e.data = $.extend(true, {}, defaults, e.data, built || {}); // abort if there are no items to display if (!e.data.items || $.isEmptyObject(e.data.items)) { // Note: jQuery captures and ignores errors from event handlers if (window.console) { (console.error || console.log).call(console, 'No items specified to show in contextMenu'); } throw new Error('No Items specified'); } // backreference for custom command type creation e.data.$trigger = $currentTrigger; op.create(e.data); } op.show.call($this, e.data, e.pageX, e.pageY); } }, // contextMenu left-click trigger click: function (e) { e.preventDefault(); e.stopImmediatePropagation(); $(this).trigger($.Event('contextmenu', {data: e.data, pageX: e.pageX, pageY: e.pageY})); }, // contextMenu right-click trigger mousedown: function (e) { // register mouse down var $this = $(this); // hide any previous menus if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) { $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide'); } // activate on right click if (e.button === 2) { $currentTrigger = $this.data('contextMenuActive', true); } }, // contextMenu right-click trigger mouseup: function (e) { // show menu var $this = $(this); if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) { e.preventDefault(); e.stopImmediatePropagation(); $currentTrigger = $this; $this.trigger($.Event('contextmenu', {data: e.data, pageX: e.pageX, pageY: e.pageY})); } $this.removeData('contextMenuActive'); }, // contextMenu hover trigger mouseenter: function (e) { var $this = $(this), $related = $(e.relatedTarget), $document = $(document); // abort if we're coming from a menu if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { return; } // abort if a menu is shown if ($currentTrigger && $currentTrigger.length) { return; } hoveract.pageX = e.pageX; hoveract.pageY = e.pageY; hoveract.data = e.data; $document.on('mousemove.contextMenuShow', handle.mousemove); hoveract.timer = setTimeout(function () { hoveract.timer = null; $document.off('mousemove.contextMenuShow'); $currentTrigger = $this; $this.trigger($.Event('contextmenu', { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY })); }, e.data.delay); }, // contextMenu hover trigger mousemove: function (e) { hoveract.pageX = e.pageX; hoveract.pageY = e.pageY; }, // contextMenu hover trigger mouseleave: function (e) { // abort if we're leaving for a menu var $related = $(e.relatedTarget); if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { return; } try { clearTimeout(hoveract.timer); } catch (e) { } hoveract.timer = null; }, // click on layer to hide contextMenu layerClick: function (e, opt, onhide) { var $this = $(this), root = (opt !== undefined) ? opt : $this.data('contextMenuRoot'), button = e.button, x = e.pageX, y = e.pageY, fakeClick = x === undefined, target, offset; // If the click is not real, things break: https://github.com/swisnl/jQuery-contextMenu/issues/132 if(fakeClick){ if (root !== null && typeof root !== 'undefined' && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); } return; } // if the click closing is done through windwow event listener rather than a transparent layer if (!root.$layer) { target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop()); if (root.$menu === null || typeof root.$menu === 'undefined' || !root.$menu[0].contains(target)) { root.$menu.trigger('contextmenu:hide'); if (typeof onhide !== 'undefined') onhide(); } return; } e.preventDefault(); setTimeout(function () { var $window; var triggerAction = ((root.trigger === 'left' && button === 0) || (root.trigger === 'right' && button === 2)); // find the element that would've been clicked, wasn't the layer in the way if (document.elementFromPoint && root.$layer) { root.$layer.hide(); target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop()); // also need to try and focus this element if we're in a contenteditable area, // as the layer will prevent the browser mouse action we want if (target !== null && target.isContentEditable) { var range = document.createRange(), sel = window.getSelection(); range.selectNode(target); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } $(target).trigger(e); root.$layer.show(); } if (root.hideOnSecondTrigger && triggerAction && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); return; } if (root.reposition && triggerAction) { if (document.elementFromPoint) { if (root.$trigger.is(target)) { root.position.call(root.$trigger, root, x, y); return; } } else { offset = root.$trigger.offset(); $window = $(window); // while this looks kinda awful, it's the best way to avoid // unnecessarily calculating any positions offset.top += $window.scrollTop(); if (offset.top <= e.pageY) { offset.left += $window.scrollLeft(); if (offset.left <= e.pageX) { offset.bottom = offset.top + root.$trigger.outerHeight(); if (offset.bottom >= e.pageY) { offset.right = offset.left + root.$trigger.outerWidth(); if (offset.right >= e.pageX) { // reposition root.position.call(root.$trigger, root, x, y); return; } } } } } } if (target && triggerAction) { root.$trigger.one('contextmenu:hidden', function () { $(target).contextMenu({x: x, y: y, button: button}); }); } if (root !== null && typeof root !== 'undefined' && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); } }, 50); }, // key handled :hover keyStop: function (e, opt) { if (!opt.isInput) { e.preventDefault(); } e.stopPropagation(); }, key: function (e) { var opt = {}; // Only get the data from $currentTrigger if it exists if ($currentTrigger) { opt = $currentTrigger.data('contextMenu') || {}; } // If the trigger happen on a element that are above the contextmenu do this if (typeof opt.zIndex === 'undefined') { opt.zIndex = 0; } var targetZIndex = 0; var getZIndexOfTriggerTarget = function (target) { if (target.style.zIndex !== '') { targetZIndex = target.style.zIndex; } else { if (target.offsetParent !== null && typeof target.offsetParent !== 'undefined') { getZIndexOfTriggerTarget(target.offsetParent); } else if (target.parentElement !== null && typeof target.parentElement !== 'undefined') { getZIndexOfTriggerTarget(target.parentElement); } } }; getZIndexOfTriggerTarget(e.target); // If targetZIndex is heigher then opt.zIndex dont progress any futher. // This is used to make sure that if you are using a dialog with a input / textarea / contenteditable div // and its above the contextmenu it wont steal keys events if (opt.$menu && parseInt(targetZIndex,10) > parseInt(opt.$menu.css("zIndex"),10)) { return; } switch (e.keyCode) { case 9: case 38: // up handle.keyStop(e, opt); // if keyCode is [38 (up)] or [9 (tab) with shift] if (opt.isInput) { if (e.keyCode === 9 && e.shiftKey) { e.preventDefault(); if (opt.$selected) { opt.$selected.find('input, textarea, select').blur(); } if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('prevcommand'); } return; } else if (e.keyCode === 38 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { // checkboxes don't capture this key e.preventDefault(); return; } } else if (e.keyCode !== 9 || e.shiftKey) { if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('prevcommand'); } return; } break; // omitting break; // case 9: // tab - reached through omitted break; case 40: // down handle.keyStop(e, opt); if (opt.isInput) { if (e.keyCode === 9) { e.preventDefault(); if (opt.$selected) { opt.$selected.find('input, textarea, select').blur(); } if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('nextcommand'); } return; } else if (e.keyCode === 40 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { // checkboxes don't capture this key e.preventDefault(); return; } } else { if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('nextcommand'); } return; } break; case 37: // left handle.keyStop(e, opt); if (opt.isInput || !opt.$selected || !opt.$selected.length) { break; } if (!opt.$selected.parent().hasClass('context-menu-root')) { var $parent = opt.$selected.parent().parent(); opt.$selected.trigger('contextmenu:blur'); opt.$selected = $parent; return; } break; case 39: // right handle.keyStop(e, opt); if (opt.isInput || !opt.$selected || !opt.$selected.length) { break; } var itemdata = opt.$selected.data('contextMenu') || {}; if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) { opt.$selected = null; itemdata.$selected = null; itemdata.$menu.trigger('nextcommand'); return; } break; case 35: // end case 36: // home if (opt.$selected && opt.$selected.find('input, textarea, select').length) { return; } else { (opt.$selected && opt.$selected.parent() || opt.$menu) .children(':not(.' + opt.classNames.disabled + ', .' + opt.classNames.notSelectable + ')')[e.keyCode === 36 ? 'first' : 'last']() .trigger('contextmenu:focus'); e.preventDefault(); return; } break; case 13: // enter handle.keyStop(e, opt); if (opt.isInput) { if (opt.$selected && !opt.$selected.is('textarea, select')) { e.preventDefault(); return; } break; } if (typeof opt.$selected !== 'undefined' && opt.$selected !== null) { opt.$selected.trigger('mouseup'); } return; case 32: // space case 33: // page up case 34: // page down // prevent browser from scrolling down while menu is visible handle.keyStop(e, opt); return; case 27: // esc handle.keyStop(e, opt); if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('contextmenu:hide'); } return; default: // 0-9, a-z var k = (String.fromCharCode(e.keyCode)).toUpperCase(); if (opt.accesskeys && opt.accesskeys[k]) { // according to the specs accesskeys must be invoked immediately opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu ? 'contextmenu:focus' : 'mouseup'); return; } break; } // pass event to selected item, // stop propagation to avoid endless recursion e.stopPropagation(); if (typeof opt.$selected !== 'undefined' && opt.$selected !== null) { opt.$selected.trigger(e); } }, // select previous possible command in menu prevItem: function (e) { e.stopPropagation(); var opt = $(this).data('contextMenu') || {}; var root = $(this).data('contextMenuRoot') || {}; // obtain currently selected menu if (opt.$selected) { var $s = opt.$selected; opt = opt.$selected.parent().data('contextMenu') || {}; opt.$selected = $s; } var $children = opt.$menu.children(), $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(), $round = $prev; // skip disabled or hidden elements while ($prev.hasClass(root.classNames.disabled) || $prev.hasClass(root.classNames.notSelectable) || $prev.is(':hidden')) { if ($prev.prev().length) { $prev = $prev.prev(); } else { $prev = $children.last(); } if ($prev.is($round)) { // break endless loop return; } } // leave current if (opt.$selected) { handle.itemMouseleave.call(opt.$selected.get(0), e); } // activate next handle.itemMouseenter.call($prev.get(0), e); // focus input var $input = $prev.find('input, textarea, select'); if ($input.length) { $input.focus(); } }, // select next possible command in menu nextItem: function (e) { e.stopPropagation(); var opt = $(this).data('contextMenu') || {}; var root = $(this).data('contextMenuRoot') || {}; // obtain currently selected menu if (opt.$selected) { var $s = opt.$selected; opt = opt.$selected.parent().data('contextMenu') || {}; opt.$selected = $s; } var $children = opt.$menu.children(), $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(), $round = $next; // skip disabled while ($next.hasClass(root.classNames.disabled) || $next.hasClass(root.classNames.notSelectable) || $next.is(':hidden')) { if ($next.next().length) { $next = $next.next(); } else { $next = $children.first(); } if ($next.is($round)) { // break endless loop return; } } // leave current if (opt.$selected) { handle.itemMouseleave.call(opt.$selected.get(0), e); } // activate next handle.itemMouseenter.call($next.get(0), e); // focus input var $input = $next.find('input, textarea, select'); if ($input.length) { $input.focus(); } }, // flag that we're inside an input so the key handler can act accordingly focusInput: function () { var $this = $(this).closest('.context-menu-item'), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; root.$selected = opt.$selected = $this; root.isInput = opt.isInput = true; }, // flag that we're inside an input so the key handler can act accordingly blurInput: function () { var $this = $(this).closest('.context-menu-item'), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; root.isInput = opt.isInput = false; }, // :hover on menu menuMouseenter: function () { var root = $(this).data().contextMenuRoot; root.hovering = true; }, // :hover on menu menuMouseleave: function (e) { var root = $(this).data().contextMenuRoot; if (root.$layer && root.$layer.is(e.relatedTarget)) { root.hovering = false; } }, // :hover done manually so key handling is possible itemMouseenter: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; // prevent fast hover on mobile tap-through if (isInteractionTooFast($this)) { return; } root.hovering = true; // abort if we're re-entering if (e && root.$layer && root.$layer.is(e.relatedTarget)) { e.preventDefault(); e.stopImmediatePropagation(); } // make sure only one item is selected (opt.$menu ? opt : root).$menu .children('.' + root.classNames.hover).trigger('contextmenu:blur') .children('.hover').trigger('contextmenu:blur'); if ($this.hasClass(root.classNames.disabled) || $this.hasClass(root.classNames.notSelectable)) { opt.$selected = null; return; } $this.trigger('contextmenu:focus'); }, // :hover done manually so key handling is possible itemMouseleave: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) { if (typeof root.$selected !== 'undefined' && root.$selected !== null) { root.$selected.trigger('contextmenu:blur'); } e.preventDefault(); e.stopImmediatePropagation(); root.$selected = opt.$selected = opt.$node; return; } if(opt && opt.$menu && opt.$menu.hasClass('context-menu-visible')){ return; } $this.trigger('contextmenu:blur'); }, // contextMenu item click itemClick: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot, key = data.contextMenuKey, callback; // prevent fast click-through on mobile taps if (isInteractionTooFast($this)) { e.preventDefault(); e.stopImmediatePropagation(); return; } // abort if the key is unknown or disabled or is a menu // explicitly handle non-selectable submenu clicks first to stop propagation if ($this.is('.context-menu-submenu') && root.selectableSubMenu === false) { e.preventDefault(); e.stopImmediatePropagation(); // Stop event here for non-selectable submenus return; } // original check for other non-clickable/disabled items if (!opt.items[key] || $this.is('.' + root.classNames.disabled + ', .context-menu-separator, .' + root.classNames.notSelectable)) { return; } // if it wasn't a non-selectable submenu or other disabled item, prevent default and stop propagation before callback e.preventDefault(); e.stopImmediatePropagation(); if ($.isFunction(opt.callbacks[key]) && Object.prototype.hasOwnProperty.call(opt.callbacks, key)) { // item-specific callback callback = opt.callbacks[key]; } else if ($.isFunction(root.callback)) { // default callback callback = root.callback; } else { // no callback, no action return; } // hide menu if callback doesn't stop that if (callback.call(root.$trigger, key, root, e) !== false) { root.$menu.trigger('contextmenu:hide'); } else if (root.$menu.parent().length) { op.update.call(root.$trigger, root); } }, // ignore click events on input elements inputClick: function (e) { e.stopImmediatePropagation(); }, // hide hideMenu: function (e, data) { var root = $(this).data('contextMenuRoot'); op.hide.call(root.$trigger, root, data && data.force); }, // focus focusItem: function (e) { e.stopPropagation(); var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if ($this.hasClass(root.classNames.disabled) || $this.hasClass(root.classNames.notSelectable)) { return; } $this .addClass([root.classNames.hover, root.classNames.visible].join(' ')) // select other items and included items .parent().find('.context-menu-item').not($this) .removeClass(root.classNames.visible) .filter('.' + root.classNames.hover) .trigger('contextmenu:blur'); // remember selected opt.$selected = root.$selected = $this; if(opt && opt.$node && opt.$node.hasClass('context-menu-submenu')){ opt.$node.addClass(root.classNames.hover); } // position sub-menu - do after show so dumb $.ui.position can keep up if (opt.$node) { root.positionSubmenu.call(opt.$node, opt.$menu); if (opt.$menu) { var focusShowTimestamp = Date.now(); opt.$menu.data('_showTimestamp', focusShowTimestamp); } } }, // blur blurItem: function (e) { e.stopPropagation(); var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if (opt.autoHide) { // for tablets and touch screens this needs to remain $this.removeClass(root.classNames.visible); } $this.removeClass(root.classNames.hover); opt.$selected = null; } }, // operations op = { show: function (opt, x, y) { var $trigger = $(this), css = {}; // hide any open menus if ($('#context-menu-layer').length > 0) $('#context-menu-layer').trigger('mousedown'); else $(document).trigger('contextmenu:hide'); // backreference for callbacks opt.$trigger = $trigger; // show event if (opt.events.show.call($trigger, opt) === false) { $currentTrigger = null; return; } // create or update context menu var hasVisibleItems = op.update.call($trigger, opt); if (hasVisibleItems === false) { $currentTrigger = null; return; } // position menu opt.position.call($trigger, opt, x, y); // make sure we're in front if (opt.zIndex) { var additionalZValue = opt.zIndex; // If opt.zIndex is a function, call the function to get the right zIndex. if (typeof opt.zIndex === 'function') { additionalZValue = opt.zIndex.call($trigger, opt); } css.zIndex = zindex($trigger) + additionalZValue; } // add layer op.layer.call(opt.$menu, opt, css.zIndex); // adjust sub-menu zIndexes opt.$menu.find('ul').css('zIndex', css.zIndex + 1); // position and show context menu opt.$menu.css(css)[opt.animation.show](opt.animation.duration, function () { $trigger.trigger('contextmenu:visible'); var rootShowTimestamp = Date.now(); opt.$menu.data('_showTimestamp', rootShowTimestamp); op.activated(opt); opt.events.activated(opt); }); // make options available and set state $trigger .data('contextMenu', opt) .addClass('context-menu-active'); // register key handler $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key); // register autoHide handler if (opt.autoHide) { // mouse position handler $(document).on('mousemove.contextMenuAutoHide', function (e) { // need to capture the offset on mousemove, // since the page might've been scrolled since activation var pos = $trigger.offset(); pos.right = pos.left + $trigger.outerWidth(); pos.bottom = pos.top + $trigger.outerHeight(); if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) { /* Additional hover check after short time, you might just miss the edge of the menu */ setTimeout(function () { if (!opt.hovering && opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('contextmenu:hide'); } }, 50); } }); } }, hide: function (opt, force) { var $trigger = $(this); if (!opt) { opt = $trigger.data('contextMenu') || {}; } // hide event if (!force && opt.events && opt.events.hide.call($trigger, opt) === false) { return; } // remove options and revert state $trigger .removeData('contextMenu') .removeClass('context-menu-active'); if (opt.$layer) { // keep layer for a bit so the contextmenu event can be aborted properly by opera setTimeout((function ($layer) { return function () { $layer.remove(); }; })(opt.$layer), 10); try { delete opt.$layer; } catch (e) { opt.$layer = null; } } // remove handle $currentTrigger = null; // remove selected opt.$menu.find('.' + opt.classNames.hover).trigger('contextmenu:blur'); opt.$selected = null; // collapse all submenus opt.$menu.find('.' + opt.classNames.visible).removeClass(opt.classNames.visible); // unregister key and mouse handlers // $(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705 $(document).off('.contextMenuAutoHide').off('keydown.contextMenu'); // hide menu if (opt.$menu) { opt.$menu[opt.animation.hide](opt.animation.duration, function () { // tear down dynamically built menu after animation is completed. if (opt.build) { opt.$menu.remove(); $.each(opt, function (key) { switch (key) { case 'ns': case 'selector': case 'build': case 'trigger': return true; default: opt[key] = undefined; try { delete opt[key]; } catch (e) { } return true; } }); } setTimeout(function () { $trigger.trigger('contextmenu:hidden'); }, 10); }); } }, create: function (opt, root) { if (typeof root === 'undefined') { root = opt; } // define handler for fast input clicks var handleFastInputClick = function(e) { var $inputClicked = $(this); if (isInteractionTooFast($inputClicked)) { e.preventDefault(); e.stopImmediatePropagation(); return false; } }; // create contextMenu opt.$menu = $('').addClass(opt.className || '').data({ 'contextMenu': opt, 'contextMenuRoot': root }); if(opt.dataAttr){ $.each(opt.dataAttr, function (key, item) { opt.$menu.attr('data-' + opt.key, item); }); } $.each(['callbacks', 'commands', 'inputs'], function (i, k) { opt[k] = {}; if (!root[k]) { root[k] = {}; } }); if (!root.accesskeys) { root.accesskeys = {}; } function createNameNode(item) { var $name = $(''); if (item._accesskey) { if (item._beforeAccesskey) { $name.append(document.createTextNode(item._beforeAccesskey)); } $('') .addClass('context-menu-accesskey') .text(item._accesskey) .appendTo($name); if (item._afterAccesskey) { $name.append(document.createTextNode(item._afterAccesskey)); } } else { if (item.isHtmlName) { // restrict use with access keys if (typeof item.accesskey !== 'undefined') { throw new Error('accesskeys are not compatible with HTML names and cannot be used together in the same item'); } $name.html(item.name); } else { $name.text(item.name); } } return $name; } // create contextMenu items $.each(opt.items, function (key, item) { var $t = $('').addClass(item.className || ''), $label = null, $input = null; // iOS needs to see a click-event bound to an element to actually // have the TouchEvents infrastructure trigger the click event $t.on('click', $.noop); // Make old school string seperator a real item so checks wont be // akward later. // And normalize 'cm_separator' into 'cm_seperator'. if (typeof item === 'string' || item.type === 'cm_separator') { item = {type: 'cm_seperator'}; } item.$node = $t.data({ 'contextMenu': opt, 'contextMenuRoot': root, 'contextMenuKey': key }); // register accesskey // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that if (typeof item.accesskey !== 'undefined') { var aks = splitAccesskey(item.accesskey); for (var i = 0, ak; ak = aks[i]; i++) { if (!root.accesskeys[ak]) { root.accesskeys[ak] = item; var matched = item.name.match(new RegExp('^(.*?)(' + ak + ')(.*)$', 'i')); if (matched) { item._beforeAccesskey = matched[1]; item._accesskey = matched[2]; item._afterAccesskey = matched[3]; } break; } } } if (item.type && types[item.type]) { // run custom type handler types[item.type].call($t, item, opt, root); // register commands $.each([opt, root], function (i, k) { k.commands[key] = item; // Overwrite only if undefined or the item is appended to the root. This so it // doesn't overwrite callbacks of root elements if the name is the same. if ($.isFunction(item.callback) && (typeof k.callbacks[key] === 'undefined' || typeof opt.type === 'undefined')) { k.callbacks[key] = item.callback; } }); } else { // add label for input if (item.type === 'cm_seperator') { $t.addClass('context-menu-separator ' + root.classNames.notSelectable); } else if (item.type === 'html') { $t.addClass('context-menu-html ' + root.classNames.notSelectable); } else if (item.type !== 'sub' && item.type) { $label = $('').appendTo($t); createNameNode(item).appendTo($label); $t.addClass('context-menu-input'); opt.hasTypes = true; $.each([opt, root], function (i, k) { k.commands[key] = item; k.inputs[key] = item; }); } else if (item.items) { item.type = 'sub'; } switch (item.type) { case 'cm_seperator': break; case 'text': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .appendTo($label); break; case 'textarea': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .appendTo($label); if (item.height) { $input.height(item.height); } break; case 'checkbox': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .prop('checked', !!item.selected) .prependTo($label); // prevent checkbox default action on fast click-through $input.on('click', handleFastInputClick); break; case 'radio': $input = $('') .attr('name', 'context-menu-input-' + item.radio) .val(item.value || '') .prop('checked', !!item.selected) .prependTo($label); // prevent radio default action on fast click-through $input.on('click', handleFastInputClick); break; case 'select': $input = $('') .attr('name', 'context-menu-input-' + key) .appendTo($label); if (item.options) { $.each(item.options, function (value, text) { $('').val(value).text(text).appendTo($input); }); $input.val(item.selected); } break; case 'sub': createNameNode(item).appendTo($t); item.appendTo = item.$node; $t.data('contextMenu', item).addClass('context-menu-submenu'); item.callback = null; // If item contains items, and this is a promise, we should create it later // check if subitems is of type promise. If it is a promise we need to create // it later, after promise has been resolved. if ('function' === typeof item.items.then) { // probably a promise, process it, when completed it will create the sub menu's. op.processPromises(item, root, item.items); } else { // normal submenu. op.create(item, root); } break; case 'html': $(item.html).appendTo($t); break; default: $.each([opt, root], function (i, k) { k.commands[key] = item; // Overwrite only if undefined or the item is appended to the root. This so it // doesn't overwrite callbacks of root elements if the name is the same. if ($.isFunction(item.callback) && (typeof k.callbacks[key] === 'undefined' || typeof opt.type === 'undefined')) { k.callbacks[key] = item.callback; } }); createNameNode(item).appendTo($t); break; } // disable key listener in if (item.type && item.type !== 'sub' && item.type !== 'html' && item.type !== 'cm_seperator') { $input .on('focus', handle.focusInput) .on('blur', handle.blurInput); if (item.events) { $input.on(item.events, opt); } } // add icons if (item.icon) { if ($.isFunction(item.icon)) { item._icon = item.icon.call(this, this, $t, key, item); } else { if (typeof(item.icon) === 'string' && ( item.icon.substring(0, 4) === 'fab ' || item.icon.substring(0, 4) === 'fas ' || item.icon.substring(0, 4) === 'fad ' || item.icon.substring(0, 4) === 'far ' || item.icon.substring(0, 4) === 'fal ') ) { // to enable font awesome $t.addClass(root.classNames.icon + ' ' + root.classNames.icon + '--fa5'); item._icon = $(''); } else if (typeof(item.icon) === 'string' && item.icon.substring(0, 3) === 'fa-') { item._icon = root.classNames.icon + ' ' + root.classNames.icon + '--fa fa ' + item.icon; } else { item._icon = root.classNames.icon + ' ' + root.classNames.icon + '-' + item.icon; } } if(typeof(item._icon) === "string"){ $t.addClass(item._icon); } else { $t.prepend(item._icon); } } } // cache contained elements item.$input = $input; item.$label = $label; // attach item to menu $t.appendTo(opt.$menu); // Disable text selection if (!opt.hasTypes && $.support.eventSelectstart) { // browsers support user-select: none, // IE has a special event for text-selection // browsers supporting neither will not be preventing text-selection $t.on('selectstart.disableTextSelect', handle.abortevent); } }); // attach contextMenu to (to bypass any possible overflow:hidden issues on parents of the trigger element) if (!opt.$node) { opt.$menu.css('display', 'none').addClass('context-menu-root'); } opt.$menu.appendTo(opt.appendTo || document.body); }, resize: function ($menu, nested) { var domMenu; // determine widths of submenus, as CSS won't grow them automatically // position:absolute within position:absolute; min-width:100; max-width:200; results in width: 100; // kinda sucks hard... // determine width of absolutely positioned element $menu.css({position: 'absolute', display: 'block'}); // don't apply yet, because that would break nested elements' widths $menu.data('width', (domMenu = $menu.get(0)).getBoundingClientRect ? Math.ceil(domMenu.getBoundingClientRect().width) : $menu.outerWidth() + 1); // outerWidth() returns rounded pixels // reset styles so they allow nested elements to grow/shrink naturally $menu.css({ position: 'static', minWidth: '0px', maxWidth: '100000px' }); // identify width of nested menus $menu.find('> li > ul').each(function () { op.resize($(this), true); }); // reset and apply changes in the end because nested // elements' widths wouldn't be calculatable otherwise if (!nested) { $menu.find('ul').addBack().css({ position: '', display: '', minWidth: '', maxWidth: '' }).outerWidth(function () { return $(this).data('width'); }); } }, update: function (opt, root) { var $trigger = this; if (typeof root === 'undefined') { root = opt; op.resize(opt.$menu); } var hasVisibleItems = false; // re-check disabled for each item opt.$menu.children().each(function () { var $item = $(this), key = $item.data('contextMenuKey'), item = opt.items[key], disabled = ($.isFunction(item.disabled) && item.disabled.call($trigger, key, root)) || item.disabled === true, visible; if ($.isFunction(item.visible)) { visible = item.visible.call($trigger, key, root); } else if (typeof item.visible !== 'undefined') { visible = item.visible === true; } else { visible = true; } if (visible) { hasVisibleItems = true; } $item[visible ? 'show' : 'hide'](); // dis- / enable item $item[disabled ? 'addClass' : 'removeClass'](root.classNames.disabled); if ($.isFunction(item.icon)) { $item.removeClass(item._icon); var iconResult = item.icon.call(this, $trigger, $item, key, item); if(typeof(iconResult) === "string"){ $item.addClass(iconResult); } else { $item.prepend(iconResult); } } if (item.type) { // dis- / enable input elements $item.find('input, select, textarea').prop('disabled', disabled); // update input states switch (item.type) { case 'text': case 'textarea': item.$input.val(item.value || ''); break; case 'checkbox': case 'radio': item.$input.val(item.value || '').prop('checked', !!item.selected); break; case 'select': item.$input.val((item.selected === 0 ? "0" : item.selected) || ''); break; } } if (item.$menu) { // update sub-menu var subMenuHasVisibleItems = op.update.call($trigger, item, root); if (subMenuHasVisibleItems) { hasVisibleItems = true; } } }); return hasVisibleItems; }, layer: function (opt, zIndex) { if (!opt.useModal) { var listener = function (ev) { handle.layerClick(ev, opt, function() { document.removeEventListener('mousedown', listener, true); }); }; document.addEventListener('mousedown', listener, true); return; } // add transparent layer for click area // filter and background for Internet Explorer, Issue #23 var $layer = opt.$layer = $('') .css({ height: $win.height(), width: $win.width(), display: 'block', position: 'fixed', 'z-index': zIndex - 1, top: 0, left: 0, opacity: 0, filter: 'alpha(opacity=0)', 'background-color': '#000' }) .data('contextMenuRoot', opt) .appendTo(document.body) .on('contextmenu', handle.abortevent) .on('mousedown', handle.layerClick); // IE6 doesn't know position:fixed; if (typeof document.body.style.maxWidth === 'undefined') { // IE6 doesn't support maxWidth $layer.css({ 'position': 'absolute', 'height': $(document).height() }); } return $layer; }, processPromises: function (opt, root, promise) { // Start opt.$node.addClass(root.classNames.iconLoadingClass); function completedPromise(opt, root, items) { // Completed promise (dev called promise.resolve). We now have a list of items which can // be used to create the rest of the context menu. if (typeof items === 'undefined') { // Null result, dev should have checked errorPromise(undefined);//own error object } finishPromiseProcess(opt, root, items); } function errorPromise(opt, root, errorItem) { // User called promise.reject() with an error item, if not, provide own error item. if (typeof errorItem === 'undefined') { errorItem = { "error": { name: "No items and no error item", icon: "context-menu-icon context-menu-icon-quit" } }; if (window.console) { (console.error || console.log).call(console, 'When you reject a promise, provide an "items" object, equal to normal sub-menu items'); } } else if (typeof errorItem === 'string') { errorItem = {"error": {name: errorItem}}; } finishPromiseProcess(opt, root, errorItem); } function finishPromiseProcess(opt, root, items) { if (typeof root.$menu === 'undefined' || !root.$menu.is(':visible')) { return; } opt.$node.removeClass(root.classNames.iconLoadingClass); opt.items = items; op.create(opt, root, true); // Create submenu op.update(opt, root); // Correctly update position if user is already hovered over menu item root.positionSubmenu.call(opt.$node, opt.$menu); // positionSubmenu, will only do anything if user already hovered over menu item that just got new subitems. } // Wait for promise completion. .then(success, error, notify) (we don't track notify). Bind the opt // and root to avoid scope problems promise.then(completedPromise.bind(this, opt, root), errorPromise.bind(this, opt, root)); }, // operation that will run after contextMenu showed on screen activated: function(opt){ var $menu = opt.$menu; var $menuOffset = $menu.offset(); var winHeight = $(window).height(); var winWidth = $(window).width(); var winScrollTop = $(window).scrollTop(); var winScrollLeft = $(window).scrollLeft(); var menuHeight = $menu.height(); var outerHeight = $menu.outerHeight(); var outerWidth = $menu.outerWidth(); if(menuHeight > winHeight){ $menu.css({ 'height' : winHeight + 'px', 'overflow-x': 'hidden', 'overflow-y': 'auto', 'top': winScrollTop + 'px' }); } else if($menuOffset.top < winScrollTop){ $menu.css({ 'top': winScrollTop + 'px' }); } else if($menuOffset.top + outerHeight > winScrollTop + winHeight){ $menu.css({ 'top': $menuOffset.top - (($menuOffset.top + outerHeight) - (winScrollTop + winHeight)) + "px" }); } if($menuOffset.left + outerWidth > winScrollLeft + winWidth){ $menu.css({ 'left': $menuOffset.left - (($menuOffset.left + outerWidth) - (winScrollLeft + winWidth)) + "px" }); } } }; // split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key function splitAccesskey(val) { var t = val.split(/\s+/); var keys = []; for (var i = 0, k; k = t[i]; i++) { k = k.charAt(0).toUpperCase(); // first character only // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it. // a map to look up already used access keys would be nice keys.push(k); } return keys; } // handle contextMenu triggers $.fn.contextMenu = function (operation) { var $t = this, $o = operation; if (this.length > 0) { // this is not a build on demand menu if (typeof operation === 'undefined') { this.first().trigger('contextmenu'); } else if (typeof operation.x !== 'undefined' && typeof operation.y !== 'undefined') { this.first().trigger($.Event('contextmenu', { pageX: operation.x, pageY: operation.y, mouseButton: operation.button })); } else if (operation === 'hide') { var $menu = this.first().data('contextMenu') ? this.first().data('contextMenu').$menu : null; if ($menu) { $menu.trigger('contextmenu:hide'); } } else if (operation === 'destroy') { $.contextMenu('destroy', {context: this}); } else if ($.isPlainObject(operation)) { operation.context = this; $.contextMenu('create', operation); } else if (operation) { this.removeClass('context-menu-disabled'); } else if (!operation) { this.addClass('context-menu-disabled'); } } else { $.each(menus, function () { if (this.selector === $t.selector) { $o.data = this; $.extend($o.data, {trigger: 'demand'}); } }); handle.contextmenu.call($o.target, $o); } return this; }; // manage contextMenu instances $.contextMenu = function (operation, options) { if (typeof operation !== 'string') { options = operation; operation = 'create'; } if (typeof options === 'string') { options = {selector: options}; } else if (typeof options === 'undefined') { options = {}; } // merge with default options var o = $.extend(true, {}, defaults, options || {}); var $document = $(document); var $context = $document; var _hasContext = false; if (!o.context || !o.context.length) { o.context = document; } else { // you never know what they throw at you... $context = $(o.context).first(); o.context = $context.get(0); _hasContext = !$(o.context).is(document); } switch (operation) { case 'update': // Updates visibility and such if(_hasContext){ op.update($context); } else { for(var menu in menus){ if(menus.hasOwnProperty(menu)){ op.update(menus[menu]); } } } break; case 'create': // no selector no joy if (!o.selector) { throw new Error('No selector specified'); } // make sure internal classes are not bound to if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) { throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className'); } if (!o.build && (!o.items || $.isEmptyObject(o.items))) { throw new Error('No Items specified'); } counter++; o.ns = '.contextMenu' + counter; if (!_hasContext) { namespaces[o.selector] = o.ns; } menus[o.ns] = o; // default to right click if (!o.trigger) { o.trigger = 'right'; } if (!initialized) { var itemClick = o.itemClickEvent === 'click' ? 'click.contextMenu' : 'mouseup.contextMenu'; var contextMenuItemObj = { // 'mouseup.contextMenu': handle.itemClick, // 'click.contextMenu': handle.itemClick, 'contextmenu:focus.contextMenu': handle.focusItem, 'contextmenu:blur.contextMenu': handle.blurItem, 'contextmenu.contextMenu': handle.abortevent, 'mouseenter.contextMenu': handle.itemMouseenter, 'mouseleave.contextMenu': handle.itemMouseleave }; contextMenuItemObj[itemClick] = handle.itemClick; // make sure item click is registered first $document .on({ 'contextmenu:hide.contextMenu': handle.hideMenu, 'prevcommand.contextMenu': handle.prevItem, 'nextcommand.contextMenu': handle.nextItem, 'contextmenu.contextMenu': handle.abortevent, 'mouseenter.contextMenu': handle.menuMouseenter, 'mouseleave.contextMenu': handle.menuMouseleave }, '.context-menu-list') .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick) .on(contextMenuItemObj, '.context-menu-item'); initialized = true; } // engage native contextmenu event $context .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu); if (_hasContext) { // add remove hook, just in case $context.on('remove' + o.ns, function () { $(this).contextMenu('destroy'); }); } switch (o.trigger) { case 'hover': $context .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter) .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave); break; case 'left': $context.on('click' + o.ns, o.selector, o, handle.click); break; case 'touchstart': $context.on('touchstart' + o.ns, o.selector, o, handle.click); break; /* default: // http://www.quirksmode.org/dom/events/contextmenu.html $document .on('mousedown' + o.ns, o.selector, o, handle.mousedown) .on('mouseup' + o.ns, o.selector, o, handle.mouseup); break; */ } // create menu if (!o.build) { op.create(o); } break; case 'destroy': var $visibleMenu; if (_hasContext) { // get proper options var context = o.context; $.each(menus, function (ns, o) { if (!o) { return true; } // Is this menu equest to the context called from if (!$(context).is(o.selector)) { return true; } $visibleMenu = $('.context-menu-list').filter(':visible'); if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is($(o.context).find(o.selector))) { $visibleMenu.trigger('contextmenu:hide', {force: true}); } try { if (menus[o.ns].$menu) { menus[o.ns].$menu.remove(); } delete menus[o.ns]; } catch (e) { menus[o.ns] = null; } $(o.context).off(o.ns); return true; }); } else if (!o.selector) { $document.off('.contextMenu .contextMenuAutoHide'); $.each(menus, function (ns, o) { $(o.context).off(o.ns); }); namespaces = {}; menus = {}; counter = 0; initialized = false; $('#context-menu-layer, .context-menu-list').remove(); } else if (namespaces[o.selector]) { $visibleMenu = $('.context-menu-list').filter(':visible'); if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is(o.selector)) { $visibleMenu.trigger('contextmenu:hide', {force: true}); } try { if (menus[namespaces[o.selector]].$menu) { menus[namespaces[o.selector]].$menu.remove(); } delete menus[namespaces[o.selector]]; } catch (e) { menus[namespaces[o.selector]] = null; } $document.off(namespaces[o.selector]); } break; case 'html5': // if and are not handled by the browser, // or options was a bool true, // initialize $.contextMenu for them if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options === 'boolean' && options)) { $('menu[type="context"]').each(function () { if (this.id) { $.contextMenu({ selector: '[contextmenu=' + this.id + ']', items: $.contextMenu.fromMenu(this) }); } }).css('display', 'none'); } break; default: throw new Error('Unknown operation "' + operation + '"'); } return this; }; // import values into commands $.contextMenu.setInputValues = function (opt, data) { if (typeof data === 'undefined') { data = {}; } $.each(opt.inputs, function (key, item) { switch (item.type) { case 'text': case 'textarea': item.value = data[key] || ''; break; case 'checkbox': item.selected = data[key] ? true : false; break; case 'radio': item.selected = (data[item.radio] || '') === item.value; break; case 'select': item.selected = data[key] || ''; break; } }); }; // export values from commands $.contextMenu.getInputValues = function (opt, data) { if (typeof data === 'undefined') { data = {}; } $.each(opt.inputs, function (key, item) { switch (item.type) { case 'text': case 'textarea': case 'select': data[key] = item.$input.val(); break; case 'checkbox': data[key] = item.$input.prop('checked'); break; case 'radio': if (item.$input.prop('checked')) { data[item.radio] = item.value; } break; } }); return data; }; // find function inputLabel(node) { return (node.id && $('label[for="' + node.id + '"]').val()) || node.name; } // convert to items object function menuChildren(items, $children, counter) { if (!counter) { counter = 0; } $children.each(function () { var $node = $(this), node = this, nodeName = this.nodeName.toLowerCase(), label, item; // extract if (nodeName === 'label' && $node.find('input, textarea, select').length) { label = $node.text(); $node = $node.children().first(); node = $node.get(0); nodeName = node.nodeName.toLowerCase(); } /* * accepts flow-content as children. that means , and such are valid menu items. * Not being the sadistic kind, $.contextMenu only accepts: * , , , , , , and of course . * Everything else will be imported as an html node, which is not interfaced with contextMenu. */ // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command switch (nodeName) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element case 'menu': item = {name: $node.attr('label'), items: {}}; counter = menuChildren(item.items, $node.children(), counter); break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command case 'a': // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command case 'button': item = { name: $node.text(), disabled: !!$node.attr('disabled'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command case 'menuitem': case 'command': switch ($node.attr('type')) { case undefined: case 'command': case 'menuitem': item = { name: $node.attr('label'), disabled: !!$node.attr('disabled'), icon: $node.attr('icon'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; case 'checkbox': item = { type: 'checkbox', disabled: !!$node.attr('disabled'), name: $node.attr('label'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', disabled: !!$node.attr('disabled'), name: $node.attr('label'), radio: $node.attr('radiogroup'), value: $node.attr('id'), selected: !!$node.attr('checked') }; break; default: item = undefined; } break; case 'hr': item = '-------'; break; case 'input': switch ($node.attr('type')) { case 'text': item = { type: 'text', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'checkbox': item = { type: 'checkbox', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), radio: !!$node.attr('name'), value: $node.val(), selected: !!$node.attr('checked') }; break; default: item = undefined; break; } break; case 'select': item = { type: 'select', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: $node.val(), options: {} }; $node.children().each(function () { item.options[this.value] = $(this).text(); }); break; case 'textarea': item = { type: 'textarea', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'label': break; default: item = {type: 'html', html: $node.clone(true)}; break; } if (item) { counter++; items['key' + counter] = item; } }); return counter; } // convert html5 menu $.contextMenu.fromMenu = function (element) { var $this = $(element), items = {}; menuChildren(items, $this.children()); return items; }; // make defaults accessible $.contextMenu.defaults = defaults; $.contextMenu.types = types; // export internal functions - undocumented, for hacking only! $.contextMenu.handle = handle; $.contextMenu.op = op; $.contextMenu.menus = menus; }); ================================================ FILE: src/jquery.ui.position.js ================================================ /*! jQuery UI - v1.12.0 - 2016-07-15 * http://jqueryui.com * Includes: position.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ (function( factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define([ "jquery" ], factory ); } else { // Browser globals factory( jQuery ); } }(function( $ ) { $.ui = $.ui || {}; var version = $.ui.version = "1.12.0"; /*! * jQuery UI Position 1.12.0 * http://jqueryui.com * * Copyright jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/position/ */ //>>label: Position //>>group: Core //>>description: Positions elements relative to other elements. //>>docs: http://api.jqueryui.com/position/ //>>demos: http://jqueryui.com/position/ ( function() { var cachedScrollbarWidth, supportsOffsetFractions, max = Math.max, abs = Math.abs, round = Math.round, rhorizontal = /left|center|right/, rvertical = /top|center|bottom/, roffset = /[\+\-]\d+(\.[\d]+)?%?/, rposition = /^\w+/, rpercent = /%$/, _position = $.fn.position; // Support: IE <=9 only supportsOffsetFractions = function() { var element = $( "" ) .css( "position", "absolute" ) .appendTo( "body" ) .offset( { top: 1.5, left: 1.5 } ), support = element.offset().top === 1.5; element.remove(); supportsOffsetFractions = function() { return support; }; return support; }; function getOffsets( offsets, width, height ) { return [ parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) ]; } function parseCss( element, property ) { return parseInt( $.css( element, property ), 10 ) || 0; } function getDimensions( elem ) { var raw = elem[ 0 ]; if ( raw.nodeType === 9 ) { return { width: elem.width(), height: elem.height(), offset: { top: 0, left: 0 } }; } if ( $.isWindow( raw ) ) { return { width: elem.width(), height: elem.height(), offset: { top: elem.scrollTop(), left: elem.scrollLeft() } }; } if ( raw.preventDefault ) { return { width: 0, height: 0, offset: { top: raw.pageY, left: raw.pageX } }; } return { width: elem.outerWidth(), height: elem.outerHeight(), offset: elem.offset() }; } $.position = { scrollbarWidth: function() { if ( cachedScrollbarWidth !== undefined ) { return cachedScrollbarWidth; } var w1, w2, div = $( "" + "" ), innerDiv = div.children()[ 0 ]; $( "body" ).append( div ); w1 = innerDiv.offsetWidth; div.css( "overflow", "scroll" ); w2 = innerDiv.offsetWidth; if ( w1 === w2 ) { w2 = div[ 0 ].clientWidth; } div.remove(); return ( cachedScrollbarWidth = w1 - w2 ); }, getScrollInfo: function( within ) { var overflowX = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-x" ), overflowY = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-y" ), hasOverflowX = overflowX === "scroll" || ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), hasOverflowY = overflowY === "scroll" || ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); return { width: hasOverflowY ? $.position.scrollbarWidth() : 0, height: hasOverflowX ? $.position.scrollbarWidth() : 0 }; }, getWithinInfo: function( element ) { var withinElement = $( element || window ), isWindow = $.isWindow( withinElement[ 0 ] ), isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, hasOffset = !isWindow && !isDocument; return { element: withinElement, isWindow: isWindow, isDocument: isDocument, offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, scrollLeft: withinElement.scrollLeft(), scrollTop: withinElement.scrollTop(), width: withinElement.outerWidth(), height: withinElement.outerHeight() }; } }; $.fn.position = function( options ) { if ( !options || !options.of ) { return _position.apply( this, arguments ); } // Make a copy, we don't want to modify arguments options = $.extend( {}, options ); var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, target = $( options.of ), within = $.position.getWithinInfo( options.within ), scrollInfo = $.position.getScrollInfo( within ), collision = ( options.collision || "flip" ).split( " " ), offsets = {}; dimensions = getDimensions( target ); if ( target[ 0 ].preventDefault ) { // Force left top to allow flipping options.at = "left top"; } targetWidth = dimensions.width; targetHeight = dimensions.height; targetOffset = dimensions.offset; // Clone to reuse original targetOffset later basePosition = $.extend( {}, targetOffset ); // Force my and at to have valid horizontal and vertical positions // if a value is missing or invalid, it will be converted to center $.each( [ "my", "at" ], function() { var pos = ( options[ this ] || "" ).split( " " ), horizontalOffset, verticalOffset; if ( pos.length === 1 ) { pos = rhorizontal.test( pos[ 0 ] ) ? pos.concat( [ "center" ] ) : rvertical.test( pos[ 0 ] ) ? [ "center" ].concat( pos ) : [ "center", "center" ]; } pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; // Calculate offsets horizontalOffset = roffset.exec( pos[ 0 ] ); verticalOffset = roffset.exec( pos[ 1 ] ); offsets[ this ] = [ horizontalOffset ? horizontalOffset[ 0 ] : 0, verticalOffset ? verticalOffset[ 0 ] : 0 ]; // Reduce to just the positions without the offsets options[ this ] = [ rposition.exec( pos[ 0 ] )[ 0 ], rposition.exec( pos[ 1 ] )[ 0 ] ]; } ); // Normalize collision option if ( collision.length === 1 ) { collision[ 1 ] = collision[ 0 ]; } if ( options.at[ 0 ] === "right" ) { basePosition.left += targetWidth; } else if ( options.at[ 0 ] === "center" ) { basePosition.left += targetWidth / 2; } if ( options.at[ 1 ] === "bottom" ) { basePosition.top += targetHeight; } else if ( options.at[ 1 ] === "center" ) { basePosition.top += targetHeight / 2; } atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); basePosition.left += atOffset[ 0 ]; basePosition.top += atOffset[ 1 ]; return this.each( function() { var collisionPosition, using, elem = $( this ), elemWidth = elem.outerWidth(), elemHeight = elem.outerHeight(), marginLeft = parseCss( this, "marginLeft" ), marginTop = parseCss( this, "marginTop" ), collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, position = $.extend( {}, basePosition ), myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); if ( options.my[ 0 ] === "right" ) { position.left -= elemWidth; } else if ( options.my[ 0 ] === "center" ) { position.left -= elemWidth / 2; } if ( options.my[ 1 ] === "bottom" ) { position.top -= elemHeight; } else if ( options.my[ 1 ] === "center" ) { position.top -= elemHeight / 2; } position.left += myOffset[ 0 ]; position.top += myOffset[ 1 ]; // If the browser doesn't support fractions, then round for consistent results if ( !supportsOffsetFractions() ) { position.left = round( position.left ); position.top = round( position.top ); } collisionPosition = { marginLeft: marginLeft, marginTop: marginTop }; $.each( [ "left", "top" ], function( i, dir ) { if ( $.ui.position[ collision[ i ] ] ) { $.ui.position[ collision[ i ] ][ dir ]( position, { targetWidth: targetWidth, targetHeight: targetHeight, elemWidth: elemWidth, elemHeight: elemHeight, collisionPosition: collisionPosition, collisionWidth: collisionWidth, collisionHeight: collisionHeight, offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], my: options.my, at: options.at, within: within, elem: elem } ); } } ); if ( options.using ) { // Adds feedback as second argument to using callback, if present using = function( props ) { var left = targetOffset.left - position.left, right = left + targetWidth - elemWidth, top = targetOffset.top - position.top, bottom = top + targetHeight - elemHeight, feedback = { target: { element: target, left: targetOffset.left, top: targetOffset.top, width: targetWidth, height: targetHeight }, element: { element: elem, left: position.left, top: position.top, width: elemWidth, height: elemHeight }, horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" }; if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { feedback.horizontal = "center"; } if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { feedback.vertical = "middle"; } if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { feedback.important = "horizontal"; } else { feedback.important = "vertical"; } options.using.call( this, props, feedback ); }; } elem.offset( $.extend( position, { using: using } ) ); } ); }; $.ui.position = { fit: { left: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, outerWidth = within.width, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = withinOffset - collisionPosLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, newOverRight; // Element is wider than within if ( data.collisionWidth > outerWidth ) { // Element is initially over the left side of within if ( overLeft > 0 && overRight <= 0 ) { newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; position.left += overLeft - newOverRight; // Element is initially over right side of within } else if ( overRight > 0 && overLeft <= 0 ) { position.left = withinOffset; // Element is initially over both left and right sides of within } else { if ( overLeft > overRight ) { position.left = withinOffset + outerWidth - data.collisionWidth; } else { position.left = withinOffset; } } // Too far left -> align with left edge } else if ( overLeft > 0 ) { position.left += overLeft; // Too far right -> align with right edge } else if ( overRight > 0 ) { position.left -= overRight; // Adjust based on position and margin } else { position.left = max( position.left - collisionPosLeft, position.left ); } }, top: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollTop : within.offset.top, outerHeight = data.within.height, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = withinOffset - collisionPosTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, newOverBottom; // Element is taller than within if ( data.collisionHeight > outerHeight ) { // Element is initially over the top of within if ( overTop > 0 && overBottom <= 0 ) { newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; position.top += overTop - newOverBottom; // Element is initially over bottom of within } else if ( overBottom > 0 && overTop <= 0 ) { position.top = withinOffset; // Element is initially over both top and bottom of within } else { if ( overTop > overBottom ) { position.top = withinOffset + outerHeight - data.collisionHeight; } else { position.top = withinOffset; } } // Too far up -> align with top } else if ( overTop > 0 ) { position.top += overTop; // Too far down -> align with bottom edge } else if ( overBottom > 0 ) { position.top -= overBottom; // Adjust based on position and margin } else { position.top = max( position.top - collisionPosTop, position.top ); } } }, flip: { left: function( position, data ) { var within = data.within, withinOffset = within.offset.left + within.scrollLeft, outerWidth = within.width, offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = collisionPosLeft - offsetLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, myOffset = data.my[ 0 ] === "left" ? -data.elemWidth : data.my[ 0 ] === "right" ? data.elemWidth : 0, atOffset = data.at[ 0 ] === "left" ? data.targetWidth : data.at[ 0 ] === "right" ? -data.targetWidth : 0, offset = -2 * data.offset[ 0 ], newOverRight, newOverLeft; if ( overLeft < 0 ) { newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { position.left += myOffset + atOffset + offset; } } else if ( overRight > 0 ) { newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { position.left += myOffset + atOffset + offset; } } }, top: function( position, data ) { var within = data.within, withinOffset = within.offset.top + within.scrollTop, outerHeight = within.height, offsetTop = within.isWindow ? within.scrollTop : within.offset.top, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = collisionPosTop - offsetTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, top = data.my[ 1 ] === "top", myOffset = top ? -data.elemHeight : data.my[ 1 ] === "bottom" ? data.elemHeight : 0, atOffset = data.at[ 1 ] === "top" ? data.targetHeight : data.at[ 1 ] === "bottom" ? -data.targetHeight : 0, offset = -2 * data.offset[ 1 ], newOverTop, newOverBottom; if ( overTop < 0 ) { newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { position.top += myOffset + atOffset + offset; } } else if ( overBottom > 0 ) { newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { position.top += myOffset + atOffset + offset; } } } }, flipfit: { left: function() { $.ui.position.flip.left.apply( this, arguments ); $.ui.position.fit.left.apply( this, arguments ); }, top: function() { $.ui.position.flip.top.apply( this, arguments ); $.ui.position.fit.top.apply( this, arguments ); } } }; } )(); var position = $.ui.position; })); ================================================ FILE: src/sass/_icons.scss ================================================ .context-menu-icon-add { @include context-menu-item-icon(add); } .context-menu-icon-copy { @include context-menu-item-icon(copy); } .context-menu-icon-cut { @include context-menu-item-icon(cut); } .context-menu-icon-delete { @include context-menu-item-icon(delete); } .context-menu-icon-edit { @include context-menu-item-icon(edit); } .context-menu-icon-loading { @include context-menu-item-icon(loading); } .context-menu-icon-paste { @include context-menu-item-icon(paste); } .context-menu-icon-quit { @include context-menu-item-icon(quit); } ================================================ FILE: src/sass/_variables.scss ================================================ // Container Sizing $context-menu-min-width: 13em !default; $context-menu-max-width: $context-menu-min-width * 2 !default; $context-menu-container-padding: .25em 0 !default; $context-menu-container-margin: .3em !default; $context-menu-border-radius: .2em !default; // Container Font $context-menu-font-family: inherit !default; $context-menu-font-size: inherit !default; // Container Color $context-menu-background-color: #FFF !default; $context-menu-border-width: 1px !default; $context-menu-border-style: solid !default; $context-menu-border-color: #bebebe !default; $context-menu-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5) !default; // Item Sizing $context-menu-padding-vertical: .2em !default; $context-menu-padding-horizontal: 2em !default; // Item Default $context-menu-item-padding: $context-menu-padding-vertical $context-menu-padding-horizontal !default; $context-menu-text-color: #2F2F2F !default; $context-menu-item-color: $context-menu-background-color !default; // Item Hover $context-menu-item-color-hover: #2980B9 !default; $context-menu-text-color-hover: #FFF !default; // Item Disabled $context-menu-item-color-disabled: $context-menu-background-color !default; $context-menu-text-color-disabled: lighten($context-menu-text-color, 55) !default; // Submenu $context-menu-submenu-arrow-color: $context-menu-text-color !default; // Separator $context-menu-separator-height: 1px !default; $context-menu-separator-style: solid !default; $context-menu-separator-color: darken($context-menu-background-color, 10) !default; $context-menu-separator-margin: .35em 0 !default; // Icons $context-menu-icon-font-path: 'font/' !default; $context-menu-icon-font-name: 'context-menu-icons' !default; $context-menu-icon-size: 1em !default; $context-menu-icon-color: #2980B9 !default; $context-menu-icon-color-hover: $context-menu-text-color-hover !default; @keyframes cm-spin { 0% { -webkit-transform: translateY(-50%) rotate(0deg); transform: translateY(-50%) rotate(0deg) } 100% { -webkit-transform: translateY(-50%) rotate(359deg); transform: translateY(-50%) rotate(359deg) } } ================================================ FILE: src/sass/icons/_icon_classes.scss.tpl ================================================ <% _.each(glyphs, function(glyph) { %> .<%= className %>-<%= glyph.name %> { @include <%= mixinName %>(<%= glyph.name %>); }<% }); %> ================================================ FILE: src/sass/icons/_mixins.scss ================================================ @import "variables"; $context-menu-icons: () !default; @function context-menu-font-url($url) { @if function-exists(asset-url) { @return asset-url($url); } @else if function-exists(font-url) { @return font-url($url); } @else { @return url($url); } } @font-face { font-family: '#{$context-menu-icon-font-name}'; src: context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.eot?#{$context-menu-icons-cachebust}'); src: context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.eot?#{$context-menu-icons-cachebust}#iefix') format('embedded-opentype'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.woff2?#{$context-menu-icons-cachebust}') format('woff2'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.woff?#{$context-menu-icons-cachebust}') format('woff'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.ttf?#{$context-menu-icons-cachebust}') format('truetype'); font-weight: normal; font-style: normal; } @mixin context-menu-icon-without-mediaquery($position: before, $icon: false, $icons-font: $context-menu-icon-font-name) { @if not map-has-key($context-menu-icons, $icon) { @error "Invalid icon `#{$icon}`."; } @if $position == both { $position: 'before, &:after'; } // Either a :before or :after pseudo-element, or both, defaulting to :before &:#{$position} { @if $icon { // A particular icon has been specified content: #{"\"\\"}#{map-get($context-menu-icons, $icon) + "\""}; } // Include any extra rules supplied for the pseudo-element @content; } } @mixin context-menu-icon-screen-only($position: before, $icon: false, $icons-font: $context-menu-icon-font-name) { @media screen { @include context-menu-icon-without-mediaquery($position, $icon, $icons-font) { @content; } } } // For adding font icons to elements using CSS pseudo-elements // http://jaydenseric.com/blog/fun-with-sass-and-font-icons @mixin context-menu-icon($position: before, $icon: false, $icons-font: $context-menu-icon-font-name, $screen-only: false) { @if $screen-only { @include context-menu-icon-screen-only($position, $icon, $icons-font) { @content; } } @else { @include context-menu-icon-without-mediaquery($position, $icon, $icons-font) { @content; } } } @mixin base-context-menu-icon($icon-font: "context-menu-icons") { &::before { color: $context-menu-icon-color; font-family: $icon-font; font-style: normal; font-weight: normal; font-size: $context-menu-icon-size; left: 0; line-height: 1; position: absolute; text-align: center; top: 50%; transform: translateY(-50%); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; width: 2em; } } @mixin context-menu-item-icon($icon: false) { @include context-menu-icon(before, $icon); } ================================================ FILE: src/sass/icons/_variables.scss ================================================ // DON'T MANUALLY EDIT THIS FILE; run `gulp build-icons` instead. $context-menu-icons-cachebust: "2dq4x"; $context-menu-icons: ( add: "EA01", copy: "EA02", cut: "EA03", delete: "EA04", edit: "EA05", loading: "EA06", paste: "EA07", quit: "EA08", ); ================================================ FILE: src/sass/icons/_variables.scss.tpl ================================================ // DON'T MANUALLY EDIT THIS FILE; run `gulp build-icons` instead. $context-menu-icons-cachebust: "<%= (0|Math.random()*9e6).toString(36) %>"; $context-menu-icons: (<% _.each(glyphs, function(glyph) { %> <%= glyph.name %>: "<%= glyph.unicode[0].charCodeAt(0).toString(16).toUpperCase() %>",<% }); %> ); ================================================ FILE: src/sass/jquery.contextMenu.scss ================================================ /*! * jQuery contextMenu - Plugin for simple contextMenu handling * * Version: v@VERSION * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * * Copyright (c) 2011-@YEAR SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * * Date: @DATE */ @import "variables"; @import "icons/mixins"; @import "icons"; .context-menu-icon { @include base-context-menu-icon; &.context-menu-hover:before { color: $context-menu-icon-color-hover; } &.context-menu-disabled::before { color: $context-menu-text-color-disabled; } &.context-menu-icon-loading:before { animation: cm-spin 2s infinite; } &.context-menu-icon--fa { display: list-item; font-family: inherit; line-height: inherit; @include base-context-menu-icon(FontAwesome); &.context-menu-hover:before { color: $context-menu-icon-color-hover; } &.context-menu-disabled::before { color: $context-menu-text-color-disabled; } } &.context-menu-icon--fa5 { display: list-item; font-family: inherit; line-height: inherit; i, svg { color: $context-menu-icon-color; left: 0.5em; position: absolute; top: 0.3em; } &.context-menu-hover { > i, > svg { color: $context-menu-icon-color-hover; } } &.context-menu-disabled { i, svg { color: $context-menu-text-color-disabled; } } } } .context-menu-list { background: $context-menu-background-color; border: $context-menu-border-width $context-menu-border-style $context-menu-border-color; border-radius: $context-menu-border-radius; box-shadow: $context-menu-box-shadow; display: inline-block; font-family: $context-menu-font-family; font-size: $context-menu-font-size; list-style-type: none; margin: $context-menu-container-margin; max-width: $context-menu-max-width; min-width: $context-menu-min-width; padding: $context-menu-container-padding; position: absolute; } .context-menu-item { background-color: $context-menu-background-color; box-sizing: content-box; color: $context-menu-text-color; padding: $context-menu-item-padding; position: relative; user-select: none; } .context-menu-separator { border-bottom: $context-menu-separator-height $context-menu-separator-style $context-menu-separator-color; margin: $context-menu-separator-margin; padding: 0; } .context-menu-item > label > input, .context-menu-item > label > textarea { user-select: text; } .context-menu-item.context-menu-hover { background-color: $context-menu-item-color-hover; color: $context-menu-text-color-hover; cursor: pointer; } .context-menu-item.context-menu-disabled { background-color: $context-menu-item-color-disabled; color: $context-menu-text-color-disabled; cursor: default; } .context-menu-input.context-menu-hover { cursor: default; color: $context-menu-text-color; } .context-menu-submenu:after { content: ''; border-style: solid; border-width: .25em 0 .25em .25em; border-color: transparent transparent transparent $context-menu-submenu-arrow-color; height: 0; position: absolute; right: .5em; top: 50%; transform: translateY(-50%); width: 0; z-index: 1; } /** * Inputs */ .context-menu-item.context-menu-input { padding: .3em .6em; } /* vertically align inside labels */ .context-menu-input > label > * { vertical-align: top; } /* position checkboxes and radios as icons */ .context-menu-input > label > input[type="checkbox"], .context-menu-input > label > input[type="radio"] { margin-right: .4em; position: relative; top: .12em; } .context-menu-input > label { margin: 0; } .context-menu-input > label, .context-menu-input > label > input[type="text"], .context-menu-input > label > textarea, .context-menu-input > label > select { box-sizing: border-box; display: block; width: 100%; } .context-menu-input > label > textarea { height: 7em; } .context-menu-item > .context-menu-list { display: none; /* re-positioned by js */ right: -.3em; top: .3em; } .context-menu-item.context-menu-visible > .context-menu-list { display: block; } .context-menu-accesskey { text-decoration: underline; } ================================================ FILE: test/index.html ================================================ QUnit Test Suite ================================================ FILE: test/integration/custom-command.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Click custom comand menu item triggers menu callback': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.numberOfElements('.context-menu-root>li') .is(3, '3 context menu items are shown') .click('.context-menu-root li.labels') .assert.text('#msg').to.contain('clicked: label', 'contextMenu callback was triggered') .done(); }, 'Click custom comand menu item label triggers custom action - red': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li.labels .label1') .assert.text('#msg', 'clicked: label | text: label 1', 'custom action was triggered') .done(); }, 'Click custom comand menu item label triggers custom action - blue': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li.labels .label3') .assert.text('#msg', 'clicked: label | text: label 3', 'custom action was triggered') .done(); } }; ================================================ FILE: test/integration/disabled-callback.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-callback_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .click('.context-menu-root li:last-child') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Clicking on enabled item works': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:first-child') .assert.text('#msg', 'clicked: edit', 'Enabled menu item sets text') .done(); } }; ================================================ FILE: test/integration/disabled-changing.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(3, '3 context menu items are shown') .click('.context-menu-root li:nth-child(2)') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Toggle disabled item status': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:nth-child(3)') .assert.doesntExist('.context-menu-root .disabled', 'All menu items are enabled') .click('.context-menu-root li:nth-child(2)') .assert.text('#msg', 'clicked: cut', 'Enabled menu item sets text') .done(); }, 'Toggled status is saved after menu is closed': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:nth-child(3)') .assert.doesntExist('.context-menu-root .disabled', 'All menu items are enabled') .execute(helper.closeMenu, '.context-menu-one') .wait(100) .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.doesntExist('.context-menu-root .disabled', 'All menu items are still enabled') .done(); } }; ================================================ FILE: test/integration/disabled-menu.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Disabled trigger doesnt open context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .done(); }, 'Enabled trigger opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .click('#toggle-disabled') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .$('.context-menu-root') .assert.visible('Menu is present') .assert.exists('It opens context menu') .end() .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); }, 'Repeatedly disabled trigger doesnt open context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .click('#toggle-disabled') .assert.doesntExist('.context-menu-disabled', 'Context menu trigger is enambled') .click('#toggle-disabled') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled again') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .done(); } }; ================================================ FILE: test/integration/disabled.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .click('.context-menu-root li:last-child') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Clicking on enabled item works': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:first-child') .assert.text('#msg', 'clicked: edit', 'Enabled menu item sets text') .done(); } }; ================================================ FILE: test/integration/dynamic-create.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Dynamically created context menu is shown': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic-create.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .done(); } }; ================================================ FILE: test/integration/dynamic.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Dynamically created opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic.html') .click('#add-trigger') .waitForElement('.menu-injected') .execute(helper.rightClick, '.menu-injected') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') .done(); }, '3rd dynamically created also opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic.html') .click('#add-trigger') .click('#add-trigger') .click('#add-trigger') .wait(200) //.waitForElement('.menu-injected') .assert.numberOfElements('.menu-injected') .is(3, '3 DIVs are added') .execute(helper.rightClick, '.menu-injected:last-of-type') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') .done(); } }; ================================================ FILE: test/integration/html/accesskeys.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Accesskeys Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/accesskeys_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Accesskeys Example code Example HTML Demo: Accesskeys right click me Example code Example HTML ================================================ FILE: test/integration/html/async-create.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Create Context Menu (asynchronous) Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/callback.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Callback Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/callback_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Callback Example code Example HTML Demo: Callback right click me Example code Example HTML ================================================ FILE: test/integration/html/custom-command.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom command Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/custom-command_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom command Example code Example HTML Demo: Custom command right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-callback.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Callback Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-callback_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Callback Example code Example HTML Demo: Disabled Callback right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-changing.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled changing Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-changing_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled changing Example code Example HTML Demo: Disabled changing right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-menu.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled menu Example code Example HTML right click me Enable Menu Example code Example HTML ================================================ FILE: test/integration/html/disabled.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Example code Example HTML Demo: Disabled right click me Example code Example HTML ================================================ FILE: test/integration/html/dynamic-create.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Adding new Context Menu Triggers Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/dynamic.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Adding new Context Menu Triggers Example code Example HTML jQuery.contextMenu allows you to define a <menu> before the trigger elements are available. Button Example code Example HTML ================================================ FILE: test/integration/html/html5-import.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Importing HTML5 <menu type="context"> Example code Example HTML jQuery.contextMenu allows you to import HTML5's <menu> structures to use in older browsers. right click me Example code Example HTML <menu id="html5menu" style="display:none" class="showcase"> <command label="rotate" icon="edit" onclick="alert('rotate')"> <command label="resize" onclick="alert('resize')"> <command label="twitter" onclick="alert('twitter')"> <hr> <command label="facebook" onclick="alert('facebook')"> </menu> </menu> ================================================ FILE: test/integration/html/html5-polyfill-firefox8.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: HTML5 Polyfill (Firefox) Example code Example HTML right click me Example code Example HTML <menu id="html5firefox8" type="context" > <menuitem label="rotate" onclick="alert('rotate')" hint="I'm a hint"></menuitem> <menuitem label="resize" onclick="alert('resize')"></menuitem> <menuitem label="disabled" onclick="alert('disabled')" disabled></menuitem> <menu label="share"> <menuitem label="twitter" onclick="alert('twitter')"></menuitem> <menuitem label="facebook" onclick="alert('facebook')"></menuitem> <hr> <menuitem type="checkbox" label="(checkbox) yes or no?" onclick="alert('checkbox: ' + (this.checked ? 'yep!' : 'nope'))"></menuitem> <hr> <menuitem type="radio" label="(radio) yes" radiogroup="alpha" checked onclick="alert('radio: yes')"></menuitem> <menuitem type="radio" label="(radio) no" radiogroup="alpha" onclick="alert('radio: no')"></menuitem> </menu> </menu> ================================================ FILE: test/integration/html/html5-polyfill.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: HTML5 Polyfill Example code Example HTML jQuery.contextMenu allows you to import HTML5's <menu> structures to use in older browsers. right click me Example code Example HTML <menu id="html5polyfill" type="context" style="display:none"> <command label="rotate" onclick="alert('rotate')" icon="images/cut.png"> <command label="resize" onclick="alert('resize')" icon="images/door.png"> <menu label="share"> <command label="twitter" onclick="alert('twitter')" icon="images/page_white_copy.png"> <hr> <command label="facebook" onclick="alert('facebook')" icon="images/page_white_edit.png"> <hr> <label>foo bar<input type="text" name="foo"></label> </menu> </menu> foo bar ================================================ FILE: test/integration/html/input.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Input Commands Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/keeping-contextmenu-open.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Keeping the Menu visible Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/menu-title.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Menu Title Example CSS Example code Example HTML right click me right click me right click me Example CSS Example code Example HTML <span class="context-menu-one btn btn-neutral">right click me</span> <span class="context-menu-two btn btn-neutral">right click me</span> <span class="context-menu-three btn btn-neutral">right click me</span> ================================================ FILE: test/integration/html/on-dom-element.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Context Menu on DOM Element Example code Example HTML right click me 1 right click me 2 right click me 3 right click me 4 Example code Example HTML <ul id="the-node"> <li><span class="context-menu-one btn btn-neutral">right click me 1</span></li> <li><span class="context-menu-one btn btn-neutral">right click me 2</span></li> <li>right click me 3</li> <li>right click me 4</li> </ul> ================================================ FILE: test/integration/html/sub-menus.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Submenus Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/sub-menus_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Submenus Example code Example HTML Demo: Submenus right click me Example code Example HTML ================================================ FILE: test/integration/html/trigger-custom.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom Activated Context Menu Example code Example HTML press that button Button Example code Example HTML ================================================ FILE: test/integration/html/trigger-hover-autohide.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Hover Activated Context Menu With Autohide Example code Example HTML hover over me Example code Example HTML ================================================ FILE: test/integration/html/trigger-hover.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Hover Activated Context Menu Example code Example HTML hover over me Example code Example HTML ================================================ FILE: test/integration/html/trigger-left-click.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Left-Click Trigger Example code Example HTML left click me Example code Example HTML ================================================ FILE: test/integration/html/trigger-swipe.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Swipe Trigger Example code Example HTML This demo uses the (third party) TouchSwipe plugin. swype right Example code Example HTML ================================================ FILE: test/integration/input.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); var text1 = '.context-menu-root input[name="context-menu-input-name"]'; var textArea1 = '.context-menu-root textarea[name="context-menu-input-area1"]'; var textArea2 = '.context-menu-root textarea[name="context-menu-input-area2"]'; module.exports = { 'HTML5 input-based menu is shown correctly': function (test) { test .open('file://' + pwd + '/test/integration/html/input.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(14, '14 context menu items are shown') .assert.numberOfElements('.context-menu-root input') .is(6, '6 HTML input items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); }, 'HTML5 input-based menu stores state when closed': function (test) { test .open('file://' + pwd + '/test/integration/html/input.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .type(text1, 'lorem ipsum') .type(textArea1, 'test area with height') .type(textArea2, 'shots go off') .execute(helper.closeMenu, '.context-menu-one') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.val(text1, 'lorem ipsum', 'Input text should contain entered text') .assert.val(textArea1, 'test area with height', 'Text area 1 should contain entered text') .assert.val(textArea2, 'shots go off', 'Text area 2 should contain entered text') .done(); } }; ================================================ FILE: test/integration/keeping-contextmenu-open.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Ensure context menu is shown': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .done(); }, 'Close context menu after first menu item is clicked': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.visible('.context-menu-root li:first-child', 'Menu item is present') .click('.context-menu-root li:first-child') .assert.doesntExist('#context-menu-layer', 'It closes context menu') .done(); }, 'Keep context menu open after second menu item is clicked': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.visible('.context-menu-root', 'Menu is present') .click('.context-menu-root li:last-child') .assert.exists('#context-menu-layer', 'It closes context menu') .done(); } }; ================================================ FILE: test/integration/on-dom-element.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Right-click on multiple DOM elements': function (test) { test .open('file://' + pwd + '/test/integration/html/on-dom-element.html') .execute(helper.rightClick, '#the-node li:first-child') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') // right click on the other DOM element .execute(helper.rightClick, '#the-node li:nth-child(3)') .wait(100) // wait for the old menu to close and new to reopen .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It re-opens the same context menu') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure still only one context menu is open') .done(); } }; ================================================ FILE: test/integration/trigger-custom.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Mouse hover opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/trigger-custom.html') .click('#activate-menu') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .click('#context-menu-layer') .wait(100) .assert.doesntExist('#context-menu-layer', 'Click outside closes context menu') .done(); } }; ================================================ FILE: test/integration/trigger-left-click.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Left-click opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/trigger-left-click.html') .click('.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); } }; ================================================ FILE: test/integration/trigger-right-click.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Right-click opens context menu': function (test) { test .open('file://' + pwd + '/demo.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); } }; ================================================ FILE: test/specs/accesskeys.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test accesskeys', function() { it('should navigate to submenu 2 levels deep and see correct alert for charlie', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2') browser.click('span=charlie') assert.equal(browser.alertText(), 'clicked: fold2-key3'); browser.alertAccept(); }); it('Typing on keyboard triggers "edit" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('e'); assert.equal(browser.alertText(), 'clicked: edit'); browser.alertAccept(); }); it('Typing on keyboard triggers "cut" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('c'); assert.equal(browser.alertText(), 'clicked: cut'); browser.alertAccept(); }); it('Typing on keyboard triggers "copy" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('o'); assert.equal(browser.alertText(), 'clicked: copy'); browser.alertAccept(); }); it('Typing on keyboard triggers "paste" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('p'); assert.equal(browser.alertText(), 'clicked: paste'); browser.alertAccept(); }); }); ================================================ FILE: test/specs/aync-create.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test async create', function() { it('should render async created context menu', function () { browser.url('file://' + pwd + '/test/integration/html/async-create.html'); browser.rightClick('.context-menu-one'); browser.waitForExist('#context-menu-layer'); assert.equal(3, browser.elements('.context-menu-root li').value.length); }); }); ================================================ FILE: test/specs/callback.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test callback', function() { function openCallbackMenu() { browser.url('file://' + pwd + '/test/integration/html/callback.html'); browser.rightClick('.context-menu-one'); browser.waitForExist('#context-menu-layer'); assert.equal(true, browser.isVisible('.context-menu-root'), 'menu is visible'); } it('Ensure edit menu item triggers callback', function () { openCallbackMenu(); browser.leftClick('.context-menu-root li:nth-child(1)'); assert.equal('edit was clicked', browser.alertText()); browser.alertAccept(); assert.equal(false, browser.isVisible('£context-menu-layer'), 'menu is hidden'); }); it('Ensure cut menu item triggers global callback', function () { openCallbackMenu(); browser.leftClick('.context-menu-root li:nth-child(2)'); assert.equal('global: cut', browser.alertText()); browser.alertAccept(); assert.equal(false, browser.isVisible('£context-menu-layer'), 'menu is hidden'); }); }); ================================================ FILE: test/specs/submenu.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test submenus', function() { it('should navigate to submenu 2 levels deep and see correct alert for charlie', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2') browser.click('span=charlie') assert.equal(browser.alertText(), 'clicked: fold2-key3'); browser.alertAccept(); }); it('should navigate to submenu 2 levels deep and see first menu highlighted', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2'); var elements = browser.elements('.context-menu-hover'); assert.equal(2, elements.value.length); }); }); ================================================ FILE: test/unit/contextmenu.test.js ================================================ var menuOpenCounter = 0; var menuCloseCounter = 0; var itemSelectedCounter = 0; var itemSelectedStack = []; var menuRuntime = null; function testQUnit(name, itemClickEvent, triggerEvent) { QUnit.module(name, { afterEach: function(){ destroyContextMenuAndCleanup(); } }); // before each test function createContextMenu(items, classname) { if(typeof(classname) == 'undefined'){ classname = 'context-menu'; } var $fixture = $('#qunit-fixture'); // ensure `#qunit-fixture` exists when testing with karma runner if ($fixture.length === 0) { $('').appendTo("body"); $fixture = $('#qunit-fixture'); } $fixture.append("right click me!"); if(!items){ items = { copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }; } $.contextMenu({ selector: '.' + classname, events: { show: function(opt) { menuRuntime = opt; menuOpenCounter = menuOpenCounter + 1; }, hide: function() { menuCloseCounter = menuCloseCounter + 1; } }, callback: function(key, options) { itemSelectedCounter = itemSelectedCounter + 1; itemSelectedStack.push(key); }, items: items, itemClickEvent: itemClickEvent }); } // after each test function destroyContextMenuAndCleanup() { $.contextMenu('destroy'); // clean up `#qunit-fixture` when testing in karma runner var $fixture = $('#qunit-fixture'); if ($fixture.length) { $fixture.html(''); } // reset vars menuOpenCounter = 0; menuCloseCounter = 0; itemSelectedCounter = 0; itemSelectedStack = []; menuRuntime = null; } QUnit.test('$.contextMenu object exists', function(assert) { assert.ok($.contextMenu, '$.contextMenu plugin is loaded'); assert.notEqual($.contextMenu, undefined, '$.contextMenu is not undefined'); }); QUnit.test('open contextMenu', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); assert.equal(menuOpenCounter, 1, 'contextMenu was opened once'); }); QUnit.test('open contextMenu at 0,0', function(assert) { createContextMenu(); $(".context-menu").contextMenu({x: 0, y: 0}); assert.equal(menuOpenCounter, 1, 'contextMenu was opened once'); }); QUnit.test('close contextMenu', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); assert.equal(menuCloseCounter, 1, 'contextMenu was closed once'); }); QUnit.test('navigate contextMenu items', function(assert) { createContextMenu(); var itemWasFocused = 0; var itemWasBlurred = 0; // listen to focus and blur events $(document.body) .on("contextmenu:focus", ".context-menu-item", function(e) { itemWasFocused = itemWasFocused + 1; }) .on("contextmenu:blur", ".context-menu-item", function(e) { itemWasBlurred = itemWasBlurred + 1; }); $(".context-menu").contextMenu(); menuRuntime.$menu.trigger('nextcommand'); // triggers contextmenu:focus assert.equal(itemWasFocused, 1, 'first menu item was focused once'); itemWasFocused = 0; menuRuntime.$menu.trigger('nextcommand'); // triggers contextmenu:blur & contextmenu:focus assert.equal(itemWasFocused, 1, 'first menu item was blurred'); assert.equal(itemWasBlurred, 1, 'second menu item was focused'); }); QUnit.test('activate contextMenu item', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); menuRuntime.$menu.trigger('nextcommand'); menuRuntime.$selected.trigger(triggerEvent); assert.equal(itemSelectedCounter, 1, 'selected menu item was clicked once'); }); QUnit.test('do not open destroyed context menu', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }); createContextMenu({ copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }, 'context-menu-two'); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); $(".context-menu-two").contextMenu(); $(".context-menu-two").contextMenu('hide'); assert.equal(menuOpenCounter, 2, 'contextMenu was opened twice'); $(".context-menu-two").contextMenu('destroy'); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); $(".context-menu-two").contextMenu(); $(".context-menu-two").contextMenu('hide'); assert.equal(menuOpenCounter, 3, 'destroyed contextMenu was not opened'); }); QUnit.test('do not open context menu with no visible items', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'copy', visible: function(){return false;}}, paste: {name: 'Paste', icon: 'paste', visible: function(){return false;}} }); $(".context-menu").contextMenu(); assert.equal($('.context-menu-item').is(':visible'), false, 'no menu items visible'); }); QUnit.test('visible function should only trigger once', function(assert) { var visibleTriggered = 0; createContextMenu({ copy: {name: 'Copy', icon: 'copy', visible: function(){visibleTriggered++; return true;}}, paste: {name: 'Paste', icon: 'paste', visible: function(){return false;}} }); $(".context-menu").contextMenu(); assert.equal(visibleTriggered, 1, 'selected menu wat not opened'); }); QUnit.test('items in seconds submenu to not override callbacks', function (assert) { var firstCallback = false, firstSubCallback = false, secondSubCallback = false; createContextMenu({ firstitem: { name: 'firstitem', icon: 'copy', callback : function(){ firstCallback = true; } }, firstsubmenu: { name: 'Copy', icon: 'copy', items: { firstitem : { name : "firstitem", icon : "copy", callback : function(){ firstSubCallback = true; } } } }, secondsubmenu: { name: 'Copy', icon: 'copy', items: { firstitem : { name : "firstitem", icon : "copy", callback : function(){ secondSubCallback = true; } } } } }); $('.context-menu-item').first().trigger(triggerEvent); $('.context-menu-submenu .context-menu-item').each(function(i,e){ $(e).trigger(triggerEvent) }); assert.equal(firstCallback, 1); assert.equal(firstSubCallback, 1); assert.equal(secondSubCallback, 1); }); QUnit.test('font-awesome creates icon elements', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'fas fa-beer'} }); $(".context-menu").contextMenu(); assert.equal($('i.fas.fa-beer').length, 1, 'FontAwesome tag was not created'); }); } testQUnit('contextMenu events', '', 'mouseup'); testQUnit('contextMenu events - click handler', 'click', 'click'); ================================================ FILE: wdio.conf.js ================================================ exports.config = { // // ================= // Service Providers // ================= // WebdriverIO supports Sauce Labs, Browserstack, and Testing Bot (other cloud providers // should work too though). These services define specific user and key (or access key) // values you need to put in here in order to connect to these services. // user: process.env.SAUCE_USERNAME, key: process.env.SAUCE_ACCESS_KEY, // // ================== // Specify Test Files // ================== // Define which test specs should run. The pattern is relative to the directory // from which `wdio` was called. Notice that, if you are calling `wdio` from an // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working // directory is where your package.json resides, so `wdio` will be called from there. // specs: [ './test/specs/**/*.js' ], // Patterns to exclude. exclude: [ // 'path/to/excluded/files' ], // // ============ // Capabilities // ============ // Define your capabilities here. WebdriverIO can run multiple capabilities at the same // time. Depending on the number of capabilities, WebdriverIO launches several test // sessions. Within your capabilities you can overwrite the spec and exclude options in // order to group specific specs to a specific capability. // // First, you can define how many instances should be started at the same time. Let's // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec // files and you set maxInstances to 10, all spec files will get tested at the same time // and 30 processes will get spawned. The property handles how many capabilities // from the same test should run tests. // maxInstances: 10, // // If you have trouble getting all important capabilities together, check out the // Sauce Labs platform configurator - a great tool to configure your capabilities: // https://docs.saucelabs.com/reference/platforms-configurator // capabilities: [{ // maxInstances can get overwritten per capability. So if you have an in-house Selenium // grid with only 5 firefox instance available you can make sure that not more than // 5 instance gets started at a time. maxInstances: 5, // browserName: 'chrome' }], // // =================== // Test Configurations // =================== // Define all options that are relevant for the WebdriverIO instance here // // By default WebdriverIO commands are executed in a synchronous way using // the wdio-sync package. If you still want to run your tests in an async way // e.g. using promises you can set the sync option to false. sync: true, // // Level of logging verbosity: silent | verbose | command | data | result | error logLevel: 'error', // // Enables colors for log output. coloredLogs: true, // // Saves a screenshot to a given path if a command fails. screenshotPath: './errorShots/', // // Set a base URL in order to shorten url command calls. If your url parameter starts // with "/", then the base url gets prepended. baseUrl: 'http://localhost', // // Default timeout for all waitFor* commands. waitforTimeout: 10000, // // Default timeout in milliseconds for request // if Selenium Grid doesn't send response connectionRetryTimeout: 90000, // // Default request retries count connectionRetryCount: 3, // // Initialize the browser instance with a WebdriverIO plugin. The object should have the // plugin name as key and the desired plugin options as properties. Make sure you have // the plugin installed before running any tests. The following plugins are currently // available: // WebdriverCSS: https://github.com/webdriverio/webdrivercss // WebdriverRTC: https://github.com/webdriverio/webdriverrtc // Browserevent: https://github.com/webdriverio/browserevent // plugins: { // webdrivercss: { // screenshotRoot: 'my-shots', // failedComparisonsRoot: 'diffs', // misMatchTolerance: 0.05, // screenWidth: [320,480,640,1024] // }, // webdriverrtc: {}, // browserevent: {} // }, // // Test runner services // Services take over a specific job you don't want to take care of. They enhance // your test setup with almost no effort. Unlike plugins, they don't add new // commands. Instead, they hook themselves up into the test process. // services: ['selenium-standalone'], services: ['selenium-standalone', 'sauce'], // services: ['sauce'], // // Framework you want to run your specs with. // The following are supported: Mocha, Jasmine, and Cucumber // see also: http://webdriver.io/guide/testrunner/frameworks.html // // Make sure you have the wdio adapter package for the specific framework installed // before running any tests. framework: 'mocha', // // Test reporter for stdout. // The only one supported by default is 'dot' // see also: http://webdriver.io/guide/testrunner/reporters.html reporters: ['dot'], // // Options to be passed to Mocha. // See the full list at http://mochajs.org/ mochaOpts: { ui: 'bdd' }, // // ===== // Hooks // ===== // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance // it and to build services around it. You can either apply a single function or an array of // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got // resolved to continue. // // Gets executed once before all workers get launched. // onPrepare: function (config, capabilities) { // }, // // Gets executed before test execution begins. At this point you can access all global // variables, such as `browser`. It is the perfect place to define custom commands. // before: function (capabilities, specs) { // }, // // Hook that gets executed before the suite starts // beforeSuite: function (suite) { // }, // // Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling // beforeEach in Mocha) // beforeHook: function () { // }, // // Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling // afterEach in Mocha) // afterHook: function () { // }, // // Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts. // beforeTest: function (test) { // }, // // Runs before a WebdriverIO command gets executed. // beforeCommand: function (commandName, args) { // }, // // Runs after a WebdriverIO command gets executed // afterCommand: function (commandName, args, result, error) { // }, // // Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts. // afterTest: function (test) { // }, // // Hook that gets executed after the suite has ended // afterSuite: function (suite) { // }, // // Gets executed after all tests are done. You still have access to all global variables from // the test. // after: function (result, capabilities, specs) { // }, // // Gets executed after all workers got shut down and the process is about to exit. It is not // possible to defer the end of the process using a promise. // onComplete: function(exitCode) { // } }
r
, , and of course . * Everything else will be imported as an html node, which is not interfaced with contextMenu. */ // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command switch (nodeName) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element case 'menu': item = {name: $node.attr('label'), items: {}}; counter = menuChildren(item.items, $node.children(), counter); break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command case 'a': // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command case 'button': item = { name: $node.text(), disabled: !!$node.attr('disabled'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command case 'menuitem': case 'command': switch ($node.attr('type')) { case undefined: case 'command': case 'menuitem': item = { name: $node.attr('label'), disabled: !!$node.attr('disabled'), icon: $node.attr('icon'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; case 'checkbox': item = { type: 'checkbox', disabled: !!$node.attr('disabled'), name: $node.attr('label'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', disabled: !!$node.attr('disabled'), name: $node.attr('label'), radio: $node.attr('radiogroup'), value: $node.attr('id'), selected: !!$node.attr('checked') }; break; default: item = undefined; } break; case 'hr': item = '-------'; break; case 'input': switch ($node.attr('type')) { case 'text': item = { type: 'text', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'checkbox': item = { type: 'checkbox', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), radio: !!$node.attr('name'), value: $node.val(), selected: !!$node.attr('checked') }; break; default: item = undefined; break; } break; case 'select': item = { type: 'select', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: $node.val(), options: {} }; $node.children().each(function () { item.options[this.value] = $(this).text(); }); break; case 'textarea': item = { type: 'textarea', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'label': break; default: item = {type: 'html', html: $node.clone(true)}; break; } if (item) { counter++; items['key' + counter] = item; } }); return counter; } // convert html5 menu $.contextMenu.fromMenu = function (element) { var $this = $(element), items = {}; menuChildren(items, $this.children()); return items; }; // make defaults accessible $.contextMenu.defaults = defaults; $.contextMenu.types = types; // export internal functions - undocumented, for hacking only! $.contextMenu.handle = handle; $.contextMenu.op = op; $.contextMenu.menus = menus; }); ================================================ FILE: dist/jquery.ui.position.js ================================================ /*! jQuery UI - v1.12.1 - 2016-09-16 * http://jqueryui.com * Includes: position.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ (function( factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define([ "jquery" ], factory ); } else { // Browser globals factory( jQuery ); } }(function( $ ) { $.ui = $.ui || {}; var version = $.ui.version = "1.12.1"; /*! * jQuery UI Position 1.12.1 * http://jqueryui.com * * Copyright jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/position/ */ //>>label: Position //>>group: Core //>>description: Positions elements relative to other elements. //>>docs: http://api.jqueryui.com/position/ //>>demos: http://jqueryui.com/position/ ( function() { var cachedScrollbarWidth, max = Math.max, abs = Math.abs, rhorizontal = /left|center|right/, rvertical = /top|center|bottom/, roffset = /[\+\-]\d+(\.[\d]+)?%?/, rposition = /^\w+/, rpercent = /%$/, _position = $.fn.position; function getOffsets( offsets, width, height ) { return [ parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) ]; } function parseCss( element, property ) { return parseInt( $.css( element, property ), 10 ) || 0; } function getDimensions( elem ) { var raw = elem[ 0 ]; if ( raw.nodeType === 9 ) { return { width: elem.width(), height: elem.height(), offset: { top: 0, left: 0 } }; } if ( $.isWindow( raw ) ) { return { width: elem.width(), height: elem.height(), offset: { top: elem.scrollTop(), left: elem.scrollLeft() } }; } if ( raw.preventDefault ) { return { width: 0, height: 0, offset: { top: raw.pageY, left: raw.pageX } }; } return { width: elem.outerWidth(), height: elem.outerHeight(), offset: elem.offset() }; } $.position = { scrollbarWidth: function() { if ( cachedScrollbarWidth !== undefined ) { return cachedScrollbarWidth; } var w1, w2, div = $( "" + "" ), innerDiv = div.children()[ 0 ]; $( "body" ).append( div ); w1 = innerDiv.offsetWidth; div.css( "overflow", "scroll" ); w2 = innerDiv.offsetWidth; if ( w1 === w2 ) { w2 = div[ 0 ].clientWidth; } div.remove(); return ( cachedScrollbarWidth = w1 - w2 ); }, getScrollInfo: function( within ) { var overflowX = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-x" ), overflowY = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-y" ), hasOverflowX = overflowX === "scroll" || ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), hasOverflowY = overflowY === "scroll" || ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); return { width: hasOverflowY ? $.position.scrollbarWidth() : 0, height: hasOverflowX ? $.position.scrollbarWidth() : 0 }; }, getWithinInfo: function( element ) { var withinElement = $( element || window ), isWindow = $.isWindow( withinElement[ 0 ] ), isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, hasOffset = !isWindow && !isDocument; return { element: withinElement, isWindow: isWindow, isDocument: isDocument, offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, scrollLeft: withinElement.scrollLeft(), scrollTop: withinElement.scrollTop(), width: withinElement.outerWidth(), height: withinElement.outerHeight() }; } }; $.fn.position = function( options ) { if ( !options || !options.of ) { return _position.apply( this, arguments ); } // Make a copy, we don't want to modify arguments options = $.extend( {}, options ); var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, target = $( options.of ), within = $.position.getWithinInfo( options.within ), scrollInfo = $.position.getScrollInfo( within ), collision = ( options.collision || "flip" ).split( " " ), offsets = {}; dimensions = getDimensions( target ); if ( target[ 0 ].preventDefault ) { // Force left top to allow flipping options.at = "left top"; } targetWidth = dimensions.width; targetHeight = dimensions.height; targetOffset = dimensions.offset; // Clone to reuse original targetOffset later basePosition = $.extend( {}, targetOffset ); // Force my and at to have valid horizontal and vertical positions // if a value is missing or invalid, it will be converted to center $.each( [ "my", "at" ], function() { var pos = ( options[ this ] || "" ).split( " " ), horizontalOffset, verticalOffset; if ( pos.length === 1 ) { pos = rhorizontal.test( pos[ 0 ] ) ? pos.concat( [ "center" ] ) : rvertical.test( pos[ 0 ] ) ? [ "center" ].concat( pos ) : [ "center", "center" ]; } pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; // Calculate offsets horizontalOffset = roffset.exec( pos[ 0 ] ); verticalOffset = roffset.exec( pos[ 1 ] ); offsets[ this ] = [ horizontalOffset ? horizontalOffset[ 0 ] : 0, verticalOffset ? verticalOffset[ 0 ] : 0 ]; // Reduce to just the positions without the offsets options[ this ] = [ rposition.exec( pos[ 0 ] )[ 0 ], rposition.exec( pos[ 1 ] )[ 0 ] ]; } ); // Normalize collision option if ( collision.length === 1 ) { collision[ 1 ] = collision[ 0 ]; } if ( options.at[ 0 ] === "right" ) { basePosition.left += targetWidth; } else if ( options.at[ 0 ] === "center" ) { basePosition.left += targetWidth / 2; } if ( options.at[ 1 ] === "bottom" ) { basePosition.top += targetHeight; } else if ( options.at[ 1 ] === "center" ) { basePosition.top += targetHeight / 2; } atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); basePosition.left += atOffset[ 0 ]; basePosition.top += atOffset[ 1 ]; return this.each( function() { var collisionPosition, using, elem = $( this ), elemWidth = elem.outerWidth(), elemHeight = elem.outerHeight(), marginLeft = parseCss( this, "marginLeft" ), marginTop = parseCss( this, "marginTop" ), collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, position = $.extend( {}, basePosition ), myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); if ( options.my[ 0 ] === "right" ) { position.left -= elemWidth; } else if ( options.my[ 0 ] === "center" ) { position.left -= elemWidth / 2; } if ( options.my[ 1 ] === "bottom" ) { position.top -= elemHeight; } else if ( options.my[ 1 ] === "center" ) { position.top -= elemHeight / 2; } position.left += myOffset[ 0 ]; position.top += myOffset[ 1 ]; collisionPosition = { marginLeft: marginLeft, marginTop: marginTop }; $.each( [ "left", "top" ], function( i, dir ) { if ( $.ui.position[ collision[ i ] ] ) { $.ui.position[ collision[ i ] ][ dir ]( position, { targetWidth: targetWidth, targetHeight: targetHeight, elemWidth: elemWidth, elemHeight: elemHeight, collisionPosition: collisionPosition, collisionWidth: collisionWidth, collisionHeight: collisionHeight, offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], my: options.my, at: options.at, within: within, elem: elem } ); } } ); if ( options.using ) { // Adds feedback as second argument to using callback, if present using = function( props ) { var left = targetOffset.left - position.left, right = left + targetWidth - elemWidth, top = targetOffset.top - position.top, bottom = top + targetHeight - elemHeight, feedback = { target: { element: target, left: targetOffset.left, top: targetOffset.top, width: targetWidth, height: targetHeight }, element: { element: elem, left: position.left, top: position.top, width: elemWidth, height: elemHeight }, horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" }; if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { feedback.horizontal = "center"; } if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { feedback.vertical = "middle"; } if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { feedback.important = "horizontal"; } else { feedback.important = "vertical"; } options.using.call( this, props, feedback ); }; } elem.offset( $.extend( position, { using: using } ) ); } ); }; $.ui.position = { fit: { left: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, outerWidth = within.width, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = withinOffset - collisionPosLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, newOverRight; // Element is wider than within if ( data.collisionWidth > outerWidth ) { // Element is initially over the left side of within if ( overLeft > 0 && overRight <= 0 ) { newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; position.left += overLeft - newOverRight; // Element is initially over right side of within } else if ( overRight > 0 && overLeft <= 0 ) { position.left = withinOffset; // Element is initially over both left and right sides of within } else { if ( overLeft > overRight ) { position.left = withinOffset + outerWidth - data.collisionWidth; } else { position.left = withinOffset; } } // Too far left -> align with left edge } else if ( overLeft > 0 ) { position.left += overLeft; // Too far right -> align with right edge } else if ( overRight > 0 ) { position.left -= overRight; // Adjust based on position and margin } else { position.left = max( position.left - collisionPosLeft, position.left ); } }, top: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollTop : within.offset.top, outerHeight = data.within.height, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = withinOffset - collisionPosTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, newOverBottom; // Element is taller than within if ( data.collisionHeight > outerHeight ) { // Element is initially over the top of within if ( overTop > 0 && overBottom <= 0 ) { newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; position.top += overTop - newOverBottom; // Element is initially over bottom of within } else if ( overBottom > 0 && overTop <= 0 ) { position.top = withinOffset; // Element is initially over both top and bottom of within } else { if ( overTop > overBottom ) { position.top = withinOffset + outerHeight - data.collisionHeight; } else { position.top = withinOffset; } } // Too far up -> align with top } else if ( overTop > 0 ) { position.top += overTop; // Too far down -> align with bottom edge } else if ( overBottom > 0 ) { position.top -= overBottom; // Adjust based on position and margin } else { position.top = max( position.top - collisionPosTop, position.top ); } } }, flip: { left: function( position, data ) { var within = data.within, withinOffset = within.offset.left + within.scrollLeft, outerWidth = within.width, offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = collisionPosLeft - offsetLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, myOffset = data.my[ 0 ] === "left" ? -data.elemWidth : data.my[ 0 ] === "right" ? data.elemWidth : 0, atOffset = data.at[ 0 ] === "left" ? data.targetWidth : data.at[ 0 ] === "right" ? -data.targetWidth : 0, offset = -2 * data.offset[ 0 ], newOverRight, newOverLeft; if ( overLeft < 0 ) { newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { position.left += myOffset + atOffset + offset; } } else if ( overRight > 0 ) { newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { position.left += myOffset + atOffset + offset; } } }, top: function( position, data ) { var within = data.within, withinOffset = within.offset.top + within.scrollTop, outerHeight = within.height, offsetTop = within.isWindow ? within.scrollTop : within.offset.top, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = collisionPosTop - offsetTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, top = data.my[ 1 ] === "top", myOffset = top ? -data.elemHeight : data.my[ 1 ] === "bottom" ? data.elemHeight : 0, atOffset = data.at[ 1 ] === "top" ? data.targetHeight : data.at[ 1 ] === "bottom" ? -data.targetHeight : 0, offset = -2 * data.offset[ 1 ], newOverTop, newOverBottom; if ( overTop < 0 ) { newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { position.top += myOffset + atOffset + offset; } } else if ( overBottom > 0 ) { newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { position.top += myOffset + atOffset + offset; } } } }, flipfit: { left: function() { $.ui.position.flip.left.apply( this, arguments ); $.ui.position.fit.left.apply( this, arguments ); }, top: function() { $.ui.position.flip.top.apply( this, arguments ); $.ui.position.fit.top.apply( this, arguments ); } } }; } )(); var position = $.ui.position; })); ================================================ FILE: documentation/CONTRIBUTE.md ================================================ To generate documentation install couscous and run in this folder. Use ```couscous preview``` to run local server with the documentation. http://couscous.io/docs/getting-started.html The generated html will also be copied to the tests/integration/html folder for integration tests. ================================================ FILE: documentation/couscous.yml ================================================ title: jQuery contextMenu (2.x) subTitle: Management facility for context menus. Developed for a large number of triggering objects. HTML5 Polyfill. #template: # url: https://github.com/CouscousPHP/Template-ReadTheDocs exclude: - website - website-dark baseUrl: https://swisnl.github.io/jQuery-contextMenu scripts: before: - mkdir website/dist - cp -R ../dist/* website/dist/ - git clone -b gh-pages https://github.com/swisnl/jQuery-contextMenu docs-3x - cp -R docs-3x/3.x website/ after: - rm -rf website/dist/ - rm -rf docs-3x/ - cp .couscous/generated/demo/* ../test/integration/html github: user: swisnl repo: jQuery-contextMenu # The left menu bar menu: items: introduction: text: Introduction relativeUrl: / items: author: text: Author absoluteUrl: https://www.swis.nl/over-ons/bjorn-brala demo: text: Demo # You can use relative urls relativeUrl: demo.html documentation: text: Documentation relativeUrl: docs.html items: options: text: Options relativeUrl: docs.html items: text: Defining menu items relativeUrl: docs/items.html plugin-commands: text: Plugin commands relativeUrl: docs/plugin-commands.html custom-icons: text: Customize icons relativeUrl: docs/customize.html fontawesome-icons: text: FontAwesome icons relativeUrl: demo/fontawesome-icons.html font-awesome: text: Font Awesome support relativeUrl: docs/font-awesome.html runtime-options: text: Runtime options relativeUrl: docs/runtime-options.html custom-command-types: text: Custom command types relativeUrl: docs/custom-command-types.html events: text: Events relativeUrl: docs/events.html html5-polyfill-docs: text: HTML5 polyfill relativeUrl: docs/html5-polyfill.html demo-gallery: text: Demo gallery relativeUrl: demo.html items: simple-context-menu: text: Simple Context Menu relativeUrl: demo.html fontawesome-icons: text: FontAwesome icons relativeUrl: demo/fontawesome-icons.html accesskeys: text: Accesskeys relativeUrl: demo/accesskeys.html async-create: text: Create Context Menu (asynchronous) relativeUrl: demo/async-create.html async-promise: text: Create Context Menu (promise) relativeUrl: demo/async-promise.html callback: text: Command's action (callbacks) relativeUrl: demo/callback.html custom-command: text: Custom Command Types relativeUrl: demo/custom-command.html disabled: text: Disabled Command relativeUrl: demo/disabled.html disabled-callback: text: Disabled Callback Command relativeUrl: demo/disabled-callback.html disabled-changing: text: Changing Command's disabled status relativeUrl: demo/disabled-changing.html disabled-menu: text: Disabled Menu relativeUrl: demo/disabled-menu.html dynamic: text: Adding new Context Menu Triggers relativeUrl: demo/dynamic.html dynamic-create: text: Create Context Menu on demand relativeUrl: demo/dynamic-create.html html5-import: text: Importing HTML5 menu relativeUrl: demo/html5-import.html html5-polyfill: text: HTML5 polyfill relativeUrl: demo/html5-polyfill.html html5-polyfill-firefox8: text: HTML5 polyfill (Firefox) relativeUrl: demo/html5-polyfill-firefox8.html input: text: Input Commands relativeUrl: demo/input.html keeping-contextmenu-open: text: Keeping the context menu open relativeUrl: demo/keeping-contextmenu-open.html menu-title: text: Menus with titles relativeUrl: demo/menu-title.html on-dom-element: text: Context Menu on DOM Element relativeUrl: demo/on-dom-element.html sub-menus: text: Submenus relativeUrl: demo/sub-menus.html trigger-custom: text: Custom Activated Context Menu relativeUrl: demo/trigger-custom.html trigger-hover: text: Hover Activated Context Menu relativeUrl: demo/trigger-hover.html trigger-hover-autohide: text: Hover Activated Context Menu With Autohide relativeUrl: demo/trigger-hover-autohide.html trigger-left-click: text: Left-Click Trigger relativeUrl: demo/trigger-left-click.html trigger-swipe: text: Swipe Trigger relativeUrl: demo/trigger-swipe.html ================================================ FILE: documentation/demo/accesskeys.md ================================================ --- currentMenu: accesskeys --- # Demo: Accesskeys - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/accesskeys_test.md ================================================ - [Demo: Accesskeys](#demo-accesskeys) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Accesskeys right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/async-create.md ================================================ --- currentMenu: async-create --- # Demo: Create Context Menu (asynchronous) - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/async-promise.md ================================================ --- currentMenu: async-promise --- # Demo: Submenu through promise (asynchronous) - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/callback.md ================================================ --- currentMenu: callback --- # Demo: Callback - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/callback_test.md ================================================ - [Demo: Callback](#demo-callback) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Callback right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/custom-command.md ================================================ --- currentMenu: custom-command --- # Demo: Custom command - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/custom-command_test.md ================================================ - [Demo: Custom command](#demo-custom-command) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Custom command right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-callback.md ================================================ --- currentMenu: disabled-callback --- # Demo: Disabled Callback - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-callback_test.md ================================================ - [Demo: Disabled Callback](#demo-disabled-callback) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Disabled Callback right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-changing.md ================================================ --- currentMenu: disabled-changing --- # Demo: Disabled changing - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-changing_test.md ================================================ - [Demo: Disabled changing](#demo-disabled-changing) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Disabled changing right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled-menu.md ================================================ --- currentMenu: disabled-menu --- # Demo: Disabled menu - [Example code](#example-code) - [Example HTML](#example-html) right click me Enable Menu ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled.md ================================================ --- currentMenu: disabled --- # Demo: Disabled - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/disabled_test.md ================================================ - [Demo: Disabled](#demo-disabled) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Disabled right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/dynamic-create.md ================================================ --- currentMenu: dynamic-create --- # Demo: Create Context Menu on Demand - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/dynamic.md ================================================ --- currentMenu: dynamic --- # Demo: Adding new Context Menu Triggers - [Example code](#example-code) - [Example HTML](#example-html) `jQuery.contextMenu` allows you to define a <menu> before the trigger elements are available. Button ## Example code ## Example HTML ================================================ FILE: documentation/demo/fontawesome-icons.md ================================================ --- currentMenu: fontawesome-icons --- # Demo: FontAwesome icons - [Example code](#example-code) - [Example HTML](#example-html) The menu allows you to use [FontAwesome](http://fontawesome.io/) [icons](http://fontawesome.io/icons/) in your menu. Just include the CSS for FontAwesome and you are ready to go. right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/html5-import.md ================================================ --- currentMenu: html5-import --- # Demo: Importing HTML5 `` - [Example code](#example-code) - [Example HTML](#example-html) `jQuery.contextMenu` allows you to import HTML5's `` structures to use in older browsers. right click me ## Example code ## Example HTML ```html ``` ================================================ FILE: documentation/demo/html5-polyfill-firefox8.md ================================================ --- currentMenu: html5-polyfill-firefox8 --- # Demo: HTML5 Polyfill (Firefox) - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ```html ``` ================================================ FILE: documentation/demo/html5-polyfill.md ================================================ --- currentMenu: html5-polyfill --- # Demo: HTML5 Polyfill - [Example code](#example-code) - [Example HTML](#example-html) `jQuery.contextMenu` allows you to import HTML5's <menu> structures to use in older browsers. right click me ## Example code ## Example HTML ```html foo bar ``` foo bar ================================================ FILE: documentation/demo/input.md ================================================ --- currentMenu: input --- # Demo: Input Commands - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/keeping-contextmenu-open.md ================================================ --- currentMenu: keeping-contextmenu-open --- # Demo: Keeping the Menu visible - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/menu-promise.md ================================================ --- currentMenu: menu-promise --- # Demo: Submenu through promise (asynchronous) - [Example code](#example-code) - [Example HTML](#example-html) right-clickable context menus immediate: async: ## Example code ## Example HTML ================================================ FILE: documentation/demo/menu-title.md ================================================ --- currentMenu: menu-title --- # Demo: Menu Title - [Example CSS](#example-css) - [Example code](#example-code) - [Example HTML](#example-html) right click me right click me right click me ## Example CSS ## Example code ## Example HTML ```html right click me right click me right click me ``` ================================================ FILE: documentation/demo/on-dom-element.md ================================================ --- currentMenu: on-dom-element --- # Demo: Context Menu on DOM Element - [Example code](#example-code) - [Example HTML](#example-html) right click me 1 right click me 2 right click me 3 right click me 4 ## Example code ## Example HTML ```html right click me 1 right click me 2 right click me 3 right click me 4 ``` ================================================ FILE: documentation/demo/sub-menus-promise.md ================================================ --- currentMenu: sub-menus-promise --- # Demo: Submenus with promise - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/sub-menus.md ================================================ --- currentMenu: sub-menus --- # Demo: Submenus - [Example code](#example-code) - [Example HTML](#example-html) right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/sub-menus_test.md ================================================ - [Demo: Submenus](#demo-submenus) - [Example code](#example-code) - [Example HTML](#example-html) # Demo: Submenus right click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-custom.md ================================================ --- currentMenu: trigger-custom --- # Demo: Custom Activated Context Menu - [Example code](#example-code) - [Example HTML](#example-html) press that button Button ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-hover-autohide.md ================================================ --- currentMenu: trigger-hover-autohide --- # Demo: Hover Activated Context Menu With Autohide - [Example code](#example-code) - [Example HTML](#example-html) hover over me ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-hover.md ================================================ --- currentMenu: trigger-hover --- # Demo: Hover Activated Context Menu - [Example code](#example-code) - [Example HTML](#example-html) hover over me ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-left-click.md ================================================ --- currentMenu: trigger-left-click --- # Demo: Left-Click Trigger - [Example code](#example-code) - [Example HTML](#example-html) left click me ## Example code ## Example HTML ================================================ FILE: documentation/demo/trigger-swipe.md ================================================ --- currentMenu: trigger-swipe --- # Demo: Swipe Trigger - [Example code](#example-code) - [Example HTML](#example-html) This demo uses the (third party) [TouchSwipe](https://github.com/mattbryson/TouchSwipe-Jquery-Plugin) plugin. swype right ## Example code ## Example HTML ================================================ FILE: documentation/demo.md ================================================ --- currentMenu: simple-context-menu --- # Demo: Simple Context Menu - [Example code: Simple Context Menu](#example-code-simple-context-menu) - [Example HTML: Simple Context Menu](#example-html-simple-context-menu) - [jQuery Context Menu Demo Gallery](#jquery-context-menu-demo-gallery) right click me ## Example code: Simple Context Menu ## Example HTML: Simple Context Menu ## jQuery Context Menu Demo Gallery * [Simple Context Menu](demo.html) * [Context Menu on DOM Element](demo/on-dom-element.html) * [Adding new Context Menu Triggers](demo/dynamic.html) * [Create Context Menu on demand](demo/dynamic-create.html) * [Create Context Menu (asynchronous)](demo/async-create.html) * [Keeping the context menu open](demo/keeping-contextmenu-open.html) * [Command's action (callbacks)](demo/callback.html) * [Left-Click Trigger](demo/trigger-left-click.html) * [Swipe Trigger](demo/trigger-swipe.html) * [Hover Activated Context Menu](demo/trigger-hover.html) * [Hover Activated Context Menu With Autohide](demo/trigger-hover-autohide.html) * [Custom Activated Context Menu](demo/trigger-custom.html) * [Disabled Menu](demo/disabled-menu.html) * [Disabled Command](demo/disabled.html) * [Disabled Callback Command](demo/disabled-callback.html) * [Changing Command's disabled status](demo/disabled-changing.html) * [Accesskeys](demo/accesskeys.html) * [Submenus](demo/sub-menus.html) * [Input Commands](demo/input.html) * [Custom Command Types](demo/custom-command.html) * [Menus with titles](demo/menu-title.html) * [Importing HTML5 ](demo/html5-import.html) * [HTML5 Polyfill](demo/html5-polyfill.html) * [HTML5 Polyfill (Firefox)](demo/html5-polyfill-firefox8.html) ================================================ FILE: documentation/docs/custom-command-types.md ================================================ --- currentMenu: custom-command-types --- # Custom Command Types Besides the built-in command types custom handlers can be defined. The command generator must be placed in `$.contextMenu.types`. It is identified by the key given in that object. The generator function is executed in the context of the new command's `` within the menu. item is the object passed at creation. Use this to pass values from your definition to the generator. `opt` is the current menu level, `root` is the menu's root-level `opt` (relevant for sub-menus only). A custom command type can be whatever you like it to be, it can behave how ever you want it to behave. Besides the keyboard interaction paradigm (`up`, `down`, `tab`, `escape`) key-events are passed on to the `` which can be accessed via `$(this).on('keydown', …);` Note that you'll probably want to disable default action handling (click, pressing enter) in favor of the custom command's behavior. ```javascript $.contextMenu.types.myType = function(item, opt, root) { $('' + item.customName + '').appendTo(this); this.on('contextmenu:focus', function(e) { // setup some awesome stuff }).on('contextmenu:blur', function(e) { // tear down whatever you did }).on('keydown', function(e) { // some funky key handling, maybe? }); }; $.contextMenu({ selector: '.context-menu-custom', items: { label: {type: "myType", customName: "Foo Bar"} } }); ``` ================================================ FILE: documentation/docs/customize.md ================================================ --- currentMenu: custom-icons --- ## Customize icons You can add icons to src/icons and run ``gulp build-icons``. This will make the icons available for use in the contextmenu using the icon property. So for example the file checkmark.svg wil result in the CSS context-menu-icon-checkmark which you can use by using the [icon option](items#icon) when defining a menu item. Is is also possible to just use FontAwesome icons, see the [demo of FontAwesome](https://swisnl.github.io/jQuery-contextMenu/demo/fontawesome-icons). ### Example ```javascript var items = { firstCommand: { name: "Paste", icon: "checkmark" // Class context-menu-icon-checkmark is used on the menu item. This is generated from checkmark.svg } } ``` Font-Awesome icons used from [encharm/Font-Awesome-SVG-PNG](https://github.com/encharm/Font-Awesome-SVG-PNG). You can download more there if you like. Finally, you will need to re-build the CSS using [`sass`](http://sass-lang.com), otherwise you may see mismatchings between the icon references. Use the command ``gulp css`` to re-build the CSS in the dist directory. The new CSS files will contain the icons you added. ## Customize CSS You can use the _variables.scss to adjust variables on pretty much everything you want to change. ================================================ FILE: documentation/docs/events.md ================================================ --- currentMenu: events --- # Events - [contextmenu](#contextmenu) - [prevcommand](#prevcommand) - [nextcommand](#nextcommand) - [contextmenu:hide](#contextmenuhide) - [contextmenu:focus](#contextmenufocus) - [contextmenu:blur](#contextmenublur) - [keydown](#keydown) List of events that are triggered on the menu. You can manually trigger some events to control the menu. ## contextmenu `contextmenu` : Trigger context menu to be shown for a trigger object. Available on trigger object. The Event must be supplied with coordinates for the menu: `{pageX: 123, pageY:123}` ``` $('.context-menu-one').first().trigger( $.Event('contextmenu', {pageX: 123, pageY: 123}) ); $('.context-menu-one').first().trigger("contextmenu"); ``` will invoke `determinePosition` to position the menu. ## prevcommand `prevcommand` : Select / highlight the previous possible command Available on context menu. ``` opt.$menu.trigger("prevcommand"); ``` ## nextcommand `nextcommand` : Select / highlight the next possible command Available on context menu. ``` opt.$menu.trigger("nextcommand"); ``` ## contextmenu:hide `contextmenu:hide` : Hide the menu Available on context menu. ``` opt.$menu.trigger("contextmenu:hide"); ``` ## contextmenu:focus `contextmenu:focus` : React to a command item being focused Triggered on context menu item when mouse or keyboard interaction lead to a "hover state" for that command item. ``` $(document.body).on("contextmenu:focus", ".context-menu-item", function(e){ console.log("focus:", this); } ); ``` ## contextmenu:blur `contextmenu:blur` : Available on each context menu item. Triggered on context menu item when mouse or keyboard interaction lead from a "hover state" to "default state" for that command item. ``` $(document.body).on("contextmenu:blur", ".context-menu-item", function(e){ console.log("blur:", this); } ); ``` ## keydown `keydown` : Available on each context menu item. Triggered on context menu item when keyboard interaction could not be handled by jQuery.contextMenu. ``` $(document.body).on("keydown", ".context-menu-item", function(e){ console.log("key:", e.keyCode); } ); ``` ================================================ FILE: documentation/docs/font-awesome.md ================================================ --- currentMenu: font-awesome ------------------------- ## Customize icons It is possible to use font-awesome icons if you like. You need to include the [Font Awesome CSS](https://www.bootstrapcdn.com/fontawesome/) in your application. That will enable you to use the icon classes to use those icons. Check out the [demo](https://swisnl.github.io/jQuery-contextMenu/demo/fontawesome-icons) ## Bring your own icons It is also possible to use your own SVG icons if you like, you can [customize](customize) this by using the SASS files. ## Recent Font Awesome support The contextmenu supports the new Font Awesome by including the correct font-awesome CSS and adding the new classes as icon for a menu item. When using for example `fas fa-beer` it will crete the `i` tag for the icon in the menu and adjust CSS accordingly. ================================================ FILE: documentation/docs/html5-polyfill.md ================================================ --- currentMenu: html5-polyfill --- # HTML5 `` shiv/polyfill - [HTML5 `` import](#html5-menu-import) - [HTML5 `` shiv/polyfill](#html5-menu-shivpolyfill) ## HTML5 `` import considering the following HTML `$.contextMenu.fromMenu($('#html5menu'))` will return a proper items object. ``` ``` `$.contextMenu.fromMenu()` will properly import (and thus handle) the following elements. Everything else is imported as `{type: "html"}` ``` (W3C Specification) (Firefox) the text ``` The `` must be hidden but not removed, as all command events (clicks) are passed-thru to the original command element! Note: While the specs note ``s to be rendered as regular commands, `$.contextMenu` will render an actual ``. ## HTML5 `` shiv/polyfill Engaging the HTML5 polyfill (ignoring `$.contextMenu` if context menus are available natively): ``` $(function(){ $.contextMenu("html5"); }); ``` Engaging the HTML5 polyfill (ignoring browser native implementation): ``` $(function(){ $.contextMenu("html5", true); }); ``` ================================================ FILE: documentation/docs/input-helpers.md ================================================ --- currentMenu: input-helpers --- # Helpers - [Import values for ``](#import-values-for-input) - [Export values from ``](#export-values-from-input) ## Import values for `` To fill input commands with values from a map: ``` {events: { hide: function(opt){ $.contextMenu.getInputValues(opt, {command1: "foo", command2: "bar"}); } } } ``` To fill input commands with values from data-attributes: ``` {events: { hide: function(opt){ $.contextMenu.getInputValues(opt, this.data()); } } } ``` ## Export values from `` To fetch values from input commands: ``` {events: { hide: function(opt){ var values = $.contextMenu.setInputValues(opt} } } ``` To save values from input commands to data-attributes: ``` {events: { hide: function(opt){ $.contextMenu.setInputValues(opt, this.data()); } } } ``` ================================================ FILE: documentation/docs/items.md ================================================ --- currentMenu: items --- # Items - [options.items](#optionsitems) - [name](#name) - [callback](#callback) - [className](#classname) - [icon](#icon) - [disabled](#disabled) - [visible](#visible) - [type](#type) - [events](#events) - [value](#value) - [selected](#selected) - [radio](#radio) - [options](#options) - [height](#height) - [items](#items) - [accesskey](#accesskey) The items map contains the commands to list in the menu. Each command has a unique key identifying an item object. The value may either be an item (properties explained below), or a string (which will insert a separator, disregarding the string's content). It is also possible to define a seperator the same as an item, and use the `type`:`cm_separator` to define it. ```javascript var items = { firstCommand: itemOptions, separator1: "-----", separator2: { "type": "cm_separator" }, command2: itemOptions } ``` Since 2.3 it is also possible to use a promise as item, so you can build submenu's based on a snynchronous promis. Check out the [demo using a promise](demo/async-promise.md) for an example how to use this. The example uses jQuery deferred, but any promise should do. Promised can only be used in combination with the [build option](docs#build). ## options.items ### name Specify the human readable name of the command in the menu. This is used as the label for the option. `name`: `string` #### Example ```javascript var items = { firstCommand: { name: "Copy" } } ``` ### isHtmlName When truthy, the defined `name` value is HTML. The value will be rendered using `$.html()` instead of `$.text()`. __Note: Cannot be used with the [accesskey](#accesskey) option in the same item.__ `isHtmlName`: `boolean` #### Example ```javascript var items = { firstCommand: { name: "Copy Text", isHtmlName: true } } ``` ### callback Specifies the callback to execute if clicked on The Callback is executed in the context of the triggering object. The first argument is the key of the command. The second argument is the options object. The Callback may return false to prevent the menu from being hidden. If no callback and no default callback is specified, the item will not have an action `callback`: `function(itemKey, opt, originalEvent)` #### Example ```javascript var items = { firstCommand: { name: "Copy", callback: function(itemKey, opt, e){ // Alert the key of the item and the trigger element's id. alert("Clicked on " + itemKey + " on element " + opt.$trigger.id); // Do not close the menu after clicking an item return false; } } } ``` ### className Specifies additional classNames to add to the menu item. Seperate multiple classes by using spaces. `className`: `string` #### Example ```javascript var items = { firstCommand: { name: "Copy", className: 'contextmenu-item-custom contextmenu-item-custom__highlight' } } ``` ### icon Specifies the icon class to set for the item. When using a string icons must be defined in CSS with selectors like `.context-menu-item.context-menu-icon-edit`, where `edit` is the icon class specified. When using a callback you can return a class string to use that as the class on the item. You can also modify the element by using the `$itemElement` argument. `icon`: `string` or `function(opt, $itemElement, itemKey, item)` #### Example ```javascript var items = { firstCommand: { name: "Copy", icon: function(opt, $itemElement, itemKey, item){ // Set the content to the menu trigger selector and add an bootstrap icon to the item. $itemElement.html(' ' + opt.selector); // Add the context-menu-icon-updated class to the item return 'context-menu-icon-updated'; } }, secondCommand: { name: "Paste", icon: "paste" // Class context-menu-icon-paste is used on the menu item. } } ``` ### disabled Specifies if the command is disabled (`true`) or enabled (`false`). May be a callback returning a `boolean`. The callback is executed in the context of the triggering object (so this inside the function refers to the element the context menu was shown for). The first argument is the `key` of the command. The second argument is the `options object`. `disabled`: `boolean` or `function(itemKey, opt)` #### Example ```javascript var items = { firstCommand: { name: "Copy", disabled: function(key, opt){ // Disable this item if the menu was triggered on a div if(opt.$trigger.nodeName === 'div'){ return true; } } } } ``` ### visible Specifies if the command is visible (`true`) or not (`false`). May be a callback returning a boolean. The callback is executed in the context of the triggering object (so this inside the function refers to the element the context menu was shown for). The first argument is the key of the command. The second argument is the `options object`. `visible`: `boolean` or `function(itemKey, opt)` #### Example ```javascript var items = { firstCommand: { name: "Copy", visible: function(key, opt){ // Hide this item if the menu was triggered on a div if(opt.$trigger.nodeName === 'div'){ return false; } } } } ``` ### type Specifies the type of the command. `type`: `null`, `undefined`, `text`, `textarea`, `checkbox`, `radio`, `select`, `html` default: `null` Value | Description ---- | ---- `null`, `undefined` , `""` | The command is a simple clickable item. `"text"` | Makes the command an `` of type `text`.The name followed by the `` are encapsulated in a ``. `"textarea"` | Makes the command a ``. The name followed by the `` are encapsulated in a ``. `"checkbox"` | Makes the command an `` of type checkbox. The name preceeded by the `` are encapsulated in a ``. The checkbox-element is moved to the icon space `"radio"` | Makes the command an `` of type radio. The name preceeded by the `` are encapsulated in a ``. The radio-element is moved to the icon space `"select"` | Makes the command a ``. The name followed by the `` are encapsulated in a ``. `"html"` | Makes an non-command element. When you select `type: 'html'` add the html to the `html` property. So: `{ item: { type: 'html', html: 'html!' } }`. You can also just use the item name with the [`isHtmlName`](isHtmlName) property. #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { name: { name: "Text", type: 'text', value: "Hello World", events: { keyup: function(e) { // add some fancy key handling here? window.console && console.log('key: '+ e.keyCode); } } }, sep1: "---------", // yesno: { name: "Boolean", type: 'checkbox', selected: true }, sep2: "---------", // radio1: { name: "Radio1", type: 'radio', radio: 'radio', value: '1' }, radio2: { name: "Radio2", type: 'radio', radio: 'radio', value: '2', selected: true }, sep3: "---------", // select: { name: "Select", type: 'select', options: {1: 'one', 2: 'two', 3: 'three'}, selected: 2 }, // area1: { name: "Textarea with height", type: 'textarea', value: "Hello World", height: 40 }, area2: { name: "Textarea", type: 'textarea', value: "Hello World" }, sep4: "---------", key: { name: "Something Clickable", callback: $.noop } } }); ``` ### events Events to register on `` elements. The contents of the options object are passed to jQuery event.data. __Only used with [types](#type) `text`, `textarea`, `radio`, `checkbox` and `select`.__ `events`: `object` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', events: { command1: { name: "Foobar", type: "text", events: { keyup: function(e){ alert(e.keyCode); alert(e.data.$trigger.attr("id")); } } } } }); ``` ### value The value of the `` element. __Only used with [types](#type) `text`, `textarea`, `radio`.__ `value`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', command1: { name: "Foobar", type: "text", value: "default value" } }); ``` ### selected The selected option of a `select` element and the checked property for `checkbox` and `radio` types. __Only used with [types](#type) `select`, `checkbox`, `radio`.__ `selected`: `string` or `boolean` Value | Description ---- | ---- `boolean` | Use with `checkbox` and `radio` to check. `string` | Use with `select` to select that option. #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // select: { name: "Select", type: 'select', options: {1: 'one', 2: 'two', 3: 'three'}, selected: "2" } } }); ``` ### radio Specifies the group of the radio elements. __Only used with [type](#type) `radio`.__ `radio`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // radio1: { name: "Radio1", type: 'radio', radio: 'radio', value: '1' }, radio2: { name: "Radio2", type: 'radio', radio: 'radio', value: '2', selected: true } } }); ``` ### options Specifies the `` elements for the `` element. __Only used with [type](#type) `select`.__ `options`: `object` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // select: { name: "Select", type: 'select', options: {1: 'one', 2: 'two', 3: 'three'}, selected: "2" } } }); ``` ### height The height in pixel `` element. If not specified, the height is defined by CSS. __Only used with [type](#type) `textarea`.__ `height`: `int` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // myTextarea: { name: "Textarea", type: 'textarea', height: 200 } } }); ``` ### items Commands to show in a sub-menu. You can nest as many as you like. `items`: `object` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items: { // myItemWithSubmenu: { name: "Textarea", { items { mySubmenu { name: "Command 1" callback: function(key, opt){ alert("Clicked on " + key); } } } } } } }); ``` ### accesskey Character(s) to be used as accesskey. Considering `a b c` $.contextMenu will first try to use »a« as the accesskey, if already taken, it'll fall through to »b«. Words are reduced to the first character, so »hello world« is treated as »h w«. Note: Accesskeys are treated unique throughout one menu. This means an item in a sub-menu can't occupy the same accesskey as an item in the main menu. `accesskey`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', accesskey: 'a' callback: function(itemKey, opt){ alert('I pressed a!'); } }); ``` ### dataAttr Allows to pass data attributes (`data-*`) that get applied to the menu item. It should be passed as an object with key and a value. #### Example ```javascript var items = { firstCommand: { name: "Copy", dataAttr: { menuTitle: "My custom title" } } } ``` ================================================ FILE: documentation/docs/plugin-commands.md ================================================ --- currentMenu: plugin-commands --- # Plugin commands - [Disable a contextMenu trigger](#disable-a-contextmenu-trigger) - [Enable a contextMenu trigger](#enable-a-contextmenu-trigger) - [Manually show a contextMenu](#manually-show-a-contextmenu) - [Manually hide a contextMenu](#manually-hide-a-contextmenu) - [Unregister all contextMenus](#unregister-all-contextmenus) ## Disable a contextMenu trigger disable contextMenu to be shown on specified trigger elements ``` $(".some-selector").contextMenu(false); ``` ## Enable a contextMenu trigger enable contextMenu to be shown on specified trigger elements ``` $(".some-selector").contextMenu(true); ``` ## Manually show a contextMenu show the contextMenu of the first element of the selector (position determined by determinePosition): ``` $(".some-selector").contextMenu(); $(".some-selector").contextMenu({x: 123, y: 123}); ``` ## Manually hide a contextMenu hide the contextMenu of the first element of the selector: ``` $(".some-selector").contextMenu("hide"); Unregister contextMenu ``` ## Unregister a specific contextMenu To unregister / destroy a specific contextMenu: ``` $.contextMenu( 'destroy', selector ); ``` selector expects the (string) selector that the contextMenu was registered to ## Unregister all contextMenus To unregister / destroy all contextMenus: ``` $.contextMenu( 'destroy' ); ``` ================================================ FILE: documentation/docs/runtime-options.md ================================================ --- currentMenu: runtime-options --- # Runtime options (opt) - [$node](#node) - [$input](#input) - [$label](#label) - [$menu](#menu) - [$trigger](#trigger) - [callbacks](#callbacks) - [commands](#commands) - [inputs](#inputs) - [hasTypes](#hastypes) - [ns](#ns) The runtime options are passed to most callbacks on registration. This gives you the ability to access DOM elemnts and configuration dynamicly. One way of using these in in the general [callback](#callback) when an item is clicked. #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', items : { name: "textfield", type: "text", value: "welcome!" }, callback: function(itemKey, opt){ // Alert the classes on the item that was clicked. alert(opt.$node.attr('class')); // Alert "welcome!" alert(opt.inputs[itemsKey].$input.val()); } }); ``` ### $selected Reference to the `` command element. `$selected`: `jQuery element` ### $input Reference to the `` or `` of the command element. __Only available with [type](#type) "text", "textarea", "checkbox", "radio" and "select".__ `$input`: `jQuery element` ### $label Reference to the `` of the command element. __Only available with [type](#type) "text", "textarea", "checkbox", "radio" and "select".__ `$label`: `jQuery element` ### $menu Or the menu element of the contextmenu or the `` sub-menu element when called inside a submenu. `$node`: `jQuery element` ### $trigger The element triggering the menu. `$trigger`: `jQuery element` ### callbacks Registered [callbacks](#callback) of all commands (including those of sub-menus). Warning: If you use the same keys for an item in any place, it will overwrite that callback here. `callbacks`: `array` ### commands Registered commands (including those of sub-menus). Warning: If you use the same keys for an item in any place, it will overwrite that command here. `commands`: `array` ### inputs Registered commands of input-type (including those of sub-menus). Warning: If you use the same keys for an item in any place, it will overwrite that command here. Access a specific ``: `opt.inputs[key].$input` `inputs`: `jQuery element` ### hasTypes flag denoting if the menu contains input elements. `hasTypes`: `jQuery element` ### ns The namespace (including leading dot) all events for this contextMenu instance were registered under. `ns`: `string` ================================================ FILE: documentation/docs.md ================================================ --- currentMenu: options --- # Documentation - [Register new contextMenu](#register-new-contextmenu) - [Update contextMenu state](#update-contextmenu-state) - [Options (at registration)](#options-at-registration) - [selector](#selector) - [items](#items) - [appendTo](#appendto) - [trigger](#trigger) - [reposition](#reposition) - [delay](#delay) - [autoHide](#autohide) - [zIndex](#zindex) - [className](#classname) - [classNames](#classnames) - [animation](#animation) - [events](#events) - [position](#position) - [determinePosition](#determineposition) - [callback](#callback) - [build](#build) ## Register new contextMenu To register a new contextMenu: * Note: For SVG support use jQuery >= 1.12|2.2 ```javascript $.contextMenu( options ); ``` ## Update contextMenu state It is possible to refresh the state of the contextmenu [disabled](https://swisnl.github.io/jQuery-contextMenu/docs/items.html#disabled), [visibility](https://swisnl.github.io/jQuery-contextMenu/docs/items.html#visible), [icons](https://swisnl.github.io/jQuery-contextMenu/docs/items.html#icon) and [input values](https://swisnl.github.io/jQuery-contextMenu/docs/items.html#type) through the `update` command. This will reevaluate any custom callbacks. ```javascript $('.context-menu-one').contextMenu('update'); // update single menu $.contextMenu('update') // update all open menus ``` ## Options (at registration) ### selector The jQuery selector matching the elements to trigger on. This option is mandatory. `selector`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu' }); ``` ### items Object with [items](docs/items.html) to be listed in contextMenu. See [items](docs/items.html) for a full documentation on how to build your menu items. `items`: `object` Object containing [items](docs/items.html) objects. #### Example ```javascript $.contextMenu({ selector: '.context-menu', items: { copy: { name: "Copy", callback: function(key, opt){ alert("Clicked on " + key); } } } }); ``` ### appendTo Specifies the selector string or DOMElement the generated menu is to be appended to. `appendTo`: `string` or `DOMElement` default: `document.body` #### Example ```javascript // select the container with a selector $.contextMenu({ selector: 'span.context-menu', appendTo: 'div#context-menus-container' }); // select the container with a dom element var element = document.getElementById('context-menus-container'); $.contextMenu({ selector: 'span.context-menu', appendTo: element }); ``` ### trigger Specifies what event on the element specified in the [selector](#selector) triggers the contextmenu. `trigger`: `string` default: `'right'` Value | Description ---- | ---- `right` | Right mouse button `left` | Left mouse button `hover` | Hover the element `touchstart` | Touchstart only `none` | No trigger #### Example ```javascript // trigger with left mouse button $.contextMenu({ selector: 'span.context-menu', trigger: 'left' }); // trigger on hover $.contextMenu({ selector: 'span.context-menu', trigger: 'hover' }); ``` ### hideOnSecondTrigger Flag denoting if a second trigger should close the menu, as long as the trigger happened on one of the trigger-element's child nodes. This overrides the reposition option. `hideOnSecondTrigger`: `boolean` default: `false` ### selectableSubMenu Specifies if menu items containing submenus should be clickable or not. `selectableSubMenu`: `boolean` default: `false` Value | Description ---- | ---- `true` | All Enabled menu items, even containing others are clickable `false` | Items containing subitems are not clickable #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', selectableSubMenu: true }); ``` ### reposition Specifies if a menu should be repositioned (`true`) or rebuilt (`false`) if a second [trigger](#trigger) event (like a right click) is performed on the same element (or its children) while the menu is still visible. `reposition`: `boolean` default: `true` Value | Description ---- | ---- `true` | Reposition menu when triggered `false` | Rebuild menu when triggered #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', reposition: false }); ``` ### delay Specifies the time in milliseconds to wait before showing the menu. Only applies to [trigger](#trigger): "hover" `delay`: `int` default: `200` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', delay: 500 }); ``` ### autoHide Specifies if the menu must be hidden when the mouse pointer is moved out of the [trigger](#trigger) and [menu items](#items). `autoHide`: `boolean` default: `false` Value | Description ---- | ---- `true` | Hide the menu on mouseout `false` | Do not hide the menu on mouseout #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', autoHide: true }); ``` ### zIndex Specifies the offset to add to the calculated zIndex of the [trigger](#trigger) element. Set to `0` to prevent zIndex manipulation. Can be a function that returns an int to calculate the zIndex on build. `zIndex`: `int`|`function` default: `1` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', zIndex: 10 }); $.contextMenu({ selector: 'span.context-menu', zIndex: function($trigger, opt){ return 120; }); ``` ### className Specifies additional classNames to add to the menu element. Seperate multiple classes by using spaces. `className`: `string` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', className: 'contextmenu-custom contextmenu-custom__highlight' }); ``` ### classNames Specifies the base class names of the contextmenu elements. This can be used to change the class names of some classes that might conflict with frameworks like Bootstrap. `classNames`: `object` ```javascript // Classname configuration to be able avoid conflicts in frameworks var options = { classNames : { hover: 'hover', // Item hover disabled: 'disabled', // Item disabled visible: 'visible', // Item visible notSelectable: 'not-selectable', // Item not selectable icon: 'context-menu-icon', // Base icon class iconEdit: 'context-menu-icon-edit', iconCut: 'context-menu-icon-cut', iconCopy: 'context-menu-icon-copy', iconPaste: 'context-menu-icon-paste', iconDelete: 'context-menu-icon-delete', iconAdd: 'context-menu-icon-add', iconQuit: 'context-menu-icon-quit', iconLoadingClass: 'context-menu-icon-loading' } } ``` ### animation Animation properties take effect on showing and hiding the menu. Duration specifies the duration of the animation in milliseconds. `show` and `hide` specify [jQuery methods](http://api.jquery.com/category/effects/) to show and hide elements. `animation`: `object` default: `{duration: 500, show: 'slideDown', hide: 'slideUp'}` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', animation: `{duration: 250, show: 'fadeIn', hide: 'fadeOut'}` }); ``` ### events The `show` and `hide` events are triggered *before* the menu is shown or hidden. The event handlers are executed in the context of the triggering object. This will thus reference the jQuery handle of the [trigger](#trigger) object. A reference to the current options object is passed, the options object is a collection of current options and references to the DOM nodes of the menu. The event handlers may return `false` to prevent the `show` or `hide` process. `events`: `object` Value | Description ---- | ---- `events.preShow` | Called before show of the contextmenu, when returning false default browser context menu is shown `events.show` | Called on show of the contextmenu `events.hide` | Called before hide of the contextmenu `events.activated` | Called after activation of the contextmenu #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', events: { show : function(options){ // Add class to the menu this.addClass('currently-showing-menu'); // Show an alert with the selector of the menu if( confirm('Open menu with selector ' + options.selector + '?') === true ){ return true; } else { // Prevent the menu to be shown. return false; } }, hide : function(options){ if( confirm('Hide menu with selector ' + options.selector + '?') === true ){ return true; } else { // Prevent the menu to be hidden. return false; } }, activated : function(options){ if( confirm('Hide menu with selector ' + options.selector + '?') === true ){ console.log('Menu Activated'); } } }); ``` ### position Callback to override the position of the context menu. The function is executed in the context of the trigger object. The first argument is the `$menu` jQuery object, which is the menu element. The second and third arguments are `x` and `y` coordinates provided by the `show` event. The `x` and `y` may either be integers denoting the offset from the top left corner, `undefined`, or the string `"maintain"`. If the string `"maintain"` is provided, the current position of the `$menu` must be used. If the coordinates are `undefined`, appropriate coordinates must be determined. An example of how this can be achieved is provided with [determinePosition](#determinePosition). `position`: `function(opt.$menu, x, y)` Value `x` or `y` | Description ---- | ---- `int` | Offset in pixels from top-left of trigger element. `"maintain"` | Maintain current `x` or `y` coordinate `undefined` | Unknown, [determinePosition](#determinePosition) is called. #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', position: function(opt, x, y){ opt.$menu.css({top: 123, left: 123}); } }); ``` ### determinePosition Determine the position of the menu in respect to the given [trigger](#trigger) object, this function is called when there is no `x` and `y` set on the [position](#position) call. `determinePosition`: `function(opt.$menu)` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', determinePosition: function($menu){ // Position using jQuery.ui.position // http://api.jqueryui.com/position/ $menu.css('display', 'block') .position({ my: "center top", at: "center bottom", of: this, offset: "0 5"}) .css('display', 'none'); } }); ``` ### callback Specifies the default callback to be used in case an [item](#items) does not expose its own callback. The default callback behaves just like item.callback. `callback`: `function(itemKey, opt)` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', callback: function(itemKey, opt){ // Alert the key of the item and the trigger element's id. alert("Clicked on " + itemKey + " on element " + opt.$trigger.attr("id")); // Do not close the menu after clicking an item return false; } }); ``` ### build The callback is executed with two arguments given: the jQuery reference to the triggering element and the original contextmenu event. It is executed without context (so this won't refer to anything useful). If the build callback is found at registration, the menu is not built right away. The menu creation is delayed to the point where the menu is actually called to show. Dynamic menus don't stay in the DOM. After a menu created with build is hidden, its DOM-footprint is destroyed. With build, only the options [selector](#selector) and [trigger](#trigger) may be specified in the [options](#options-at-registration) object. All other options need to be returned from the build callback. the build callback may return a boolean false to signal contextMenu to not display a context menu `build`: `function($triggerElement, event)` #### Example ```javascript $.contextMenu({ selector: 'span.context-menu', build: function($triggerElement, e){ return { callback: function(){}, items: { menuItem: {name: "My on demand menu item"} } }; } }); ``` ### itemClickEvent Allows the selection of the `click` event instead of the `mouseup` event to handle the user mouse interaction with the contexMenu. The default event is `mouseup`. Set the option to `"click"` to change to the `click` event. `itemClickEvent`: `"click"` This option is global: the first contexMenu registered sets it. To change it afterwards all the contextMenu have to be unregistered with `$.contextMenu( 'destroy' );` before the change has effect again. ================================================ FILE: documentation/index.md ================================================ --- currentMenu: introduction --- # [jQuery contextMenu](https://github.com/swisnl/jQuery-contextMenu) ## Contextmenu plugin & polyfill - [Features](#features) - [Authors](#authors) - [License](#license) The contextMenu Plugin was designed for web applications in need of menus on a possibly large amount of objects. Unlike implementations as [a beautiful site's](http://abeautifulsite.net/blog/2008/09/jquery-context-menu-plugin/) or [trendskitchens'](http://www.trendskitchens.co.nz/jquery/contextmenu/) this contextMenu treats the menu as the primary object. That means, that a single menu is defined that can be used by multiple objects. Unlike the mentioned plugins, contextMenu doesn't need to bind itself to triggering objects. This allows injecting and removing triggers without having to re-initialize or update contextMenu.  contextMenu can provide a simple list of clickable commands, or offer an in-menu form. This makes very simple attribute modification possible. See the [input example](demo/input.html). Once a menu is registered, it cannot be altered. That means no commands can be added or removed from the menu. This allows contextMenu to keep a single definition in memory, which enables it to work with hundreds of trigger objects. contextMenu knows the two callbacks _show_ and _hide_ which can be used to update the state of commands within the menu. This allows en/disabling commands, changing icons or updating the values of contained `` elements. As of version 1.5 context menus can be created dynamically. That means the described behavior (once created, cannot be altered) still applies - but can be circumvented. Menus can be created on demand and they can be different depending on the triggering element. ## Getting started If you use NPM, install the `jquery-contextmenu` and include it in your build process. If you just want to load the library into your website you can either donwload the contents of the `dist` folder or use [cdnjs.com](https://cdnjs.com/libraries/jquery-contextmenu). ``` ``` 1. Download contents of the `dist` folder of this library to you project site, for example to `scripts/contextmenu` 2. `` 3. `` 4. `` 5. `` ## Features * trigger contextMenu with right-click, [left-click](demo/trigger-left-click.html), [hover](demo/trigger-hover.html) or own [custom trigger](demo/trigger-custom.html) events * delegated event handling removing the need for re-initialization when trigger objects are [added / removed](demo/dynamic.html) * dynamic [on-demand](demo/dynamic-create.html) menu creation * optional icons for commands * [input elements](demo/input.html) (text, textarea, checkbox, radio, select) within the menu * custom html elements (command free) * show/hide callbacks to update the state of commands * small memory footprint even with hundreds of trigger objects * adjust position of menu to fit in viewport * [enable / disable](demo/disabled-changing.html) commands * nested [sub-menus](demo/sub-menus.html) * full keyboard interaction * [HTML5 ``](demo/html5-import.html) support * CSS is for styling, javascript is not... ## Authors * [Björn Brala (SWIS)](http://www.swis.nl/over-ons/bjorn-brala) * [Rodney Rehm](http://rodneyrehm.de/en/) * [Christian Baartse](https://github.com/christiaan) (single callback per menu) * [Addy Osmani](https://github.com/addyosmani) (compatibility with native context menu in Firefox 8) ## License $.contextMenu is published under the [MIT license](http://www.opensource.org/licenses/mit-license). ================================================ FILE: documentation/website/.gitignore ================================================ /.couscous/ /bower_components/ ================================================ FILE: documentation/website/LICENSE ================================================ The MIT License (MIT) Copyright (c) 2013 Dave Snider Copyright (c) 2015 Matthieu Napoli This project was forked of the work by Dave Sniper at https://github.com/snide/sphinx_rtd_theme Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: documentation/website/README.md ================================================ - [](#) - [currentMenu: home ](#currentmenu-home) - [Read The Docs template for Couscous](#read-the-docs-template-for-couscous) - [Usage](#usage) - [Configuration](#configuration) - [Menu](#menu) - [```markdown ](#markdown) - [currentMenu: home ](#currentmenu-home-1) - [TODO](#todo) --- currentMenu: home --- # Read The Docs template for Couscous [Read The Docs](https://readthedocs.org/) builds websites using a Sphinx theme. [That theme](https://github.com/snide/sphinx_rtd_theme) is open source and released under the MIT license. This project is a port of the Sphinx theme to Couscous.  ## Usage To use the template, set it up in your `couscous.yml` configuration file: ```yaml template: url: https://github.com/CouscousPHP/Template-ReadTheDocs ``` ## Configuration Here are all the variables you can set in your `couscous.yml`: ```yaml # Base URL of the published website baseUrl: http://username.github.io/project # Used to link to the GitHub project github: user: myself repo: my-project title: My project subTitle: This is a great project. # The left menu bar menu: items: home: text: FAQ # You can use relative urls relativeUrl: doc/faq.html foo: text: Another link # Or absolute urls absoluteUrl: https://example.com ``` Note that the menu items can also contain HTML: ```yaml home: text: " FAQ" relativeUrl: doc/faq.html ``` ## Menu To set the current menu item (i.e. highlighted menu item), set the `currentMenu` key in the Markdown files: ```markdown --- currentMenu: home --- # Welcome ``` ## TODO Want to help? The original template came with a lot of SASS files and features. Some features are not ported to this Couscous template (e.g. the search). What would be good to do: - find a way to cleanly handle the SASS files and the `theme.css` (which contains the generated/minified CSS) which right now are duplicates. Relying on SASS wouldn't be a good idea though since it would be end-users have to install SASS. - clean up unused CSS and SASS style (e.g. search bar, …) In the meantime, the original minified `theme.css` is used and it works just great. ================================================ FILE: documentation/website/couscous.yml ================================================ template: directory: . github: user: CouscousPHP repo: Template-ReadTheDocs title: ReadTheDocs template subTitle: A template based on readthedocs.org # The left menu bar menu: items: home: text: Home page # You can use relative urls relativeUrl: getting-started: text: Getting Started relativeUrl: faq: text: FAQ relativeUrl: couscous: text: Couscous.io # Or absolute urls absoluteUrl: http://couscous.io ================================================ FILE: documentation/website/css/screen.css ================================================ html .icon { display:block; } .rst-content ul { margin-bottom:24px; } .rst-content ul li { line-height:24px; } h3 { color:#2980B9; } h4,h5,h6 { color:#195176; } .maintained-by-swis { float:right; margin-right:0; } .edit-on-github { float:right; /*margin-right:20px;*/ } body .wy-side-nav-search > a { padding-left:0; } body .wy-side-nav-search { background-color: #1C7F99; text-align: left; padding:0.4045em 1.618em; } .clear { clear:both; } .wy-table-responsive table.docutils { max-width: 695px; } .swis-branding { display: block; position: fixed; left:0; bottom:0; color:#b3b3b3; font-size:14px; width:300px; padding:10px; background:#343131; } div.swis-branding a:hover { background:none; text-decoration: underline; } div.swis-branding a { display: block; line-height: 46px; margin: 5px 0 -10px; text-align: left; padding:0; } div.swis-branding .swis-logo { width:70px; float:left; clear:left; margin-right:10px; } html body { padding-bottom:150px; } /***************** ****************** * HTML Element stijlen * * Vergeet deze niet over te nemen in editor.css (body ==> #tinymce) *****************/ @font-face { /* font-family: 'proxima_nova_rgregular';*/ font-family: 'Proxima Nova'; src: url('../fonts/ProximaNova-Reg-webfont.eot'); src: local('☺'), url('../fonts/ProximaNova-Reg-webfont.svg#proxima_nova_rgregular') format('svg'), url('../fonts/ProximaNova-Reg-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/ProximaNova-Reg-webfont.woff') format('woff'), url('../fonts/ProximaNova-Reg-webfont.ttf') format('truetype'); font-weight: normal; font-style: normal; } @font-face { /*font-family: 'proxima_nova_ltsemibold';*/ font-family: 'Proxima Nova'; src: url('../fonts/ProximaNova-Sbold-webfont.eot'); src: local('☺'), url('../fonts/ProximaNova-Sbold-webfont.svg#proxima_nova_ltsemibold') format('svg'), url('../fonts/ProximaNova-Sbold-webfont.eot?#iefix') format('embedded-opentype'), url('../fonts/ProximaNova-Sbold-webfont.woff') format('woff'), url('../fonts/ProximaNova-Sbold-webfont.ttf') format('truetype'); font-weight: bold; font-style: normal; } @font-face { font-family: 'Proxima Nova Bold'; src: url('../fonts/oud/proximanova-bold.eot.eot'); src: local('☺'), url('../fonts/oud/proximanova-bold.svg#proxima_nova_ltbold') format('svg'), url('../fonts/oud/proximanova-bold.eot?#iefix') format('embedded-opentype'), url('../fonts/oud/proximanova-bold.woff') format('woff'), url('../fonts/oud/proximanova-bold.ttf.ttf') format('truetype'); font-weight: bold; font-style: normal; } /** * @license * MyFonts Webfont Build ID 2970176, 2015-02-11T06:20:04-0500 * * The fonts listed in this notice are subject to the End User License * Agreement(s) entered into by the website owner. All other parties are * explicitly restricted from using the Licensed Webfonts(s). * * You may obtain a valid license at the URLs below. * * Webfont: ProximaNovaA-Light by Mark Simonson * URL: http://www.myfonts.com/fonts/marksimonson/proxima-nova/a-light/ * * Webfont: ProximaNovaS-Light by Mark Simonson * URL: http://www.myfonts.com/fonts/marksimonson/proxima-nova/s-light/ * * Webfont: ProximaNova-Light by Mark Simonson * URL: http://www.myfonts.com/fonts/marksimonson/proxima-nova/light/ * * * License: http://www.myfonts.com/viewlicense?type=web&buildid=2970176 * Licensed pageviews: 1,000,000 * Webfonts copyright: Copyright (c) Mark Simonson, 2005. All rights reserved. * * © 2015 MyFonts Inc */ /* @import must be at top of file, otherwise CSS will not work */ @import url("//hello.myfonts.net/count/2d5240"); @font-face { font-family: 'ProximaNova-Light'; src: url('../fonts/2D5240_2_0.eot'); src: url('../fonts/2D5240_2_0.eot?#iefix') format('embedded-opentype'), url('../fonts/2D5240_2_0.woff2') format('woff2'), url('../fonts/2D5240_2_0.woff') format('woff'), url('../fonts/2D5240_2_0.ttf') format('truetype'); font-weight: 100; font-style: normal; } ================================================ FILE: documentation/website/css/theme-fixes.css ================================================ .wy-side-nav-search>a, .wy-side-nav-search .wy-dropdown>a { margin-bottom: 0; } /* Code blocks */ pre > code { display: block; font-size: 12px; white-space: inherit; max-width: inherit; background: transparent; border: none; overflow-x: auto; padding: 0.5em; color: #333; } pre > code.hljs { background: transparent; } pre { border: 1px solid #e1e4e5; background: #fff; margin: 1px 0 24px 0; } ================================================ FILE: documentation/website/css/theme.css ================================================ * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box } article, aside, details, figcaption, figure, footer, header, hgroup, nav, section { display: block } audio, canvas, video { display: inline-block; *display: inline; *zoom: 1 } audio:not([controls]) { display: none } [hidden] { display: none } * { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box } html { font-size: 100%; -webkit-text-size-adjust: 100%; -ms-text-size-adjust: 100% } body { margin: 0 } a:hover, a:active { outline: 0 } abbr[title] { border-bottom: 1px dotted } b, strong { font-weight: bold } blockquote { margin: 0 } dfn { font-style: italic } ins { background: #ff9; color: #000; text-decoration: none } mark { background: #ff0; color: #000; font-style: italic; font-weight: bold } pre, code, .rst-content tt, kbd, samp { font-family: monospace, serif; _font-family: "courier new", monospace; font-size: 1em } pre { white-space: pre } q { quotes: none } q:before, q:after { content: ""; content: none } small { font-size: 85% } sub, sup { font-size: 75%; line-height: 0; position: relative; vertical-align: baseline } sup { top: -0.5em } sub { bottom: -0.25em } nav ul, nav ol, nav dl { margin: 0; padding: 0; list-style: none; list-style-image: none } nav li { list-style: none } dd { margin: 0 } img { border: 0; -ms-interpolation-mode: bicubic; vertical-align: middle; max-width: 100% } svg:not(:root) { overflow: hidden } figure { margin: 0 } form { margin: 0 } fieldset { border: 0; margin: 0; padding: 0 } label { cursor: pointer } legend { border: 0; *margin-left: -7px; padding: 0; white-space: normal } button, input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle } button, input { line-height: normal } button, input[type="button"], input[type="reset"], input[type="submit"] { cursor: pointer; -webkit-appearance: button; *overflow: visible } button[disabled], input[disabled] { cursor: default } input[type="checkbox"], input[type="radio"] { box-sizing: border-box; padding: 0; *width: 13px; *height: 13px } input[type="search"] { -webkit-appearance: textfield; -moz-box-sizing: content-box; -webkit-box-sizing: content-box; box-sizing: content-box } input[type="search"]::-webkit-search-decoration, input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0 } textarea { overflow: auto; vertical-align: top; resize: vertical } table { border-collapse: collapse; border-spacing: 0 } td { vertical-align: top } .chromeframe { margin: 0.2em 0; background: #ccc; color: #000; padding: 0.2em 0 } .ir { display: block; border: 0; text-indent: -999em; overflow: hidden; background-color: transparent; background-repeat: no-repeat; text-align: left; direction: ltr; *line-height: 0 } .ir br { display: none } .hidden { display: none !important; visibility: hidden } .visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px } .visuallyhidden.focusable:active, .visuallyhidden.focusable:focus { clip: auto; height: auto; margin: 0; overflow: visible; position: static; width: auto } .invisible { visibility: hidden } .relative { position: relative } big, small { font-size: 100% } @media print { html, body, section { background: none !important } * { box-shadow: none !important; text-shadow: none !important; filter: none !important; -ms-filter: none !important } a, a:visited { text-decoration: underline } .ir a:after, a[href^="javascript:"]:after, a[href^="#"]:after { content: "" } pre, blockquote { page-break-inside: avoid } thead { display: table-header-group } tr, img { page-break-inside: avoid } img { max-width: 100% !important } @page { margin: 0.5cm } p, h2, h3 { orphans: 3; widows: 3 } h2, h3 { page-break-after: avoid } } .fa:before, .rst-content .admonition-title:before, .rst-content h1 .headerlink:before, .rst-content h2 .headerlink:before, .rst-content h3 .headerlink:before, .rst-content h4 .headerlink:before, .rst-content h5 .headerlink:before, .rst-content h6 .headerlink:before, .rst-content dl dt .headerlink:before, .icon:before, .wy-dropdown .caret:before, .wy-inline-validate.wy-inline-validate-success .wy-input-context:before, .wy-inline-validate.wy-inline-validate-danger .wy-input-context:before, .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, .wy-inline-validate.wy-inline-validate-info .wy-input-context:before, .wy-alert, .rst-content .note, .rst-content .attention, .rst-content .caution, .rst-content .danger, .rst-content .error, .rst-content .hint, .rst-content .important, .rst-content .tip, .rst-content .warning, .rst-content .seealso, .rst-content .admonition-todo, .btn, input[type="text"], input[type="password"], input[type="email"], input[type="url"], input[type="date"], input[type="month"], input[type="time"], input[type="datetime"], input[type="datetime-local"], input[type="week"], input[type="number"], input[type="search"], input[type="tel"], input[type="color"], select, textarea, .wy-menu-vertical li.on a, .wy-menu-vertical li.current > a, .wy-side-nav-search > a, .wy-side-nav-search .wy-dropdown > a, .wy-nav-top a { -webkit-font-smoothing: antialiased } .clearfix { *zoom: 1 } .clearfix:before, .clearfix:after { display: table; content: "" } .clearfix:after { clear: both } /*! * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) */ @font-face { font-family: 'FontAwesome'; src: url("../fonts/fontawesome-webfont.eot?v=4.1.0"); src: url("../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0") format("embedded-opentype"), url("../fonts/fontawesome-webfont.woff?v=4.1.0") format("woff"), url("../fonts/fontawesome-webfont.ttf?v=4.1.0") format("truetype"), url("../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular") format("svg"); font-weight: normal; font-style: normal } .fa, .rst-content .admonition-title, .rst-content h1 .headerlink, .rst-content h2 .headerlink, .rst-content h3 .headerlink, .rst-content h4 .headerlink, .rst-content h5 .headerlink, .rst-content h6 .headerlink, .rst-content dl dt .headerlink, .icon { display: inline-block; font-family: FontAwesome; font-style: normal; font-weight: normal; line-height: 1; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale } .fa-lg { font-size: 1.33333em; line-height: 0.75em; vertical-align: -15% } .fa-2x { font-size: 2em } .fa-3x { font-size: 3em } .fa-4x { font-size: 4em } .fa-5x { font-size: 5em } .fa-fw { width: 1.28571em; text-align: center } .fa-ul { padding-left: 0; margin-left: 2.14286em; list-style-type: none } .fa-ul > li { position: relative } .fa-li { position: absolute; left: -2.14286em; width: 2.14286em; top: 0.14286em; text-align: center } .fa-li.fa-lg { left: -1.85714em } .fa-border { padding: .2em .25em .15em; border: solid 0.08em #eee; border-radius: .1em } .pull-right { float: right } .pull-left { float: left } .fa.pull-left, .rst-content .pull-left.admonition-title, .rst-content h1 .pull-left.headerlink, .rst-content h2 .pull-left.headerlink, .rst-content h3 .pull-left.headerlink, .rst-content h4 .pull-left.headerlink, .rst-content h5 .pull-left.headerlink, .rst-content h6 .pull-left.headerlink, .rst-content dl dt .pull-left.headerlink, .pull-left.icon { margin-right: .3em } .fa.pull-right, .rst-content .pull-right.admonition-title, .rst-content h1 .pull-right.headerlink, .rst-content h2 .pull-right.headerlink, .rst-content h3 .pull-right.headerlink, .rst-content h4 .pull-right.headerlink, .rst-content h5 .pull-right.headerlink, .rst-content h6 .pull-right.headerlink, .rst-content dl dt .pull-right.headerlink, .pull-right.icon { margin-left: .3em } .fa-spin { -webkit-animation: spin 2s infinite linear; -moz-animation: spin 2s infinite linear; -o-animation: spin 2s infinite linear; animation: spin 2s infinite linear } @-moz-keyframes spin { 0% { -moz-transform: rotate(0deg) } 100% { -moz-transform: rotate(359deg) } } @-webkit-keyframes spin { 0% { -webkit-transform: rotate(0deg) } 100% { -webkit-transform: rotate(359deg) } } @-o-keyframes spin { 0% { -o-transform: rotate(0deg) } 100% { -o-transform: rotate(359deg) } } @keyframes spin { 0% { -webkit-transform: rotate(0deg); transform: rotate(0deg) } 100% { -webkit-transform: rotate(359deg); transform: rotate(359deg) } } .fa-rotate-90 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1); -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: rotate(90deg) } .fa-rotate-180 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg) } .fa-rotate-270 { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3); -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -ms-transform: rotate(270deg); -o-transform: rotate(270deg); transform: rotate(270deg) } .fa-flip-horizontal { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0); -webkit-transform: scale(-1, 1); -moz-transform: scale(-1, 1); -ms-transform: scale(-1, 1); -o-transform: scale(-1, 1); transform: scale(-1, 1) } .fa-flip-vertical { filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2); -webkit-transform: scale(1, -1); -moz-transform: scale(1, -1); -ms-transform: scale(1, -1); -o-transform: scale(1, -1); transform: scale(1, -1) } .fa-stack { position: relative; display: inline-block; width: 2em; height: 2em; line-height: 2em; vertical-align: middle } .fa-stack-1x, .fa-stack-2x { position: absolute; left: 0; width: 100%; text-align: center } .fa-stack-1x { line-height: inherit } .fa-stack-2x { font-size: 2em } .fa-inverse { color: #fff } .fa-glass:before { content: "" } .fa-music:before { content: "" } .fa-search:before, .icon-search:before { content: "" } .fa-envelope-o:before { content: "" } .fa-heart:before { content: "" } .fa-star:before { content: "" } .fa-star-o:before { content: "" } .fa-user:before { content: "" } .fa-film:before { content: "" } .fa-th-large:before { content: "" } .fa-th:before { content: "" } .fa-th-list:before { content: "" } .fa-check:before { content: "" } .fa-times:before { content: "" } .fa-search-plus:before { content: "" } .fa-search-minus:before { content: "" } .fa-power-off:before { content: "" } .fa-signal:before { content: "" } .fa-gear:before, .fa-cog:before { content: "" } .fa-trash-o:before { content: "" } .fa-home:before, .icon-home:before { content: "" } .fa-file-o:before { content: "" } .fa-clock-o:before { content: "" } .fa-road:before { content: "" } .fa-download:before { content: "" } .fa-arrow-circle-o-down:before { content: "" } .fa-arrow-circle-o-up:before { content: "" } .fa-inbox:before { content: "" } .fa-play-circle-o:before { content: "" } .fa-rotate-right:before, .fa-repeat:before { content: "" } .fa-refresh:before { content: "" } .fa-list-alt:before { content: "" } .fa-lock:before { content: "" } .fa-flag:before { content: "" } .fa-headphones:before { content: "" } .fa-volume-off:before { content: "" } .fa-volume-down:before { content: "" } .fa-volume-up:before { content: "" } .fa-qrcode:before { content: "" } .fa-barcode:before { content: "" } .fa-tag:before { content: "" } .fa-tags:before { content: "" } .fa-book:before, .icon-book:before { content: "" } .fa-bookmark:before { content: "" } .fa-print:before { content: "" } .fa-camera:before { content: "" } .fa-font:before { content: "" } .fa-bold:before { content: "" } .fa-italic:before { content: "" } .fa-text-height:before { content: "" } .fa-text-width:before { content: "" } .fa-align-left:before { content: "" } .fa-align-center:before { content: "" } .fa-align-right:before { content: "" } .fa-align-justify:before { content: "" } .fa-list:before { content: "" } .fa-dedent:before, .fa-outdent:before { content: "" } .fa-indent:before { content: "" } .fa-video-camera:before { content: "" } .fa-photo:before, .fa-image:before, .fa-picture-o:before { content: "" } .fa-pencil:before { content: "" } .fa-map-marker:before { content: "" } .fa-adjust:before { content: "" } .fa-tint:before { content: "" } .fa-edit:before, .fa-pencil-square-o:before { content: "" } .fa-share-square-o:before { content: "" } .fa-check-square-o:before { content: "" } .fa-arrows:before { content: "" } .fa-step-backward:before { content: "" } .fa-fast-backward:before { content: "" } .fa-backward:before { content: "" } .fa-play:before { content: "" } .fa-pause:before { content: "" } .fa-stop:before { content: "" } .fa-forward:before { content: "" } .fa-fast-forward:before { content: "" } .fa-step-forward:before { content: "" } .fa-eject:before { content: "" } .fa-chevron-left:before { content: "" } .fa-chevron-right:before { content: "" } .fa-plus-circle:before { content: "" } .fa-minus-circle:before { content: "" } .fa-times-circle:before, .wy-inline-validate.wy-inline-validate-danger .wy-input-context:before { content: "" } .fa-check-circle:before, .wy-inline-validate.wy-inline-validate-success .wy-input-context:before { content: "" } .fa-question-circle:before { content: "" } .fa-info-circle:before { content: "" } .fa-crosshairs:before { content: "" } .fa-times-circle-o:before { content: "" } .fa-check-circle-o:before { content: "" } .fa-ban:before { content: "" } .fa-arrow-left:before { content: "" } .fa-arrow-right:before { content: "" } .fa-arrow-up:before { content: "" } .fa-arrow-down:before { content: "" } .fa-mail-forward:before, .fa-share:before { content: "" } .fa-expand:before { content: "" } .fa-compress:before { content: "" } .fa-plus:before { content: "" } .fa-minus:before { content: "" } .fa-asterisk:before { content: "" } .fa-exclamation-circle:before, .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, .wy-inline-validate.wy-inline-validate-info .wy-input-context:before, .rst-content .admonition-title:before { content: "" } .fa-gift:before { content: "" } .fa-leaf:before { content: "" } .fa-fire:before, .icon-fire:before { content: "" } .fa-eye:before { content: "" } .fa-eye-slash:before { content: "" } .fa-warning:before, .fa-exclamation-triangle:before { content: "" } .fa-plane:before { content: "" } .fa-calendar:before { content: "" } .fa-random:before { content: "" } .fa-comment:before { content: "" } .fa-magnet:before { content: "" } .fa-chevron-up:before { content: "" } .fa-chevron-down:before { content: "" } .fa-retweet:before { content: "" } .fa-shopping-cart:before { content: "" } .fa-folder:before { content: "" } .fa-folder-open:before { content: "" } .fa-arrows-v:before { content: "" } .fa-arrows-h:before { content: "" } .fa-bar-chart-o:before { content: "" } .fa-twitter-square:before { content: "" } .fa-facebook-square:before { content: "" } .fa-camera-retro:before { content: "" } .fa-key:before { content: "" } .fa-gears:before, .fa-cogs:before { content: "" } .fa-comments:before { content: "" } .fa-thumbs-o-up:before { content: "" } .fa-thumbs-o-down:before { content: "" } .fa-star-half:before { content: "" } .fa-heart-o:before { content: "" } .fa-sign-out:before { content: "" } .fa-linkedin-square:before { content: "" } .fa-thumb-tack:before { content: "" } .fa-external-link:before { content: "" } .fa-sign-in:before { content: "" } .fa-trophy:before { content: "" } .fa-github-square:before { content: "" } .fa-upload:before { content: "" } .fa-lemon-o:before { content: "" } .fa-phone:before { content: "" } .fa-square-o:before { content: "" } .fa-bookmark-o:before { content: "" } .fa-phone-square:before { content: "" } .fa-twitter:before { content: "" } .fa-facebook:before { content: "" } .fa-github:before, .icon-github:before { content: "" } .fa-unlock:before { content: "" } .fa-credit-card:before { content: "" } .fa-rss:before { content: "" } .fa-hdd-o:before { content: "" } .fa-bullhorn:before { content: "" } .fa-bell:before { content: "" } .fa-certificate:before { content: "" } .fa-hand-o-right:before { content: "" } .fa-hand-o-left:before { content: "" } .fa-hand-o-up:before { content: "" } .fa-hand-o-down:before { content: "" } .fa-arrow-circle-left:before, .icon-circle-arrow-left:before { content: "" } .fa-arrow-circle-right:before, .icon-circle-arrow-right:before { content: "" } .fa-arrow-circle-up:before { content: "" } .fa-arrow-circle-down:before { content: "" } .fa-globe:before { content: "" } .fa-wrench:before { content: "" } .fa-tasks:before { content: "" } .fa-filter:before { content: "" } .fa-briefcase:before { content: "" } .fa-arrows-alt:before { content: "" } .fa-group:before, .fa-users:before { content: "" } .fa-chain:before, .fa-link:before, .icon-link:before { content: "" } .fa-cloud:before { content: "" } .fa-flask:before { content: "" } .fa-cut:before, .fa-scissors:before { content: "" } .fa-copy:before, .fa-files-o:before { content: "" } .fa-paperclip:before { content: "" } .fa-save:before, .fa-floppy-o:before { content: "" } .fa-square:before { content: "" } .fa-navicon:before, .fa-reorder:before, .fa-bars:before { content: "" } .fa-list-ul:before { content: "" } .fa-list-ol:before { content: "" } .fa-strikethrough:before { content: "" } .fa-underline:before { content: "" } .fa-table:before { content: "" } .fa-magic:before { content: "" } .fa-truck:before { content: "" } .fa-pinterest:before { content: "" } .fa-pinterest-square:before { content: "" } .fa-google-plus-square:before { content: "" } .fa-google-plus:before { content: "" } .fa-money:before { content: "" } .fa-caret-down:before, .wy-dropdown .caret:before, .icon-caret-down:before { content: "" } .fa-caret-up:before { content: "" } .fa-caret-left:before { content: "" } .fa-caret-right:before { content: "" } .fa-columns:before { content: "" } .fa-unsorted:before, .fa-sort:before { content: "" } .fa-sort-down:before, .fa-sort-desc:before { content: "" } .fa-sort-up:before, .fa-sort-asc:before { content: "" } .fa-envelope:before { content: "" } .fa-linkedin:before { content: "" } .fa-rotate-left:before, .fa-undo:before { content: "" } .fa-legal:before, .fa-gavel:before { content: "" } .fa-dashboard:before, .fa-tachometer:before { content: "" } .fa-comment-o:before { content: "" } .fa-comments-o:before { content: "" } .fa-flash:before, .fa-bolt:before { content: "" } .fa-sitemap:before { content: "" } .fa-umbrella:before { content: "" } .fa-paste:before, .fa-clipboard:before { content: "" } .fa-lightbulb-o:before { content: "" } .fa-exchange:before { content: "" } .fa-cloud-download:before { content: "" } .fa-cloud-upload:before { content: "" } .fa-user-md:before { content: "" } .fa-stethoscope:before { content: "" } .fa-suitcase:before { content: "" } .fa-bell-o:before { content: "" } .fa-coffee:before { content: "" } .fa-cutlery:before { content: "" } .fa-file-text-o:before { content: "" } .fa-building-o:before { content: "" } .fa-hospital-o:before { content: "" } .fa-ambulance:before { content: "" } .fa-medkit:before { content: "" } .fa-fighter-jet:before { content: "" } .fa-beer:before { content: "" } .fa-h-square:before { content: "" } .fa-plus-square:before { content: "" } .fa-angle-double-left:before { content: "" } .fa-angle-double-right:before { content: "" } .fa-angle-double-up:before { content: "" } .fa-angle-double-down:before { content: "" } .fa-angle-left:before { content: "" } .fa-angle-right:before { content: "" } .fa-angle-up:before { content: "" } .fa-angle-down:before { content: "" } .fa-desktop:before { content: "" } .fa-laptop:before { content: "" } .fa-tablet:before { content: "" } .fa-mobile-phone:before, .fa-mobile:before { content: "" } .fa-circle-o:before { content: "" } .fa-quote-left:before { content: "" } .fa-quote-right:before { content: "" } .fa-spinner:before { content: "" } .fa-circle:before { content: "" } .fa-mail-reply:before, .fa-reply:before { content: "" } .fa-github-alt:before { content: "" } .fa-folder-o:before { content: "" } .fa-folder-open-o:before { content: "" } .fa-smile-o:before { content: "" } .fa-frown-o:before { content: "" } .fa-meh-o:before { content: "" } .fa-gamepad:before { content: "" } .fa-keyboard-o:before { content: "" } .fa-flag-o:before { content: "" } .fa-flag-checkered:before { content: "" } .fa-terminal:before { content: "" } .fa-code:before { content: "" } .fa-mail-reply-all:before, .fa-reply-all:before { content: "" } .fa-star-half-empty:before, .fa-star-half-full:before, .fa-star-half-o:before { content: "" } .fa-location-arrow:before { content: "" } .fa-crop:before { content: "" } .fa-code-fork:before { content: "" } .fa-unlink:before, .fa-chain-broken:before { content: "" } .fa-question:before { content: "" } .fa-info:before { content: "" } .fa-exclamation:before { content: "" } .fa-superscript:before { content: "" } .fa-subscript:before { content: "" } .fa-eraser:before { content: "" } .fa-puzzle-piece:before { content: "" } .fa-microphone:before { content: "" } .fa-microphone-slash:before { content: "" } .fa-shield:before { content: "" } .fa-calendar-o:before { content: "" } .fa-fire-extinguisher:before { content: "" } .fa-rocket:before { content: "" } .fa-maxcdn:before { content: "" } .fa-chevron-circle-left:before { content: "" } .fa-chevron-circle-right:before { content: "" } .fa-chevron-circle-up:before { content: "" } .fa-chevron-circle-down:before { content: "" } .fa-html5:before { content: "" } .fa-css3:before { content: "" } .fa-anchor:before { content: "" } .fa-unlock-alt:before { content: "" } .fa-bullseye:before { content: "" } .fa-ellipsis-h:before { content: "" } .fa-ellipsis-v:before { content: "" } .fa-rss-square:before { content: "" } .fa-play-circle:before { content: "" } .fa-ticket:before { content: "" } .fa-minus-square:before { content: "" } .fa-minus-square-o:before { content: "" } .fa-level-up:before { content: "" } .fa-level-down:before { content: "" } .fa-check-square:before { content: "" } .fa-pencil-square:before { content: "" } .fa-external-link-square:before { content: "" } .fa-share-square:before { content: "" } .fa-compass:before { content: "" } .fa-toggle-down:before, .fa-caret-square-o-down:before { content: "" } .fa-toggle-up:before, .fa-caret-square-o-up:before { content: "" } .fa-toggle-right:before, .fa-caret-square-o-right:before { content: "" } .fa-euro:before, .fa-eur:before { content: "" } .fa-gbp:before { content: "" } .fa-dollar:before, .fa-usd:before { content: "" } .fa-rupee:before, .fa-inr:before { content: "" } .fa-cny:before, .fa-rmb:before, .fa-yen:before, .fa-jpy:before { content: "" } .fa-ruble:before, .fa-rouble:before, .fa-rub:before { content: "" } .fa-won:before, .fa-krw:before { content: "" } .fa-bitcoin:before, .fa-btc:before { content: "" } .fa-file:before { content: "" } .fa-file-text:before { content: "" } .fa-sort-alpha-asc:before { content: "" } .fa-sort-alpha-desc:before { content: "" } .fa-sort-amount-asc:before { content: "" } .fa-sort-amount-desc:before { content: "" } .fa-sort-numeric-asc:before { content: "" } .fa-sort-numeric-desc:before { content: "" } .fa-thumbs-up:before { content: "" } .fa-thumbs-down:before { content: "" } .fa-youtube-square:before { content: "" } .fa-youtube:before { content: "" } .fa-xing:before { content: "" } .fa-xing-square:before { content: "" } .fa-youtube-play:before { content: "" } .fa-dropbox:before { content: "" } .fa-stack-overflow:before { content: "" } .fa-instagram:before { content: "" } .fa-flickr:before { content: "" } .fa-adn:before { content: "" } .fa-bitbucket:before, .icon-bitbucket:before { content: "" } .fa-bitbucket-square:before { content: "" } .fa-tumblr:before { content: "" } .fa-tumblr-square:before { content: "" } .fa-long-arrow-down:before { content: "" } .fa-long-arrow-up:before { content: "" } .fa-long-arrow-left:before { content: "" } .fa-long-arrow-right:before { content: "" } .fa-apple:before { content: "" } .fa-windows:before { content: "" } .fa-android:before { content: "" } .fa-linux:before { content: "" } .fa-dribbble:before { content: "" } .fa-skype:before { content: "" } .fa-foursquare:before { content: "" } .fa-trello:before { content: "" } .fa-female:before { content: "" } .fa-male:before { content: "" } .fa-gittip:before { content: "" } .fa-sun-o:before { content: "" } .fa-moon-o:before { content: "" } .fa-archive:before { content: "" } .fa-bug:before { content: "" } .fa-vk:before { content: "" } .fa-weibo:before { content: "" } .fa-renren:before { content: "" } .fa-pagelines:before { content: "" } .fa-stack-exchange:before { content: "" } .fa-arrow-circle-o-right:before { content: "" } .fa-arrow-circle-o-left:before { content: "" } .fa-toggle-left:before, .fa-caret-square-o-left:before { content: "" } .fa-dot-circle-o:before { content: "" } .fa-wheelchair:before { content: "" } .fa-vimeo-square:before { content: "" } .fa-turkish-lira:before, .fa-try:before { content: "" } .fa-plus-square-o:before { content: "" } .fa-space-shuttle:before { content: "" } .fa-slack:before { content: "" } .fa-envelope-square:before { content: "" } .fa-wordpress:before { content: "" } .fa-openid:before { content: "" } .fa-institution:before, .fa-bank:before, .fa-university:before { content: "" } .fa-mortar-board:before, .fa-graduation-cap:before { content: "" } .fa-yahoo:before { content: "" } .fa-google:before { content: "" } .fa-reddit:before { content: "" } .fa-reddit-square:before { content: "" } .fa-stumbleupon-circle:before { content: "" } .fa-stumbleupon:before { content: "" } .fa-delicious:before { content: "" } .fa-digg:before { content: "" } .fa-pied-piper-square:before, .fa-pied-piper:before { content: "" } .fa-pied-piper-alt:before { content: "" } .fa-drupal:before { content: "" } .fa-joomla:before { content: "" } .fa-language:before { content: "" } .fa-fax:before { content: "" } .fa-building:before { content: "" } .fa-child:before { content: "" } .fa-paw:before { content: "" } .fa-spoon:before { content: "" } .fa-cube:before { content: "" } .fa-cubes:before { content: "" } .fa-behance:before { content: "" } .fa-behance-square:before { content: "" } .fa-steam:before { content: "" } .fa-steam-square:before { content: "" } .fa-recycle:before { content: "" } .fa-automobile:before, .fa-car:before { content: "" } .fa-cab:before, .fa-taxi:before { content: "" } .fa-tree:before { content: "" } .fa-spotify:before { content: "" } .fa-deviantart:before { content: "" } .fa-soundcloud:before { content: "" } .fa-database:before { content: "" } .fa-file-pdf-o:before { content: "" } .fa-file-word-o:before { content: "" } .fa-file-excel-o:before { content: "" } .fa-file-powerpoint-o:before { content: "" } .fa-file-photo-o:before, .fa-file-picture-o:before, .fa-file-image-o:before { content: "" } .fa-file-zip-o:before, .fa-file-archive-o:before { content: "" } .fa-file-sound-o:before, .fa-file-audio-o:before { content: "" } .fa-file-movie-o:before, .fa-file-video-o:before { content: "" } .fa-file-code-o:before { content: "" } .fa-vine:before { content: "" } .fa-codepen:before { content: "" } .fa-jsfiddle:before { content: "" } .fa-life-bouy:before, .fa-life-saver:before, .fa-support:before, .fa-life-ring:before { content: "" } .fa-circle-o-notch:before { content: "" } .fa-ra:before, .fa-rebel:before { content: "" } .fa-ge:before, .fa-empire:before { content: "" } .fa-git-square:before { content: "" } .fa-git:before { content: "" } .fa-hacker-news:before { content: "" } .fa-tencent-weibo:before { content: "" } .fa-qq:before { content: "" } .fa-wechat:before, .fa-weixin:before { content: "" } .fa-send:before, .fa-paper-plane:before { content: "" } .fa-send-o:before, .fa-paper-plane-o:before { content: "" } .fa-history:before { content: "" } .fa-circle-thin:before { content: "" } .fa-header:before { content: "" } .fa-paragraph:before { content: "" } .fa-sliders:before { content: "" } .fa-share-alt:before { content: "" } .fa-share-alt-square:before { content: "" } .fa-bomb:before { content: "" } .fa, .rst-content .admonition-title, .rst-content h1 .headerlink, .rst-content h2 .headerlink, .rst-content h3 .headerlink, .rst-content h4 .headerlink, .rst-content h5 .headerlink, .rst-content h6 .headerlink, .rst-content dl dt .headerlink, .icon, .wy-dropdown .caret, .wy-inline-validate.wy-inline-validate-success .wy-input-context, .wy-inline-validate.wy-inline-validate-danger .wy-input-context, .wy-inline-validate.wy-inline-validate-warning .wy-input-context, .wy-inline-validate.wy-inline-validate-info .wy-input-context { font-family: inherit } .fa:before, .rst-content .admonition-title:before, .rst-content h1 .headerlink:before, .rst-content h2 .headerlink:before, .rst-content h3 .headerlink:before, .rst-content h4 .headerlink:before, .rst-content h5 .headerlink:before, .rst-content h6 .headerlink:before, .rst-content dl dt .headerlink:before, .icon:before, .wy-dropdown .caret:before, .wy-inline-validate.wy-inline-validate-success .wy-input-context:before, .wy-inline-validate.wy-inline-validate-danger .wy-input-context:before, .wy-inline-validate.wy-inline-validate-warning .wy-input-context:before, .wy-inline-validate.wy-inline-validate-info .wy-input-context:before { font-family: "FontAwesome"; display: inline-block; font-style: normal; font-weight: normal; line-height: 1; text-decoration: inherit } a .fa, a .rst-content .admonition-title, .rst-content a .admonition-title, a .rst-content h1 .headerlink, .rst-content h1 a .headerlink, a .rst-content h2 .headerlink, .rst-content h2 a .headerlink, a .rst-content h3 .headerlink, .rst-content h3 a .headerlink, a .rst-content h4 .headerlink, .rst-content h4 a .headerlink, a .rst-content h5 .headerlink, .rst-content h5 a .headerlink, a .rst-content h6 .headerlink, .rst-content h6 a .headerlink, a .rst-content dl dt .headerlink, .rst-content dl dt a .headerlink, a .icon { display: inline-block; text-decoration: inherit } .btn .fa, .btn .rst-content .admonition-title, .rst-content .btn .admonition-title, .btn .rst-content h1 .headerlink, .rst-content h1 .btn .headerlink, .btn .rst-content h2 .headerlink, .rst-content h2 .btn .headerlink, .btn .rst-content h3 .headerlink, .rst-content h3 .btn .headerlink, .btn .rst-content h4 .headerlink, .rst-content h4 .btn .headerlink, .btn .rst-content h5 .headerlink, .rst-content h5 .btn .headerlink, .btn .rst-content h6 .headerlink, .rst-content h6 .btn .headerlink, .btn .rst-content dl dt .headerlink, .rst-content dl dt .btn .headerlink, .btn .icon, .nav .fa, .nav .rst-content .admonition-title, .rst-content .nav .admonition-title, .nav .rst-content h1 .headerlink, .rst-content h1 .nav .headerlink, .nav .rst-content h2 .headerlink, .rst-content h2 .nav .headerlink, .nav .rst-content h3 .headerlink, .rst-content h3 .nav .headerlink, .nav .rst-content h4 .headerlink, .rst-content h4 .nav .headerlink, .nav .rst-content h5 .headerlink, .rst-content h5 .nav .headerlink, .nav .rst-content h6 .headerlink, .rst-content h6 .nav .headerlink, .nav .rst-content dl dt .headerlink, .rst-content dl dt .nav .headerlink, .nav .icon { display: inline } .btn .fa.fa-large, .btn .rst-content .fa-large.admonition-title, .rst-content .btn .fa-large.admonition-title, .btn .rst-content h1 .fa-large.headerlink, .rst-content h1 .btn .fa-large.headerlink, .btn .rst-content h2 .fa-large.headerlink, .rst-content h2 .btn .fa-large.headerlink, .btn .rst-content h3 .fa-large.headerlink, .rst-content h3 .btn .fa-large.headerlink, .btn .rst-content h4 .fa-large.headerlink, .rst-content h4 .btn .fa-large.headerlink, .btn .rst-content h5 .fa-large.headerlink, .rst-content h5 .btn .fa-large.headerlink, .btn .rst-content h6 .fa-large.headerlink, .rst-content h6 .btn .fa-large.headerlink, .btn .rst-content dl dt .fa-large.headerlink, .rst-content dl dt .btn .fa-large.headerlink, .btn .fa-large.icon, .nav .fa.fa-large, .nav .rst-content .fa-large.admonition-title, .rst-content .nav .fa-large.admonition-title, .nav .rst-content h1 .fa-large.headerlink, .rst-content h1 .nav .fa-large.headerlink, .nav .rst-content h2 .fa-large.headerlink, .rst-content h2 .nav .fa-large.headerlink, .nav .rst-content h3 .fa-large.headerlink, .rst-content h3 .nav .fa-large.headerlink, .nav .rst-content h4 .fa-large.headerlink, .rst-content h4 .nav .fa-large.headerlink, .nav .rst-content h5 .fa-large.headerlink, .rst-content h5 .nav .fa-large.headerlink, .nav .rst-content h6 .fa-large.headerlink, .rst-content h6 .nav .fa-large.headerlink, .nav .rst-content dl dt .fa-large.headerlink, .rst-content dl dt .nav .fa-large.headerlink, .nav .fa-large.icon { line-height: 0.9em } .btn .fa.fa-spin, .btn .rst-content .fa-spin.admonition-title, .rst-content .btn .fa-spin.admonition-title, .btn .rst-content h1 .fa-spin.headerlink, .rst-content h1 .btn .fa-spin.headerlink, .btn .rst-content h2 .fa-spin.headerlink, .rst-content h2 .btn .fa-spin.headerlink, .btn .rst-content h3 .fa-spin.headerlink, .rst-content h3 .btn .fa-spin.headerlink, .btn .rst-content h4 .fa-spin.headerlink, .rst-content h4 .btn .fa-spin.headerlink, .btn .rst-content h5 .fa-spin.headerlink, .rst-content h5 .btn .fa-spin.headerlink, .btn .rst-content h6 .fa-spin.headerlink, .rst-content h6 .btn .fa-spin.headerlink, .btn .rst-content dl dt .fa-spin.headerlink, .rst-content dl dt .btn .fa-spin.headerlink, .btn .fa-spin.icon, .nav .fa.fa-spin, .nav .rst-content .fa-spin.admonition-title, .rst-content .nav .fa-spin.admonition-title, .nav .rst-content h1 .fa-spin.headerlink, .rst-content h1 .nav .fa-spin.headerlink, .nav .rst-content h2 .fa-spin.headerlink, .rst-content h2 .nav .fa-spin.headerlink, .nav .rst-content h3 .fa-spin.headerlink, .rst-content h3 .nav .fa-spin.headerlink, .nav .rst-content h4 .fa-spin.headerlink, .rst-content h4 .nav .fa-spin.headerlink, .nav .rst-content h5 .fa-spin.headerlink, .rst-content h5 .nav .fa-spin.headerlink, .nav .rst-content h6 .fa-spin.headerlink, .rst-content h6 .nav .fa-spin.headerlink, .nav .rst-content dl dt .fa-spin.headerlink, .rst-content dl dt .nav .fa-spin.headerlink, .nav .fa-spin.icon { display: inline-block } .btn.fa:before, .rst-content .btn.admonition-title:before, .rst-content h1 .btn.headerlink:before, .rst-content h2 .btn.headerlink:before, .rst-content h3 .btn.headerlink:before, .rst-content h4 .btn.headerlink:before, .rst-content h5 .btn.headerlink:before, .rst-content h6 .btn.headerlink:before, .rst-content dl dt .btn.headerlink:before, .btn.icon:before { opacity: 0.5; -webkit-transition: opacity 0.05s ease-in; -moz-transition: opacity 0.05s ease-in; transition: opacity 0.05s ease-in } .btn.fa:hover:before, .rst-content .btn.admonition-title:hover:before, .rst-content h1 .btn.headerlink:hover:before, .rst-content h2 .btn.headerlink:hover:before, .rst-content h3 .btn.headerlink:hover:before, .rst-content h4 .btn.headerlink:hover:before, .rst-content h5 .btn.headerlink:hover:before, .rst-content h6 .btn.headerlink:hover:before, .rst-content dl dt .btn.headerlink:hover:before, .btn.icon:hover:before { opacity: 1 } .btn-mini .fa:before, .btn-mini .rst-content .admonition-title:before, .rst-content .btn-mini .admonition-title:before, .btn-mini .rst-content h1 .headerlink:before, .rst-content h1 .btn-mini .headerlink:before, .btn-mini .rst-content h2 .headerlink:before, .rst-content h2 .btn-mini .headerlink:before, .btn-mini .rst-content h3 .headerlink:before, .rst-content h3 .btn-mini .headerlink:before, .btn-mini .rst-content h4 .headerlink:before, .rst-content h4 .btn-mini .headerlink:before, .btn-mini .rst-content h5 .headerlink:before, .rst-content h5 .btn-mini .headerlink:before, .btn-mini .rst-content h6 .headerlink:before, .rst-content h6 .btn-mini .headerlink:before, .btn-mini .rst-content dl dt .headerlink:before, .rst-content dl dt .btn-mini .headerlink:before, .btn-mini .icon:before { font-size: 14px; vertical-align: -15% } .wy-alert, .rst-content .note, .rst-content .attention, .rst-content .caution, .rst-content .danger, .rst-content .error, .rst-content .hint, .rst-content .important, .rst-content .tip, .rst-content .warning, .rst-content .seealso, .rst-content .admonition-todo { padding: 12px; line-height: 24px; margin-bottom: 24px; background: #e7f2fa } .wy-alert-title, .rst-content .admonition-title { color: #fff; font-weight: bold; display: block; color: #fff; background: #6ab0de; margin: -12px; padding: 6px 12px; margin-bottom: 12px } .wy-alert.wy-alert-danger, .rst-content .wy-alert-danger.note, .rst-content .wy-alert-danger.attention, .rst-content .wy-alert-danger.caution, .rst-content .danger, .rst-content .error, .rst-content .wy-alert-danger.hint, .rst-content .wy-alert-danger.important, .rst-content .wy-alert-danger.tip, .rst-content .wy-alert-danger.warning, .rst-content .wy-alert-danger.seealso, .rst-content .wy-alert-danger.admonition-todo { background: #fdf3f2 } .wy-alert.wy-alert-danger .wy-alert-title, .rst-content .wy-alert-danger.note .wy-alert-title, .rst-content .wy-alert-danger.attention .wy-alert-title, .rst-content .wy-alert-danger.caution .wy-alert-title, .rst-content .danger .wy-alert-title, .rst-content .error .wy-alert-title, .rst-content .wy-alert-danger.hint .wy-alert-title, .rst-content .wy-alert-danger.important .wy-alert-title, .rst-content .wy-alert-danger.tip .wy-alert-title, .rst-content .wy-alert-danger.warning .wy-alert-title, .rst-content .wy-alert-danger.seealso .wy-alert-title, .rst-content .wy-alert-danger.admonition-todo .wy-alert-title, .wy-alert.wy-alert-danger .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-danger .admonition-title, .rst-content .wy-alert-danger.note .admonition-title, .rst-content .wy-alert-danger.attention .admonition-title, .rst-content .wy-alert-danger.caution .admonition-title, .rst-content .danger .admonition-title, .rst-content .error .admonition-title, .rst-content .wy-alert-danger.hint .admonition-title, .rst-content .wy-alert-danger.important .admonition-title, .rst-content .wy-alert-danger.tip .admonition-title, .rst-content .wy-alert-danger.warning .admonition-title, .rst-content .wy-alert-danger.seealso .admonition-title, .rst-content .wy-alert-danger.admonition-todo .admonition-title { background: #f29f97 } .wy-alert.wy-alert-warning, .rst-content .wy-alert-warning.note, .rst-content .attention, .rst-content .caution, .rst-content .wy-alert-warning.danger, .rst-content .wy-alert-warning.error, .rst-content .wy-alert-warning.hint, .rst-content .wy-alert-warning.important, .rst-content .wy-alert-warning.tip, .rst-content .warning, .rst-content .wy-alert-warning.seealso, .rst-content .admonition-todo { background: #ffedcc } .wy-alert.wy-alert-warning .wy-alert-title, .rst-content .wy-alert-warning.note .wy-alert-title, .rst-content .attention .wy-alert-title, .rst-content .caution .wy-alert-title, .rst-content .wy-alert-warning.danger .wy-alert-title, .rst-content .wy-alert-warning.error .wy-alert-title, .rst-content .wy-alert-warning.hint .wy-alert-title, .rst-content .wy-alert-warning.important .wy-alert-title, .rst-content .wy-alert-warning.tip .wy-alert-title, .rst-content .warning .wy-alert-title, .rst-content .wy-alert-warning.seealso .wy-alert-title, .rst-content .admonition-todo .wy-alert-title, .wy-alert.wy-alert-warning .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-warning .admonition-title, .rst-content .wy-alert-warning.note .admonition-title, .rst-content .attention .admonition-title, .rst-content .caution .admonition-title, .rst-content .wy-alert-warning.danger .admonition-title, .rst-content .wy-alert-warning.error .admonition-title, .rst-content .wy-alert-warning.hint .admonition-title, .rst-content .wy-alert-warning.important .admonition-title, .rst-content .wy-alert-warning.tip .admonition-title, .rst-content .warning .admonition-title, .rst-content .wy-alert-warning.seealso .admonition-title, .rst-content .admonition-todo .admonition-title { background: #f0b37e } .wy-alert.wy-alert-info, .rst-content .note, .rst-content .wy-alert-info.attention, .rst-content .wy-alert-info.caution, .rst-content .wy-alert-info.danger, .rst-content .wy-alert-info.error, .rst-content .wy-alert-info.hint, .rst-content .wy-alert-info.important, .rst-content .wy-alert-info.tip, .rst-content .wy-alert-info.warning, .rst-content .seealso, .rst-content .wy-alert-info.admonition-todo { background: #e7f2fa } .wy-alert.wy-alert-info .wy-alert-title, .rst-content .note .wy-alert-title, .rst-content .wy-alert-info.attention .wy-alert-title, .rst-content .wy-alert-info.caution .wy-alert-title, .rst-content .wy-alert-info.danger .wy-alert-title, .rst-content .wy-alert-info.error .wy-alert-title, .rst-content .wy-alert-info.hint .wy-alert-title, .rst-content .wy-alert-info.important .wy-alert-title, .rst-content .wy-alert-info.tip .wy-alert-title, .rst-content .wy-alert-info.warning .wy-alert-title, .rst-content .seealso .wy-alert-title, .rst-content .wy-alert-info.admonition-todo .wy-alert-title, .wy-alert.wy-alert-info .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-info .admonition-title, .rst-content .note .admonition-title, .rst-content .wy-alert-info.attention .admonition-title, .rst-content .wy-alert-info.caution .admonition-title, .rst-content .wy-alert-info.danger .admonition-title, .rst-content .wy-alert-info.error .admonition-title, .rst-content .wy-alert-info.hint .admonition-title, .rst-content .wy-alert-info.important .admonition-title, .rst-content .wy-alert-info.tip .admonition-title, .rst-content .wy-alert-info.warning .admonition-title, .rst-content .seealso .admonition-title, .rst-content .wy-alert-info.admonition-todo .admonition-title { background: #6ab0de } .wy-alert.wy-alert-success, .rst-content .wy-alert-success.note, .rst-content .wy-alert-success.attention, .rst-content .wy-alert-success.caution, .rst-content .wy-alert-success.danger, .rst-content .wy-alert-success.error, .rst-content .hint, .rst-content .important, .rst-content .tip, .rst-content .wy-alert-success.warning, .rst-content .wy-alert-success.seealso, .rst-content .wy-alert-success.admonition-todo { background: #dbfaf4 } .wy-alert.wy-alert-success .wy-alert-title, .rst-content .wy-alert-success.note .wy-alert-title, .rst-content .wy-alert-success.attention .wy-alert-title, .rst-content .wy-alert-success.caution .wy-alert-title, .rst-content .wy-alert-success.danger .wy-alert-title, .rst-content .wy-alert-success.error .wy-alert-title, .rst-content .hint .wy-alert-title, .rst-content .important .wy-alert-title, .rst-content .tip .wy-alert-title, .rst-content .wy-alert-success.warning .wy-alert-title, .rst-content .wy-alert-success.seealso .wy-alert-title, .rst-content .wy-alert-success.admonition-todo .wy-alert-title, .wy-alert.wy-alert-success .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-success .admonition-title, .rst-content .wy-alert-success.note .admonition-title, .rst-content .wy-alert-success.attention .admonition-title, .rst-content .wy-alert-success.caution .admonition-title, .rst-content .wy-alert-success.danger .admonition-title, .rst-content .wy-alert-success.error .admonition-title, .rst-content .hint .admonition-title, .rst-content .important .admonition-title, .rst-content .tip .admonition-title, .rst-content .wy-alert-success.warning .admonition-title, .rst-content .wy-alert-success.seealso .admonition-title, .rst-content .wy-alert-success.admonition-todo .admonition-title { background: #1abc9c } .wy-alert.wy-alert-neutral, .rst-content .wy-alert-neutral.note, .rst-content .wy-alert-neutral.attention, .rst-content .wy-alert-neutral.caution, .rst-content .wy-alert-neutral.danger, .rst-content .wy-alert-neutral.error, .rst-content .wy-alert-neutral.hint, .rst-content .wy-alert-neutral.important, .rst-content .wy-alert-neutral.tip, .rst-content .wy-alert-neutral.warning, .rst-content .wy-alert-neutral.seealso, .rst-content .wy-alert-neutral.admonition-todo { background: #f3f6f6 } .wy-alert.wy-alert-neutral .wy-alert-title, .rst-content .wy-alert-neutral.note .wy-alert-title, .rst-content .wy-alert-neutral.attention .wy-alert-title, .rst-content .wy-alert-neutral.caution .wy-alert-title, .rst-content .wy-alert-neutral.danger .wy-alert-title, .rst-content .wy-alert-neutral.error .wy-alert-title, .rst-content .wy-alert-neutral.hint .wy-alert-title, .rst-content .wy-alert-neutral.important .wy-alert-title, .rst-content .wy-alert-neutral.tip .wy-alert-title, .rst-content .wy-alert-neutral.warning .wy-alert-title, .rst-content .wy-alert-neutral.seealso .wy-alert-title, .rst-content .wy-alert-neutral.admonition-todo .wy-alert-title, .wy-alert.wy-alert-neutral .rst-content .admonition-title, .rst-content .wy-alert.wy-alert-neutral .admonition-title, .rst-content .wy-alert-neutral.note .admonition-title, .rst-content .wy-alert-neutral.attention .admonition-title, .rst-content .wy-alert-neutral.caution .admonition-title, .rst-content .wy-alert-neutral.danger .admonition-title, .rst-content .wy-alert-neutral.error .admonition-title, .rst-content .wy-alert-neutral.hint .admonition-title, .rst-content .wy-alert-neutral.important .admonition-title, .rst-content .wy-alert-neutral.tip .admonition-title, .rst-content .wy-alert-neutral.warning .admonition-title, .rst-content .wy-alert-neutral.seealso .admonition-title, .rst-content .wy-alert-neutral.admonition-todo .admonition-title { color: #404040; background: #e1e4e5 } .wy-alert.wy-alert-neutral a, .rst-content .wy-alert-neutral.note a, .rst-content .wy-alert-neutral.attention a, .rst-content .wy-alert-neutral.caution a, .rst-content .wy-alert-neutral.danger a, .rst-content .wy-alert-neutral.error a, .rst-content .wy-alert-neutral.hint a, .rst-content .wy-alert-neutral.important a, .rst-content .wy-alert-neutral.tip a, .rst-content .wy-alert-neutral.warning a, .rst-content .wy-alert-neutral.seealso a, .rst-content .wy-alert-neutral.admonition-todo a { color: #2980B9 } .wy-alert p:last-child, .rst-content .note p:last-child, .rst-content .attention p:last-child, .rst-content .caution p:last-child, .rst-content .danger p:last-child, .rst-content .error p:last-child, .rst-content .hint p:last-child, .rst-content .important p:last-child, .rst-content .tip p:last-child, .rst-content .warning p:last-child, .rst-content .seealso p:last-child, .rst-content .admonition-todo p:last-child { margin-bottom: 0 } .wy-tray-container { position: fixed; bottom: 0px; left: 0; z-index: 600 } .wy-tray-container li { display: block; width: 300px; background: transparent; color: #fff; text-align: center; box-shadow: 0 5px 5px 0 rgba(0, 0, 0, 0.1); padding: 0 24px; min-width: 20%; opacity: 0; height: 0; line-height: 56px; overflow: hidden; -webkit-transition: all 0.3s ease-in; -moz-transition: all 0.3s ease-in; transition: all 0.3s ease-in } .wy-tray-container li.wy-tray-item-success { background: #27AE60 } .wy-tray-container li.wy-tray-item-info { background: #2980B9 } .wy-tray-container li.wy-tray-item-warning { background: #E67E22 } .wy-tray-container li.wy-tray-item-danger { background: #E74C3C } .wy-tray-container li.on { opacity: 1; height: 56px } @media screen and (max-width: 768px) { .wy-tray-container { bottom: auto; top: 0; width: 100% } .wy-tray-container li { width: 100% } } button { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle; cursor: pointer; line-height: normal; -webkit-appearance: button; *overflow: visible } button::-moz-focus-inner, input::-moz-focus-inner { border: 0; padding: 0 } button[disabled] { cursor: default } .btn { display: inline-block; border-radius: 2px; line-height: normal; white-space: nowrap; text-align: center; cursor: pointer; font-size: 100%; padding: 6px 12px 8px 12px; color: #fff; border: 1px solid rgba(0, 0, 0, 0.1); background-color: #27AE60; text-decoration: none; font-weight: normal; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; box-shadow: 0px 1px 2px -1px rgba(255, 255, 255, 0.5) inset, 0px -2px 0px 0px rgba(0, 0, 0, 0.1) inset; outline-none: false; vertical-align: middle; *display: inline; zoom: 1; -webkit-user-drag: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-transition: all 0.1s linear; -moz-transition: all 0.1s linear; transition: all 0.1s linear } .btn-hover { background: #2e8ece; color: #fff } .btn:hover { background: #2cc36b; color: #fff } .btn:focus { background: #2cc36b; outline: 0 } .btn:active { box-shadow: 0px -1px 0px 0px rgba(0, 0, 0, 0.05) inset, 0px 2px 0px 0px rgba(0, 0, 0, 0.1) inset; padding: 8px 12px 6px 12px } .btn:visited { color: #fff } .btn:disabled { background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); filter: alpha(opacity=40); opacity: 0.4; cursor: not-allowed; box-shadow: none } .btn-disabled { background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); filter: alpha(opacity=40); opacity: 0.4; cursor: not-allowed; box-shadow: none } .btn-disabled:hover, .btn-disabled:focus, .btn-disabled:active { background-image: none; filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); filter: alpha(opacity=40); opacity: 0.4; cursor: not-allowed; box-shadow: none } .btn::-moz-focus-inner { padding: 0; border: 0 } .btn-small { font-size: 80% } .btn-info { background-color: #2980B9 !important } .btn-info:hover { background-color: #2e8ece !important } .btn-neutral { background-color: #f3f6f6 !important; color: #404040 !important } .btn-neutral:hover { background-color: #e5ebeb !important; color: #404040 } .btn-neutral:visited { color: #404040 !important } .btn-success { background-color: #27AE60 !important } .btn-success:hover { background-color: #295 !important } .btn-danger { background-color: #E74C3C !important } .btn-danger:hover { background-color: #ea6153 !important } .btn-warning { background-color: #E67E22 !important } .btn-warning:hover { background-color: #e98b39 !important } .btn-invert { background-color: #222 } .btn-invert:hover { background-color: #2f2f2f !important } .btn-link { background-color: transparent !important; color: #2980B9; box-shadow: none; border-color: transparent !important } .btn-link:hover { background-color: transparent !important; color: #409ad5 !important; box-shadow: none } .btn-link:active { background-color: transparent !important; color: #409ad5 !important; box-shadow: none } .btn-link:visited { color: #9B59B6 } .wy-btn-group .btn, .wy-control .btn { vertical-align: middle } .wy-btn-group { margin-bottom: 24px; *zoom: 1 } .wy-btn-group:before, .wy-btn-group:after { display: table; content: "" } .wy-btn-group:after { clear: both } .wy-dropdown { position: relative; display: inline-block } .wy-dropdown-active .wy-dropdown-menu { display: block } .wy-dropdown-menu { position: absolute; left: 0; display: none; float: left; top: 100%; min-width: 100%; background: #fcfcfc; z-index: 100; border: solid 1px #cfd7dd; box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.1); padding: 12px } .wy-dropdown-menu > dd > a { display: block; clear: both; color: #404040; white-space: nowrap; font-size: 90%; padding: 0 12px; cursor: pointer } .wy-dropdown-menu > dd > a:hover { background: #2980B9; color: #fff } .wy-dropdown-menu > dd.divider { border-top: solid 1px #cfd7dd; margin: 6px 0 } .wy-dropdown-menu > dd.search { padding-bottom: 12px } .wy-dropdown-menu > dd.search input[type="search"] { width: 100% } .wy-dropdown-menu > dd.call-to-action { background: #e3e3e3; text-transform: uppercase; font-weight: 500; font-size: 80% } .wy-dropdown-menu > dd.call-to-action:hover { background: #e3e3e3 } .wy-dropdown-menu > dd.call-to-action .btn { color: #fff } .wy-dropdown.wy-dropdown-up .wy-dropdown-menu { bottom: 100%; top: auto; left: auto; right: 0 } .wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu { background: #fcfcfc; margin-top: 2px } .wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a { padding: 6px 12px } .wy-dropdown.wy-dropdown-bubble .wy-dropdown-menu a:hover { background: #2980B9; color: #fff } .wy-dropdown.wy-dropdown-left .wy-dropdown-menu { right: 0; text-align: right } .wy-dropdown-arrow:before { content: " "; border-bottom: 5px solid #f5f5f5; border-left: 5px solid transparent; border-right: 5px solid transparent; position: absolute; display: block; top: -4px; left: 50%; margin-left: -3px } .wy-dropdown-arrow.wy-dropdown-arrow-left:before { left: 11px } .wy-form-stacked select { display: block } .wy-form-aligned input, .wy-form-aligned textarea, .wy-form-aligned select, .wy-form-aligned .wy-help-inline, .wy-form-aligned label { display: inline-block; *display: inline; *zoom: 1; vertical-align: middle } .wy-form-aligned .wy-control-group > label { display: inline-block; vertical-align: middle; width: 10em; margin: 6px 12px 0 0; float: left } .wy-form-aligned .wy-control { float: left } .wy-form-aligned .wy-control label { display: block } .wy-form-aligned .wy-control select { margin-top: 6px } fieldset { border: 0; margin: 0; padding: 0 } legend { display: block; width: 100%; border: 0; padding: 0; white-space: normal; margin-bottom: 24px; font-size: 150%; *margin-left: -7px } label { display: block; margin: 0 0 0.3125em 0; color: #333; font-size: 90% } input, select, textarea { font-size: 100%; margin: 0; vertical-align: baseline; *vertical-align: middle } .wy-control-group { margin-bottom: 24px; *zoom: 1; max-width: 68em; margin-left: auto; margin-right: auto; *zoom: 1 } .wy-control-group:before, .wy-control-group:after { display: table; content: "" } .wy-control-group:after { clear: both } .wy-control-group:before, .wy-control-group:after { display: table; content: "" } .wy-control-group:after { clear: both } .wy-control-group.wy-control-group-required > label:after { content: " *"; color: #E74C3C } .wy-control-group .wy-form-full, .wy-control-group .wy-form-halves, .wy-control-group .wy-form-thirds { padding-bottom: 12px } .wy-control-group .wy-form-full select, .wy-control-group .wy-form-halves select, .wy-control-group .wy-form-thirds select { width: 100% } .wy-control-group .wy-form-full input[type="text"], .wy-control-group .wy-form-full input[type="password"], .wy-control-group .wy-form-full input[type="email"], .wy-control-group .wy-form-full input[type="url"], .wy-control-group .wy-form-full input[type="date"], .wy-control-group .wy-form-full input[type="month"], .wy-control-group .wy-form-full input[type="time"], .wy-control-group .wy-form-full input[type="datetime"], .wy-control-group .wy-form-full input[type="datetime-local"], .wy-control-group .wy-form-full input[type="week"], .wy-control-group .wy-form-full input[type="number"], .wy-control-group .wy-form-full input[type="search"], .wy-control-group .wy-form-full input[type="tel"], .wy-control-group .wy-form-full input[type="color"], .wy-control-group .wy-form-halves input[type="text"], .wy-control-group .wy-form-halves input[type="password"], .wy-control-group .wy-form-halves input[type="email"], .wy-control-group .wy-form-halves input[type="url"], .wy-control-group .wy-form-halves input[type="date"], .wy-control-group .wy-form-halves input[type="month"], .wy-control-group .wy-form-halves input[type="time"], .wy-control-group .wy-form-halves input[type="datetime"], .wy-control-group .wy-form-halves input[type="datetime-local"], .wy-control-group .wy-form-halves input[type="week"], .wy-control-group .wy-form-halves input[type="number"], .wy-control-group .wy-form-halves input[type="search"], .wy-control-group .wy-form-halves input[type="tel"], .wy-control-group .wy-form-halves input[type="color"], .wy-control-group .wy-form-thirds input[type="text"], .wy-control-group .wy-form-thirds input[type="password"], .wy-control-group .wy-form-thirds input[type="email"], .wy-control-group .wy-form-thirds input[type="url"], .wy-control-group .wy-form-thirds input[type="date"], .wy-control-group .wy-form-thirds input[type="month"], .wy-control-group .wy-form-thirds input[type="time"], .wy-control-group .wy-form-thirds input[type="datetime"], .wy-control-group .wy-form-thirds input[type="datetime-local"], .wy-control-group .wy-form-thirds input[type="week"], .wy-control-group .wy-form-thirds input[type="number"], .wy-control-group .wy-form-thirds input[type="search"], .wy-control-group .wy-form-thirds input[type="tel"], .wy-control-group .wy-form-thirds input[type="color"] { width: 100% } .wy-control-group .wy-form-full { float: left; display: block; margin-right: 2.35765%; width: 100%; margin-right: 0 } .wy-control-group .wy-form-full:last-child { margin-right: 0 } .wy-control-group .wy-form-halves { float: left; display: block; margin-right: 2.35765%; width: 48.82117% } .wy-control-group .wy-form-halves:last-child { margin-right: 0 } .wy-control-group .wy-form-halves:nth-of-type(2n) { margin-right: 0 } .wy-control-group .wy-form-halves:nth-of-type(2n+1) { clear: left } .wy-control-group .wy-form-thirds { float: left; display: block; margin-right: 2.35765%; width: 31.76157% } .wy-control-group .wy-form-thirds:last-child { margin-right: 0 } .wy-control-group .wy-form-thirds:nth-of-type(3n) { margin-right: 0 } .wy-control-group .wy-form-thirds:nth-of-type(3n+1) { clear: left } .wy-control-group.wy-control-group-no-input .wy-control { margin: 6px 0 0 0; font-size: 90% } .wy-control-no-input { display: inline-block; margin: 6px 0 0 0; font-size: 90% } .wy-control-group.fluid-input input[type="text"], .wy-control-group.fluid-input input[type="password"], .wy-control-group.fluid-input input[type="email"], .wy-control-group.fluid-input input[type="url"], .wy-control-group.fluid-input input[type="date"], .wy-control-group.fluid-input input[type="month"], .wy-control-group.fluid-input input[type="time"], .wy-control-group.fluid-input input[type="datetime"], .wy-control-group.fluid-input input[type="datetime-local"], .wy-control-group.fluid-input input[type="week"], .wy-control-group.fluid-input input[type="number"], .wy-control-group.fluid-input input[type="search"], .wy-control-group.fluid-input input[type="tel"], .wy-control-group.fluid-input input[type="color"] { width: 100% } .wy-form-message-inline { display: inline-block; padding-left: 0.3em; color: #666; vertical-align: middle; font-size: 90% } .wy-form-message { display: block; color: #999; font-size: 70%; margin-top: 0.3125em; font-style: italic } input { line-height: normal } input[type="button"], input[type="reset"], input[type="submit"] { -webkit-appearance: button; cursor: pointer; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; *overflow: visible } input[type="text"], input[type="password"], input[type="email"], input[type="url"], input[type="date"], input[type="month"], input[type="time"], input[type="datetime"], input[type="datetime-local"], input[type="week"], input[type="number"], input[type="search"], input[type="tel"], input[type="color"] { -webkit-appearance: none; padding: 6px; display: inline-block; border: 1px solid #ccc; font-size: 80%; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; box-shadow: inset 0 1px 3px #ddd; border-radius: 0; -webkit-transition: border 0.3s linear; -moz-transition: border 0.3s linear; transition: border 0.3s linear } input[type="datetime-local"] { padding: 0.34375em 0.625em } input[disabled] { cursor: default } input[type="checkbox"], input[type="radio"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box; padding: 0; margin-right: 0.3125em; *height: 13px; *width: 13px } input[type="search"] { -webkit-box-sizing: border-box; -moz-box-sizing: border-box; box-sizing: border-box } input[type="search"]::-webkit-search-cancel-button, input[type="search"]::-webkit-search-decoration { -webkit-appearance: none } input[type="text"]:focus, input[type="password"]:focus, input[type="email"]:focus, input[type="url"]:focus, input[type="date"]:focus, input[type="month"]:focus, input[type="time"]:focus, input[type="datetime"]:focus, input[type="datetime-local"]:focus, input[type="week"]:focus, input[type="number"]:focus, input[type="search"]:focus, input[type="tel"]:focus, input[type="color"]:focus { outline: 0; outline: thin dotted \9; border-color: #333 } input.no-focus:focus { border-color: #ccc !important } input[type="file"]:focus, input[type="radio"]:focus, input[type="checkbox"]:focus { outline: thin dotted #333; outline: 1px auto #129FEA } input[type="text"][disabled], input[type="password"][disabled], input[type="email"][disabled], input[type="url"][disabled], input[type="date"][disabled], input[type="month"][disabled], input[type="time"][disabled], input[type="datetime"][disabled], input[type="datetime-local"][disabled], input[type="week"][disabled], input[type="number"][disabled], input[type="search"][disabled], input[type="tel"][disabled], input[type="color"][disabled] { cursor: not-allowed; background-color: #f3f6f6; color: #cad2d3 } input:focus:invalid, textarea:focus:invalid, select:focus:invalid { color: #E74C3C; border: 1px solid #E74C3C } input:focus:invalid:focus, textarea:focus:invalid:focus, select:focus:invalid:focus { border-color: #E74C3C } input[type="file"]:focus:invalid:focus, input[type="radio"]:focus:invalid:focus, input[type="checkbox"]:focus:invalid:focus { outline-color: #E74C3C } input.wy-input-large { padding: 12px; font-size: 100% } textarea { overflow: auto; vertical-align: top; width: 100%; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif } select, textarea { padding: 0.5em 0.625em; display: inline-block; border: 1px solid #ccc; font-size: 80%; box-shadow: inset 0 1px 3px #ddd; -webkit-transition: border 0.3s linear; -moz-transition: border 0.3s linear; transition: border 0.3s linear } select { border: 1px solid #ccc; background-color: #fff } select[multiple] { height: auto } select:focus, textarea:focus { outline: 0 } select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly] { cursor: not-allowed; background-color: #fff; color: #cad2d3; border-color: transparent } .wy-checkbox, .wy-radio { margin: 6px 0; color: #404040; display: block } .wy-checkbox input, .wy-radio input { vertical-align: baseline } .wy-form-message-inline { display: inline-block; *display: inline; *zoom: 1; vertical-align: middle } .wy-input-prefix, .wy-input-suffix { white-space: nowrap; padding: 6px } .wy-input-prefix .wy-input-context, .wy-input-suffix .wy-input-context { line-height: 27px; padding: 0 8px; display: inline-block; font-size: 80%; background-color: #f3f6f6; border: solid 1px #ccc; color: #999 } .wy-input-suffix .wy-input-context { border-left: 0 } .wy-input-prefix .wy-input-context { border-right: 0 } .wy-control-group.wy-control-group-error .wy-form-message, .wy-control-group.wy-control-group-error > label { color: #E74C3C } .wy-control-group.wy-control-group-error input[type="text"], .wy-control-group.wy-control-group-error input[type="password"], .wy-control-group.wy-control-group-error input[type="email"], .wy-control-group.wy-control-group-error input[type="url"], .wy-control-group.wy-control-group-error input[type="date"], .wy-control-group.wy-control-group-error input[type="month"], .wy-control-group.wy-control-group-error input[type="time"], .wy-control-group.wy-control-group-error input[type="datetime"], .wy-control-group.wy-control-group-error input[type="datetime-local"], .wy-control-group.wy-control-group-error input[type="week"], .wy-control-group.wy-control-group-error input[type="number"], .wy-control-group.wy-control-group-error input[type="search"], .wy-control-group.wy-control-group-error input[type="tel"], .wy-control-group.wy-control-group-error input[type="color"] { border: solid 1px #E74C3C } .wy-control-group.wy-control-group-error textarea { border: solid 1px #E74C3C } .wy-inline-validate { white-space: nowrap } .wy-inline-validate .wy-input-context { padding: 0.5em 0.625em; display: inline-block; font-size: 80% } .wy-inline-validate.wy-inline-validate-success .wy-input-context { color: #27AE60 } .wy-inline-validate.wy-inline-validate-danger .wy-input-context { color: #E74C3C } .wy-inline-validate.wy-inline-validate-warning .wy-input-context { color: #E67E22 } .wy-inline-validate.wy-inline-validate-info .wy-input-context { color: #2980B9 } .rotate-90 { -webkit-transform: rotate(90deg); -moz-transform: rotate(90deg); -ms-transform: rotate(90deg); -o-transform: rotate(90deg); transform: rotate(90deg) } .rotate-180 { -webkit-transform: rotate(180deg); -moz-transform: rotate(180deg); -ms-transform: rotate(180deg); -o-transform: rotate(180deg); transform: rotate(180deg) } .rotate-270 { -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -ms-transform: rotate(270deg); -o-transform: rotate(270deg); transform: rotate(270deg) } .mirror { -webkit-transform: scaleX(-1); -moz-transform: scaleX(-1); -ms-transform: scaleX(-1); -o-transform: scaleX(-1); transform: scaleX(-1) } .mirror.rotate-90 { -webkit-transform: scaleX(-1) rotate(90deg); -moz-transform: scaleX(-1) rotate(90deg); -ms-transform: scaleX(-1) rotate(90deg); -o-transform: scaleX(-1) rotate(90deg); transform: scaleX(-1) rotate(90deg) } .mirror.rotate-180 { -webkit-transform: scaleX(-1) rotate(180deg); -moz-transform: scaleX(-1) rotate(180deg); -ms-transform: scaleX(-1) rotate(180deg); -o-transform: scaleX(-1) rotate(180deg); transform: scaleX(-1) rotate(180deg) } .mirror.rotate-270 { -webkit-transform: scaleX(-1) rotate(270deg); -moz-transform: scaleX(-1) rotate(270deg); -ms-transform: scaleX(-1) rotate(270deg); -o-transform: scaleX(-1) rotate(270deg); transform: scaleX(-1) rotate(270deg) } @media only screen and (max-width: 480px) { .wy-form button[type="submit"] { margin: 0.7em 0 0 } .wy-form input[type="text"], .wy-form input[type="password"], .wy-form input[type="email"], .wy-form input[type="url"], .wy-form input[type="date"], .wy-form input[type="month"], .wy-form input[type="time"], .wy-form input[type="datetime"], .wy-form input[type="datetime-local"], .wy-form input[type="week"], .wy-form input[type="number"], .wy-form input[type="search"], .wy-form input[type="tel"], .wy-form input[type="color"] { margin-bottom: 0.3em; display: block } .wy-form label { margin-bottom: 0.3em; display: block } .wy-form input[type="password"], .wy-form input[type="email"], .wy-form input[type="url"], .wy-form input[type="date"], .wy-form input[type="month"], .wy-form input[type="time"], .wy-form input[type="datetime"], .wy-form input[type="datetime-local"], .wy-form input[type="week"], .wy-form input[type="number"], .wy-form input[type="search"], .wy-form input[type="tel"], .wy-form input[type="color"] { margin-bottom: 0 } .wy-form-aligned .wy-control-group label { margin-bottom: 0.3em; text-align: left; display: block; width: 100% } .wy-form-aligned .wy-control { margin: 1.5em 0 0 0 } .wy-form .wy-help-inline, .wy-form-message-inline, .wy-form-message { display: block; font-size: 80%; padding: 6px 0 } } @media screen and (max-width: 768px) { .tablet-hide { display: none } } @media screen and (max-width: 480px) { .mobile-hide { display: none } } .float-left { float: left } .float-right { float: right } .full-width { width: 100% } .wy-table, .rst-content table.docutils, .rst-content table.field-list { border-collapse: collapse; border-spacing: 0; empty-cells: show; margin-bottom: 24px } .wy-table caption, .rst-content table.docutils caption, .rst-content table.field-list caption { color: #000; font: italic 85%/1 arial, sans-serif; padding: 1em 0; text-align: center } .wy-table td, .rst-content table.docutils td, .rst-content table.field-list td, .wy-table th, .rst-content table.docutils th, .rst-content table.field-list th { font-size: 90%; margin: 0; overflow: visible; padding: 8px 16px } .wy-table td:first-child, .rst-content table.docutils td:first-child, .rst-content table.field-list td:first-child, .wy-table th:first-child, .rst-content table.docutils th:first-child, .rst-content table.field-list th:first-child { border-left-width: 0 } .wy-table thead, .rst-content table.docutils thead, .rst-content table.field-list thead { color: #000; text-align: left; vertical-align: bottom; white-space: nowrap } .wy-table thead th, .rst-content table.docutils thead th, .rst-content table.field-list thead th { font-weight: bold; border-bottom: solid 2px #e1e4e5 } .wy-table td, .rst-content table.docutils td, .rst-content table.field-list td { background-color: transparent; vertical-align: middle } .wy-table td p, .rst-content table.docutils td p, .rst-content table.field-list td p { line-height: 18px } .wy-table td p:last-child, .rst-content table.docutils td p:last-child, .rst-content table.field-list td p:last-child { margin-bottom: 0 } .wy-table .wy-table-cell-min, .rst-content table.docutils .wy-table-cell-min, .rst-content table.field-list .wy-table-cell-min { width: 1%; padding-right: 0 } .wy-table .wy-table-cell-min input[type=checkbox], .rst-content table.docutils .wy-table-cell-min input[type=checkbox], .rst-content table.field-list .wy-table-cell-min input[type=checkbox], .wy-table .wy-table-cell-min input[type=checkbox], .rst-content table.docutils .wy-table-cell-min input[type=checkbox], .rst-content table.field-list .wy-table-cell-min input[type=checkbox] { margin: 0 } .wy-table-secondary { color: gray; font-size: 90% } .wy-table-tertiary { color: gray; font-size: 80% } .wy-table-odd td, .wy-table-striped tr:nth-child(2n-1) td, .rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td { background-color: #f3f6f6 } .wy-table-backed { background-color: #f3f6f6 } .wy-table-bordered-all, .rst-content table.docutils { border: 1px solid #e1e4e5 } .wy-table-bordered-all td, .rst-content table.docutils td { border-bottom: 1px solid #e1e4e5; border-left: 1px solid #e1e4e5 } .wy-table-bordered-all tbody > tr:last-child td, .rst-content table.docutils tbody > tr:last-child td { border-bottom-width: 0 } .wy-table-bordered { border: 1px solid #e1e4e5 } .wy-table-bordered-rows td { border-bottom: 1px solid #e1e4e5 } .wy-table-bordered-rows tbody > tr:last-child td { border-bottom-width: 0 } .wy-table-horizontal tbody > tr:last-child td { border-bottom-width: 0 } .wy-table-horizontal td, .wy-table-horizontal th { border-width: 0 0 1px 0; border-bottom: 1px solid #e1e4e5 } .wy-table-horizontal tbody > tr:last-child td { border-bottom-width: 0 } .wy-table-responsive { margin-bottom: 24px; max-width: 100%; overflow: auto } .wy-table-responsive table { margin-bottom: 0 !important } .wy-table-responsive table td, .wy-table-responsive table th { white-space: nowrap } a { color: #2980B9; text-decoration: none; cursor: pointer } a:hover { color: #3091d1 } a:visited { color: #9B59B6 } html { height: 100%; overflow-x: hidden } body { font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; font-weight: normal; color: #404040; min-height: 100%; overflow-x: hidden; background: #edf0f2 } .wy-text-left { text-align: left } .wy-text-center { text-align: center } .wy-text-right { text-align: right } .wy-text-large { font-size: 120% } .wy-text-normal { font-size: 100% } .wy-text-small, small { font-size: 80% } .wy-text-strike { text-decoration: line-through } .wy-text-warning { color: #E67E22 !important } a.wy-text-warning:hover { color: #eb9950 !important } .wy-text-info { color: #2980B9 !important } a.wy-text-info:hover { color: #409ad5 !important } .wy-text-success { color: #27AE60 !important } a.wy-text-success:hover { color: #36d278 !important } .wy-text-danger { color: #E74C3C !important } a.wy-text-danger:hover { color: #ed7669 !important } .wy-text-neutral { color: #404040 !important } a.wy-text-neutral:hover { color: #595959 !important } h1, h2, h3, h4, h5, h6, legend { margin-top: 0; font-weight: 700; font-family: "Roboto Slab", "ff-tisa-web-pro", "Georgia", Arial, sans-serif } p { line-height: 24px; margin: 0; font-size: 16px; margin-bottom: 24px } h1 { font-size: 175% } h2 { font-size: 150% } h3 { font-size: 125% } h4 { font-size: 115% } h5 { font-size: 110% } h6 { font-size: 100% } hr { display: block; height: 1px; border: 0; border-top: 1px solid #e1e4e5; margin: 24px 0; padding: 0 } code, .rst-content tt { white-space: nowrap; max-width: 100%; background: #fff; border: solid 1px #e1e4e5; font-size: 75%; padding: 0 5px; font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; color: #E74C3C; overflow-x: auto } code.code-large, .rst-content tt.code-large { font-size: 90% } .wy-plain-list-disc, .rst-content .section ul, .rst-content .toctree-wrapper ul, article ul { list-style: disc; line-height: 24px; margin-bottom: 24px } .wy-plain-list-disc li, .rst-content .section ul li, .rst-content .toctree-wrapper ul li, article ul li { list-style: disc; margin-left: 24px } .wy-plain-list-disc li p:last-child, .rst-content .section ul li p:last-child, .rst-content .toctree-wrapper ul li p:last-child, article ul li p:last-child { margin-bottom: 0 } .wy-plain-list-disc li ul, .rst-content .section ul li ul, .rst-content .toctree-wrapper ul li ul, article ul li ul { margin-bottom: 0 } .wy-plain-list-disc li li, .rst-content .section ul li li, .rst-content .toctree-wrapper ul li li, article ul li li { list-style: circle } .wy-plain-list-disc li li li, .rst-content .section ul li li li, .rst-content .toctree-wrapper ul li li li, article ul li li li { list-style: square } .wy-plain-list-disc li ol li, .rst-content .section ul li ol li, .rst-content .toctree-wrapper ul li ol li, article ul li ol li { list-style: decimal } .wy-plain-list-decimal, .rst-content .section ol, .rst-content ol.arabic, article ol { list-style: decimal; line-height: 24px; margin-bottom: 24px } .wy-plain-list-decimal li, .rst-content .section ol li, .rst-content ol.arabic li, article ol li { list-style: decimal; margin-left: 24px } .wy-plain-list-decimal li p:last-child, .rst-content .section ol li p:last-child, .rst-content ol.arabic li p:last-child, article ol li p:last-child { margin-bottom: 0 } .wy-plain-list-decimal li ul, .rst-content .section ol li ul, .rst-content ol.arabic li ul, article ol li ul { margin-bottom: 0 } .wy-plain-list-decimal li ul li, .rst-content .section ol li ul li, .rst-content ol.arabic li ul li, article ol li ul li { list-style: disc } .codeblock-example { border: 1px solid #e1e4e5; border-bottom: none; padding: 24px; padding-top: 48px; font-weight: 500; background: #fff; position: relative } .codeblock-example:after { content: "Example"; position: absolute; top: 0px; left: 0px; background: #9B59B6; color: #fff; padding: 6px 12px } .codeblock-example.prettyprint-example-only { border: 1px solid #e1e4e5; margin-bottom: 24px } .codeblock, pre.literal-block, .rst-content .literal-block, .rst-content pre.literal-block, div[class^='highlight'] { border: 1px solid #e1e4e5; padding: 0px; overflow-x: auto; background: #fff; margin: 1px 0 24px 0 } .codeblock div[class^='highlight'], pre.literal-block div[class^='highlight'], .rst-content .literal-block div[class^='highlight'], div[class^='highlight'] div[class^='highlight'] { border: none; background: none; margin: 0 } div[class^='highlight'] td.code { width: 100% } .linenodiv pre { border-right: solid 1px #e6e9ea; margin: 0; padding: 12px 12px; font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; font-size: 12px; line-height: 1.5; color: #d9d9d9 } div[class^='highlight'] pre { white-space: pre; margin: 0; padding: 12px 12px; font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace; font-size: 12px; line-height: 1.5; display: block; overflow: auto; color: #404040 } @media print { .codeblock, pre.literal-block, .rst-content .literal-block, .rst-content pre.literal-block, div[class^='highlight'], div[class^='highlight'] pre { white-space: pre-wrap } } .hll { background-color: #ffc; margin: 0 -12px; padding: 0 12px; display: block } .c { color: #998; font-style: italic } .err { color: #a61717; background-color: #e3d2d2 } .k { font-weight: bold } .o { font-weight: bold } .cm { color: #998; font-style: italic } .cp { color: #999; font-weight: bold } .c1 { color: #998; font-style: italic } .cs { color: #999; font-weight: bold; font-style: italic } .gd { color: #000; background-color: #fdd } .gd .x { color: #000; background-color: #faa } .ge { font-style: italic } .gr { color: #a00 } .gh { color: #999 } .gi { color: #000; background-color: #dfd } .gi .x { color: #000; background-color: #afa } .go { color: #888 } .gp { color: #555 } .gs { font-weight: bold } .gu { color: purple; font-weight: bold } .gt { color: #a00 } .kc { font-weight: bold } .kd { font-weight: bold } .kn { font-weight: bold } .kp { font-weight: bold } .kr { font-weight: bold } .kt { color: #458; font-weight: bold } .m { color: #099 } .s { color: #d14 } .n { color: #333 } .na { color: teal } .nb { color: #0086b3 } .nc { color: #458; font-weight: bold } .no { color: teal } .ni { color: purple } .ne { color: #900; font-weight: bold } .nf { color: #900; font-weight: bold } .nn { color: #555 } .nt { color: navy } .nv { color: teal } .ow { font-weight: bold } .w { color: #bbb } .mf { color: #099 } .mh { color: #099 } .mi { color: #099 } .mo { color: #099 } .sb { color: #d14 } .sc { color: #d14 } .sd { color: #d14 } .s2 { color: #d14 } .se { color: #d14 } .sh { color: #d14 } .si { color: #d14 } .sx { color: #d14 } .sr { color: #009926 } .s1 { color: #d14 } .ss { color: #990073 } .bp { color: #999 } .vc { color: teal } .vg { color: teal } .vi { color: teal } .il { color: #099 } .gc { color: #999; background-color: #EAF2F5 } .wy-breadcrumbs li { display: inline-block } .wy-breadcrumbs li.wy-breadcrumbs-aside { float: right } .wy-breadcrumbs li a { display: inline-block; padding: 5px } .wy-breadcrumbs li a:first-child { padding-left: 0 } .wy-breadcrumbs-extra { margin-bottom: 0; color: #b3b3b3; font-size: 80%; display: inline-block } @media screen and (max-width: 480px) { .wy-breadcrumbs-extra { display: none } .wy-breadcrumbs li.wy-breadcrumbs-aside { display: none } } @media print { .wy-breadcrumbs li.wy-breadcrumbs-aside { display: none } } .wy-affix { position: fixed; top: 1.618em } .wy-menu a:hover { text-decoration: none } .wy-menu-horiz { *zoom: 1 } .wy-menu-horiz:before, .wy-menu-horiz:after { display: table; content: "" } .wy-menu-horiz:after { clear: both } .wy-menu-horiz ul, .wy-menu-horiz li { display: inline-block } .wy-menu-horiz li:hover { background: rgba(255, 255, 255, 0.1) } .wy-menu-horiz li.divide-left { border-left: solid 1px #404040 } .wy-menu-horiz li.divide-right { border-right: solid 1px #404040 } .wy-menu-horiz a { height: 32px; display: inline-block; line-height: 32px; padding: 0 16px } .wy-menu-vertical header { height: 32px; display: inline-block; line-height: 32px; padding: 0 1.618em; display: block; font-weight: bold; text-transform: uppercase; font-size: 80%; color: #2980B9; white-space: nowrap } .wy-menu-vertical ul { margin-bottom: 0 } .wy-menu-vertical li.divide-top { border-top: solid 1px #404040 } .wy-menu-vertical li.divide-bottom { border-bottom: solid 1px #404040 } .wy-menu-vertical li.current { background: #e3e3e3 } .wy-menu-vertical li.current a { color: gray; border-right: solid 1px #c9c9c9; padding: 0.4045em 2.427em } .wy-menu-vertical li.current a:hover { background: #d6d6d6 } .wy-menu-vertical li.on a, .wy-menu-vertical li.current > a { color: #404040; padding: 0.4045em 1.618em; font-weight: bold; position: relative; background: #fcfcfc; border: none; border-bottom: solid 1px #c9c9c9; border-top: solid 1px #c9c9c9; padding-left: 1.618em -4px } .wy-menu-vertical li.on a:hover, .wy-menu-vertical li.current > a:hover { background: #fcfcfc } .wy-menu-vertical li.toctree-l2.current > a { background: #c9c9c9; padding: 0.4045em 2.427em } .wy-menu-vertical li.current ul { display: block } .wy-menu-vertical li ul { margin-bottom: 0; display: none } .wy-menu-vertical .local-toc li ul { display: block } .wy-menu-vertical li ul li a { margin-bottom: 0; color: #b3b3b3; font-weight: normal } .wy-menu-vertical a { display: inline-block; line-height: 18px; padding: 0.4045em 1.618em; display: block; position: relative; font-size: 90%; color: #b3b3b3 } .wy-menu-vertical a:hover { background-color: #4e4a4a; cursor: pointer } .wy-menu-vertical a:active { background-color: #2980B9; cursor: pointer; color: #fff } .wy-side-nav-search { z-index: 200; background-color: #2980B9; text-align: center; padding: 0.809em; display: block; color: #fcfcfc; margin-bottom: 0.809em } .wy-side-nav-search input[type=text] { width: 100%; border-radius: 50px; padding: 6px 12px; border-color: #2472a4 } .wy-side-nav-search img { display: block; margin: auto auto 0.809em auto; height: 45px; width: 45px; background-color: #2980B9; padding: 5px; border-radius: 100% } .wy-side-nav-search > a, .wy-side-nav-search .wy-dropdown > a { color: #fcfcfc; font-size: 100%; font-weight: bold; display: inline-block; padding: 4px 6px; margin-bottom: 0.809em } .wy-side-nav-search > a:hover, .wy-side-nav-search .wy-dropdown > a:hover { background: rgba(255, 255, 255, 0.1) } .wy-nav .wy-menu-vertical header { color: #2980B9 } .wy-nav .wy-menu-vertical a { color: #b3b3b3 } .wy-nav .wy-menu-vertical a:hover { background-color: #2980B9; color: #fff } [data-menu-wrap] { -webkit-transition: all 0.2s ease-in; -moz-transition: all 0.2s ease-in; transition: all 0.2s ease-in; position: absolute; opacity: 1; width: 100%; opacity: 0 } [data-menu-wrap].move-center { left: 0; right: auto; opacity: 1 } [data-menu-wrap].move-left { right: auto; left: -100%; opacity: 0 } [data-menu-wrap].move-right { right: -100%; left: auto; opacity: 0 } .wy-body-for-nav { background: left repeat-y #fcfcfc; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC); background-size: 300px 1px } .wy-grid-for-nav { position: absolute; width: 100%; height: 100% } .wy-nav-side { position: absolute; top: 0; left: 0; width: 300px; overflow: hidden; min-height: 100%; background: #343131; z-index: 200 } .wy-nav-top { display: none; background: #2980B9; color: #fff; padding: 0.4045em 0.809em; position: relative; line-height: 50px; text-align: center; font-size: 100%; *zoom: 1 } .wy-nav-top:before, .wy-nav-top:after { display: table; content: "" } .wy-nav-top:after { clear: both } .wy-nav-top a { color: #fff; font-weight: bold } .wy-nav-top img { margin-right: 12px; height: 45px; width: 45px; background-color: #2980B9; padding: 5px; border-radius: 100% } .wy-nav-top i { font-size: 30px; float: left; cursor: pointer } .wy-nav-content-wrap { margin-left: 300px; background: #fcfcfc; min-height: 100% } .wy-nav-content { padding: 1.618em 3.236em; height: 100%; max-width: 800px; margin: auto } .wy-body-mask { position: fixed; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.2); display: none; z-index: 499 } .wy-body-mask.on { display: block } footer { color: #999 } footer p { margin-bottom: 12px } .rst-footer-buttons { *zoom: 1 } .rst-footer-buttons:before, .rst-footer-buttons:after { display: table; content: "" } .rst-footer-buttons:after { clear: both } #search-results .search li { margin-bottom: 24px; border-bottom: solid 1px #e1e4e5; padding-bottom: 24px } #search-results .search li:first-child { border-top: solid 1px #e1e4e5; padding-top: 24px } #search-results .search li a { font-size: 120%; margin-bottom: 12px; display: inline-block } #search-results .context { color: gray; font-size: 90% } @media screen and (max-width: 768px) { .wy-body-for-nav { background: #fcfcfc } .wy-nav-top { display: block } .wy-nav-side { left: -300px } .wy-nav-side.shift { width: 85%; left: 0 } .wy-nav-content-wrap { margin-left: 0 } .wy-nav-content-wrap .wy-nav-content { padding: 1.618em } .wy-nav-content-wrap.shift { position: fixed; min-width: 100%; left: 85%; top: 0; height: 100%; overflow: hidden } } @media screen and (min-width: 1400px) { .wy-nav-content-wrap { background: rgba(0, 0, 0, 0.05) } .wy-nav-content { margin: 0; background: #fcfcfc } } @media print { .rst-versions, footer, .wy-nav-side { display: none } .wy-nav-content-wrap { margin-left: 0 } } nav.stickynav { position: fixed; top: 0 } .rst-versions { position: fixed; bottom: 0; left: 0; width: 300px; color: #fcfcfc; background: #1f1d1d; border-top: solid 10px #343131; font-family: "Proxima Nova", "Helvetica Neue", Arial, sans-serif; z-index: 400 } .rst-versions a { color: #2980B9; text-decoration: none } .rst-versions .rst-badge-small { display: none } .rst-versions .rst-current-version { padding: 12px; background-color: #272525; display: block; text-align: right; font-size: 90%; cursor: pointer; color: #27AE60; *zoom: 1 } .rst-versions .rst-current-version:before, .rst-versions .rst-current-version:after { display: table; content: "" } .rst-versions .rst-current-version:after { clear: both } .rst-versions .rst-current-version .fa, .rst-versions .rst-current-version .rst-content .admonition-title, .rst-content .rst-versions .rst-current-version .admonition-title, .rst-versions .rst-current-version .rst-content h1 .headerlink, .rst-content h1 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h2 .headerlink, .rst-content h2 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h3 .headerlink, .rst-content h3 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h4 .headerlink, .rst-content h4 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h5 .headerlink, .rst-content h5 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content h6 .headerlink, .rst-content h6 .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .rst-content dl dt .headerlink, .rst-content dl dt .rst-versions .rst-current-version .headerlink, .rst-versions .rst-current-version .icon { color: #fcfcfc } .rst-versions .rst-current-version .fa-book, .rst-versions .rst-current-version .icon-book { float: left } .rst-versions .rst-current-version .icon-book { float: left } .rst-versions .rst-current-version.rst-out-of-date { background-color: #E74C3C; color: #fff } .rst-versions .rst-current-version.rst-active-old-version { background-color: #F1C40F; color: #000 } .rst-versions.shift-up .rst-other-versions { display: block } .rst-versions .rst-other-versions { font-size: 90%; padding: 12px; color: gray; display: none } .rst-versions .rst-other-versions hr { display: block; height: 1px; border: 0; margin: 20px 0; padding: 0; border-top: solid 1px #413d3d } .rst-versions .rst-other-versions dd { display: inline-block; margin: 0 } .rst-versions .rst-other-versions dd a { display: inline-block; padding: 6px; color: #fcfcfc } .rst-versions.rst-badge { width: auto; bottom: 20px; right: 20px; left: auto; border: none; max-width: 300px } .rst-versions.rst-badge .icon-book { float: none } .rst-versions.rst-badge .fa-book, .rst-versions.rst-badge .icon-book { float: none } .rst-versions.rst-badge.shift-up .rst-current-version { text-align: right } .rst-versions.rst-badge.shift-up .rst-current-version .fa-book, .rst-versions.rst-badge.shift-up .rst-current-version .icon-book { float: left } .rst-versions.rst-badge.shift-up .rst-current-version .icon-book { float: left } .rst-versions.rst-badge .rst-current-version { width: auto; height: 30px; line-height: 30px; padding: 0 6px; display: block; text-align: center } @media screen and (max-width: 768px) { .rst-versions { width: 85%; display: none } .rst-versions.shift { display: block } img { width: 100%; height: auto } } .rst-content img { max-width: 100%; height: auto !important } .rst-content div.figure { margin-bottom: 24px } .rst-content div.figure.align-center { text-align: center } .rst-content .section > img, .rst-content .section > a > img { margin-bottom: 24px } .rst-content blockquote { margin-left: 24px; line-height: 24px; margin-bottom: 24px } .rst-content .note .last, .rst-content .attention .last, .rst-content .caution .last, .rst-content .danger .last, .rst-content .error .last, .rst-content .hint .last, .rst-content .important .last, .rst-content .tip .last, .rst-content .warning .last, .rst-content .seealso .last, .rst-content .admonition-todo .last { margin-bottom: 0 } .rst-content .admonition-title:before { margin-right: 4px } .rst-content .admonition table { border-color: rgba(0, 0, 0, 0.1) } .rst-content .admonition table td, .rst-content .admonition table th { background: transparent !important; border-color: rgba(0, 0, 0, 0.1) !important } .rst-content .section ol.loweralpha, .rst-content .section ol.loweralpha li { list-style: lower-alpha } .rst-content .section ol.upperalpha, .rst-content .section ol.upperalpha li { list-style: upper-alpha } .rst-content .section ol p, .rst-content .section ul p { margin-bottom: 12px } .rst-content .line-block { margin-left: 24px } .rst-content .topic-title { font-weight: bold; margin-bottom: 12px } .rst-content .toc-backref { color: #404040 } .rst-content .align-right { float: right; margin: 0px 0px 24px 24px } .rst-content .align-left { float: left; margin: 0px 24px 24px 0px } .rst-content .align-center { margin: auto; display: block } .rst-content h1 .headerlink, .rst-content h2 .headerlink, .rst-content h3 .headerlink, .rst-content h4 .headerlink, .rst-content h5 .headerlink, .rst-content h6 .headerlink, .rst-content dl dt .headerlink { display: none; visibility: hidden; font-size: 14px } .rst-content h1 .headerlink:after, .rst-content h2 .headerlink:after, .rst-content h3 .headerlink:after, .rst-content h4 .headerlink:after, .rst-content h5 .headerlink:after, .rst-content h6 .headerlink:after, .rst-content dl dt .headerlink:after { visibility: visible; content: ""; font-family: FontAwesome; display: inline-block } .rst-content h1:hover .headerlink, .rst-content h2:hover .headerlink, .rst-content h3:hover .headerlink, .rst-content h4:hover .headerlink, .rst-content h5:hover .headerlink, .rst-content h6:hover .headerlink, .rst-content dl dt:hover .headerlink { display: inline-block } .rst-content .sidebar { float: right; width: 40%; display: block; margin: 0 0 24px 24px; padding: 24px; background: #f3f6f6; border: solid 1px #e1e4e5 } .rst-content .sidebar p, .rst-content .sidebar ul, .rst-content .sidebar dl { font-size: 90% } .rst-content .sidebar .last { margin-bottom: 0 } .rst-content .sidebar .sidebar-title { display: block; font-family: "Roboto Slab", "ff-tisa-web-pro", "Georgia", Arial, sans-serif; font-weight: bold; background: #e1e4e5; padding: 6px 12px; margin: -24px; margin-bottom: 24px; font-size: 100% } .rst-content .highlighted { background: #F1C40F; display: inline-block; font-weight: bold; padding: 0 6px } .rst-content .footnote-reference, .rst-content .citation-reference { vertical-align: super; font-size: 90% } .rst-content table.docutils.citation, .rst-content table.docutils.footnote { background: none; border: none; color: #999 } .rst-content table.docutils.citation td, .rst-content table.docutils.citation tr, .rst-content table.docutils.footnote td, .rst-content table.docutils.footnote tr { border: none; background-color: transparent !important; white-space: normal } .rst-content table.docutils.citation td.label, .rst-content table.docutils.footnote td.label { padding-left: 0; padding-right: 0; vertical-align: top } .rst-content table.field-list { border: none } .rst-content table.field-list td { border: none; padding-top: 5px } .rst-content table.field-list td > strong { display: inline-block; margin-top: 3px } .rst-content table.field-list .field-name { padding-right: 10px; text-align: left; white-space: nowrap } .rst-content table.field-list .field-body { text-align: left; padding-left: 0 } .rst-content tt { color: #000 } .rst-content tt big, .rst-content tt em { font-size: 100% !important; line-height: normal } .rst-content tt .xref, a .rst-content tt { font-weight: bold } .rst-content a tt { color: #2980B9 } .rst-content dl { margin-bottom: 24px } .rst-content dl dt { font-weight: bold } .rst-content dl p, .rst-content dl table, .rst-content dl ul, .rst-content dl ol { margin-bottom: 12px !important } .rst-content dl dd { margin: 0 0 12px 24px } .rst-content dl:not(.docutils) { margin-bottom: 24px } .rst-content dl:not(.docutils) dt { display: inline-block; margin: 6px 0; font-size: 90%; line-height: normal; background: #e7f2fa; color: #2980B9; border-top: solid 3px #6ab0de; padding: 6px; position: relative } .rst-content dl:not(.docutils) dt:before { color: #6ab0de } .rst-content dl:not(.docutils) dt .headerlink { color: #404040; font-size: 100% !important } .rst-content dl:not(.docutils) dl dt { margin-bottom: 6px; border: none; border-left: solid 3px #ccc; background: #f0f0f0; color: gray } .rst-content dl:not(.docutils) dl dt .headerlink { color: #404040; font-size: 100% !important } .rst-content dl:not(.docutils) dt:first-child { margin-top: 0 } .rst-content dl:not(.docutils) tt { font-weight: bold } .rst-content dl:not(.docutils) tt.descname, .rst-content dl:not(.docutils) tt.descclassname { background-color: transparent; border: none; padding: 0; font-size: 100% !important } .rst-content dl:not(.docutils) tt.descname { font-weight: bold } .rst-content dl:not(.docutils) .optional { display: inline-block; padding: 0 4px; color: #000; font-weight: bold } .rst-content dl:not(.docutils) .property { display: inline-block; padding-right: 8px } .rst-content .viewcode-link, .rst-content .viewcode-back { display: inline-block; color: #27AE60; font-size: 80%; padding-left: 24px } .rst-content .viewcode-back { display: block; float: right } .rst-content p.rubric { margin-bottom: 12px; font-weight: bold } @media screen and (max-width: 480px) { .rst-content .sidebar { width: 100% } } span[id*='MathJax-Span'] { color: #404040 } .math { text-align: center } /*# sourceMappingURL=theme.css.map */ ================================================ FILE: documentation/website/default.twig ================================================ {{ title }} {{ title|default('The title') }} {% for itemId, item in menu.items %} {{ item.text|raw }} {% if item.items is defined %} {% for subItemId, subItem in item.items %} {{ subItem.text|raw }} {% endfor %} {% endif %} {% endfor %} Maintained by: Creative Digital Agency {# MOBILE NAV, TRIGGLES SIDE NAV ON TOGGLE #} {{ title|default('The title') }} {# PAGE CONTENT #} {# Maintained by SWIS#} Fork on GitHub {{ content|raw }} ================================================ FILE: documentation/website/js/main.js ================================================ (function($, undefined){ $(function() { $('.document table').addClass('docutils'); $('.showcase').each(function(){ var $this = $(that || this), text, nodeName, lang, that; if ($this.data('showcaseImport')) { $this = $($this.data('showcaseImport')); that = $this.get(0); } nodeName = (that || this).nodeName.toLowerCase(); lang = nodeName == 'script' ? 'js' : (nodeName == 'style' ? 'css' : 'html'); if (lang == 'html') { text = $('').append($this.clone()).html(); } else { text = $this.text(); } var newNode = $('') .append($('').text(text)) .insertBefore(this); that && $(this).remove(); }); }); })(jQuery); ================================================ FILE: documentation/website/js/theme.js ================================================ $( document ).ready(function() { // Shift nav in mobile when clicking the menu. $(document).on('click', "[data-toggle='wy-nav-top']", function() { $("[data-toggle='wy-nav-shift']").toggleClass("shift"); $("[data-toggle='rst-versions']").toggleClass("shift"); }); // Close menu when you click a link. $(document).on('click', ".wy-menu-vertical .current ul li a", function() { $("[data-toggle='wy-nav-shift']").removeClass("shift"); $("[data-toggle='rst-versions']").toggleClass("shift"); }); $(document).on('click', "[data-toggle='rst-current-version']", function() { $("[data-toggle='rst-versions']").toggleClass("shift-up"); }); // Make tables responsive $("table.docutils:not(.field-list)").wrap(""); }); window.SphinxRtdTheme = (function (jquery) { var stickyNav = (function () { var navBar, win, stickyNavCssClass = 'stickynav', applyStickNav = function () { if (navBar.height() <= win.height()) { navBar.addClass(stickyNavCssClass); } else { navBar.removeClass(stickyNavCssClass); } }, enable = function () { applyStickNav(); win.on('resize', applyStickNav); }, init = function () { navBar = jquery('nav.wy-nav-side:first'); win = jquery(window); }; jquery(init); return { enable : enable }; }()); return { StickyNav : stickyNav }; }($)); ================================================ FILE: documentation/website/original-theme/bower.json ================================================ { "name": "couscous-readthedocs-template", "dependencies": { "wyrm": "~0.0.x" } } ================================================ FILE: documentation/website/original-theme/css/badge_only.css ================================================ .fa:before{-webkit-font-smoothing:antialiased}.clearfix{*zoom:1}.clearfix:before,.clearfix:after{display:table;content:""}.clearfix:after{clear:both}@font-face{font-family:FontAwesome;font-weight:normal;font-style:normal;src:url("../font/fontawesome_webfont.eot");src:url("../font/fontawesome_webfont.eot?#iefix") format("embedded-opentype"),url("../font/fontawesome_webfont.woff") format("woff"),url("../font/fontawesome_webfont.ttf") format("truetype"),url("../font/fontawesome_webfont.svg#FontAwesome") format("svg")}.fa:before{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;text-decoration:inherit}a .fa{display:inline-block;text-decoration:inherit}li .fa{display:inline-block}li .fa-large:before,li .fa-large:before{width:1.875em}ul.fas{list-style-type:none;margin-left:2em;text-indent:-0.8em}ul.fas li .fa{width:0.8em}ul.fas li .fa-large:before,ul.fas li .fa-large:before{vertical-align:baseline}.fa-book:before{content:""}.icon-book:before{content:""}.fa-caret-down:before{content:""}.icon-caret-down:before{content:""}.fa-caret-up:before{content:""}.icon-caret-up:before{content:""}.fa-caret-left:before{content:""}.icon-caret-left:before{content:""}.fa-caret-right:before{content:""}.icon-caret-right:before{content:""}.rst-versions{position:fixed;bottom:0;left:0;width:300px;color:#fcfcfc;background:#1f1d1d;border-top:solid 10px #343131;font-family:"Lato","proxima-nova","Helvetica Neue",Arial,sans-serif;z-index:400}.rst-versions a{color:#2980B9;text-decoration:none}.rst-versions .rst-badge-small{display:none}.rst-versions .rst-current-version{padding:12px;background-color:#272525;display:block;text-align:right;font-size:90%;cursor:pointer;color:#27AE60;*zoom:1}.rst-versions .rst-current-version:before,.rst-versions .rst-current-version:after{display:table;content:""}.rst-versions .rst-current-version:after{clear:both}.rst-versions .rst-current-version .fa{color:#fcfcfc}.rst-versions .rst-current-version .fa-book{float:left}.rst-versions .rst-current-version .icon-book{float:left}.rst-versions .rst-current-version.rst-out-of-date{background-color:#E74C3C;color:#fff}.rst-versions .rst-current-version.rst-active-old-version{background-color:#F1C40F;color:#000}.rst-versions.shift-up .rst-other-versions{display:block}.rst-versions .rst-other-versions{font-size:90%;padding:12px;color:gray;display:none}.rst-versions .rst-other-versions hr{display:block;height:1px;border:0;margin:20px 0;padding:0;border-top:solid 1px #413d3d}.rst-versions .rst-other-versions dd{display:inline-block;margin:0}.rst-versions .rst-other-versions dd a{display:inline-block;padding:6px;color:#fcfcfc}.rst-versions.rst-badge{width:auto;bottom:20px;right:20px;left:auto;border:none;max-width:300px}.rst-versions.rst-badge .icon-book{float:none}.rst-versions.rst-badge .fa-book{float:none}.rst-versions.rst-badge.shift-up .rst-current-version{text-align:right}.rst-versions.rst-badge.shift-up .rst-current-version .fa-book{float:left}.rst-versions.rst-badge.shift-up .rst-current-version .icon-book{float:left}.rst-versions.rst-badge .rst-current-version{width:auto;height:30px;line-height:30px;padding:0 6px;display:block;text-align:center}@media screen and (max-width: 768px){.rst-versions{width:85%;display:none}.rst-versions.shift{display:block}img{width:100%;height:auto}} /*# sourceMappingURL=badge_only.css.map */ ================================================ FILE: documentation/website/original-theme/sass/_theme_badge.sass ================================================ .rst-versions position: fixed bottom: 0 left: 0 width: $nav-desktop-width color: $section-background-color background: darken($menu-background-color, 8%) border-top: solid 10px $menu-background-color font-family: $base-font-family z-index: $z-index-tray a color: $link_color text-decoration: none .rst-badge-small display: none .rst-current-version padding: $base-line-height / 2 background-color: darken($menu-background-color, 5%) display: block text-align: right font-size: 90% cursor: pointer color: $green +clearfix .fa color: $section-background-color .fa-book float: left .icon-book float: left &.rst-out-of-date background-color: $red color: $white &.rst-active-old-version background-color: $yellow color: $black &.shift-up .rst-other-versions display: block .rst-other-versions font-size: 90% padding: $base-line-height / 2 color: $text-medium display: none hr display: block height: 1px border: 0 margin: 20px 0 padding: 0 border-top: solid 1px lighten($menu-background-color, 5%) dd display: inline-block margin: 0 a display: inline-block padding: $base-line-height / 4 color: $section-background-color &.rst-badge width: auto bottom: 20px right: 20px left: auto border: none max-width: $nav-desktop-width .icon-book float: none .fa-book float: none &.shift-up .rst-current-version text-align: right .fa-book float: left .icon-book float: left .rst-current-version width: auto height: 30px line-height: 30px padding: 0 $base-line-height / 4 display: block text-align: center +media($tablet) .rst-versions width: 85% display: none &.shift display: block img width: 100% height: auto ================================================ FILE: documentation/website/original-theme/sass/_theme_badge_fa.sass ================================================ // Slimmer version of FA for use on the badge_only.sass file. +font-face(FontAwesome, '#{$font-awesome-dir}fontawesome_webfont') .fa:before display: inline-block font-family: FontAwesome font-style: normal font-weight: normal line-height: 1 text-decoration: inherit +font-smooth a .fa display: inline-block text-decoration: inherit li .fa display: inline-block .fa-large:before, .fa-large:before /* 1.5 increased font size for fa-large * 1.25 width width: 1.5 * 1.25em ul.fas list-style-type: none margin-left: 2em text-indent: -0.8em li .fa width: .8em .fa-large:before, .fa-large:before /* 1.5 increased font size for fa-large * 1.25 width vertical-align: baseline // width: 1.5*1.25em .fa-book:before content: "\f02d" .icon-book:before content: "\f02d" .fa-caret-down:before content: "\f0d7" .icon-caret-down:before content: "\f0d7" .fa-caret-up:before content: "\f0d8" .icon-caret-up:before content: "\f0d8" .fa-caret-left:before content: "\f0d9" .icon-caret-left:before content: "\f0d9" .fa-caret-right:before content: "\f0da" .icon-caret-right:before content: "\f0da" ================================================ FILE: documentation/website/original-theme/sass/_theme_breadcrumbs.sass ================================================ .wy-breadcrumbs li display: inline-block &.wy-breadcrumbs-aside float: right a display: inline-block padding: 5px &:first-child padding-left: 0 .wy-breadcrumbs-extra margin-bottom: 0 color: $text-light font-size: 80% display: inline-block +media($mobile) .wy-breadcrumbs-extra display: none .wy-breadcrumbs li.wy-breadcrumbs-aside display: none @media print .wy-breadcrumbs li.wy-breadcrumbs-aside display: none ================================================ FILE: documentation/website/original-theme/sass/_theme_font_awesome_compatibility.sass ================================================ .icon @extend .fa .icon-home @extend .fa-home .icon-search @extend .fa-search .icon-book @extend .fa-book .icon-caret-down @extend .fa-caret-down .icon-github @extend .fa-github .icon-bitbucket @extend .fa-bitbucket .icon-fire @extend .fa-fire .icon-circle-arrow-right @extend .fa-arrow-circle-right .icon-circle-arrow-left @extend .fa-arrow-circle-left .icon-link @extend .fa-link ================================================ FILE: documentation/website/original-theme/sass/_theme_layout.sass ================================================ .wy-affix position: fixed top: $gutter .wy-menu a:hover text-decoration: none .wy-menu-horiz +clearfix ul, li display: inline-block li:hover background: rgba(255,255,255,.1) li &.divide-left border-left: solid 1px hsl(0, 0%, 25%) &.divide-right border-right: solid 1px hsl(0, 0%, 25%) a height: $base-font-size * 2 display: inline-block line-height: $base-font-size * 2 padding: 0 $base-font-size .wy-menu-vertical header height: $base-font-size * 2 display: inline-block line-height: $base-font-size * 2 padding: 0 $gutter display: block font-weight: bold text-transform: uppercase font-size: 80% color: $menu-logo-color white-space: nowrap ul margin-bottom: 0 li &.divide-top border-top: solid 1px hsl(0, 0%, 25%) &.divide-bottom border-bottom: solid 1px hsl(0, 0%, 25%) &.current background: darken($section-background-color, 10%) a color: $text-medium border-right: solid 1px darken($section-background-color, 20%) padding: $gutter / 4 $gutter * 1.5 &:hover background: darken($section-background-color, 15%) // On state for the first level li.on a, li.current > a color: $text-color padding: $gutter / 4 $gutter font-weight: bold position: relative background: $section-background-color border: none border-bottom: solid 1px darken($section-background-color, 20%) border-top: solid 1px darken($section-background-color, 20%) padding-left: $gutter -4px +font-smooth &:hover background: $section-background-color // This is the on state for pages beyond second level li.toctree-l2.current > a background: darken($section-background-color, 20%) padding: $gutter / 4 $gutter * 1.5 li.current ul display: block li ul margin-bottom: 0 display: none .local-toc li ul display: block li ul li a margin-bottom: 0 color: $text-light font-weight: normal a display: inline-block line-height: 18px padding: $gutter / 4 $gutter display: block position: relative font-size: 90% color: $text-light &:hover background-color: lighten($menu-background-color, 10%) cursor: pointer &:active background-color: $menu-logo-color cursor: pointer color: $white .wy-side-nav-search z-index: $z-index-popover background-color: $link-color text-align: center padding: $gutter / 2 display: block color: $section-background-color margin-bottom: $gutter / 2 input[type=text] width: 100% border-radius: 50px padding: 6px 12px border-color: darken($link-color, 5%) img display: block margin: auto auto $gutter / 2 auto height: 45px width: 45px background-color: $menu-logo-color padding: 5px border-radius: 100% > a, .wy-dropdown > a color: $section-background-color font-size: 100% font-weight: bold display: inline-block padding: $base-line-height / 6 $base-line-height / 4 margin-bottom: $gutter / 2 +font-smooth &:hover background: rgba(255,255,255,.1) .wy-nav .wy-menu-vertical header color: $link-color a color: $text-light &:hover background-color: $link-color color: $white [data-menu-wrap] +transition(all .2s ease-in) position: absolute opacity: 1 width: 100% opacity: 0 &.move-center left: 0 right: auto opacity: 1 &.move-left right: auto left: -100% opacity: 0 &.move-right right: -100% left: auto opacity: 0 .wy-body-for-nav background: left repeat-y $section-background-color background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAIAAACQd1PeAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDoxOERBMTRGRDBFMUUxMUUzODUwMkJCOThDMEVFNURFMCIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDoxOERBMTRGRTBFMUUxMUUzODUwMkJCOThDMEVFNURFMCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjE4REExNEZCMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjE4REExNEZDMEUxRTExRTM4NTAyQkI5OEMwRUU1REUwIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+EwrlwAAAAA5JREFUeNpiMDU0BAgwAAE2AJgB9BnaAAAAAElFTkSuQmCC) background-size: $nav-desktop-width 1px .wy-grid-for-nav position: absolute width: 100% height: 100% .wy-nav-side position: absolute top: 0 left: 0 width: $nav-desktop-width overflow: hidden min-height: 100% background: $menu-background-color z-index: $z-index-popover .wy-nav-top display: none background: $link-color color: $white padding: $gutter / 4 $gutter / 2 position: relative line-height: 50px text-align: center font-size: 100% +clearfix a color: $white font-weight: bold +font-smooth img margin-right: $base-line-height / 2 height: 45px width: 45px background-color: $menu-logo-color padding: 5px border-radius: 100% i font-size: 30px float: left cursor: pointer .wy-nav-content-wrap margin-left: $nav-desktop-width background: $section-background-color min-height: 100% .wy-nav-content padding: $gutter $gutter * 2 height: 100% max-width: 800px margin: auto .wy-body-mask position: fixed width: 100% height: 100% background: rgba(0,0,0,.2) display: none z-index: $z-index-modal - 1 &.on display: block footer color: $gray-light p margin-bottom: $base-line-height / 2 .rst-footer-buttons +clearfix #search-results .search li margin-bottom: $base-line-height border-bottom: solid 1px $table_border_color padding-bottom: $base-line-height .search li:first-child border-top: solid 1px $table_border_color padding-top: $base-line-height .search li a font-size: 120% margin-bottom: $base-line-height / 2 display: inline-block .context color: $text-medium font-size: 90% +media($tablet) .wy-body-for-nav background: $section-background-color .wy-nav-top display: block .wy-nav-side @if $nav-desktop-position == left left: -$nav-desktop-width @else right: -$nav-desktop-width &.shift width: 85% left: 0 .wy-nav-content-wrap margin-left: 0 .wy-nav-content padding: $gutter &.shift position: fixed min-width: 100% left: 85% top: 0 height: 100% overflow: hidden +media($desktop-wider) .wy-nav-content-wrap background: rgba(0,0,0,.05) .wy-nav-content margin: 0 background: $section-background-color @media print .rst-versions, footer, .wy-nav-side display: none .wy-nav-content-wrap margin-left: 0 nav.stickynav position: fixed top: 0 ================================================ FILE: documentation/website/original-theme/sass/_theme_mathjax.sass ================================================ span[id*='MathJax-Span'] color: $mathjax-color .math text-align: center ================================================ FILE: documentation/website/original-theme/sass/_theme_rst.sass ================================================ // ------------------------------------------------------------------------------------------------------------------- // CONTRIBUTORS, PLEASE READ THIS! // ------------------------------------------------------------------------------------------------------------------- // Couple things... // 1. Lots of this @extends from wyrm_core/_type.sass (http://www.github.com/snide/wyrm/. // * Try not to replace any @extends code. It's pretty generic stuff meant to work together. // * That said, know that I'm very unlikely to accept PRs from wyrm just to change style here. // 2. I plan to remove the !importants in here. Part of it is due to lazyness, part to sphinx's fondness for nesting. // 3. Try to use variables from wyrm_core/wy_variables.sass. Notable are... // * $base-line-height // All margins, padding and line-height should use this in .25 increments. // * $text-color, $text-light, $text-dark...etc // * $base-font-family, $custom-font-family, $code-font-family // 4. If you have changes for mobile/tablet, put them at the bottom of the sass file. // -------------------------------------------------------------------------------------------------------------------- .rst-content // Sphinx by default applies HxW style attributes to images. This fixes that oversite. img max-width: 100% height: auto !important div.figure margin-bottom: $base-line-height div.figure.align-center text-align: center // Usually it's a good idea to give images some space. .section > img, .section > a > img margin-bottom: $base-line-height // Questionable whether this is nice or not. It styles eternal links, but comes with some baggage. // a.reference.external:after // font-family: FontAwesome // content: " \f08e " // color: $text-light // vertical-align: super // font-size: 60% // For the most part, its safe to assume that sphinx wants you to use a blockquote as an indent. It gets // used in many different ways, so don't assume you can apply some fancy style, just leave it be. blockquote margin-left: $base-line-height line-height: $base-line-height margin-bottom: $base-line-height .literal-block, pre.literal-block @extend .codeblock // These are the various note pullouts that sphinx applies .note, .attention, .caution, .danger, .error, .hint, .important, .tip, .warning, .seealso, .admonition-todo @extend .wy-alert .last margin-bottom: 0 .admonition-title @extend .wy-alert-title @extend .fa @extend .fa-exclamation-circle &:before margin-right: 4px .note, .seealso @extend .wy-alert.wy-alert-info .hint, .tip, .important @extend .wy-alert.wy-alert-success .error, .danger @extend .wy-alert.wy-alert-danger .warning, .caution, .attention, .admonition-todo @extend .wy-alert.wy-alert-warning // Some people put tables in notes. Let's give them very basic support. .admonition table border-color: rgba(0,0,0,.1) td, th background: transparent !important border-color: rgba(0,0,0,.1) !important .section ul, .toctree-wrapper ul @extend .wy-plain-list-disc .section ol.loweralpha, .section ol.loweralpha li list-style: lower-alpha .section ol.upperalpha, .section ol.upperalpha li list-style: upper-alpha .section ol, ol.arabic @extend .wy-plain-list-decimal .section ol p, .section ul p margin-bottom: $base-line-height / 2 .line-block margin-left: $base-line-height // Generics handling of headings and toc stuff. .topic-title font-weight: bold margin-bottom: $base-line-height / 2 .toc-backref color: $text-color .align-right float: right margin: 0px 0px $base-line-height $base-line-height .align-left float: left margin: 0px $base-line-height $base-line-height 0px .align-center margin: auto display: block // This is the #href that shows up on hover. Sphinx's is terrible so I hack it away. h1, h2, h3, h4, h5, h6, dl dt .headerlink display: none visibility: hidden font-size: 14px @extend .fa &:after visibility: visible content: "\f0c1" font-family: FontAwesome display: inline-block &:hover .headerlink display: inline-block // Sidebar content. You'll see at the bottom of this file I change it in mobile. .sidebar float: right width: 40% display: block margin: 0 0 $base-line-height $base-line-height padding: $base-line-height background: $table-stripe-color border: solid 1px $table-border-color // Sidebar content is usually less relevant, so adjust the margins and sizes. p, ul, dl font-size: 90% .last margin-bottom: 0 .sidebar-title display: block font-family: $custom-font-family font-weight: bold background: $table-border-color padding: $base-line-height / 4 $base-line-height / 2 margin: -$base-line-height margin-bottom: $base-line-height font-size: 100% // Sphinx can highlight searched text with ?highlighted=searchterm .highlighted background: $yellow display: inline-block font-weight: bold padding: 0 $base-line-height / 4 // These are the little citation links [1] that show up within paragraphs. .footnote-reference, .citation-reference vertical-align: super font-size: 90% // Tables! Sphinx LOVES TABLES. Most of wyrm assumes you're only going to use a table as a table // so I have to write a bunch of unique stuff for Sphinx to style them up differently like it's 2003. table.docutils.citation, table.docutils.footnote background: none border: none color: $gray-light td, tr border: none background-color: transparent !important white-space: normal td.label padding-left: 0 padding-right: 0 vertical-align: top table.docutils @extend .wy-table @extend .wy-table-bordered-all &:not(.field-list) @extend .wy-table-striped // This table is what gets spit out for auto-generated API stuff. I style it smaller bits of padding. table.field-list @extend .wy-table border: none td border: none padding-top: 5px td > strong display: inline-block margin-top: 3px .field-name padding-right: 10px text-align: left white-space: nowrap .field-body text-align: left padding-left: 0 // These are the "literals" that get spit out when you mark stuff as ``code`` as your write. tt @extend code color: $black big, em font-size: 100% !important line-height: normal .xref, a & font-weight: bold // If the literal is inside an a tag, let's color it like a link a tt color: $link-color dl margin-bottom: $base-line-height dt font-weight: bold // Most of the content within these dls are one liners, so I halve the normal margins. p, table, ul, ol margin-bottom: $base-line-height / 2 !important // rST seems to want dds to be treated as the browser would, indented. dd margin: 0 0 $base-line-height / 2 $base-line-height // This is what Sphinx spits out for it's autodocs. Depending upon what language the person is referencing // these things usually have a class of "method" or "class" or something similar, but really who knows. // Sphinx doesn't give me a generic class on these, so unfortunately I have to apply it to the root dl. // This makes me terribly unhappy and makes this code very nesty. Unfortunately I've seen hand-written docs // that output similar, but not quite the same nesting so this is really the best we can do. dl:not(.docutils) margin-bottom: $base-line-height // This would be the equivilant of a .. class:: dt display: inline-block margin: $base-line-height / 4 0 font-size: 90% line-height: normal background: lighten($blue, 50%) color: $blue border-top: solid 3px lighten($blue, 20%) padding: $base-line-height / 4 position: relative &:before color: lighten($blue, 20%) .headerlink color: $text-color font-size: 100% !important // And this would be the .. method:: dl dt margin-bottom: $base-line-height / 4 border: none border-left: solid 3px hsl(0,0%,80%) background: hsl(0,0%,94%) color: $text-medium .headerlink color: $text-color font-size: 100% !important dt:first-child margin-top: 0 // Since dts get the callout style, we treat this less as callouts. tt font-weight: bold &.descname, &.descclassname background-color: transparent border: none padding: 0 font-size: 100% !important &.descname font-weight: bold // This is for more advanced parameter control .optional display: inline-block padding: 0 4px color: $black font-weight: bold .property display: inline-block padding-right: 8px // Doc links to sourcecode .viewcode-link, .viewcode-back display: inline-block color: $green font-size: 80% padding-left: $base-line-height .viewcode-back display: block float: right p.rubric margin-bottom: 12px font-weight: bold // Mobile specific +media($mobile) .rst-content .sidebar width: 100% ================================================ FILE: documentation/website/original-theme/sass/_theme_variables.sass ================================================ // In here are varibles used for sphinx_rtd_theme, they either add to or overwrite the default ones // that are set in wyrm_core/wy_variables.sass. You'll find wyrm in bower_components if you're looking // for a reference. $font-awesome-dir: "../font/" $static-img: "../img/" $mathjax-color: $text-color $base-font-family: "Lato", "proxima-nova", "Helvetica Neue", Arial, sans-serif $custom-font-family: "Roboto Slab", "ff-tisa-web-pro", "Georgia", Arial, sans-serif $custom-font-family2: Georgia, serif $code-font-family: Consolas, "Andale Mono WT", "Andale Mono", "Lucida Console", "Lucida Sans Typewriter", "DejaVu Sans Mono", "Bitstream Vera Sans Mono", "Liberation Mono", "Nimbus Mono L", Monaco, "Courier New", Courier, monospace ================================================ FILE: documentation/website/original-theme/sass/badge_only.sass ================================================ // ------------------------------------------------------------ // CONTRIBUTORS, PLEASE READ THIS! // ------------------------------------------------------------ // This generates the RTD sticky badge for non RTD themes. As // always, only files labeled "theme_*.sass should be edited". // ------------------------------------------------------------ $border-box-sizing: false !default @import wyrm_core/wy_variables @import "theme_variables" @import bourbon @import neat @import wyrm_core/mixin @import wyrm_core/grid_settings @import "theme_badge_fa" @import "theme_badge" ================================================ FILE: documentation/website/original-theme/sass/theme.sass ================================================ // ------------------------------------------------------------ // CONTRIBUTORS, PLEASE READ THIS! // ------------------------------------------------------------ // This theme pulls from other frontend projects. The only // things you should edit are the sass files that start with // "theme_*.sass". All other files are loaded through bower. // ------------------------------------------------------------ // Variable defaults set by Wyrm @import wyrm_core/wy_variables // Variable overrides that change coloring and fonts for this theme. @import "theme_variables" // bourbon.io framework @import bourbon // Bourbon.io/neat framework, with some default media queries @import wyrm_core/grid_settings @import neat // Some corrections for neat @import wyrm_core/neat_extra // Custom reset @import wyrm_core/reset // Wyrm mixins @import wyrm_core/mixin // Font Awesome 4.0 with wyrm extras @import font-awesome @import wyrm_core/font_icon_defaults // Wyrm core styles used in this theme @import wyrm_core/alert @import wyrm_core/button @import wyrm_core/dropdown @import wyrm_core/form @import wyrm_core/generic @import wyrm_core/table @import wyrm_core/type // Pygments styling @import wyrm_addons/pygments/pygments @import wyrm_addons/pygments/pygments_light // Theme specific styles. These are likely the files you want to edit. @import "theme_breadcrumbs" @import "theme_layout" @import "theme_badge" @import "theme_rst" @import "theme_mathjax" @import "theme_font_awesome_compatibility" ================================================ FILE: gulpfile.js ================================================ 'use strict'; var gulp = require('gulp'); var plugins = require('gulp-load-plugins')(); var sass = require('gulp-sass'); var pkg = require('./package'); var pump = require('pump'); var scripts = { name: 'jquery.contextMenu.js', min: 'jquery.contextMenu.min.js', all: [ 'gulpfile.js', 'src/jquery.contextMenu.js', 'dist/jquery.contextMenu.js' ], main: 'dist/jquery.contextMenu.js', src: [ 'src/jquery.contextMenu.js' ], dest: 'dist', libs: [ ] }; var styles = { name: 'jquery.contextMenu.css', min: 'jquery.contextMenu.min.css', all: [ 'src/sass/**/*.scss' ], main: 'dist/jquery.contextMenu.css', src: 'src/sass/jquery.contextMenu.scss', dest: 'dist' }; var icons = { src: 'src/icons/*.svg', templateFileFont: 'src/sass/icons/_variables.scss.tpl', templateFileIconClasses: 'src/sass/icons/_icon_classes.scss.tpl', fontOutputPath: 'dist/font', scssOutputPath: 'src/sass/icons/' }; var replacement = { regexp: /@\w+/g, filter: function (placeholder) { switch (placeholder) { case '@VERSION': placeholder = pkg.version; break; case '@YEAR': placeholder = (new Date()).getFullYear(); break; case '@DATE': placeholder = (new Date()).toISOString(); break; } return placeholder; } }; gulp.task('jshint', function (cb) { pump([ gulp.src(scripts.src), plugins.jshint('src/.jshintrc'), plugins.jshint.reporter('default') ],cb); }); gulp.task('jsdist', function (cb) { pump([ gulp.src(scripts.src), plugins.sourcemaps.init(), plugins.replace(replacement.regexp, replacement.filter), gulp.dest(scripts.dest), plugins.rename(scripts.min), plugins.uglify(), plugins.sourcemaps.write('.'), gulp.dest(scripts.dest) ], cb); }); gulp.task('jslibs', function (cb){ pump([ gulp.src(scripts.libs), plugins.rename({prefix: 'jquery.ui.'}), gulp.dest('src'), gulp.dest('dist'), plugins.rename({extname: '.min.js'}), gulp.dest('dist'), plugins.uglify(), plugins.sourcemaps.write('.'), gulp.dest(scripts.dest) ], cb); }); gulp.task('css', function (cb) { return pump([ gulp.src(styles.src), sass(), plugins.csslint('src/.csslintrc'), plugins.csslint.formatter(), plugins.sourcemaps.init(), plugins.replace(replacement.regexp, replacement.filter), plugins.autoprefixer(), plugins.csscomb('src/.csscomb.json'), plugins.rename(styles.name), gulp.dest(styles.dest), plugins.rename(styles.min), plugins.cleanCss(), plugins.sourcemaps.write('.'), gulp.dest(styles.dest) ], cb); }); gulp.task('build-icons', function (done) { var iconfont = require('gulp-iconfont'); var consolidate = require('gulp-consolidate'); return gulp.src(icons.src) .pipe(iconfont({ fontName: 'context-menu-icons', fontHeight: 1024, descent: 64, normalize: true, appendCodepoints: false, startCodepoint: 0xE001, formats: ['ttf', 'eot', 'woff', 'woff2'] })) .on('glyphs', function (glyphs) { var options = { glyphs: glyphs, className: 'context-menu-icon', mixinName: 'context-menu-item-icon' }; gulp.src(icons.templateFileFont) .pipe(consolidate('lodash', options)) .pipe(plugins.rename({basename: '_variables', extname: '.scss'})) .pipe(gulp.dest(icons.scssOutputPath)); gulp.src(icons.templateFileIconClasses) .pipe(consolidate('lodash', options)) .pipe(plugins.rename('_icons.scss')) .pipe(gulp.dest('src/sass')); // set path to export your sample HTML }) .pipe(gulp.dest(icons.fontOutputPath)); }); /** * Update paths in integration tests that are generated by the documentation * generator so they use the local source. */ gulp.task('integration-test-paths', function(){ return gulp.src('test/integration/html/*.html'). pipe(plugins.replace('https\:\/\/swisnl\.github\.io\/jQuery-contextMenu\/dist\/jquery\.ui\.position\.min\.js', '\.\.\/\.\.\/\.\.\/dist\/jquery\.ui\.position\.min\.js')). pipe(plugins.replace('https\:\/\/swisnl\.github\.io\/jQuery\-contextMenu\/dist\/', '\.\.\/\.\.\/\.\.\/src\/')). pipe(plugins.replace('\/src\/jquery.contextMenu.css', '\/dist\/jquery.contextMenu.css')). pipe(gulp.dest('test/integration/html/')); }); gulp.task('js', gulp.series('jshint', 'jsdist', (done) => { done(); })); gulp.task('watch', gulp.parallel('js', 'css', function (done) { gulp.watch(scripts.src,gulp.series('js')); gulp.watch(styles.all, gulp.series('css')); done(); })); gulp.task('build', gulp.series('build-icons', 'css', 'js', 'integration-test-paths', (done) => { done(); })); gulp.task('default', gulp.series('watch', (done) => { done(); })); ================================================ FILE: karma-saucelabs.conf.js ================================================ module.exports = function (config) { if (!process.env.SAUCE_USERNAME || !process.env.SAUCE_ACCESS_KEY) { console.log('Make sure the SAUCE_USERNAME and SAUCE_ACCESS_KEY environment variables are set.') process.exit(1) } var testedCapabilities = {}; var browsers = []; var capabilities = { 'Windows 7': { 'internet explorer': ['11', '10', '9'], }, 'Windows 10': { 'firefox': ['latest', 'latest-1'], 'chrome': ['latest', 'latest-1'], 'MicrosoftEdge': ['latest'] }, 'macOS 10.12': { 'firefox': ['latest'], 'chrome': ['latest'], 'safari': ['latest'] } }; var buildDate = new Date().toISOString(); for (var osVersion in capabilities) { for (var browserKey in capabilities[osVersion]) { for(var i=0; i< capabilities[osVersion][browserKey].length; i++){ var browserVersion = capabilities[osVersion][browserKey][i]; testedCapabilities[osVersion + ' ' + browserKey + ' ' + browserVersion] = { base: 'SauceLabs', platform: osVersion, browserName: browserKey, version: browserVersion, name: osVersion + ' ' + browserKey + ' ' + browserVersion, build: buildDate }; } if(browsers.indexOf(browserKey) == -1){ browsers.push(browsers); } } } config.set({ basePath: '', // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['qunit'], // list of files / patterns to load in the browser files: [ // dependencies { pattern: 'node_modules/jquery/dist/jquery.js', watched: false, served: true, included: true }, { pattern: 'src/jquery.ui.position.js', watched: false, served: true, included: true }, { pattern: 'src/jquery.contextMenu.js', watched: false, served: true, included: true }, // test modules 'test/unit/*.js' ], reporters: ['dots', 'saucelabs'], port: 9876, colors: true, sauceLabs: { testName: 'jQuery contextMenu saucelabs', recordScreenshots: false, public: 'public' }, // Increase timeout in case connection in CI is slow captureTimeout: 600000, customLaunchers: testedCapabilities, browsers: Object.keys(testedCapabilities), singleRun: true }) } ================================================ FILE: karma.conf.js ================================================ // Karma configuration // Generated on Wed Oct 29 2014 01:56:16 GMT+0100 (CET) module.exports = function(config) { config.set({ // base path that will be used to resolve all patterns (eg. files, exclude) basePath: '', // frameworks to use // available frameworks: https://npmjs.org/browse/keyword/karma-adapter frameworks: ['qunit'], // list of files / patterns to load in the browser files: [ // dependencies { pattern: 'node_modules/jquery/dist/jquery.js', watched: false, served: true, included: true }, { pattern: 'src/jquery.ui.position.js', watched: false, served: true, included: true }, { pattern: 'src/jquery.contextMenu.js', watched: false, served: true, included: true }, // test modules 'test/unit/*.js' ], // list of files to exclude exclude: [ ], // preprocess matching files before serving them to the browser // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor preprocessors: { }, // test results reporter to use // possible values: 'dots', 'progress' // available reporters: https://npmjs.org/browse/keyword/karma-reporter reporters: ['dots'], // web server port port: 9876, // enable / disable colors in the output (reporters and logs) colors: true, // level of logging // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG logLevel: config.LOG_INFO, // enable / disable watching file and executing tests whenever any file changes autoWatch: false, // start these browsers // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher browsers: ['PhantomJS'], plugins : [ 'karma-phantomjs-launcher', 'karma-qunit' ], // Continuous Integration mode // if true, Karma captures browsers, runs the tests and exits singleRun: true }); }; ================================================ FILE: package.json ================================================ { "name": "jquery-contextmenu", "title": "jQuery.contextMenu()", "version": "2.10.1", "author": { "name": "Björn Brala", "url": "http://www.swis.nl" }, "license": "MIT", "peerDependencies": { "jquery": ">=1.8.2" }, "devDependencies": { "doctoc": "^1.3.1", "gulp": "^4.0.2", "gulp-autoprefixer": "^7.0.1", "gulp-clean-css": "^4.2.0", "gulp-concat": "~2.6.0", "gulp-consolidate": "^0.2.0", "gulp-csscomb": "^3.0.8", "gulp-csslint": "^1.0.1", "gulp-debug": "^4.0.0", "gulp-dest": "^0.2.3", "gulp-htmlcomb": "^0.2.0", "gulp-iconfont": "^10.0.1", "gulp-jscs": "^4.1.0", "gulp-jshint": "^2.1.0", "gulp-load-plugins": "^2.0.1", "gulp-qunit": "^2.0.0", "gulp-rename": "^2.0.0", "gulp-replace": "^1.0.0", "gulp-sass": "^4.0.1", "gulp-sourcemaps": "^2.6.4", "gulp-uglify": "^3.0.0", "jshint": "^2.9.4", "karma": "^5.0.2", "karma-chrome-launcher": "^3.1.0", "karma-phantomjs-launcher": "^1.0.2", "karma-qunit": "^4.0.0", "karma-sauce-launcher": "4.1.3", "lodash": "^4.17.5", "pump": "^3.0.0", "qunit": "^2.0.1", "qunitjs": "^2.0.1", "wdio-dot-reporter": "^0.0.10", "wdio-mocha-framework": "^0.6.2", "wdio-sauce-service": "^0.4.8", "wdio-selenium-standalone-service": "^0.0.12", "webdriverio": "^6.1.3" }, "description": "Management facility for context menus. Developed for a large number of triggering objects. HTML5 Polyfill", "keywords": [ "contextmenu", "context menu", "context-menu", "menu", "html5 menu", "html5 contextmenu", "html5 context menu", "html5 context-menu", "jquery-plugin", "ecosystem:jquery" ], "homepage": "http://swisnl.github.io/jQuery-contextMenu/", "contributors": [ { "name": "Rodney Rehm", "url": "http://rodneyrehm.de" }, { "name": "Addy Osmani", "url": "https://github.com/addyosmani" }, { "name": "Christiaan Baartse", "url": "https://github.com/christiaan" } ], "repository": { "type": "git", "url": "git://github.com/swisnl/jQuery-contextMenu.git" }, "main": "dist/jquery.contextMenu.js", "files": [ "dist", "src" ], "scripts": { "test": "npm run test-unit", "test-unit": "./node_modules/karma/bin/karma start", "test-sauce": "./node_modules/karma/bin/karma start karma-saucelabs.conf.js", "test-accept": "./node_modules/.bin/wdio wdio.conf.js" }, "browserslist": [ "Android 2.3", "Android >= 4", "Chrome >= 20", "Firefox >= 24", "Explorer >= 8", "iOS >= 6", "Opera >= 12", "Safari >= 6" ], "dependencies": { "jquery": "^3.5.0" } } ================================================ FILE: src/.csscomb.json ================================================ { "always-semicolon": true, "block-indent": 2, "color-case": "lower", "color-shorthand": true, "element-case": "lower", "eof-newline": true, "leading-zero": false, "remove-empty-rulesets": true, "space-after-colon": 1, "space-after-combinator": 1, "space-before-selector-delimiter": 0, "space-between-declarations": "\n", "space-after-opening-brace": "\n", "space-before-closing-brace": "\n", "space-before-colon": 0, "space-before-combinator": 1, "space-before-opening-brace": 1, "strip-spaces": true, "unitless-zero": true, "vendor-prefix-align": true, "sort-order": [ [ "position", "top", "right", "bottom", "left", "z-index", "display", "float", "width", "min-width", "max-width", "height", "min-height", "max-height", "-webkit-box-sizing", "-moz-box-sizing", "box-sizing", "-webkit-appearance", "padding", "padding-top", "padding-right", "padding-bottom", "padding-left", "margin", "margin-top", "margin-right", "margin-bottom", "margin-left", "overflow", "overflow-x", "overflow-y", "-webkit-overflow-scrolling", "-ms-overflow-x", "-ms-overflow-y", "-ms-overflow-style", "clip", "clear", "font", "font-family", "font-size", "font-style", "font-weight", "font-variant", "font-size-adjust", "font-stretch", "font-effect", "font-emphasize", "font-emphasize-position", "font-emphasize-style", "font-smooth", "-webkit-hyphens", "-moz-hyphens", "hyphens", "line-height", "color", "text-align", "-webkit-text-align-last", "-moz-text-align-last", "-ms-text-align-last", "text-align-last", "text-emphasis", "text-emphasis-color", "text-emphasis-style", "text-emphasis-position", "text-decoration", "text-indent", "text-justify", "text-outline", "-ms-text-overflow", "text-overflow", "text-overflow-ellipsis", "text-overflow-mode", "text-shadow", "text-transform", "text-wrap", "-webkit-text-size-adjust", "-ms-text-size-adjust", "letter-spacing", "-ms-word-break", "word-break", "word-spacing", "-ms-word-wrap", "word-wrap", "-moz-tab-size", "-o-tab-size", "tab-size", "white-space", "vertical-align", "list-style", "list-style-position", "list-style-type", "list-style-image", "pointer-events", "-ms-touch-action", "touch-action", "cursor", "visibility", "zoom", "flex-direction", "flex-order", "flex-pack", "flex-align", "table-layout", "empty-cells", "caption-side", "border-spacing", "border-collapse", "content", "quotes", "counter-reset", "counter-increment", "resize", "-webkit-user-select", "-moz-user-select", "-ms-user-select", "-o-user-select", "user-select", "nav-index", "nav-up", "nav-right", "nav-down", "nav-left", "background", "background-color", "background-image", "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", "filter:progid:DXImageTransform.Microsoft.gradient", "filter:progid:DXImageTransform.Microsoft.AlphaImageLoader", "filter", "background-repeat", "background-attachment", "background-position", "background-position-x", "background-position-y", "-webkit-background-clip", "-moz-background-clip", "background-clip", "background-origin", "-webkit-background-size", "-moz-background-size", "-o-background-size", "background-size", "border", "border-color", "border-style", "border-width", "border-top", "border-top-color", "border-top-style", "border-top-width", "border-right", "border-right-color", "border-right-style", "border-right-width", "border-bottom", "border-bottom-color", "border-bottom-style", "border-bottom-width", "border-left", "border-left-color", "border-left-style", "border-left-width", "border-radius", "border-top-left-radius", "border-top-right-radius", "border-bottom-right-radius", "border-bottom-left-radius", "-webkit-border-image", "-moz-border-image", "-o-border-image", "border-image", "-webkit-border-image-source", "-moz-border-image-source", "-o-border-image-source", "border-image-source", "-webkit-border-image-slice", "-moz-border-image-slice", "-o-border-image-slice", "border-image-slice", "-webkit-border-image-width", "-moz-border-image-width", "-o-border-image-width", "border-image-width", "-webkit-border-image-outset", "-moz-border-image-outset", "-o-border-image-outset", "border-image-outset", "-webkit-border-image-repeat", "-moz-border-image-repeat", "-o-border-image-repeat", "border-image-repeat", "outline", "outline-width", "outline-style", "outline-color", "outline-offset", "-webkit-box-shadow", "-moz-box-shadow", "box-shadow", "filter:progid:DXImageTransform.Microsoft.Alpha(Opacity", "-ms-filter:\\'progid:DXImageTransform.Microsoft.Alpha", "opacity", "-ms-interpolation-mode", "-webkit-transition", "-moz-transition", "-ms-transition", "-o-transition", "transition", "-webkit-transition-delay", "-moz-transition-delay", "-ms-transition-delay", "-o-transition-delay", "transition-delay", "-webkit-transition-timing-function", "-moz-transition-timing-function", "-ms-transition-timing-function", "-o-transition-timing-function", "transition-timing-function", "-webkit-transition-duration", "-moz-transition-duration", "-ms-transition-duration", "-o-transition-duration", "transition-duration", "-webkit-transition-property", "-moz-transition-property", "-ms-transition-property", "-o-transition-property", "transition-property", "-webkit-transform", "-moz-transform", "-ms-transform", "-o-transform", "transform", "-webkit-transform-origin", "-moz-transform-origin", "-ms-transform-origin", "-o-transform-origin", "transform-origin", "-webkit-animation", "-moz-animation", "-ms-animation", "-o-animation", "animation", "-webkit-animation-name", "-moz-animation-name", "-ms-animation-name", "-o-animation-name", "animation-name", "-webkit-animation-duration", "-moz-animation-duration", "-ms-animation-duration", "-o-animation-duration", "animation-duration", "-webkit-animation-play-state", "-moz-animation-play-state", "-ms-animation-play-state", "-o-animation-play-state", "animation-play-state", "-webkit-animation-timing-function", "-moz-animation-timing-function", "-ms-animation-timing-function", "-o-animation-timing-function", "animation-timing-function", "-webkit-animation-delay", "-moz-animation-delay", "-ms-animation-delay", "-o-animation-delay", "animation-delay", "-webkit-animation-iteration-count", "-moz-animation-iteration-count", "-ms-animation-iteration-count", "-o-animation-iteration-count", "animation-iteration-count", "-webkit-animation-direction", "-moz-animation-direction", "-ms-animation-direction", "-o-animation-direction", "animation-direction" ] ] } ================================================ FILE: src/.csslintrc ================================================ { "adjoining-classes": false, "box-sizing": false, "box-model": false, "compatible-vendor-prefixes": false, "floats": false, "font-sizes": false, "gradients": false, "important": false, "known-properties": false, "outline-none": false, "qualified-headings": false, "regex-selectors": false, "shorthand": false, "text-indent": false, "zero-units": false, "unique-headings": false, "universal-selector": false, "unqualified-attributes": false } ================================================ FILE: src/.jshintrc ================================================ { "bitwise": true, "curly": true, "eqeqeq": true, "forin": true, "freeze": true, "funcscope": true, "iterator": true, "latedef": true, "noarg": true, "nocomma": false, "noempty": true, "nonbsp": true, "nonew": true, "undef": true, "unused": true, "strict": false, "node": true, "browser": true, "jquery": true, "qunit": true, "globals": { "define": true } } ================================================ FILE: src/jquery.contextMenu.js ================================================ /** * jQuery contextMenu v@VERSION - Plugin for simple contextMenu handling * * Version: v@VERSION * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * * Copyright (c) 2011-@YEAR SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * * Date: @DATE */ // jscs:disable /* jshint ignore:start */ (function (factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as anonymous module. define(['jquery'], factory); } else if (typeof exports === 'object') { // Node / CommonJS factory(require('jquery')); } else { // Browser globals. factory(jQuery); } })(function ($) { 'use strict'; // helper function to check for rapid interactions after menu display var isInteractionTooFast = function($element) { if (!('ontouchstart' in window || navigator.maxTouchPoints > 0 || navigator.msMaxTouchPoints > 0)) { return false; } var interactionTime = Date.now(); var $liItem = $element.is('input, textarea, select') ? $element.closest('.context-menu-item') : $element; if (!$liItem || !$liItem.length) { return false; } var $parentMenu = $liItem.parent(); if (!$parentMenu || !$parentMenu.length) { return false; } // only apply the check for items within submenus if ($parentMenu.hasClass('context-menu-root')) { return false; } var showTimestamp = $parentMenu.data('_showTimestamp'); var timeDifference = showTimestamp ? interactionTime - showTimestamp : Infinity; // threshold for fast interaction (e.g., mobile tap) var threshold = 50; // ms return timeDifference < threshold; }; // TODO: - // ARIA stuff: menuitem, menuitemcheckbox und menuitemradio // create structure if $.support[htmlCommand || htmlMenuitem] and !opt.disableNative // determine html5 compatibility $.support.htmlMenuitem = ('HTMLMenuItemElement' in window); $.support.htmlCommand = ('HTMLCommandElement' in window); $.support.eventSelectstart = ('onselectstart' in document.documentElement); /* // should the need arise, test for css user-select $.support.cssUserSelect = (function(){ var t = false, e = document.createElement('div'); $.each('Moz|Webkit|Khtml|O|ms|Icab|'.split('|'), function(i, prefix) { var propCC = prefix + (prefix ? 'U' : 'u') + 'serSelect', prop = (prefix ? ('-' + prefix.toLowerCase() + '-') : '') + 'user-select'; e.style.cssText = prop + ': text;'; if (e.style[propCC] == 'text') { t = true; return false; } return true; }); return t; })(); */ if (!$.ui || !$.widget) { // duck punch $.cleanData like jQueryUI does to get that remove event $.cleanData = (function (orig) { return function (elems) { var events, elem, i; for (i = 0; elems[i] != null; i++) { elem = elems[i]; try { // Only trigger remove when necessary to save time events = $._data(elem, 'events'); if (events && events.remove) { $(elem).triggerHandler('remove'); } // Http://bugs.jquery.com/ticket/8235 } catch (e) { } } orig(elems); }; })($.cleanData); } /* jshint ignore:end */ // jscs:enable var // currently active contextMenu trigger $currentTrigger = null, // is contextMenu initialized with at least one menu? initialized = false, // window handle $win = $(window), // number of registered menus counter = 0, // mapping selector to namespace namespaces = {}, // mapping namespace to options menus = {}, // custom command type handlers types = {}, // default values defaults = { // selector of contextMenu trigger selector: null, // where to append the menu to appendTo: null, // method to trigger context menu ["right", "left", "hover"] trigger: 'right', // hide menu when mouse leaves trigger / menu elements autoHide: false, // ms to wait before showing a hover-triggered context menu delay: 200, // flag denoting if a second trigger should simply move (true) or rebuild (false) an open menu // as long as the trigger happened on one of the trigger-element's child nodes reposition: true, // Flag denoting if a second trigger should close the menu, as long as // the trigger happened on one of the trigger-element's child nodes. // This overrides the reposition option. hideOnSecondTrigger: false, // use a modal layer for closing the menu rather than a captured event on document useModal: true, //ability to select submenu selectableSubMenu: false, // Default classname configuration to be able avoid conflicts in frameworks classNames: { hover: 'context-menu-hover', // Item hover disabled: 'context-menu-disabled', // Item disabled visible: 'context-menu-visible', // Item visible notSelectable: 'context-menu-not-selectable', // Item not selectable icon: 'context-menu-icon', iconEdit: 'context-menu-icon-edit', iconCut: 'context-menu-icon-cut', iconCopy: 'context-menu-icon-copy', iconPaste: 'context-menu-icon-paste', iconDelete: 'context-menu-icon-delete', iconAdd: 'context-menu-icon-add', iconQuit: 'context-menu-icon-quit', iconLoadingClass: 'context-menu-icon-loading' }, // determine position to show menu at determinePosition: function ($menu) { // position to the lower middle of the trigger element if ($.ui && $.ui.position) { // .position() is provided as a jQuery UI utility // (...and it won't work on hidden elements) $menu.css('display', 'block').position({ my: 'center top', at: 'center bottom', of: this, offset: '0 5', collision: 'fit' }).css('display', 'none'); } else { // determine contextMenu position var offset = this.offset(); offset.top += this.outerHeight(); offset.left += this.outerWidth() / 2 - $menu.outerWidth() / 2; $menu.css(offset); } }, // position menu position: function (opt, x, y) { var offset; // determine contextMenu position if (!x && !y) { opt.determinePosition.call(this, opt.$menu); return; } else if (x === 'maintain' && y === 'maintain') { // x and y must not be changed (after re-show on command click) offset = opt.$menu.position(); } else { // x and y are given (by mouse event) var offsetParentOffset = opt.$menu.offsetParent().offset(); offset = {top: y - offsetParentOffset.top, left: x -offsetParentOffset.left}; } // correct offset if viewport demands it var bottom = $win.scrollTop() + $win.height(), right = $win.scrollLeft() + $win.width(), height = opt.$menu.outerHeight(), width = opt.$menu.outerWidth(); if (offset.top + height > bottom) { offset.top -= height; } if (offset.top < 0) { offset.top = 0; } if (offset.left + width > right) { offset.left -= width; } if (offset.left < 0) { offset.left = 0; } opt.$menu.css(offset); }, // position the sub-menu positionSubmenu: function ($menu) { if (typeof $menu === 'undefined') { // When user hovers over item (which has sub items) handle.focusItem will call this. // but the submenu does not exist yet if opt.items is a promise. just return, will // call positionSubmenu after promise is completed. return; } if ($.ui && $.ui.position) { // .position() is provided as a jQuery UI utility // (...and it won't work on hidden elements) $menu.css('display', 'block').position({ my: 'left top-5', at: 'right top', of: this, collision: 'flipfit fit' }).css('display', ''); } else { // determine contextMenu position var offset = { top: -9, left: this.outerWidth() - 5 }; $menu.css(offset); } }, // offset to add to zIndex zIndex: 1, // show hide animation settings animation: { duration: 50, show: 'slideDown', hide: 'slideUp' }, // events events: { preShow: $.noop, show: $.noop, hide: $.noop, activated: $.noop }, // default callback callback: null, // list of contextMenu items items: {} }, // mouse position for hover activation hoveract = { timer: null, pageX: null, pageY: null }, // determine zIndex zindex = function ($t) { var zin = 0, $tt = $t; while (true) { zin = Math.max(zin, parseInt($tt.css('z-index'), 10) || 0); $tt = $tt.parent(); if (!$tt || !$tt.length || 'html body'.indexOf($tt.prop('nodeName').toLowerCase()) > -1) { break; } } return zin; }, // event handlers handle = { // abort anything abortevent: function (e) { e.preventDefault(); e.stopImmediatePropagation(); }, // contextmenu show dispatcher contextmenu: function (e) { var $this = $(this); //Show browser context-menu when preShow returns false if (e.data.events.preShow($this,e) === false) { return; } // disable actual context-menu if we are using the right mouse button as the trigger if (e.data.trigger === 'right') { e.preventDefault(); e.stopImmediatePropagation(); } // abort native-triggered events unless we're triggering on right click if ((e.data.trigger !== 'right' && e.data.trigger !== 'demand') && e.originalEvent) { return; } // Let the current contextmenu decide if it should show or not based on its own trigger settings if (typeof e.mouseButton !== 'undefined' && e.data) { if (!(e.data.trigger === 'left' && e.mouseButton === 0) && !(e.data.trigger === 'right' && e.mouseButton === 2)) { // Mouse click is not valid. return; } } // abort event if menu is visible for this trigger if ($this.hasClass('context-menu-active')) { return; } if (!$this.hasClass('context-menu-disabled')) { // theoretically need to fire a show event at // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#context-menus // var evt = jQuery.Event("show", { data: data, pageX: e.pageX, pageY: e.pageY, relatedTarget: this }); // e.data.$menu.trigger(evt); $currentTrigger = $this; if (e.data.build) { var built = e.data.build($currentTrigger, e); // abort if build() returned false if (built === false) { return; } // dynamically build menu on invocation e.data = $.extend(true, {}, defaults, e.data, built || {}); // abort if there are no items to display if (!e.data.items || $.isEmptyObject(e.data.items)) { // Note: jQuery captures and ignores errors from event handlers if (window.console) { (console.error || console.log).call(console, 'No items specified to show in contextMenu'); } throw new Error('No Items specified'); } // backreference for custom command type creation e.data.$trigger = $currentTrigger; op.create(e.data); } op.show.call($this, e.data, e.pageX, e.pageY); } }, // contextMenu left-click trigger click: function (e) { e.preventDefault(); e.stopImmediatePropagation(); $(this).trigger($.Event('contextmenu', {data: e.data, pageX: e.pageX, pageY: e.pageY})); }, // contextMenu right-click trigger mousedown: function (e) { // register mouse down var $this = $(this); // hide any previous menus if ($currentTrigger && $currentTrigger.length && !$currentTrigger.is($this)) { $currentTrigger.data('contextMenu').$menu.trigger('contextmenu:hide'); } // activate on right click if (e.button === 2) { $currentTrigger = $this.data('contextMenuActive', true); } }, // contextMenu right-click trigger mouseup: function (e) { // show menu var $this = $(this); if ($this.data('contextMenuActive') && $currentTrigger && $currentTrigger.length && $currentTrigger.is($this) && !$this.hasClass('context-menu-disabled')) { e.preventDefault(); e.stopImmediatePropagation(); $currentTrigger = $this; $this.trigger($.Event('contextmenu', {data: e.data, pageX: e.pageX, pageY: e.pageY})); } $this.removeData('contextMenuActive'); }, // contextMenu hover trigger mouseenter: function (e) { var $this = $(this), $related = $(e.relatedTarget), $document = $(document); // abort if we're coming from a menu if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { return; } // abort if a menu is shown if ($currentTrigger && $currentTrigger.length) { return; } hoveract.pageX = e.pageX; hoveract.pageY = e.pageY; hoveract.data = e.data; $document.on('mousemove.contextMenuShow', handle.mousemove); hoveract.timer = setTimeout(function () { hoveract.timer = null; $document.off('mousemove.contextMenuShow'); $currentTrigger = $this; $this.trigger($.Event('contextmenu', { data: hoveract.data, pageX: hoveract.pageX, pageY: hoveract.pageY })); }, e.data.delay); }, // contextMenu hover trigger mousemove: function (e) { hoveract.pageX = e.pageX; hoveract.pageY = e.pageY; }, // contextMenu hover trigger mouseleave: function (e) { // abort if we're leaving for a menu var $related = $(e.relatedTarget); if ($related.is('.context-menu-list') || $related.closest('.context-menu-list').length) { return; } try { clearTimeout(hoveract.timer); } catch (e) { } hoveract.timer = null; }, // click on layer to hide contextMenu layerClick: function (e, opt, onhide) { var $this = $(this), root = (opt !== undefined) ? opt : $this.data('contextMenuRoot'), button = e.button, x = e.pageX, y = e.pageY, fakeClick = x === undefined, target, offset; // If the click is not real, things break: https://github.com/swisnl/jQuery-contextMenu/issues/132 if(fakeClick){ if (root !== null && typeof root !== 'undefined' && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); } return; } // if the click closing is done through windwow event listener rather than a transparent layer if (!root.$layer) { target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop()); if (root.$menu === null || typeof root.$menu === 'undefined' || !root.$menu[0].contains(target)) { root.$menu.trigger('contextmenu:hide'); if (typeof onhide !== 'undefined') onhide(); } return; } e.preventDefault(); setTimeout(function () { var $window; var triggerAction = ((root.trigger === 'left' && button === 0) || (root.trigger === 'right' && button === 2)); // find the element that would've been clicked, wasn't the layer in the way if (document.elementFromPoint && root.$layer) { root.$layer.hide(); target = document.elementFromPoint(x - $win.scrollLeft(), y - $win.scrollTop()); // also need to try and focus this element if we're in a contenteditable area, // as the layer will prevent the browser mouse action we want if (target !== null && target.isContentEditable) { var range = document.createRange(), sel = window.getSelection(); range.selectNode(target); range.collapse(true); sel.removeAllRanges(); sel.addRange(range); } $(target).trigger(e); root.$layer.show(); } if (root.hideOnSecondTrigger && triggerAction && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); return; } if (root.reposition && triggerAction) { if (document.elementFromPoint) { if (root.$trigger.is(target)) { root.position.call(root.$trigger, root, x, y); return; } } else { offset = root.$trigger.offset(); $window = $(window); // while this looks kinda awful, it's the best way to avoid // unnecessarily calculating any positions offset.top += $window.scrollTop(); if (offset.top <= e.pageY) { offset.left += $window.scrollLeft(); if (offset.left <= e.pageX) { offset.bottom = offset.top + root.$trigger.outerHeight(); if (offset.bottom >= e.pageY) { offset.right = offset.left + root.$trigger.outerWidth(); if (offset.right >= e.pageX) { // reposition root.position.call(root.$trigger, root, x, y); return; } } } } } } if (target && triggerAction) { root.$trigger.one('contextmenu:hidden', function () { $(target).contextMenu({x: x, y: y, button: button}); }); } if (root !== null && typeof root !== 'undefined' && root.$menu !== null && typeof root.$menu !== 'undefined') { root.$menu.trigger('contextmenu:hide'); } }, 50); }, // key handled :hover keyStop: function (e, opt) { if (!opt.isInput) { e.preventDefault(); } e.stopPropagation(); }, key: function (e) { var opt = {}; // Only get the data from $currentTrigger if it exists if ($currentTrigger) { opt = $currentTrigger.data('contextMenu') || {}; } // If the trigger happen on a element that are above the contextmenu do this if (typeof opt.zIndex === 'undefined') { opt.zIndex = 0; } var targetZIndex = 0; var getZIndexOfTriggerTarget = function (target) { if (target.style.zIndex !== '') { targetZIndex = target.style.zIndex; } else { if (target.offsetParent !== null && typeof target.offsetParent !== 'undefined') { getZIndexOfTriggerTarget(target.offsetParent); } else if (target.parentElement !== null && typeof target.parentElement !== 'undefined') { getZIndexOfTriggerTarget(target.parentElement); } } }; getZIndexOfTriggerTarget(e.target); // If targetZIndex is heigher then opt.zIndex dont progress any futher. // This is used to make sure that if you are using a dialog with a input / textarea / contenteditable div // and its above the contextmenu it wont steal keys events if (opt.$menu && parseInt(targetZIndex,10) > parseInt(opt.$menu.css("zIndex"),10)) { return; } switch (e.keyCode) { case 9: case 38: // up handle.keyStop(e, opt); // if keyCode is [38 (up)] or [9 (tab) with shift] if (opt.isInput) { if (e.keyCode === 9 && e.shiftKey) { e.preventDefault(); if (opt.$selected) { opt.$selected.find('input, textarea, select').blur(); } if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('prevcommand'); } return; } else if (e.keyCode === 38 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { // checkboxes don't capture this key e.preventDefault(); return; } } else if (e.keyCode !== 9 || e.shiftKey) { if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('prevcommand'); } return; } break; // omitting break; // case 9: // tab - reached through omitted break; case 40: // down handle.keyStop(e, opt); if (opt.isInput) { if (e.keyCode === 9) { e.preventDefault(); if (opt.$selected) { opt.$selected.find('input, textarea, select').blur(); } if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('nextcommand'); } return; } else if (e.keyCode === 40 && opt.$selected.find('input, textarea, select').prop('type') === 'checkbox') { // checkboxes don't capture this key e.preventDefault(); return; } } else { if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('nextcommand'); } return; } break; case 37: // left handle.keyStop(e, opt); if (opt.isInput || !opt.$selected || !opt.$selected.length) { break; } if (!opt.$selected.parent().hasClass('context-menu-root')) { var $parent = opt.$selected.parent().parent(); opt.$selected.trigger('contextmenu:blur'); opt.$selected = $parent; return; } break; case 39: // right handle.keyStop(e, opt); if (opt.isInput || !opt.$selected || !opt.$selected.length) { break; } var itemdata = opt.$selected.data('contextMenu') || {}; if (itemdata.$menu && opt.$selected.hasClass('context-menu-submenu')) { opt.$selected = null; itemdata.$selected = null; itemdata.$menu.trigger('nextcommand'); return; } break; case 35: // end case 36: // home if (opt.$selected && opt.$selected.find('input, textarea, select').length) { return; } else { (opt.$selected && opt.$selected.parent() || opt.$menu) .children(':not(.' + opt.classNames.disabled + ', .' + opt.classNames.notSelectable + ')')[e.keyCode === 36 ? 'first' : 'last']() .trigger('contextmenu:focus'); e.preventDefault(); return; } break; case 13: // enter handle.keyStop(e, opt); if (opt.isInput) { if (opt.$selected && !opt.$selected.is('textarea, select')) { e.preventDefault(); return; } break; } if (typeof opt.$selected !== 'undefined' && opt.$selected !== null) { opt.$selected.trigger('mouseup'); } return; case 32: // space case 33: // page up case 34: // page down // prevent browser from scrolling down while menu is visible handle.keyStop(e, opt); return; case 27: // esc handle.keyStop(e, opt); if (opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('contextmenu:hide'); } return; default: // 0-9, a-z var k = (String.fromCharCode(e.keyCode)).toUpperCase(); if (opt.accesskeys && opt.accesskeys[k]) { // according to the specs accesskeys must be invoked immediately opt.accesskeys[k].$node.trigger(opt.accesskeys[k].$menu ? 'contextmenu:focus' : 'mouseup'); return; } break; } // pass event to selected item, // stop propagation to avoid endless recursion e.stopPropagation(); if (typeof opt.$selected !== 'undefined' && opt.$selected !== null) { opt.$selected.trigger(e); } }, // select previous possible command in menu prevItem: function (e) { e.stopPropagation(); var opt = $(this).data('contextMenu') || {}; var root = $(this).data('contextMenuRoot') || {}; // obtain currently selected menu if (opt.$selected) { var $s = opt.$selected; opt = opt.$selected.parent().data('contextMenu') || {}; opt.$selected = $s; } var $children = opt.$menu.children(), $prev = !opt.$selected || !opt.$selected.prev().length ? $children.last() : opt.$selected.prev(), $round = $prev; // skip disabled or hidden elements while ($prev.hasClass(root.classNames.disabled) || $prev.hasClass(root.classNames.notSelectable) || $prev.is(':hidden')) { if ($prev.prev().length) { $prev = $prev.prev(); } else { $prev = $children.last(); } if ($prev.is($round)) { // break endless loop return; } } // leave current if (opt.$selected) { handle.itemMouseleave.call(opt.$selected.get(0), e); } // activate next handle.itemMouseenter.call($prev.get(0), e); // focus input var $input = $prev.find('input, textarea, select'); if ($input.length) { $input.focus(); } }, // select next possible command in menu nextItem: function (e) { e.stopPropagation(); var opt = $(this).data('contextMenu') || {}; var root = $(this).data('contextMenuRoot') || {}; // obtain currently selected menu if (opt.$selected) { var $s = opt.$selected; opt = opt.$selected.parent().data('contextMenu') || {}; opt.$selected = $s; } var $children = opt.$menu.children(), $next = !opt.$selected || !opt.$selected.next().length ? $children.first() : opt.$selected.next(), $round = $next; // skip disabled while ($next.hasClass(root.classNames.disabled) || $next.hasClass(root.classNames.notSelectable) || $next.is(':hidden')) { if ($next.next().length) { $next = $next.next(); } else { $next = $children.first(); } if ($next.is($round)) { // break endless loop return; } } // leave current if (opt.$selected) { handle.itemMouseleave.call(opt.$selected.get(0), e); } // activate next handle.itemMouseenter.call($next.get(0), e); // focus input var $input = $next.find('input, textarea, select'); if ($input.length) { $input.focus(); } }, // flag that we're inside an input so the key handler can act accordingly focusInput: function () { var $this = $(this).closest('.context-menu-item'), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; root.$selected = opt.$selected = $this; root.isInput = opt.isInput = true; }, // flag that we're inside an input so the key handler can act accordingly blurInput: function () { var $this = $(this).closest('.context-menu-item'), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; root.isInput = opt.isInput = false; }, // :hover on menu menuMouseenter: function () { var root = $(this).data().contextMenuRoot; root.hovering = true; }, // :hover on menu menuMouseleave: function (e) { var root = $(this).data().contextMenuRoot; if (root.$layer && root.$layer.is(e.relatedTarget)) { root.hovering = false; } }, // :hover done manually so key handling is possible itemMouseenter: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; // prevent fast hover on mobile tap-through if (isInteractionTooFast($this)) { return; } root.hovering = true; // abort if we're re-entering if (e && root.$layer && root.$layer.is(e.relatedTarget)) { e.preventDefault(); e.stopImmediatePropagation(); } // make sure only one item is selected (opt.$menu ? opt : root).$menu .children('.' + root.classNames.hover).trigger('contextmenu:blur') .children('.hover').trigger('contextmenu:blur'); if ($this.hasClass(root.classNames.disabled) || $this.hasClass(root.classNames.notSelectable)) { opt.$selected = null; return; } $this.trigger('contextmenu:focus'); }, // :hover done manually so key handling is possible itemMouseleave: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if (root !== opt && root.$layer && root.$layer.is(e.relatedTarget)) { if (typeof root.$selected !== 'undefined' && root.$selected !== null) { root.$selected.trigger('contextmenu:blur'); } e.preventDefault(); e.stopImmediatePropagation(); root.$selected = opt.$selected = opt.$node; return; } if(opt && opt.$menu && opt.$menu.hasClass('context-menu-visible')){ return; } $this.trigger('contextmenu:blur'); }, // contextMenu item click itemClick: function (e) { var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot, key = data.contextMenuKey, callback; // prevent fast click-through on mobile taps if (isInteractionTooFast($this)) { e.preventDefault(); e.stopImmediatePropagation(); return; } // abort if the key is unknown or disabled or is a menu // explicitly handle non-selectable submenu clicks first to stop propagation if ($this.is('.context-menu-submenu') && root.selectableSubMenu === false) { e.preventDefault(); e.stopImmediatePropagation(); // Stop event here for non-selectable submenus return; } // original check for other non-clickable/disabled items if (!opt.items[key] || $this.is('.' + root.classNames.disabled + ', .context-menu-separator, .' + root.classNames.notSelectable)) { return; } // if it wasn't a non-selectable submenu or other disabled item, prevent default and stop propagation before callback e.preventDefault(); e.stopImmediatePropagation(); if ($.isFunction(opt.callbacks[key]) && Object.prototype.hasOwnProperty.call(opt.callbacks, key)) { // item-specific callback callback = opt.callbacks[key]; } else if ($.isFunction(root.callback)) { // default callback callback = root.callback; } else { // no callback, no action return; } // hide menu if callback doesn't stop that if (callback.call(root.$trigger, key, root, e) !== false) { root.$menu.trigger('contextmenu:hide'); } else if (root.$menu.parent().length) { op.update.call(root.$trigger, root); } }, // ignore click events on input elements inputClick: function (e) { e.stopImmediatePropagation(); }, // hide hideMenu: function (e, data) { var root = $(this).data('contextMenuRoot'); op.hide.call(root.$trigger, root, data && data.force); }, // focus focusItem: function (e) { e.stopPropagation(); var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if ($this.hasClass(root.classNames.disabled) || $this.hasClass(root.classNames.notSelectable)) { return; } $this .addClass([root.classNames.hover, root.classNames.visible].join(' ')) // select other items and included items .parent().find('.context-menu-item').not($this) .removeClass(root.classNames.visible) .filter('.' + root.classNames.hover) .trigger('contextmenu:blur'); // remember selected opt.$selected = root.$selected = $this; if(opt && opt.$node && opt.$node.hasClass('context-menu-submenu')){ opt.$node.addClass(root.classNames.hover); } // position sub-menu - do after show so dumb $.ui.position can keep up if (opt.$node) { root.positionSubmenu.call(opt.$node, opt.$menu); if (opt.$menu) { var focusShowTimestamp = Date.now(); opt.$menu.data('_showTimestamp', focusShowTimestamp); } } }, // blur blurItem: function (e) { e.stopPropagation(); var $this = $(this), data = $this.data(), opt = data.contextMenu, root = data.contextMenuRoot; if (opt.autoHide) { // for tablets and touch screens this needs to remain $this.removeClass(root.classNames.visible); } $this.removeClass(root.classNames.hover); opt.$selected = null; } }, // operations op = { show: function (opt, x, y) { var $trigger = $(this), css = {}; // hide any open menus if ($('#context-menu-layer').length > 0) $('#context-menu-layer').trigger('mousedown'); else $(document).trigger('contextmenu:hide'); // backreference for callbacks opt.$trigger = $trigger; // show event if (opt.events.show.call($trigger, opt) === false) { $currentTrigger = null; return; } // create or update context menu var hasVisibleItems = op.update.call($trigger, opt); if (hasVisibleItems === false) { $currentTrigger = null; return; } // position menu opt.position.call($trigger, opt, x, y); // make sure we're in front if (opt.zIndex) { var additionalZValue = opt.zIndex; // If opt.zIndex is a function, call the function to get the right zIndex. if (typeof opt.zIndex === 'function') { additionalZValue = opt.zIndex.call($trigger, opt); } css.zIndex = zindex($trigger) + additionalZValue; } // add layer op.layer.call(opt.$menu, opt, css.zIndex); // adjust sub-menu zIndexes opt.$menu.find('ul').css('zIndex', css.zIndex + 1); // position and show context menu opt.$menu.css(css)[opt.animation.show](opt.animation.duration, function () { $trigger.trigger('contextmenu:visible'); var rootShowTimestamp = Date.now(); opt.$menu.data('_showTimestamp', rootShowTimestamp); op.activated(opt); opt.events.activated(opt); }); // make options available and set state $trigger .data('contextMenu', opt) .addClass('context-menu-active'); // register key handler $(document).off('keydown.contextMenu').on('keydown.contextMenu', handle.key); // register autoHide handler if (opt.autoHide) { // mouse position handler $(document).on('mousemove.contextMenuAutoHide', function (e) { // need to capture the offset on mousemove, // since the page might've been scrolled since activation var pos = $trigger.offset(); pos.right = pos.left + $trigger.outerWidth(); pos.bottom = pos.top + $trigger.outerHeight(); if (opt.$layer && !opt.hovering && (!(e.pageX >= pos.left && e.pageX <= pos.right) || !(e.pageY >= pos.top && e.pageY <= pos.bottom))) { /* Additional hover check after short time, you might just miss the edge of the menu */ setTimeout(function () { if (!opt.hovering && opt.$menu !== null && typeof opt.$menu !== 'undefined') { opt.$menu.trigger('contextmenu:hide'); } }, 50); } }); } }, hide: function (opt, force) { var $trigger = $(this); if (!opt) { opt = $trigger.data('contextMenu') || {}; } // hide event if (!force && opt.events && opt.events.hide.call($trigger, opt) === false) { return; } // remove options and revert state $trigger .removeData('contextMenu') .removeClass('context-menu-active'); if (opt.$layer) { // keep layer for a bit so the contextmenu event can be aborted properly by opera setTimeout((function ($layer) { return function () { $layer.remove(); }; })(opt.$layer), 10); try { delete opt.$layer; } catch (e) { opt.$layer = null; } } // remove handle $currentTrigger = null; // remove selected opt.$menu.find('.' + opt.classNames.hover).trigger('contextmenu:blur'); opt.$selected = null; // collapse all submenus opt.$menu.find('.' + opt.classNames.visible).removeClass(opt.classNames.visible); // unregister key and mouse handlers // $(document).off('.contextMenuAutoHide keydown.contextMenu'); // http://bugs.jquery.com/ticket/10705 $(document).off('.contextMenuAutoHide').off('keydown.contextMenu'); // hide menu if (opt.$menu) { opt.$menu[opt.animation.hide](opt.animation.duration, function () { // tear down dynamically built menu after animation is completed. if (opt.build) { opt.$menu.remove(); $.each(opt, function (key) { switch (key) { case 'ns': case 'selector': case 'build': case 'trigger': return true; default: opt[key] = undefined; try { delete opt[key]; } catch (e) { } return true; } }); } setTimeout(function () { $trigger.trigger('contextmenu:hidden'); }, 10); }); } }, create: function (opt, root) { if (typeof root === 'undefined') { root = opt; } // define handler for fast input clicks var handleFastInputClick = function(e) { var $inputClicked = $(this); if (isInteractionTooFast($inputClicked)) { e.preventDefault(); e.stopImmediatePropagation(); return false; } }; // create contextMenu opt.$menu = $('').addClass(opt.className || '').data({ 'contextMenu': opt, 'contextMenuRoot': root }); if(opt.dataAttr){ $.each(opt.dataAttr, function (key, item) { opt.$menu.attr('data-' + opt.key, item); }); } $.each(['callbacks', 'commands', 'inputs'], function (i, k) { opt[k] = {}; if (!root[k]) { root[k] = {}; } }); if (!root.accesskeys) { root.accesskeys = {}; } function createNameNode(item) { var $name = $(''); if (item._accesskey) { if (item._beforeAccesskey) { $name.append(document.createTextNode(item._beforeAccesskey)); } $('') .addClass('context-menu-accesskey') .text(item._accesskey) .appendTo($name); if (item._afterAccesskey) { $name.append(document.createTextNode(item._afterAccesskey)); } } else { if (item.isHtmlName) { // restrict use with access keys if (typeof item.accesskey !== 'undefined') { throw new Error('accesskeys are not compatible with HTML names and cannot be used together in the same item'); } $name.html(item.name); } else { $name.text(item.name); } } return $name; } // create contextMenu items $.each(opt.items, function (key, item) { var $t = $('').addClass(item.className || ''), $label = null, $input = null; // iOS needs to see a click-event bound to an element to actually // have the TouchEvents infrastructure trigger the click event $t.on('click', $.noop); // Make old school string seperator a real item so checks wont be // akward later. // And normalize 'cm_separator' into 'cm_seperator'. if (typeof item === 'string' || item.type === 'cm_separator') { item = {type: 'cm_seperator'}; } item.$node = $t.data({ 'contextMenu': opt, 'contextMenuRoot': root, 'contextMenuKey': key }); // register accesskey // NOTE: the accesskey attribute should be applicable to any element, but Safari5 and Chrome13 still can't do that if (typeof item.accesskey !== 'undefined') { var aks = splitAccesskey(item.accesskey); for (var i = 0, ak; ak = aks[i]; i++) { if (!root.accesskeys[ak]) { root.accesskeys[ak] = item; var matched = item.name.match(new RegExp('^(.*?)(' + ak + ')(.*)$', 'i')); if (matched) { item._beforeAccesskey = matched[1]; item._accesskey = matched[2]; item._afterAccesskey = matched[3]; } break; } } } if (item.type && types[item.type]) { // run custom type handler types[item.type].call($t, item, opt, root); // register commands $.each([opt, root], function (i, k) { k.commands[key] = item; // Overwrite only if undefined or the item is appended to the root. This so it // doesn't overwrite callbacks of root elements if the name is the same. if ($.isFunction(item.callback) && (typeof k.callbacks[key] === 'undefined' || typeof opt.type === 'undefined')) { k.callbacks[key] = item.callback; } }); } else { // add label for input if (item.type === 'cm_seperator') { $t.addClass('context-menu-separator ' + root.classNames.notSelectable); } else if (item.type === 'html') { $t.addClass('context-menu-html ' + root.classNames.notSelectable); } else if (item.type !== 'sub' && item.type) { $label = $('').appendTo($t); createNameNode(item).appendTo($label); $t.addClass('context-menu-input'); opt.hasTypes = true; $.each([opt, root], function (i, k) { k.commands[key] = item; k.inputs[key] = item; }); } else if (item.items) { item.type = 'sub'; } switch (item.type) { case 'cm_seperator': break; case 'text': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .appendTo($label); break; case 'textarea': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .appendTo($label); if (item.height) { $input.height(item.height); } break; case 'checkbox': $input = $('') .attr('name', 'context-menu-input-' + key) .val(item.value || '') .prop('checked', !!item.selected) .prependTo($label); // prevent checkbox default action on fast click-through $input.on('click', handleFastInputClick); break; case 'radio': $input = $('') .attr('name', 'context-menu-input-' + item.radio) .val(item.value || '') .prop('checked', !!item.selected) .prependTo($label); // prevent radio default action on fast click-through $input.on('click', handleFastInputClick); break; case 'select': $input = $('') .attr('name', 'context-menu-input-' + key) .appendTo($label); if (item.options) { $.each(item.options, function (value, text) { $('').val(value).text(text).appendTo($input); }); $input.val(item.selected); } break; case 'sub': createNameNode(item).appendTo($t); item.appendTo = item.$node; $t.data('contextMenu', item).addClass('context-menu-submenu'); item.callback = null; // If item contains items, and this is a promise, we should create it later // check if subitems is of type promise. If it is a promise we need to create // it later, after promise has been resolved. if ('function' === typeof item.items.then) { // probably a promise, process it, when completed it will create the sub menu's. op.processPromises(item, root, item.items); } else { // normal submenu. op.create(item, root); } break; case 'html': $(item.html).appendTo($t); break; default: $.each([opt, root], function (i, k) { k.commands[key] = item; // Overwrite only if undefined or the item is appended to the root. This so it // doesn't overwrite callbacks of root elements if the name is the same. if ($.isFunction(item.callback) && (typeof k.callbacks[key] === 'undefined' || typeof opt.type === 'undefined')) { k.callbacks[key] = item.callback; } }); createNameNode(item).appendTo($t); break; } // disable key listener in if (item.type && item.type !== 'sub' && item.type !== 'html' && item.type !== 'cm_seperator') { $input .on('focus', handle.focusInput) .on('blur', handle.blurInput); if (item.events) { $input.on(item.events, opt); } } // add icons if (item.icon) { if ($.isFunction(item.icon)) { item._icon = item.icon.call(this, this, $t, key, item); } else { if (typeof(item.icon) === 'string' && ( item.icon.substring(0, 4) === 'fab ' || item.icon.substring(0, 4) === 'fas ' || item.icon.substring(0, 4) === 'fad ' || item.icon.substring(0, 4) === 'far ' || item.icon.substring(0, 4) === 'fal ') ) { // to enable font awesome $t.addClass(root.classNames.icon + ' ' + root.classNames.icon + '--fa5'); item._icon = $(''); } else if (typeof(item.icon) === 'string' && item.icon.substring(0, 3) === 'fa-') { item._icon = root.classNames.icon + ' ' + root.classNames.icon + '--fa fa ' + item.icon; } else { item._icon = root.classNames.icon + ' ' + root.classNames.icon + '-' + item.icon; } } if(typeof(item._icon) === "string"){ $t.addClass(item._icon); } else { $t.prepend(item._icon); } } } // cache contained elements item.$input = $input; item.$label = $label; // attach item to menu $t.appendTo(opt.$menu); // Disable text selection if (!opt.hasTypes && $.support.eventSelectstart) { // browsers support user-select: none, // IE has a special event for text-selection // browsers supporting neither will not be preventing text-selection $t.on('selectstart.disableTextSelect', handle.abortevent); } }); // attach contextMenu to (to bypass any possible overflow:hidden issues on parents of the trigger element) if (!opt.$node) { opt.$menu.css('display', 'none').addClass('context-menu-root'); } opt.$menu.appendTo(opt.appendTo || document.body); }, resize: function ($menu, nested) { var domMenu; // determine widths of submenus, as CSS won't grow them automatically // position:absolute within position:absolute; min-width:100; max-width:200; results in width: 100; // kinda sucks hard... // determine width of absolutely positioned element $menu.css({position: 'absolute', display: 'block'}); // don't apply yet, because that would break nested elements' widths $menu.data('width', (domMenu = $menu.get(0)).getBoundingClientRect ? Math.ceil(domMenu.getBoundingClientRect().width) : $menu.outerWidth() + 1); // outerWidth() returns rounded pixels // reset styles so they allow nested elements to grow/shrink naturally $menu.css({ position: 'static', minWidth: '0px', maxWidth: '100000px' }); // identify width of nested menus $menu.find('> li > ul').each(function () { op.resize($(this), true); }); // reset and apply changes in the end because nested // elements' widths wouldn't be calculatable otherwise if (!nested) { $menu.find('ul').addBack().css({ position: '', display: '', minWidth: '', maxWidth: '' }).outerWidth(function () { return $(this).data('width'); }); } }, update: function (opt, root) { var $trigger = this; if (typeof root === 'undefined') { root = opt; op.resize(opt.$menu); } var hasVisibleItems = false; // re-check disabled for each item opt.$menu.children().each(function () { var $item = $(this), key = $item.data('contextMenuKey'), item = opt.items[key], disabled = ($.isFunction(item.disabled) && item.disabled.call($trigger, key, root)) || item.disabled === true, visible; if ($.isFunction(item.visible)) { visible = item.visible.call($trigger, key, root); } else if (typeof item.visible !== 'undefined') { visible = item.visible === true; } else { visible = true; } if (visible) { hasVisibleItems = true; } $item[visible ? 'show' : 'hide'](); // dis- / enable item $item[disabled ? 'addClass' : 'removeClass'](root.classNames.disabled); if ($.isFunction(item.icon)) { $item.removeClass(item._icon); var iconResult = item.icon.call(this, $trigger, $item, key, item); if(typeof(iconResult) === "string"){ $item.addClass(iconResult); } else { $item.prepend(iconResult); } } if (item.type) { // dis- / enable input elements $item.find('input, select, textarea').prop('disabled', disabled); // update input states switch (item.type) { case 'text': case 'textarea': item.$input.val(item.value || ''); break; case 'checkbox': case 'radio': item.$input.val(item.value || '').prop('checked', !!item.selected); break; case 'select': item.$input.val((item.selected === 0 ? "0" : item.selected) || ''); break; } } if (item.$menu) { // update sub-menu var subMenuHasVisibleItems = op.update.call($trigger, item, root); if (subMenuHasVisibleItems) { hasVisibleItems = true; } } }); return hasVisibleItems; }, layer: function (opt, zIndex) { if (!opt.useModal) { var listener = function (ev) { handle.layerClick(ev, opt, function() { document.removeEventListener('mousedown', listener, true); }); }; document.addEventListener('mousedown', listener, true); return; } // add transparent layer for click area // filter and background for Internet Explorer, Issue #23 var $layer = opt.$layer = $('') .css({ height: $win.height(), width: $win.width(), display: 'block', position: 'fixed', 'z-index': zIndex - 1, top: 0, left: 0, opacity: 0, filter: 'alpha(opacity=0)', 'background-color': '#000' }) .data('contextMenuRoot', opt) .appendTo(document.body) .on('contextmenu', handle.abortevent) .on('mousedown', handle.layerClick); // IE6 doesn't know position:fixed; if (typeof document.body.style.maxWidth === 'undefined') { // IE6 doesn't support maxWidth $layer.css({ 'position': 'absolute', 'height': $(document).height() }); } return $layer; }, processPromises: function (opt, root, promise) { // Start opt.$node.addClass(root.classNames.iconLoadingClass); function completedPromise(opt, root, items) { // Completed promise (dev called promise.resolve). We now have a list of items which can // be used to create the rest of the context menu. if (typeof items === 'undefined') { // Null result, dev should have checked errorPromise(undefined);//own error object } finishPromiseProcess(opt, root, items); } function errorPromise(opt, root, errorItem) { // User called promise.reject() with an error item, if not, provide own error item. if (typeof errorItem === 'undefined') { errorItem = { "error": { name: "No items and no error item", icon: "context-menu-icon context-menu-icon-quit" } }; if (window.console) { (console.error || console.log).call(console, 'When you reject a promise, provide an "items" object, equal to normal sub-menu items'); } } else if (typeof errorItem === 'string') { errorItem = {"error": {name: errorItem}}; } finishPromiseProcess(opt, root, errorItem); } function finishPromiseProcess(opt, root, items) { if (typeof root.$menu === 'undefined' || !root.$menu.is(':visible')) { return; } opt.$node.removeClass(root.classNames.iconLoadingClass); opt.items = items; op.create(opt, root, true); // Create submenu op.update(opt, root); // Correctly update position if user is already hovered over menu item root.positionSubmenu.call(opt.$node, opt.$menu); // positionSubmenu, will only do anything if user already hovered over menu item that just got new subitems. } // Wait for promise completion. .then(success, error, notify) (we don't track notify). Bind the opt // and root to avoid scope problems promise.then(completedPromise.bind(this, opt, root), errorPromise.bind(this, opt, root)); }, // operation that will run after contextMenu showed on screen activated: function(opt){ var $menu = opt.$menu; var $menuOffset = $menu.offset(); var winHeight = $(window).height(); var winWidth = $(window).width(); var winScrollTop = $(window).scrollTop(); var winScrollLeft = $(window).scrollLeft(); var menuHeight = $menu.height(); var outerHeight = $menu.outerHeight(); var outerWidth = $menu.outerWidth(); if(menuHeight > winHeight){ $menu.css({ 'height' : winHeight + 'px', 'overflow-x': 'hidden', 'overflow-y': 'auto', 'top': winScrollTop + 'px' }); } else if($menuOffset.top < winScrollTop){ $menu.css({ 'top': winScrollTop + 'px' }); } else if($menuOffset.top + outerHeight > winScrollTop + winHeight){ $menu.css({ 'top': $menuOffset.top - (($menuOffset.top + outerHeight) - (winScrollTop + winHeight)) + "px" }); } if($menuOffset.left + outerWidth > winScrollLeft + winWidth){ $menu.css({ 'left': $menuOffset.left - (($menuOffset.left + outerWidth) - (winScrollLeft + winWidth)) + "px" }); } } }; // split accesskey according to http://www.whatwg.org/specs/web-apps/current-work/multipage/editing.html#assigned-access-key function splitAccesskey(val) { var t = val.split(/\s+/); var keys = []; for (var i = 0, k; k = t[i]; i++) { k = k.charAt(0).toUpperCase(); // first character only // theoretically non-accessible characters should be ignored, but different systems, different keyboard layouts, ... screw it. // a map to look up already used access keys would be nice keys.push(k); } return keys; } // handle contextMenu triggers $.fn.contextMenu = function (operation) { var $t = this, $o = operation; if (this.length > 0) { // this is not a build on demand menu if (typeof operation === 'undefined') { this.first().trigger('contextmenu'); } else if (typeof operation.x !== 'undefined' && typeof operation.y !== 'undefined') { this.first().trigger($.Event('contextmenu', { pageX: operation.x, pageY: operation.y, mouseButton: operation.button })); } else if (operation === 'hide') { var $menu = this.first().data('contextMenu') ? this.first().data('contextMenu').$menu : null; if ($menu) { $menu.trigger('contextmenu:hide'); } } else if (operation === 'destroy') { $.contextMenu('destroy', {context: this}); } else if ($.isPlainObject(operation)) { operation.context = this; $.contextMenu('create', operation); } else if (operation) { this.removeClass('context-menu-disabled'); } else if (!operation) { this.addClass('context-menu-disabled'); } } else { $.each(menus, function () { if (this.selector === $t.selector) { $o.data = this; $.extend($o.data, {trigger: 'demand'}); } }); handle.contextmenu.call($o.target, $o); } return this; }; // manage contextMenu instances $.contextMenu = function (operation, options) { if (typeof operation !== 'string') { options = operation; operation = 'create'; } if (typeof options === 'string') { options = {selector: options}; } else if (typeof options === 'undefined') { options = {}; } // merge with default options var o = $.extend(true, {}, defaults, options || {}); var $document = $(document); var $context = $document; var _hasContext = false; if (!o.context || !o.context.length) { o.context = document; } else { // you never know what they throw at you... $context = $(o.context).first(); o.context = $context.get(0); _hasContext = !$(o.context).is(document); } switch (operation) { case 'update': // Updates visibility and such if(_hasContext){ op.update($context); } else { for(var menu in menus){ if(menus.hasOwnProperty(menu)){ op.update(menus[menu]); } } } break; case 'create': // no selector no joy if (!o.selector) { throw new Error('No selector specified'); } // make sure internal classes are not bound to if (o.selector.match(/.context-menu-(list|item|input)($|\s)/)) { throw new Error('Cannot bind to selector "' + o.selector + '" as it contains a reserved className'); } if (!o.build && (!o.items || $.isEmptyObject(o.items))) { throw new Error('No Items specified'); } counter++; o.ns = '.contextMenu' + counter; if (!_hasContext) { namespaces[o.selector] = o.ns; } menus[o.ns] = o; // default to right click if (!o.trigger) { o.trigger = 'right'; } if (!initialized) { var itemClick = o.itemClickEvent === 'click' ? 'click.contextMenu' : 'mouseup.contextMenu'; var contextMenuItemObj = { // 'mouseup.contextMenu': handle.itemClick, // 'click.contextMenu': handle.itemClick, 'contextmenu:focus.contextMenu': handle.focusItem, 'contextmenu:blur.contextMenu': handle.blurItem, 'contextmenu.contextMenu': handle.abortevent, 'mouseenter.contextMenu': handle.itemMouseenter, 'mouseleave.contextMenu': handle.itemMouseleave }; contextMenuItemObj[itemClick] = handle.itemClick; // make sure item click is registered first $document .on({ 'contextmenu:hide.contextMenu': handle.hideMenu, 'prevcommand.contextMenu': handle.prevItem, 'nextcommand.contextMenu': handle.nextItem, 'contextmenu.contextMenu': handle.abortevent, 'mouseenter.contextMenu': handle.menuMouseenter, 'mouseleave.contextMenu': handle.menuMouseleave }, '.context-menu-list') .on('mouseup.contextMenu', '.context-menu-input', handle.inputClick) .on(contextMenuItemObj, '.context-menu-item'); initialized = true; } // engage native contextmenu event $context .on('contextmenu' + o.ns, o.selector, o, handle.contextmenu); if (_hasContext) { // add remove hook, just in case $context.on('remove' + o.ns, function () { $(this).contextMenu('destroy'); }); } switch (o.trigger) { case 'hover': $context .on('mouseenter' + o.ns, o.selector, o, handle.mouseenter) .on('mouseleave' + o.ns, o.selector, o, handle.mouseleave); break; case 'left': $context.on('click' + o.ns, o.selector, o, handle.click); break; case 'touchstart': $context.on('touchstart' + o.ns, o.selector, o, handle.click); break; /* default: // http://www.quirksmode.org/dom/events/contextmenu.html $document .on('mousedown' + o.ns, o.selector, o, handle.mousedown) .on('mouseup' + o.ns, o.selector, o, handle.mouseup); break; */ } // create menu if (!o.build) { op.create(o); } break; case 'destroy': var $visibleMenu; if (_hasContext) { // get proper options var context = o.context; $.each(menus, function (ns, o) { if (!o) { return true; } // Is this menu equest to the context called from if (!$(context).is(o.selector)) { return true; } $visibleMenu = $('.context-menu-list').filter(':visible'); if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is($(o.context).find(o.selector))) { $visibleMenu.trigger('contextmenu:hide', {force: true}); } try { if (menus[o.ns].$menu) { menus[o.ns].$menu.remove(); } delete menus[o.ns]; } catch (e) { menus[o.ns] = null; } $(o.context).off(o.ns); return true; }); } else if (!o.selector) { $document.off('.contextMenu .contextMenuAutoHide'); $.each(menus, function (ns, o) { $(o.context).off(o.ns); }); namespaces = {}; menus = {}; counter = 0; initialized = false; $('#context-menu-layer, .context-menu-list').remove(); } else if (namespaces[o.selector]) { $visibleMenu = $('.context-menu-list').filter(':visible'); if ($visibleMenu.length && $visibleMenu.data().contextMenuRoot.$trigger.is(o.selector)) { $visibleMenu.trigger('contextmenu:hide', {force: true}); } try { if (menus[namespaces[o.selector]].$menu) { menus[namespaces[o.selector]].$menu.remove(); } delete menus[namespaces[o.selector]]; } catch (e) { menus[namespaces[o.selector]] = null; } $document.off(namespaces[o.selector]); } break; case 'html5': // if and are not handled by the browser, // or options was a bool true, // initialize $.contextMenu for them if ((!$.support.htmlCommand && !$.support.htmlMenuitem) || (typeof options === 'boolean' && options)) { $('menu[type="context"]').each(function () { if (this.id) { $.contextMenu({ selector: '[contextmenu=' + this.id + ']', items: $.contextMenu.fromMenu(this) }); } }).css('display', 'none'); } break; default: throw new Error('Unknown operation "' + operation + '"'); } return this; }; // import values into commands $.contextMenu.setInputValues = function (opt, data) { if (typeof data === 'undefined') { data = {}; } $.each(opt.inputs, function (key, item) { switch (item.type) { case 'text': case 'textarea': item.value = data[key] || ''; break; case 'checkbox': item.selected = data[key] ? true : false; break; case 'radio': item.selected = (data[item.radio] || '') === item.value; break; case 'select': item.selected = data[key] || ''; break; } }); }; // export values from commands $.contextMenu.getInputValues = function (opt, data) { if (typeof data === 'undefined') { data = {}; } $.each(opt.inputs, function (key, item) { switch (item.type) { case 'text': case 'textarea': case 'select': data[key] = item.$input.val(); break; case 'checkbox': data[key] = item.$input.prop('checked'); break; case 'radio': if (item.$input.prop('checked')) { data[item.radio] = item.value; } break; } }); return data; }; // find function inputLabel(node) { return (node.id && $('label[for="' + node.id + '"]').val()) || node.name; } // convert to items object function menuChildren(items, $children, counter) { if (!counter) { counter = 0; } $children.each(function () { var $node = $(this), node = this, nodeName = this.nodeName.toLowerCase(), label, item; // extract if (nodeName === 'label' && $node.find('input, textarea, select').length) { label = $node.text(); $node = $node.children().first(); node = $node.get(0); nodeName = node.nodeName.toLowerCase(); } /* * accepts flow-content as children. that means , and such are valid menu items. * Not being the sadistic kind, $.contextMenu only accepts: * , , , , , , and of course . * Everything else will be imported as an html node, which is not interfaced with contextMenu. */ // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command switch (nodeName) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element case 'menu': item = {name: $node.attr('label'), items: {}}; counter = menuChildren(item.items, $node.children(), counter); break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command case 'a': // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command case 'button': item = { name: $node.text(), disabled: !!$node.attr('disabled'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command case 'menuitem': case 'command': switch ($node.attr('type')) { case undefined: case 'command': case 'menuitem': item = { name: $node.attr('label'), disabled: !!$node.attr('disabled'), icon: $node.attr('icon'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; case 'checkbox': item = { type: 'checkbox', disabled: !!$node.attr('disabled'), name: $node.attr('label'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', disabled: !!$node.attr('disabled'), name: $node.attr('label'), radio: $node.attr('radiogroup'), value: $node.attr('id'), selected: !!$node.attr('checked') }; break; default: item = undefined; } break; case 'hr': item = '-------'; break; case 'input': switch ($node.attr('type')) { case 'text': item = { type: 'text', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'checkbox': item = { type: 'checkbox', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), radio: !!$node.attr('name'), value: $node.val(), selected: !!$node.attr('checked') }; break; default: item = undefined; break; } break; case 'select': item = { type: 'select', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: $node.val(), options: {} }; $node.children().each(function () { item.options[this.value] = $(this).text(); }); break; case 'textarea': item = { type: 'textarea', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'label': break; default: item = {type: 'html', html: $node.clone(true)}; break; } if (item) { counter++; items['key' + counter] = item; } }); return counter; } // convert html5 menu $.contextMenu.fromMenu = function (element) { var $this = $(element), items = {}; menuChildren(items, $this.children()); return items; }; // make defaults accessible $.contextMenu.defaults = defaults; $.contextMenu.types = types; // export internal functions - undocumented, for hacking only! $.contextMenu.handle = handle; $.contextMenu.op = op; $.contextMenu.menus = menus; }); ================================================ FILE: src/jquery.ui.position.js ================================================ /*! jQuery UI - v1.12.0 - 2016-07-15 * http://jqueryui.com * Includes: position.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ (function( factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define([ "jquery" ], factory ); } else { // Browser globals factory( jQuery ); } }(function( $ ) { $.ui = $.ui || {}; var version = $.ui.version = "1.12.0"; /*! * jQuery UI Position 1.12.0 * http://jqueryui.com * * Copyright jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/position/ */ //>>label: Position //>>group: Core //>>description: Positions elements relative to other elements. //>>docs: http://api.jqueryui.com/position/ //>>demos: http://jqueryui.com/position/ ( function() { var cachedScrollbarWidth, supportsOffsetFractions, max = Math.max, abs = Math.abs, round = Math.round, rhorizontal = /left|center|right/, rvertical = /top|center|bottom/, roffset = /[\+\-]\d+(\.[\d]+)?%?/, rposition = /^\w+/, rpercent = /%$/, _position = $.fn.position; // Support: IE <=9 only supportsOffsetFractions = function() { var element = $( "" ) .css( "position", "absolute" ) .appendTo( "body" ) .offset( { top: 1.5, left: 1.5 } ), support = element.offset().top === 1.5; element.remove(); supportsOffsetFractions = function() { return support; }; return support; }; function getOffsets( offsets, width, height ) { return [ parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) ]; } function parseCss( element, property ) { return parseInt( $.css( element, property ), 10 ) || 0; } function getDimensions( elem ) { var raw = elem[ 0 ]; if ( raw.nodeType === 9 ) { return { width: elem.width(), height: elem.height(), offset: { top: 0, left: 0 } }; } if ( $.isWindow( raw ) ) { return { width: elem.width(), height: elem.height(), offset: { top: elem.scrollTop(), left: elem.scrollLeft() } }; } if ( raw.preventDefault ) { return { width: 0, height: 0, offset: { top: raw.pageY, left: raw.pageX } }; } return { width: elem.outerWidth(), height: elem.outerHeight(), offset: elem.offset() }; } $.position = { scrollbarWidth: function() { if ( cachedScrollbarWidth !== undefined ) { return cachedScrollbarWidth; } var w1, w2, div = $( "" + "" ), innerDiv = div.children()[ 0 ]; $( "body" ).append( div ); w1 = innerDiv.offsetWidth; div.css( "overflow", "scroll" ); w2 = innerDiv.offsetWidth; if ( w1 === w2 ) { w2 = div[ 0 ].clientWidth; } div.remove(); return ( cachedScrollbarWidth = w1 - w2 ); }, getScrollInfo: function( within ) { var overflowX = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-x" ), overflowY = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-y" ), hasOverflowX = overflowX === "scroll" || ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), hasOverflowY = overflowY === "scroll" || ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); return { width: hasOverflowY ? $.position.scrollbarWidth() : 0, height: hasOverflowX ? $.position.scrollbarWidth() : 0 }; }, getWithinInfo: function( element ) { var withinElement = $( element || window ), isWindow = $.isWindow( withinElement[ 0 ] ), isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, hasOffset = !isWindow && !isDocument; return { element: withinElement, isWindow: isWindow, isDocument: isDocument, offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, scrollLeft: withinElement.scrollLeft(), scrollTop: withinElement.scrollTop(), width: withinElement.outerWidth(), height: withinElement.outerHeight() }; } }; $.fn.position = function( options ) { if ( !options || !options.of ) { return _position.apply( this, arguments ); } // Make a copy, we don't want to modify arguments options = $.extend( {}, options ); var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, target = $( options.of ), within = $.position.getWithinInfo( options.within ), scrollInfo = $.position.getScrollInfo( within ), collision = ( options.collision || "flip" ).split( " " ), offsets = {}; dimensions = getDimensions( target ); if ( target[ 0 ].preventDefault ) { // Force left top to allow flipping options.at = "left top"; } targetWidth = dimensions.width; targetHeight = dimensions.height; targetOffset = dimensions.offset; // Clone to reuse original targetOffset later basePosition = $.extend( {}, targetOffset ); // Force my and at to have valid horizontal and vertical positions // if a value is missing or invalid, it will be converted to center $.each( [ "my", "at" ], function() { var pos = ( options[ this ] || "" ).split( " " ), horizontalOffset, verticalOffset; if ( pos.length === 1 ) { pos = rhorizontal.test( pos[ 0 ] ) ? pos.concat( [ "center" ] ) : rvertical.test( pos[ 0 ] ) ? [ "center" ].concat( pos ) : [ "center", "center" ]; } pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; // Calculate offsets horizontalOffset = roffset.exec( pos[ 0 ] ); verticalOffset = roffset.exec( pos[ 1 ] ); offsets[ this ] = [ horizontalOffset ? horizontalOffset[ 0 ] : 0, verticalOffset ? verticalOffset[ 0 ] : 0 ]; // Reduce to just the positions without the offsets options[ this ] = [ rposition.exec( pos[ 0 ] )[ 0 ], rposition.exec( pos[ 1 ] )[ 0 ] ]; } ); // Normalize collision option if ( collision.length === 1 ) { collision[ 1 ] = collision[ 0 ]; } if ( options.at[ 0 ] === "right" ) { basePosition.left += targetWidth; } else if ( options.at[ 0 ] === "center" ) { basePosition.left += targetWidth / 2; } if ( options.at[ 1 ] === "bottom" ) { basePosition.top += targetHeight; } else if ( options.at[ 1 ] === "center" ) { basePosition.top += targetHeight / 2; } atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); basePosition.left += atOffset[ 0 ]; basePosition.top += atOffset[ 1 ]; return this.each( function() { var collisionPosition, using, elem = $( this ), elemWidth = elem.outerWidth(), elemHeight = elem.outerHeight(), marginLeft = parseCss( this, "marginLeft" ), marginTop = parseCss( this, "marginTop" ), collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, position = $.extend( {}, basePosition ), myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); if ( options.my[ 0 ] === "right" ) { position.left -= elemWidth; } else if ( options.my[ 0 ] === "center" ) { position.left -= elemWidth / 2; } if ( options.my[ 1 ] === "bottom" ) { position.top -= elemHeight; } else if ( options.my[ 1 ] === "center" ) { position.top -= elemHeight / 2; } position.left += myOffset[ 0 ]; position.top += myOffset[ 1 ]; // If the browser doesn't support fractions, then round for consistent results if ( !supportsOffsetFractions() ) { position.left = round( position.left ); position.top = round( position.top ); } collisionPosition = { marginLeft: marginLeft, marginTop: marginTop }; $.each( [ "left", "top" ], function( i, dir ) { if ( $.ui.position[ collision[ i ] ] ) { $.ui.position[ collision[ i ] ][ dir ]( position, { targetWidth: targetWidth, targetHeight: targetHeight, elemWidth: elemWidth, elemHeight: elemHeight, collisionPosition: collisionPosition, collisionWidth: collisionWidth, collisionHeight: collisionHeight, offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], my: options.my, at: options.at, within: within, elem: elem } ); } } ); if ( options.using ) { // Adds feedback as second argument to using callback, if present using = function( props ) { var left = targetOffset.left - position.left, right = left + targetWidth - elemWidth, top = targetOffset.top - position.top, bottom = top + targetHeight - elemHeight, feedback = { target: { element: target, left: targetOffset.left, top: targetOffset.top, width: targetWidth, height: targetHeight }, element: { element: elem, left: position.left, top: position.top, width: elemWidth, height: elemHeight }, horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" }; if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { feedback.horizontal = "center"; } if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { feedback.vertical = "middle"; } if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { feedback.important = "horizontal"; } else { feedback.important = "vertical"; } options.using.call( this, props, feedback ); }; } elem.offset( $.extend( position, { using: using } ) ); } ); }; $.ui.position = { fit: { left: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, outerWidth = within.width, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = withinOffset - collisionPosLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, newOverRight; // Element is wider than within if ( data.collisionWidth > outerWidth ) { // Element is initially over the left side of within if ( overLeft > 0 && overRight <= 0 ) { newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; position.left += overLeft - newOverRight; // Element is initially over right side of within } else if ( overRight > 0 && overLeft <= 0 ) { position.left = withinOffset; // Element is initially over both left and right sides of within } else { if ( overLeft > overRight ) { position.left = withinOffset + outerWidth - data.collisionWidth; } else { position.left = withinOffset; } } // Too far left -> align with left edge } else if ( overLeft > 0 ) { position.left += overLeft; // Too far right -> align with right edge } else if ( overRight > 0 ) { position.left -= overRight; // Adjust based on position and margin } else { position.left = max( position.left - collisionPosLeft, position.left ); } }, top: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollTop : within.offset.top, outerHeight = data.within.height, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = withinOffset - collisionPosTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, newOverBottom; // Element is taller than within if ( data.collisionHeight > outerHeight ) { // Element is initially over the top of within if ( overTop > 0 && overBottom <= 0 ) { newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; position.top += overTop - newOverBottom; // Element is initially over bottom of within } else if ( overBottom > 0 && overTop <= 0 ) { position.top = withinOffset; // Element is initially over both top and bottom of within } else { if ( overTop > overBottom ) { position.top = withinOffset + outerHeight - data.collisionHeight; } else { position.top = withinOffset; } } // Too far up -> align with top } else if ( overTop > 0 ) { position.top += overTop; // Too far down -> align with bottom edge } else if ( overBottom > 0 ) { position.top -= overBottom; // Adjust based on position and margin } else { position.top = max( position.top - collisionPosTop, position.top ); } } }, flip: { left: function( position, data ) { var within = data.within, withinOffset = within.offset.left + within.scrollLeft, outerWidth = within.width, offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = collisionPosLeft - offsetLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, myOffset = data.my[ 0 ] === "left" ? -data.elemWidth : data.my[ 0 ] === "right" ? data.elemWidth : 0, atOffset = data.at[ 0 ] === "left" ? data.targetWidth : data.at[ 0 ] === "right" ? -data.targetWidth : 0, offset = -2 * data.offset[ 0 ], newOverRight, newOverLeft; if ( overLeft < 0 ) { newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { position.left += myOffset + atOffset + offset; } } else if ( overRight > 0 ) { newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { position.left += myOffset + atOffset + offset; } } }, top: function( position, data ) { var within = data.within, withinOffset = within.offset.top + within.scrollTop, outerHeight = within.height, offsetTop = within.isWindow ? within.scrollTop : within.offset.top, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = collisionPosTop - offsetTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, top = data.my[ 1 ] === "top", myOffset = top ? -data.elemHeight : data.my[ 1 ] === "bottom" ? data.elemHeight : 0, atOffset = data.at[ 1 ] === "top" ? data.targetHeight : data.at[ 1 ] === "bottom" ? -data.targetHeight : 0, offset = -2 * data.offset[ 1 ], newOverTop, newOverBottom; if ( overTop < 0 ) { newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { position.top += myOffset + atOffset + offset; } } else if ( overBottom > 0 ) { newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { position.top += myOffset + atOffset + offset; } } } }, flipfit: { left: function() { $.ui.position.flip.left.apply( this, arguments ); $.ui.position.fit.left.apply( this, arguments ); }, top: function() { $.ui.position.flip.top.apply( this, arguments ); $.ui.position.fit.top.apply( this, arguments ); } } }; } )(); var position = $.ui.position; })); ================================================ FILE: src/sass/_icons.scss ================================================ .context-menu-icon-add { @include context-menu-item-icon(add); } .context-menu-icon-copy { @include context-menu-item-icon(copy); } .context-menu-icon-cut { @include context-menu-item-icon(cut); } .context-menu-icon-delete { @include context-menu-item-icon(delete); } .context-menu-icon-edit { @include context-menu-item-icon(edit); } .context-menu-icon-loading { @include context-menu-item-icon(loading); } .context-menu-icon-paste { @include context-menu-item-icon(paste); } .context-menu-icon-quit { @include context-menu-item-icon(quit); } ================================================ FILE: src/sass/_variables.scss ================================================ // Container Sizing $context-menu-min-width: 13em !default; $context-menu-max-width: $context-menu-min-width * 2 !default; $context-menu-container-padding: .25em 0 !default; $context-menu-container-margin: .3em !default; $context-menu-border-radius: .2em !default; // Container Font $context-menu-font-family: inherit !default; $context-menu-font-size: inherit !default; // Container Color $context-menu-background-color: #FFF !default; $context-menu-border-width: 1px !default; $context-menu-border-style: solid !default; $context-menu-border-color: #bebebe !default; $context-menu-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5) !default; // Item Sizing $context-menu-padding-vertical: .2em !default; $context-menu-padding-horizontal: 2em !default; // Item Default $context-menu-item-padding: $context-menu-padding-vertical $context-menu-padding-horizontal !default; $context-menu-text-color: #2F2F2F !default; $context-menu-item-color: $context-menu-background-color !default; // Item Hover $context-menu-item-color-hover: #2980B9 !default; $context-menu-text-color-hover: #FFF !default; // Item Disabled $context-menu-item-color-disabled: $context-menu-background-color !default; $context-menu-text-color-disabled: lighten($context-menu-text-color, 55) !default; // Submenu $context-menu-submenu-arrow-color: $context-menu-text-color !default; // Separator $context-menu-separator-height: 1px !default; $context-menu-separator-style: solid !default; $context-menu-separator-color: darken($context-menu-background-color, 10) !default; $context-menu-separator-margin: .35em 0 !default; // Icons $context-menu-icon-font-path: 'font/' !default; $context-menu-icon-font-name: 'context-menu-icons' !default; $context-menu-icon-size: 1em !default; $context-menu-icon-color: #2980B9 !default; $context-menu-icon-color-hover: $context-menu-text-color-hover !default; @keyframes cm-spin { 0% { -webkit-transform: translateY(-50%) rotate(0deg); transform: translateY(-50%) rotate(0deg) } 100% { -webkit-transform: translateY(-50%) rotate(359deg); transform: translateY(-50%) rotate(359deg) } } ================================================ FILE: src/sass/icons/_icon_classes.scss.tpl ================================================ <% _.each(glyphs, function(glyph) { %> .<%= className %>-<%= glyph.name %> { @include <%= mixinName %>(<%= glyph.name %>); }<% }); %> ================================================ FILE: src/sass/icons/_mixins.scss ================================================ @import "variables"; $context-menu-icons: () !default; @function context-menu-font-url($url) { @if function-exists(asset-url) { @return asset-url($url); } @else if function-exists(font-url) { @return font-url($url); } @else { @return url($url); } } @font-face { font-family: '#{$context-menu-icon-font-name}'; src: context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.eot?#{$context-menu-icons-cachebust}'); src: context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.eot?#{$context-menu-icons-cachebust}#iefix') format('embedded-opentype'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.woff2?#{$context-menu-icons-cachebust}') format('woff2'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.woff?#{$context-menu-icons-cachebust}') format('woff'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.ttf?#{$context-menu-icons-cachebust}') format('truetype'); font-weight: normal; font-style: normal; } @mixin context-menu-icon-without-mediaquery($position: before, $icon: false, $icons-font: $context-menu-icon-font-name) { @if not map-has-key($context-menu-icons, $icon) { @error "Invalid icon `#{$icon}`."; } @if $position == both { $position: 'before, &:after'; } // Either a :before or :after pseudo-element, or both, defaulting to :before &:#{$position} { @if $icon { // A particular icon has been specified content: #{"\"\\"}#{map-get($context-menu-icons, $icon) + "\""}; } // Include any extra rules supplied for the pseudo-element @content; } } @mixin context-menu-icon-screen-only($position: before, $icon: false, $icons-font: $context-menu-icon-font-name) { @media screen { @include context-menu-icon-without-mediaquery($position, $icon, $icons-font) { @content; } } } // For adding font icons to elements using CSS pseudo-elements // http://jaydenseric.com/blog/fun-with-sass-and-font-icons @mixin context-menu-icon($position: before, $icon: false, $icons-font: $context-menu-icon-font-name, $screen-only: false) { @if $screen-only { @include context-menu-icon-screen-only($position, $icon, $icons-font) { @content; } } @else { @include context-menu-icon-without-mediaquery($position, $icon, $icons-font) { @content; } } } @mixin base-context-menu-icon($icon-font: "context-menu-icons") { &::before { color: $context-menu-icon-color; font-family: $icon-font; font-style: normal; font-weight: normal; font-size: $context-menu-icon-size; left: 0; line-height: 1; position: absolute; text-align: center; top: 50%; transform: translateY(-50%); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; width: 2em; } } @mixin context-menu-item-icon($icon: false) { @include context-menu-icon(before, $icon); } ================================================ FILE: src/sass/icons/_variables.scss ================================================ // DON'T MANUALLY EDIT THIS FILE; run `gulp build-icons` instead. $context-menu-icons-cachebust: "2dq4x"; $context-menu-icons: ( add: "EA01", copy: "EA02", cut: "EA03", delete: "EA04", edit: "EA05", loading: "EA06", paste: "EA07", quit: "EA08", ); ================================================ FILE: src/sass/icons/_variables.scss.tpl ================================================ // DON'T MANUALLY EDIT THIS FILE; run `gulp build-icons` instead. $context-menu-icons-cachebust: "<%= (0|Math.random()*9e6).toString(36) %>"; $context-menu-icons: (<% _.each(glyphs, function(glyph) { %> <%= glyph.name %>: "<%= glyph.unicode[0].charCodeAt(0).toString(16).toUpperCase() %>",<% }); %> ); ================================================ FILE: src/sass/jquery.contextMenu.scss ================================================ /*! * jQuery contextMenu - Plugin for simple contextMenu handling * * Version: v@VERSION * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * * Copyright (c) 2011-@YEAR SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * * Date: @DATE */ @import "variables"; @import "icons/mixins"; @import "icons"; .context-menu-icon { @include base-context-menu-icon; &.context-menu-hover:before { color: $context-menu-icon-color-hover; } &.context-menu-disabled::before { color: $context-menu-text-color-disabled; } &.context-menu-icon-loading:before { animation: cm-spin 2s infinite; } &.context-menu-icon--fa { display: list-item; font-family: inherit; line-height: inherit; @include base-context-menu-icon(FontAwesome); &.context-menu-hover:before { color: $context-menu-icon-color-hover; } &.context-menu-disabled::before { color: $context-menu-text-color-disabled; } } &.context-menu-icon--fa5 { display: list-item; font-family: inherit; line-height: inherit; i, svg { color: $context-menu-icon-color; left: 0.5em; position: absolute; top: 0.3em; } &.context-menu-hover { > i, > svg { color: $context-menu-icon-color-hover; } } &.context-menu-disabled { i, svg { color: $context-menu-text-color-disabled; } } } } .context-menu-list { background: $context-menu-background-color; border: $context-menu-border-width $context-menu-border-style $context-menu-border-color; border-radius: $context-menu-border-radius; box-shadow: $context-menu-box-shadow; display: inline-block; font-family: $context-menu-font-family; font-size: $context-menu-font-size; list-style-type: none; margin: $context-menu-container-margin; max-width: $context-menu-max-width; min-width: $context-menu-min-width; padding: $context-menu-container-padding; position: absolute; } .context-menu-item { background-color: $context-menu-background-color; box-sizing: content-box; color: $context-menu-text-color; padding: $context-menu-item-padding; position: relative; user-select: none; } .context-menu-separator { border-bottom: $context-menu-separator-height $context-menu-separator-style $context-menu-separator-color; margin: $context-menu-separator-margin; padding: 0; } .context-menu-item > label > input, .context-menu-item > label > textarea { user-select: text; } .context-menu-item.context-menu-hover { background-color: $context-menu-item-color-hover; color: $context-menu-text-color-hover; cursor: pointer; } .context-menu-item.context-menu-disabled { background-color: $context-menu-item-color-disabled; color: $context-menu-text-color-disabled; cursor: default; } .context-menu-input.context-menu-hover { cursor: default; color: $context-menu-text-color; } .context-menu-submenu:after { content: ''; border-style: solid; border-width: .25em 0 .25em .25em; border-color: transparent transparent transparent $context-menu-submenu-arrow-color; height: 0; position: absolute; right: .5em; top: 50%; transform: translateY(-50%); width: 0; z-index: 1; } /** * Inputs */ .context-menu-item.context-menu-input { padding: .3em .6em; } /* vertically align inside labels */ .context-menu-input > label > * { vertical-align: top; } /* position checkboxes and radios as icons */ .context-menu-input > label > input[type="checkbox"], .context-menu-input > label > input[type="radio"] { margin-right: .4em; position: relative; top: .12em; } .context-menu-input > label { margin: 0; } .context-menu-input > label, .context-menu-input > label > input[type="text"], .context-menu-input > label > textarea, .context-menu-input > label > select { box-sizing: border-box; display: block; width: 100%; } .context-menu-input > label > textarea { height: 7em; } .context-menu-item > .context-menu-list { display: none; /* re-positioned by js */ right: -.3em; top: .3em; } .context-menu-item.context-menu-visible > .context-menu-list { display: block; } .context-menu-accesskey { text-decoration: underline; } ================================================ FILE: test/index.html ================================================ QUnit Test Suite ================================================ FILE: test/integration/custom-command.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Click custom comand menu item triggers menu callback': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.numberOfElements('.context-menu-root>li') .is(3, '3 context menu items are shown') .click('.context-menu-root li.labels') .assert.text('#msg').to.contain('clicked: label', 'contextMenu callback was triggered') .done(); }, 'Click custom comand menu item label triggers custom action - red': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li.labels .label1') .assert.text('#msg', 'clicked: label | text: label 1', 'custom action was triggered') .done(); }, 'Click custom comand menu item label triggers custom action - blue': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li.labels .label3') .assert.text('#msg', 'clicked: label | text: label 3', 'custom action was triggered') .done(); } }; ================================================ FILE: test/integration/disabled-callback.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-callback_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .click('.context-menu-root li:last-child') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Clicking on enabled item works': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:first-child') .assert.text('#msg', 'clicked: edit', 'Enabled menu item sets text') .done(); } }; ================================================ FILE: test/integration/disabled-changing.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(3, '3 context menu items are shown') .click('.context-menu-root li:nth-child(2)') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Toggle disabled item status': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:nth-child(3)') .assert.doesntExist('.context-menu-root .disabled', 'All menu items are enabled') .click('.context-menu-root li:nth-child(2)') .assert.text('#msg', 'clicked: cut', 'Enabled menu item sets text') .done(); }, 'Toggled status is saved after menu is closed': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:nth-child(3)') .assert.doesntExist('.context-menu-root .disabled', 'All menu items are enabled') .execute(helper.closeMenu, '.context-menu-one') .wait(100) .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.doesntExist('.context-menu-root .disabled', 'All menu items are still enabled') .done(); } }; ================================================ FILE: test/integration/disabled-menu.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Disabled trigger doesnt open context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .done(); }, 'Enabled trigger opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .click('#toggle-disabled') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .$('.context-menu-root') .assert.visible('Menu is present') .assert.exists('It opens context menu') .end() .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); }, 'Repeatedly disabled trigger doesnt open context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .click('#toggle-disabled') .assert.doesntExist('.context-menu-disabled', 'Context menu trigger is enambled') .click('#toggle-disabled') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled again') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .done(); } }; ================================================ FILE: test/integration/disabled.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .click('.context-menu-root li:last-child') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Clicking on enabled item works': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:first-child') .assert.text('#msg', 'clicked: edit', 'Enabled menu item sets text') .done(); } }; ================================================ FILE: test/integration/dynamic-create.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Dynamically created context menu is shown': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic-create.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .done(); } }; ================================================ FILE: test/integration/dynamic.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Dynamically created opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic.html') .click('#add-trigger') .waitForElement('.menu-injected') .execute(helper.rightClick, '.menu-injected') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') .done(); }, '3rd dynamically created also opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic.html') .click('#add-trigger') .click('#add-trigger') .click('#add-trigger') .wait(200) //.waitForElement('.menu-injected') .assert.numberOfElements('.menu-injected') .is(3, '3 DIVs are added') .execute(helper.rightClick, '.menu-injected:last-of-type') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') .done(); } }; ================================================ FILE: test/integration/html/accesskeys.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Accesskeys Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/accesskeys_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Accesskeys Example code Example HTML Demo: Accesskeys right click me Example code Example HTML ================================================ FILE: test/integration/html/async-create.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Create Context Menu (asynchronous) Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/callback.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Callback Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/callback_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Callback Example code Example HTML Demo: Callback right click me Example code Example HTML ================================================ FILE: test/integration/html/custom-command.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom command Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/custom-command_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom command Example code Example HTML Demo: Custom command right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-callback.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Callback Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-callback_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Callback Example code Example HTML Demo: Disabled Callback right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-changing.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled changing Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-changing_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled changing Example code Example HTML Demo: Disabled changing right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-menu.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled menu Example code Example HTML right click me Enable Menu Example code Example HTML ================================================ FILE: test/integration/html/disabled.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Example code Example HTML Demo: Disabled right click me Example code Example HTML ================================================ FILE: test/integration/html/dynamic-create.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Adding new Context Menu Triggers Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/dynamic.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Adding new Context Menu Triggers Example code Example HTML jQuery.contextMenu allows you to define a <menu> before the trigger elements are available. Button Example code Example HTML ================================================ FILE: test/integration/html/html5-import.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Importing HTML5 <menu type="context"> Example code Example HTML jQuery.contextMenu allows you to import HTML5's <menu> structures to use in older browsers. right click me Example code Example HTML <menu id="html5menu" style="display:none" class="showcase"> <command label="rotate" icon="edit" onclick="alert('rotate')"> <command label="resize" onclick="alert('resize')"> <command label="twitter" onclick="alert('twitter')"> <hr> <command label="facebook" onclick="alert('facebook')"> </menu> </menu> ================================================ FILE: test/integration/html/html5-polyfill-firefox8.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: HTML5 Polyfill (Firefox) Example code Example HTML right click me Example code Example HTML <menu id="html5firefox8" type="context" > <menuitem label="rotate" onclick="alert('rotate')" hint="I'm a hint"></menuitem> <menuitem label="resize" onclick="alert('resize')"></menuitem> <menuitem label="disabled" onclick="alert('disabled')" disabled></menuitem> <menu label="share"> <menuitem label="twitter" onclick="alert('twitter')"></menuitem> <menuitem label="facebook" onclick="alert('facebook')"></menuitem> <hr> <menuitem type="checkbox" label="(checkbox) yes or no?" onclick="alert('checkbox: ' + (this.checked ? 'yep!' : 'nope'))"></menuitem> <hr> <menuitem type="radio" label="(radio) yes" radiogroup="alpha" checked onclick="alert('radio: yes')"></menuitem> <menuitem type="radio" label="(radio) no" radiogroup="alpha" onclick="alert('radio: no')"></menuitem> </menu> </menu> ================================================ FILE: test/integration/html/html5-polyfill.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: HTML5 Polyfill Example code Example HTML jQuery.contextMenu allows you to import HTML5's <menu> structures to use in older browsers. right click me Example code Example HTML <menu id="html5polyfill" type="context" style="display:none"> <command label="rotate" onclick="alert('rotate')" icon="images/cut.png"> <command label="resize" onclick="alert('resize')" icon="images/door.png"> <menu label="share"> <command label="twitter" onclick="alert('twitter')" icon="images/page_white_copy.png"> <hr> <command label="facebook" onclick="alert('facebook')" icon="images/page_white_edit.png"> <hr> <label>foo bar<input type="text" name="foo"></label> </menu> </menu> foo bar ================================================ FILE: test/integration/html/input.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Input Commands Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/keeping-contextmenu-open.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Keeping the Menu visible Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/menu-title.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Menu Title Example CSS Example code Example HTML right click me right click me right click me Example CSS Example code Example HTML <span class="context-menu-one btn btn-neutral">right click me</span> <span class="context-menu-two btn btn-neutral">right click me</span> <span class="context-menu-three btn btn-neutral">right click me</span> ================================================ FILE: test/integration/html/on-dom-element.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Context Menu on DOM Element Example code Example HTML right click me 1 right click me 2 right click me 3 right click me 4 Example code Example HTML <ul id="the-node"> <li><span class="context-menu-one btn btn-neutral">right click me 1</span></li> <li><span class="context-menu-one btn btn-neutral">right click me 2</span></li> <li>right click me 3</li> <li>right click me 4</li> </ul> ================================================ FILE: test/integration/html/sub-menus.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Submenus Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/sub-menus_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Submenus Example code Example HTML Demo: Submenus right click me Example code Example HTML ================================================ FILE: test/integration/html/trigger-custom.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom Activated Context Menu Example code Example HTML press that button Button Example code Example HTML ================================================ FILE: test/integration/html/trigger-hover-autohide.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Hover Activated Context Menu With Autohide Example code Example HTML hover over me Example code Example HTML ================================================ FILE: test/integration/html/trigger-hover.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Hover Activated Context Menu Example code Example HTML hover over me Example code Example HTML ================================================ FILE: test/integration/html/trigger-left-click.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Left-Click Trigger Example code Example HTML left click me Example code Example HTML ================================================ FILE: test/integration/html/trigger-swipe.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Swipe Trigger Example code Example HTML This demo uses the (third party) TouchSwipe plugin. swype right Example code Example HTML ================================================ FILE: test/integration/input.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); var text1 = '.context-menu-root input[name="context-menu-input-name"]'; var textArea1 = '.context-menu-root textarea[name="context-menu-input-area1"]'; var textArea2 = '.context-menu-root textarea[name="context-menu-input-area2"]'; module.exports = { 'HTML5 input-based menu is shown correctly': function (test) { test .open('file://' + pwd + '/test/integration/html/input.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(14, '14 context menu items are shown') .assert.numberOfElements('.context-menu-root input') .is(6, '6 HTML input items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); }, 'HTML5 input-based menu stores state when closed': function (test) { test .open('file://' + pwd + '/test/integration/html/input.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .type(text1, 'lorem ipsum') .type(textArea1, 'test area with height') .type(textArea2, 'shots go off') .execute(helper.closeMenu, '.context-menu-one') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.val(text1, 'lorem ipsum', 'Input text should contain entered text') .assert.val(textArea1, 'test area with height', 'Text area 1 should contain entered text') .assert.val(textArea2, 'shots go off', 'Text area 2 should contain entered text') .done(); } }; ================================================ FILE: test/integration/keeping-contextmenu-open.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Ensure context menu is shown': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .done(); }, 'Close context menu after first menu item is clicked': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.visible('.context-menu-root li:first-child', 'Menu item is present') .click('.context-menu-root li:first-child') .assert.doesntExist('#context-menu-layer', 'It closes context menu') .done(); }, 'Keep context menu open after second menu item is clicked': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.visible('.context-menu-root', 'Menu is present') .click('.context-menu-root li:last-child') .assert.exists('#context-menu-layer', 'It closes context menu') .done(); } }; ================================================ FILE: test/integration/on-dom-element.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Right-click on multiple DOM elements': function (test) { test .open('file://' + pwd + '/test/integration/html/on-dom-element.html') .execute(helper.rightClick, '#the-node li:first-child') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') // right click on the other DOM element .execute(helper.rightClick, '#the-node li:nth-child(3)') .wait(100) // wait for the old menu to close and new to reopen .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It re-opens the same context menu') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure still only one context menu is open') .done(); } }; ================================================ FILE: test/integration/trigger-custom.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Mouse hover opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/trigger-custom.html') .click('#activate-menu') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .click('#context-menu-layer') .wait(100) .assert.doesntExist('#context-menu-layer', 'Click outside closes context menu') .done(); } }; ================================================ FILE: test/integration/trigger-left-click.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Left-click opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/trigger-left-click.html') .click('.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); } }; ================================================ FILE: test/integration/trigger-right-click.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Right-click opens context menu': function (test) { test .open('file://' + pwd + '/demo.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); } }; ================================================ FILE: test/specs/accesskeys.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test accesskeys', function() { it('should navigate to submenu 2 levels deep and see correct alert for charlie', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2') browser.click('span=charlie') assert.equal(browser.alertText(), 'clicked: fold2-key3'); browser.alertAccept(); }); it('Typing on keyboard triggers "edit" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('e'); assert.equal(browser.alertText(), 'clicked: edit'); browser.alertAccept(); }); it('Typing on keyboard triggers "cut" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('c'); assert.equal(browser.alertText(), 'clicked: cut'); browser.alertAccept(); }); it('Typing on keyboard triggers "copy" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('o'); assert.equal(browser.alertText(), 'clicked: copy'); browser.alertAccept(); }); it('Typing on keyboard triggers "paste" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('p'); assert.equal(browser.alertText(), 'clicked: paste'); browser.alertAccept(); }); }); ================================================ FILE: test/specs/aync-create.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test async create', function() { it('should render async created context menu', function () { browser.url('file://' + pwd + '/test/integration/html/async-create.html'); browser.rightClick('.context-menu-one'); browser.waitForExist('#context-menu-layer'); assert.equal(3, browser.elements('.context-menu-root li').value.length); }); }); ================================================ FILE: test/specs/callback.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test callback', function() { function openCallbackMenu() { browser.url('file://' + pwd + '/test/integration/html/callback.html'); browser.rightClick('.context-menu-one'); browser.waitForExist('#context-menu-layer'); assert.equal(true, browser.isVisible('.context-menu-root'), 'menu is visible'); } it('Ensure edit menu item triggers callback', function () { openCallbackMenu(); browser.leftClick('.context-menu-root li:nth-child(1)'); assert.equal('edit was clicked', browser.alertText()); browser.alertAccept(); assert.equal(false, browser.isVisible('£context-menu-layer'), 'menu is hidden'); }); it('Ensure cut menu item triggers global callback', function () { openCallbackMenu(); browser.leftClick('.context-menu-root li:nth-child(2)'); assert.equal('global: cut', browser.alertText()); browser.alertAccept(); assert.equal(false, browser.isVisible('£context-menu-layer'), 'menu is hidden'); }); }); ================================================ FILE: test/specs/submenu.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test submenus', function() { it('should navigate to submenu 2 levels deep and see correct alert for charlie', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2') browser.click('span=charlie') assert.equal(browser.alertText(), 'clicked: fold2-key3'); browser.alertAccept(); }); it('should navigate to submenu 2 levels deep and see first menu highlighted', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2'); var elements = browser.elements('.context-menu-hover'); assert.equal(2, elements.value.length); }); }); ================================================ FILE: test/unit/contextmenu.test.js ================================================ var menuOpenCounter = 0; var menuCloseCounter = 0; var itemSelectedCounter = 0; var itemSelectedStack = []; var menuRuntime = null; function testQUnit(name, itemClickEvent, triggerEvent) { QUnit.module(name, { afterEach: function(){ destroyContextMenuAndCleanup(); } }); // before each test function createContextMenu(items, classname) { if(typeof(classname) == 'undefined'){ classname = 'context-menu'; } var $fixture = $('#qunit-fixture'); // ensure `#qunit-fixture` exists when testing with karma runner if ($fixture.length === 0) { $('').appendTo("body"); $fixture = $('#qunit-fixture'); } $fixture.append("right click me!"); if(!items){ items = { copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }; } $.contextMenu({ selector: '.' + classname, events: { show: function(opt) { menuRuntime = opt; menuOpenCounter = menuOpenCounter + 1; }, hide: function() { menuCloseCounter = menuCloseCounter + 1; } }, callback: function(key, options) { itemSelectedCounter = itemSelectedCounter + 1; itemSelectedStack.push(key); }, items: items, itemClickEvent: itemClickEvent }); } // after each test function destroyContextMenuAndCleanup() { $.contextMenu('destroy'); // clean up `#qunit-fixture` when testing in karma runner var $fixture = $('#qunit-fixture'); if ($fixture.length) { $fixture.html(''); } // reset vars menuOpenCounter = 0; menuCloseCounter = 0; itemSelectedCounter = 0; itemSelectedStack = []; menuRuntime = null; } QUnit.test('$.contextMenu object exists', function(assert) { assert.ok($.contextMenu, '$.contextMenu plugin is loaded'); assert.notEqual($.contextMenu, undefined, '$.contextMenu is not undefined'); }); QUnit.test('open contextMenu', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); assert.equal(menuOpenCounter, 1, 'contextMenu was opened once'); }); QUnit.test('open contextMenu at 0,0', function(assert) { createContextMenu(); $(".context-menu").contextMenu({x: 0, y: 0}); assert.equal(menuOpenCounter, 1, 'contextMenu was opened once'); }); QUnit.test('close contextMenu', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); assert.equal(menuCloseCounter, 1, 'contextMenu was closed once'); }); QUnit.test('navigate contextMenu items', function(assert) { createContextMenu(); var itemWasFocused = 0; var itemWasBlurred = 0; // listen to focus and blur events $(document.body) .on("contextmenu:focus", ".context-menu-item", function(e) { itemWasFocused = itemWasFocused + 1; }) .on("contextmenu:blur", ".context-menu-item", function(e) { itemWasBlurred = itemWasBlurred + 1; }); $(".context-menu").contextMenu(); menuRuntime.$menu.trigger('nextcommand'); // triggers contextmenu:focus assert.equal(itemWasFocused, 1, 'first menu item was focused once'); itemWasFocused = 0; menuRuntime.$menu.trigger('nextcommand'); // triggers contextmenu:blur & contextmenu:focus assert.equal(itemWasFocused, 1, 'first menu item was blurred'); assert.equal(itemWasBlurred, 1, 'second menu item was focused'); }); QUnit.test('activate contextMenu item', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); menuRuntime.$menu.trigger('nextcommand'); menuRuntime.$selected.trigger(triggerEvent); assert.equal(itemSelectedCounter, 1, 'selected menu item was clicked once'); }); QUnit.test('do not open destroyed context menu', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }); createContextMenu({ copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }, 'context-menu-two'); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); $(".context-menu-two").contextMenu(); $(".context-menu-two").contextMenu('hide'); assert.equal(menuOpenCounter, 2, 'contextMenu was opened twice'); $(".context-menu-two").contextMenu('destroy'); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); $(".context-menu-two").contextMenu(); $(".context-menu-two").contextMenu('hide'); assert.equal(menuOpenCounter, 3, 'destroyed contextMenu was not opened'); }); QUnit.test('do not open context menu with no visible items', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'copy', visible: function(){return false;}}, paste: {name: 'Paste', icon: 'paste', visible: function(){return false;}} }); $(".context-menu").contextMenu(); assert.equal($('.context-menu-item').is(':visible'), false, 'no menu items visible'); }); QUnit.test('visible function should only trigger once', function(assert) { var visibleTriggered = 0; createContextMenu({ copy: {name: 'Copy', icon: 'copy', visible: function(){visibleTriggered++; return true;}}, paste: {name: 'Paste', icon: 'paste', visible: function(){return false;}} }); $(".context-menu").contextMenu(); assert.equal(visibleTriggered, 1, 'selected menu wat not opened'); }); QUnit.test('items in seconds submenu to not override callbacks', function (assert) { var firstCallback = false, firstSubCallback = false, secondSubCallback = false; createContextMenu({ firstitem: { name: 'firstitem', icon: 'copy', callback : function(){ firstCallback = true; } }, firstsubmenu: { name: 'Copy', icon: 'copy', items: { firstitem : { name : "firstitem", icon : "copy", callback : function(){ firstSubCallback = true; } } } }, secondsubmenu: { name: 'Copy', icon: 'copy', items: { firstitem : { name : "firstitem", icon : "copy", callback : function(){ secondSubCallback = true; } } } } }); $('.context-menu-item').first().trigger(triggerEvent); $('.context-menu-submenu .context-menu-item').each(function(i,e){ $(e).trigger(triggerEvent) }); assert.equal(firstCallback, 1); assert.equal(firstSubCallback, 1); assert.equal(secondSubCallback, 1); }); QUnit.test('font-awesome creates icon elements', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'fas fa-beer'} }); $(".context-menu").contextMenu(); assert.equal($('i.fas.fa-beer').length, 1, 'FontAwesome tag was not created'); }); } testQUnit('contextMenu events', '', 'mouseup'); testQUnit('contextMenu events - click handler', 'click', 'click'); ================================================ FILE: wdio.conf.js ================================================ exports.config = { // // ================= // Service Providers // ================= // WebdriverIO supports Sauce Labs, Browserstack, and Testing Bot (other cloud providers // should work too though). These services define specific user and key (or access key) // values you need to put in here in order to connect to these services. // user: process.env.SAUCE_USERNAME, key: process.env.SAUCE_ACCESS_KEY, // // ================== // Specify Test Files // ================== // Define which test specs should run. The pattern is relative to the directory // from which `wdio` was called. Notice that, if you are calling `wdio` from an // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working // directory is where your package.json resides, so `wdio` will be called from there. // specs: [ './test/specs/**/*.js' ], // Patterns to exclude. exclude: [ // 'path/to/excluded/files' ], // // ============ // Capabilities // ============ // Define your capabilities here. WebdriverIO can run multiple capabilities at the same // time. Depending on the number of capabilities, WebdriverIO launches several test // sessions. Within your capabilities you can overwrite the spec and exclude options in // order to group specific specs to a specific capability. // // First, you can define how many instances should be started at the same time. Let's // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec // files and you set maxInstances to 10, all spec files will get tested at the same time // and 30 processes will get spawned. The property handles how many capabilities // from the same test should run tests. // maxInstances: 10, // // If you have trouble getting all important capabilities together, check out the // Sauce Labs platform configurator - a great tool to configure your capabilities: // https://docs.saucelabs.com/reference/platforms-configurator // capabilities: [{ // maxInstances can get overwritten per capability. So if you have an in-house Selenium // grid with only 5 firefox instance available you can make sure that not more than // 5 instance gets started at a time. maxInstances: 5, // browserName: 'chrome' }], // // =================== // Test Configurations // =================== // Define all options that are relevant for the WebdriverIO instance here // // By default WebdriverIO commands are executed in a synchronous way using // the wdio-sync package. If you still want to run your tests in an async way // e.g. using promises you can set the sync option to false. sync: true, // // Level of logging verbosity: silent | verbose | command | data | result | error logLevel: 'error', // // Enables colors for log output. coloredLogs: true, // // Saves a screenshot to a given path if a command fails. screenshotPath: './errorShots/', // // Set a base URL in order to shorten url command calls. If your url parameter starts // with "/", then the base url gets prepended. baseUrl: 'http://localhost', // // Default timeout for all waitFor* commands. waitforTimeout: 10000, // // Default timeout in milliseconds for request // if Selenium Grid doesn't send response connectionRetryTimeout: 90000, // // Default request retries count connectionRetryCount: 3, // // Initialize the browser instance with a WebdriverIO plugin. The object should have the // plugin name as key and the desired plugin options as properties. Make sure you have // the plugin installed before running any tests. The following plugins are currently // available: // WebdriverCSS: https://github.com/webdriverio/webdrivercss // WebdriverRTC: https://github.com/webdriverio/webdriverrtc // Browserevent: https://github.com/webdriverio/browserevent // plugins: { // webdrivercss: { // screenshotRoot: 'my-shots', // failedComparisonsRoot: 'diffs', // misMatchTolerance: 0.05, // screenWidth: [320,480,640,1024] // }, // webdriverrtc: {}, // browserevent: {} // }, // // Test runner services // Services take over a specific job you don't want to take care of. They enhance // your test setup with almost no effort. Unlike plugins, they don't add new // commands. Instead, they hook themselves up into the test process. // services: ['selenium-standalone'], services: ['selenium-standalone', 'sauce'], // services: ['sauce'], // // Framework you want to run your specs with. // The following are supported: Mocha, Jasmine, and Cucumber // see also: http://webdriver.io/guide/testrunner/frameworks.html // // Make sure you have the wdio adapter package for the specific framework installed // before running any tests. framework: 'mocha', // // Test reporter for stdout. // The only one supported by default is 'dot' // see also: http://webdriver.io/guide/testrunner/reporters.html reporters: ['dot'], // // Options to be passed to Mocha. // See the full list at http://mochajs.org/ mochaOpts: { ui: 'bdd' }, // // ===== // Hooks // ===== // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance // it and to build services around it. You can either apply a single function or an array of // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got // resolved to continue. // // Gets executed once before all workers get launched. // onPrepare: function (config, capabilities) { // }, // // Gets executed before test execution begins. At this point you can access all global // variables, such as `browser`. It is the perfect place to define custom commands. // before: function (capabilities, specs) { // }, // // Hook that gets executed before the suite starts // beforeSuite: function (suite) { // }, // // Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling // beforeEach in Mocha) // beforeHook: function () { // }, // // Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling // afterEach in Mocha) // afterHook: function () { // }, // // Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts. // beforeTest: function (test) { // }, // // Runs before a WebdriverIO command gets executed. // beforeCommand: function (commandName, args) { // }, // // Runs after a WebdriverIO command gets executed // afterCommand: function (commandName, args, result, error) { // }, // // Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts. // afterTest: function (test) { // }, // // Hook that gets executed after the suite has ended // afterSuite: function (suite) { // }, // // Gets executed after all tests are done. You still have access to all global variables from // the test. // after: function (result, capabilities, specs) { // }, // // Gets executed after all workers got shut down and the process is about to exit. It is not // possible to defer the end of the process using a promise. // onComplete: function(exitCode) { // } }
, , and of course . * Everything else will be imported as an html node, which is not interfaced with contextMenu. */ // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#concept-command switch (nodeName) { // http://www.whatwg.org/specs/web-apps/current-work/multipage/interactive-elements.html#the-menu-element case 'menu': item = {name: $node.attr('label'), items: {}}; counter = menuChildren(item.items, $node.children(), counter); break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-a-element-to-define-a-command case 'a': // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-button-element-to-define-a-command case 'button': item = { name: $node.text(), disabled: !!$node.attr('disabled'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; // http://www.whatwg.org/specs/web-apps/current-work/multipage/commands.html#using-the-command-element-to-define-a-command case 'menuitem': case 'command': switch ($node.attr('type')) { case undefined: case 'command': case 'menuitem': item = { name: $node.attr('label'), disabled: !!$node.attr('disabled'), icon: $node.attr('icon'), callback: (function () { return function (itemKey, opt, ev) { if ($node.get(0).onclick !== null) { $node.get(0).click(); } else { opt.callback(itemKey, opt, ev); } }; })() }; break; case 'checkbox': item = { type: 'checkbox', disabled: !!$node.attr('disabled'), name: $node.attr('label'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', disabled: !!$node.attr('disabled'), name: $node.attr('label'), radio: $node.attr('radiogroup'), value: $node.attr('id'), selected: !!$node.attr('checked') }; break; default: item = undefined; } break; case 'hr': item = '-------'; break; case 'input': switch ($node.attr('type')) { case 'text': item = { type: 'text', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'checkbox': item = { type: 'checkbox', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: !!$node.attr('checked') }; break; case 'radio': item = { type: 'radio', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), radio: !!$node.attr('name'), value: $node.val(), selected: !!$node.attr('checked') }; break; default: item = undefined; break; } break; case 'select': item = { type: 'select', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), selected: $node.val(), options: {} }; $node.children().each(function () { item.options[this.value] = $(this).text(); }); break; case 'textarea': item = { type: 'textarea', name: label || inputLabel(node), disabled: !!$node.attr('disabled'), value: $node.val() }; break; case 'label': break; default: item = {type: 'html', html: $node.clone(true)}; break; } if (item) { counter++; items['key' + counter] = item; } }); return counter; } // convert html5 menu $.contextMenu.fromMenu = function (element) { var $this = $(element), items = {}; menuChildren(items, $this.children()); return items; }; // make defaults accessible $.contextMenu.defaults = defaults; $.contextMenu.types = types; // export internal functions - undocumented, for hacking only! $.contextMenu.handle = handle; $.contextMenu.op = op; $.contextMenu.menus = menus; }); ================================================ FILE: src/jquery.ui.position.js ================================================ /*! jQuery UI - v1.12.0 - 2016-07-15 * http://jqueryui.com * Includes: position.js * Copyright jQuery Foundation and other contributors; Licensed MIT */ (function( factory ) { if ( typeof define === "function" && define.amd ) { // AMD. Register as an anonymous module. define([ "jquery" ], factory ); } else { // Browser globals factory( jQuery ); } }(function( $ ) { $.ui = $.ui || {}; var version = $.ui.version = "1.12.0"; /*! * jQuery UI Position 1.12.0 * http://jqueryui.com * * Copyright jQuery Foundation and other contributors * Released under the MIT license. * http://jquery.org/license * * http://api.jqueryui.com/position/ */ //>>label: Position //>>group: Core //>>description: Positions elements relative to other elements. //>>docs: http://api.jqueryui.com/position/ //>>demos: http://jqueryui.com/position/ ( function() { var cachedScrollbarWidth, supportsOffsetFractions, max = Math.max, abs = Math.abs, round = Math.round, rhorizontal = /left|center|right/, rvertical = /top|center|bottom/, roffset = /[\+\-]\d+(\.[\d]+)?%?/, rposition = /^\w+/, rpercent = /%$/, _position = $.fn.position; // Support: IE <=9 only supportsOffsetFractions = function() { var element = $( "" ) .css( "position", "absolute" ) .appendTo( "body" ) .offset( { top: 1.5, left: 1.5 } ), support = element.offset().top === 1.5; element.remove(); supportsOffsetFractions = function() { return support; }; return support; }; function getOffsets( offsets, width, height ) { return [ parseFloat( offsets[ 0 ] ) * ( rpercent.test( offsets[ 0 ] ) ? width / 100 : 1 ), parseFloat( offsets[ 1 ] ) * ( rpercent.test( offsets[ 1 ] ) ? height / 100 : 1 ) ]; } function parseCss( element, property ) { return parseInt( $.css( element, property ), 10 ) || 0; } function getDimensions( elem ) { var raw = elem[ 0 ]; if ( raw.nodeType === 9 ) { return { width: elem.width(), height: elem.height(), offset: { top: 0, left: 0 } }; } if ( $.isWindow( raw ) ) { return { width: elem.width(), height: elem.height(), offset: { top: elem.scrollTop(), left: elem.scrollLeft() } }; } if ( raw.preventDefault ) { return { width: 0, height: 0, offset: { top: raw.pageY, left: raw.pageX } }; } return { width: elem.outerWidth(), height: elem.outerHeight(), offset: elem.offset() }; } $.position = { scrollbarWidth: function() { if ( cachedScrollbarWidth !== undefined ) { return cachedScrollbarWidth; } var w1, w2, div = $( "" + "" ), innerDiv = div.children()[ 0 ]; $( "body" ).append( div ); w1 = innerDiv.offsetWidth; div.css( "overflow", "scroll" ); w2 = innerDiv.offsetWidth; if ( w1 === w2 ) { w2 = div[ 0 ].clientWidth; } div.remove(); return ( cachedScrollbarWidth = w1 - w2 ); }, getScrollInfo: function( within ) { var overflowX = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-x" ), overflowY = within.isWindow || within.isDocument ? "" : within.element.css( "overflow-y" ), hasOverflowX = overflowX === "scroll" || ( overflowX === "auto" && within.width < within.element[ 0 ].scrollWidth ), hasOverflowY = overflowY === "scroll" || ( overflowY === "auto" && within.height < within.element[ 0 ].scrollHeight ); return { width: hasOverflowY ? $.position.scrollbarWidth() : 0, height: hasOverflowX ? $.position.scrollbarWidth() : 0 }; }, getWithinInfo: function( element ) { var withinElement = $( element || window ), isWindow = $.isWindow( withinElement[ 0 ] ), isDocument = !!withinElement[ 0 ] && withinElement[ 0 ].nodeType === 9, hasOffset = !isWindow && !isDocument; return { element: withinElement, isWindow: isWindow, isDocument: isDocument, offset: hasOffset ? $( element ).offset() : { left: 0, top: 0 }, scrollLeft: withinElement.scrollLeft(), scrollTop: withinElement.scrollTop(), width: withinElement.outerWidth(), height: withinElement.outerHeight() }; } }; $.fn.position = function( options ) { if ( !options || !options.of ) { return _position.apply( this, arguments ); } // Make a copy, we don't want to modify arguments options = $.extend( {}, options ); var atOffset, targetWidth, targetHeight, targetOffset, basePosition, dimensions, target = $( options.of ), within = $.position.getWithinInfo( options.within ), scrollInfo = $.position.getScrollInfo( within ), collision = ( options.collision || "flip" ).split( " " ), offsets = {}; dimensions = getDimensions( target ); if ( target[ 0 ].preventDefault ) { // Force left top to allow flipping options.at = "left top"; } targetWidth = dimensions.width; targetHeight = dimensions.height; targetOffset = dimensions.offset; // Clone to reuse original targetOffset later basePosition = $.extend( {}, targetOffset ); // Force my and at to have valid horizontal and vertical positions // if a value is missing or invalid, it will be converted to center $.each( [ "my", "at" ], function() { var pos = ( options[ this ] || "" ).split( " " ), horizontalOffset, verticalOffset; if ( pos.length === 1 ) { pos = rhorizontal.test( pos[ 0 ] ) ? pos.concat( [ "center" ] ) : rvertical.test( pos[ 0 ] ) ? [ "center" ].concat( pos ) : [ "center", "center" ]; } pos[ 0 ] = rhorizontal.test( pos[ 0 ] ) ? pos[ 0 ] : "center"; pos[ 1 ] = rvertical.test( pos[ 1 ] ) ? pos[ 1 ] : "center"; // Calculate offsets horizontalOffset = roffset.exec( pos[ 0 ] ); verticalOffset = roffset.exec( pos[ 1 ] ); offsets[ this ] = [ horizontalOffset ? horizontalOffset[ 0 ] : 0, verticalOffset ? verticalOffset[ 0 ] : 0 ]; // Reduce to just the positions without the offsets options[ this ] = [ rposition.exec( pos[ 0 ] )[ 0 ], rposition.exec( pos[ 1 ] )[ 0 ] ]; } ); // Normalize collision option if ( collision.length === 1 ) { collision[ 1 ] = collision[ 0 ]; } if ( options.at[ 0 ] === "right" ) { basePosition.left += targetWidth; } else if ( options.at[ 0 ] === "center" ) { basePosition.left += targetWidth / 2; } if ( options.at[ 1 ] === "bottom" ) { basePosition.top += targetHeight; } else if ( options.at[ 1 ] === "center" ) { basePosition.top += targetHeight / 2; } atOffset = getOffsets( offsets.at, targetWidth, targetHeight ); basePosition.left += atOffset[ 0 ]; basePosition.top += atOffset[ 1 ]; return this.each( function() { var collisionPosition, using, elem = $( this ), elemWidth = elem.outerWidth(), elemHeight = elem.outerHeight(), marginLeft = parseCss( this, "marginLeft" ), marginTop = parseCss( this, "marginTop" ), collisionWidth = elemWidth + marginLeft + parseCss( this, "marginRight" ) + scrollInfo.width, collisionHeight = elemHeight + marginTop + parseCss( this, "marginBottom" ) + scrollInfo.height, position = $.extend( {}, basePosition ), myOffset = getOffsets( offsets.my, elem.outerWidth(), elem.outerHeight() ); if ( options.my[ 0 ] === "right" ) { position.left -= elemWidth; } else if ( options.my[ 0 ] === "center" ) { position.left -= elemWidth / 2; } if ( options.my[ 1 ] === "bottom" ) { position.top -= elemHeight; } else if ( options.my[ 1 ] === "center" ) { position.top -= elemHeight / 2; } position.left += myOffset[ 0 ]; position.top += myOffset[ 1 ]; // If the browser doesn't support fractions, then round for consistent results if ( !supportsOffsetFractions() ) { position.left = round( position.left ); position.top = round( position.top ); } collisionPosition = { marginLeft: marginLeft, marginTop: marginTop }; $.each( [ "left", "top" ], function( i, dir ) { if ( $.ui.position[ collision[ i ] ] ) { $.ui.position[ collision[ i ] ][ dir ]( position, { targetWidth: targetWidth, targetHeight: targetHeight, elemWidth: elemWidth, elemHeight: elemHeight, collisionPosition: collisionPosition, collisionWidth: collisionWidth, collisionHeight: collisionHeight, offset: [ atOffset[ 0 ] + myOffset[ 0 ], atOffset [ 1 ] + myOffset[ 1 ] ], my: options.my, at: options.at, within: within, elem: elem } ); } } ); if ( options.using ) { // Adds feedback as second argument to using callback, if present using = function( props ) { var left = targetOffset.left - position.left, right = left + targetWidth - elemWidth, top = targetOffset.top - position.top, bottom = top + targetHeight - elemHeight, feedback = { target: { element: target, left: targetOffset.left, top: targetOffset.top, width: targetWidth, height: targetHeight }, element: { element: elem, left: position.left, top: position.top, width: elemWidth, height: elemHeight }, horizontal: right < 0 ? "left" : left > 0 ? "right" : "center", vertical: bottom < 0 ? "top" : top > 0 ? "bottom" : "middle" }; if ( targetWidth < elemWidth && abs( left + right ) < targetWidth ) { feedback.horizontal = "center"; } if ( targetHeight < elemHeight && abs( top + bottom ) < targetHeight ) { feedback.vertical = "middle"; } if ( max( abs( left ), abs( right ) ) > max( abs( top ), abs( bottom ) ) ) { feedback.important = "horizontal"; } else { feedback.important = "vertical"; } options.using.call( this, props, feedback ); }; } elem.offset( $.extend( position, { using: using } ) ); } ); }; $.ui.position = { fit: { left: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollLeft : within.offset.left, outerWidth = within.width, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = withinOffset - collisionPosLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - withinOffset, newOverRight; // Element is wider than within if ( data.collisionWidth > outerWidth ) { // Element is initially over the left side of within if ( overLeft > 0 && overRight <= 0 ) { newOverRight = position.left + overLeft + data.collisionWidth - outerWidth - withinOffset; position.left += overLeft - newOverRight; // Element is initially over right side of within } else if ( overRight > 0 && overLeft <= 0 ) { position.left = withinOffset; // Element is initially over both left and right sides of within } else { if ( overLeft > overRight ) { position.left = withinOffset + outerWidth - data.collisionWidth; } else { position.left = withinOffset; } } // Too far left -> align with left edge } else if ( overLeft > 0 ) { position.left += overLeft; // Too far right -> align with right edge } else if ( overRight > 0 ) { position.left -= overRight; // Adjust based on position and margin } else { position.left = max( position.left - collisionPosLeft, position.left ); } }, top: function( position, data ) { var within = data.within, withinOffset = within.isWindow ? within.scrollTop : within.offset.top, outerHeight = data.within.height, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = withinOffset - collisionPosTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - withinOffset, newOverBottom; // Element is taller than within if ( data.collisionHeight > outerHeight ) { // Element is initially over the top of within if ( overTop > 0 && overBottom <= 0 ) { newOverBottom = position.top + overTop + data.collisionHeight - outerHeight - withinOffset; position.top += overTop - newOverBottom; // Element is initially over bottom of within } else if ( overBottom > 0 && overTop <= 0 ) { position.top = withinOffset; // Element is initially over both top and bottom of within } else { if ( overTop > overBottom ) { position.top = withinOffset + outerHeight - data.collisionHeight; } else { position.top = withinOffset; } } // Too far up -> align with top } else if ( overTop > 0 ) { position.top += overTop; // Too far down -> align with bottom edge } else if ( overBottom > 0 ) { position.top -= overBottom; // Adjust based on position and margin } else { position.top = max( position.top - collisionPosTop, position.top ); } } }, flip: { left: function( position, data ) { var within = data.within, withinOffset = within.offset.left + within.scrollLeft, outerWidth = within.width, offsetLeft = within.isWindow ? within.scrollLeft : within.offset.left, collisionPosLeft = position.left - data.collisionPosition.marginLeft, overLeft = collisionPosLeft - offsetLeft, overRight = collisionPosLeft + data.collisionWidth - outerWidth - offsetLeft, myOffset = data.my[ 0 ] === "left" ? -data.elemWidth : data.my[ 0 ] === "right" ? data.elemWidth : 0, atOffset = data.at[ 0 ] === "left" ? data.targetWidth : data.at[ 0 ] === "right" ? -data.targetWidth : 0, offset = -2 * data.offset[ 0 ], newOverRight, newOverLeft; if ( overLeft < 0 ) { newOverRight = position.left + myOffset + atOffset + offset + data.collisionWidth - outerWidth - withinOffset; if ( newOverRight < 0 || newOverRight < abs( overLeft ) ) { position.left += myOffset + atOffset + offset; } } else if ( overRight > 0 ) { newOverLeft = position.left - data.collisionPosition.marginLeft + myOffset + atOffset + offset - offsetLeft; if ( newOverLeft > 0 || abs( newOverLeft ) < overRight ) { position.left += myOffset + atOffset + offset; } } }, top: function( position, data ) { var within = data.within, withinOffset = within.offset.top + within.scrollTop, outerHeight = within.height, offsetTop = within.isWindow ? within.scrollTop : within.offset.top, collisionPosTop = position.top - data.collisionPosition.marginTop, overTop = collisionPosTop - offsetTop, overBottom = collisionPosTop + data.collisionHeight - outerHeight - offsetTop, top = data.my[ 1 ] === "top", myOffset = top ? -data.elemHeight : data.my[ 1 ] === "bottom" ? data.elemHeight : 0, atOffset = data.at[ 1 ] === "top" ? data.targetHeight : data.at[ 1 ] === "bottom" ? -data.targetHeight : 0, offset = -2 * data.offset[ 1 ], newOverTop, newOverBottom; if ( overTop < 0 ) { newOverBottom = position.top + myOffset + atOffset + offset + data.collisionHeight - outerHeight - withinOffset; if ( newOverBottom < 0 || newOverBottom < abs( overTop ) ) { position.top += myOffset + atOffset + offset; } } else if ( overBottom > 0 ) { newOverTop = position.top - data.collisionPosition.marginTop + myOffset + atOffset + offset - offsetTop; if ( newOverTop > 0 || abs( newOverTop ) < overBottom ) { position.top += myOffset + atOffset + offset; } } } }, flipfit: { left: function() { $.ui.position.flip.left.apply( this, arguments ); $.ui.position.fit.left.apply( this, arguments ); }, top: function() { $.ui.position.flip.top.apply( this, arguments ); $.ui.position.fit.top.apply( this, arguments ); } } }; } )(); var position = $.ui.position; })); ================================================ FILE: src/sass/_icons.scss ================================================ .context-menu-icon-add { @include context-menu-item-icon(add); } .context-menu-icon-copy { @include context-menu-item-icon(copy); } .context-menu-icon-cut { @include context-menu-item-icon(cut); } .context-menu-icon-delete { @include context-menu-item-icon(delete); } .context-menu-icon-edit { @include context-menu-item-icon(edit); } .context-menu-icon-loading { @include context-menu-item-icon(loading); } .context-menu-icon-paste { @include context-menu-item-icon(paste); } .context-menu-icon-quit { @include context-menu-item-icon(quit); } ================================================ FILE: src/sass/_variables.scss ================================================ // Container Sizing $context-menu-min-width: 13em !default; $context-menu-max-width: $context-menu-min-width * 2 !default; $context-menu-container-padding: .25em 0 !default; $context-menu-container-margin: .3em !default; $context-menu-border-radius: .2em !default; // Container Font $context-menu-font-family: inherit !default; $context-menu-font-size: inherit !default; // Container Color $context-menu-background-color: #FFF !default; $context-menu-border-width: 1px !default; $context-menu-border-style: solid !default; $context-menu-border-color: #bebebe !default; $context-menu-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.5) !default; // Item Sizing $context-menu-padding-vertical: .2em !default; $context-menu-padding-horizontal: 2em !default; // Item Default $context-menu-item-padding: $context-menu-padding-vertical $context-menu-padding-horizontal !default; $context-menu-text-color: #2F2F2F !default; $context-menu-item-color: $context-menu-background-color !default; // Item Hover $context-menu-item-color-hover: #2980B9 !default; $context-menu-text-color-hover: #FFF !default; // Item Disabled $context-menu-item-color-disabled: $context-menu-background-color !default; $context-menu-text-color-disabled: lighten($context-menu-text-color, 55) !default; // Submenu $context-menu-submenu-arrow-color: $context-menu-text-color !default; // Separator $context-menu-separator-height: 1px !default; $context-menu-separator-style: solid !default; $context-menu-separator-color: darken($context-menu-background-color, 10) !default; $context-menu-separator-margin: .35em 0 !default; // Icons $context-menu-icon-font-path: 'font/' !default; $context-menu-icon-font-name: 'context-menu-icons' !default; $context-menu-icon-size: 1em !default; $context-menu-icon-color: #2980B9 !default; $context-menu-icon-color-hover: $context-menu-text-color-hover !default; @keyframes cm-spin { 0% { -webkit-transform: translateY(-50%) rotate(0deg); transform: translateY(-50%) rotate(0deg) } 100% { -webkit-transform: translateY(-50%) rotate(359deg); transform: translateY(-50%) rotate(359deg) } } ================================================ FILE: src/sass/icons/_icon_classes.scss.tpl ================================================ <% _.each(glyphs, function(glyph) { %> .<%= className %>-<%= glyph.name %> { @include <%= mixinName %>(<%= glyph.name %>); }<% }); %> ================================================ FILE: src/sass/icons/_mixins.scss ================================================ @import "variables"; $context-menu-icons: () !default; @function context-menu-font-url($url) { @if function-exists(asset-url) { @return asset-url($url); } @else if function-exists(font-url) { @return font-url($url); } @else { @return url($url); } } @font-face { font-family: '#{$context-menu-icon-font-name}'; src: context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.eot?#{$context-menu-icons-cachebust}'); src: context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.eot?#{$context-menu-icons-cachebust}#iefix') format('embedded-opentype'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.woff2?#{$context-menu-icons-cachebust}') format('woff2'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.woff?#{$context-menu-icons-cachebust}') format('woff'), context-menu-font-url('#{$context-menu-icon-font-path}#{$context-menu-icon-font-name}.ttf?#{$context-menu-icons-cachebust}') format('truetype'); font-weight: normal; font-style: normal; } @mixin context-menu-icon-without-mediaquery($position: before, $icon: false, $icons-font: $context-menu-icon-font-name) { @if not map-has-key($context-menu-icons, $icon) { @error "Invalid icon `#{$icon}`."; } @if $position == both { $position: 'before, &:after'; } // Either a :before or :after pseudo-element, or both, defaulting to :before &:#{$position} { @if $icon { // A particular icon has been specified content: #{"\"\\"}#{map-get($context-menu-icons, $icon) + "\""}; } // Include any extra rules supplied for the pseudo-element @content; } } @mixin context-menu-icon-screen-only($position: before, $icon: false, $icons-font: $context-menu-icon-font-name) { @media screen { @include context-menu-icon-without-mediaquery($position, $icon, $icons-font) { @content; } } } // For adding font icons to elements using CSS pseudo-elements // http://jaydenseric.com/blog/fun-with-sass-and-font-icons @mixin context-menu-icon($position: before, $icon: false, $icons-font: $context-menu-icon-font-name, $screen-only: false) { @if $screen-only { @include context-menu-icon-screen-only($position, $icon, $icons-font) { @content; } } @else { @include context-menu-icon-without-mediaquery($position, $icon, $icons-font) { @content; } } } @mixin base-context-menu-icon($icon-font: "context-menu-icons") { &::before { color: $context-menu-icon-color; font-family: $icon-font; font-style: normal; font-weight: normal; font-size: $context-menu-icon-size; left: 0; line-height: 1; position: absolute; text-align: center; top: 50%; transform: translateY(-50%); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; width: 2em; } } @mixin context-menu-item-icon($icon: false) { @include context-menu-icon(before, $icon); } ================================================ FILE: src/sass/icons/_variables.scss ================================================ // DON'T MANUALLY EDIT THIS FILE; run `gulp build-icons` instead. $context-menu-icons-cachebust: "2dq4x"; $context-menu-icons: ( add: "EA01", copy: "EA02", cut: "EA03", delete: "EA04", edit: "EA05", loading: "EA06", paste: "EA07", quit: "EA08", ); ================================================ FILE: src/sass/icons/_variables.scss.tpl ================================================ // DON'T MANUALLY EDIT THIS FILE; run `gulp build-icons` instead. $context-menu-icons-cachebust: "<%= (0|Math.random()*9e6).toString(36) %>"; $context-menu-icons: (<% _.each(glyphs, function(glyph) { %> <%= glyph.name %>: "<%= glyph.unicode[0].charCodeAt(0).toString(16).toUpperCase() %>",<% }); %> ); ================================================ FILE: src/sass/jquery.contextMenu.scss ================================================ /*! * jQuery contextMenu - Plugin for simple contextMenu handling * * Version: v@VERSION * * Authors: Björn Brala (SWIS.nl), Rodney Rehm, Addy Osmani (patches for FF) * Web: http://swisnl.github.io/jQuery-contextMenu/ * * Copyright (c) 2011-@YEAR SWIS BV and contributors * * Licensed under * MIT License http://www.opensource.org/licenses/mit-license * * Date: @DATE */ @import "variables"; @import "icons/mixins"; @import "icons"; .context-menu-icon { @include base-context-menu-icon; &.context-menu-hover:before { color: $context-menu-icon-color-hover; } &.context-menu-disabled::before { color: $context-menu-text-color-disabled; } &.context-menu-icon-loading:before { animation: cm-spin 2s infinite; } &.context-menu-icon--fa { display: list-item; font-family: inherit; line-height: inherit; @include base-context-menu-icon(FontAwesome); &.context-menu-hover:before { color: $context-menu-icon-color-hover; } &.context-menu-disabled::before { color: $context-menu-text-color-disabled; } } &.context-menu-icon--fa5 { display: list-item; font-family: inherit; line-height: inherit; i, svg { color: $context-menu-icon-color; left: 0.5em; position: absolute; top: 0.3em; } &.context-menu-hover { > i, > svg { color: $context-menu-icon-color-hover; } } &.context-menu-disabled { i, svg { color: $context-menu-text-color-disabled; } } } } .context-menu-list { background: $context-menu-background-color; border: $context-menu-border-width $context-menu-border-style $context-menu-border-color; border-radius: $context-menu-border-radius; box-shadow: $context-menu-box-shadow; display: inline-block; font-family: $context-menu-font-family; font-size: $context-menu-font-size; list-style-type: none; margin: $context-menu-container-margin; max-width: $context-menu-max-width; min-width: $context-menu-min-width; padding: $context-menu-container-padding; position: absolute; } .context-menu-item { background-color: $context-menu-background-color; box-sizing: content-box; color: $context-menu-text-color; padding: $context-menu-item-padding; position: relative; user-select: none; } .context-menu-separator { border-bottom: $context-menu-separator-height $context-menu-separator-style $context-menu-separator-color; margin: $context-menu-separator-margin; padding: 0; } .context-menu-item > label > input, .context-menu-item > label > textarea { user-select: text; } .context-menu-item.context-menu-hover { background-color: $context-menu-item-color-hover; color: $context-menu-text-color-hover; cursor: pointer; } .context-menu-item.context-menu-disabled { background-color: $context-menu-item-color-disabled; color: $context-menu-text-color-disabled; cursor: default; } .context-menu-input.context-menu-hover { cursor: default; color: $context-menu-text-color; } .context-menu-submenu:after { content: ''; border-style: solid; border-width: .25em 0 .25em .25em; border-color: transparent transparent transparent $context-menu-submenu-arrow-color; height: 0; position: absolute; right: .5em; top: 50%; transform: translateY(-50%); width: 0; z-index: 1; } /** * Inputs */ .context-menu-item.context-menu-input { padding: .3em .6em; } /* vertically align inside labels */ .context-menu-input > label > * { vertical-align: top; } /* position checkboxes and radios as icons */ .context-menu-input > label > input[type="checkbox"], .context-menu-input > label > input[type="radio"] { margin-right: .4em; position: relative; top: .12em; } .context-menu-input > label { margin: 0; } .context-menu-input > label, .context-menu-input > label > input[type="text"], .context-menu-input > label > textarea, .context-menu-input > label > select { box-sizing: border-box; display: block; width: 100%; } .context-menu-input > label > textarea { height: 7em; } .context-menu-item > .context-menu-list { display: none; /* re-positioned by js */ right: -.3em; top: .3em; } .context-menu-item.context-menu-visible > .context-menu-list { display: block; } .context-menu-accesskey { text-decoration: underline; } ================================================ FILE: test/index.html ================================================ QUnit Test Suite ================================================ FILE: test/integration/custom-command.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Click custom comand menu item triggers menu callback': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.numberOfElements('.context-menu-root>li') .is(3, '3 context menu items are shown') .click('.context-menu-root li.labels') .assert.text('#msg').to.contain('clicked: label', 'contextMenu callback was triggered') .done(); }, 'Click custom comand menu item label triggers custom action - red': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li.labels .label1') .assert.text('#msg', 'clicked: label | text: label 1', 'custom action was triggered') .done(); }, 'Click custom comand menu item label triggers custom action - blue': function (test) { test .open('file://' + pwd + '/test/integration/html/custom-command_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li.labels .label3') .assert.text('#msg', 'clicked: label | text: label 3', 'custom action was triggered') .done(); } }; ================================================ FILE: test/integration/disabled-callback.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-callback_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .click('.context-menu-root li:last-child') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Clicking on enabled item works': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:first-child') .assert.text('#msg', 'clicked: edit', 'Enabled menu item sets text') .done(); } }; ================================================ FILE: test/integration/disabled-changing.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(3, '3 context menu items are shown') .click('.context-menu-root li:nth-child(2)') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Toggle disabled item status': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:nth-child(3)') .assert.doesntExist('.context-menu-root .disabled', 'All menu items are enabled') .click('.context-menu-root li:nth-child(2)') .assert.text('#msg', 'clicked: cut', 'Enabled menu item sets text') .done(); }, 'Toggled status is saved after menu is closed': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-changing_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:nth-child(3)') .assert.doesntExist('.context-menu-root .disabled', 'All menu items are enabled') .execute(helper.closeMenu, '.context-menu-one') .wait(100) .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.doesntExist('.context-menu-root .disabled', 'All menu items are still enabled') .done(); } }; ================================================ FILE: test/integration/disabled-menu.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Disabled trigger doesnt open context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .done(); }, 'Enabled trigger opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .click('#toggle-disabled') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .$('.context-menu-root') .assert.visible('Menu is present') .assert.exists('It opens context menu') .end() .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); }, 'Repeatedly disabled trigger doesnt open context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled-menu.html') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .click('#toggle-disabled') .assert.doesntExist('.context-menu-disabled', 'Context menu trigger is enambled') .click('#toggle-disabled') .assert.numberOfElements('.context-menu-one') .is(1, 'Context menu trigger is disabled again') .execute(helper.rightClick, '.context-menu-one') .wait(100) .assert.notVisible('.context-menu-root', 'Menu is not present') .assert.doesntExist('#context-menu-layer', 'Context menu is not shown') .done(); } }; ================================================ FILE: test/integration/disabled.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); // this test uses custom HTML because PhantomJS // has problems showing alert modal dialogs. // We are testing callbacks against simple DOM // manipulations instead. module.exports = { 'Clicking on disabled item has no effect': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .click('.context-menu-root li:last-child') .assert.text('#msg', '', 'Disabled menu item didnt set text') .done(); }, 'Clicking on enabled item works': function (test) { test .open('file://' + pwd + '/test/integration/html/disabled_test.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .click('.context-menu-root li:first-child') .assert.text('#msg', 'clicked: edit', 'Enabled menu item sets text') .done(); } }; ================================================ FILE: test/integration/dynamic-create.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Dynamically created context menu is shown': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic-create.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .done(); } }; ================================================ FILE: test/integration/dynamic.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Dynamically created opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic.html') .click('#add-trigger') .waitForElement('.menu-injected') .execute(helper.rightClick, '.menu-injected') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') .done(); }, '3rd dynamically created also opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/dynamic.html') .click('#add-trigger') .click('#add-trigger') .click('#add-trigger') .wait(200) //.waitForElement('.menu-injected') .assert.numberOfElements('.menu-injected') .is(3, '3 DIVs are added') .execute(helper.rightClick, '.menu-injected:last-of-type') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') .done(); } }; ================================================ FILE: test/integration/html/accesskeys.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Accesskeys Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/accesskeys_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Accesskeys Example code Example HTML Demo: Accesskeys right click me Example code Example HTML ================================================ FILE: test/integration/html/async-create.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Create Context Menu (asynchronous) Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/callback.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Callback Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/callback_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Callback Example code Example HTML Demo: Callback right click me Example code Example HTML ================================================ FILE: test/integration/html/custom-command.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom command Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/custom-command_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom command Example code Example HTML Demo: Custom command right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-callback.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Callback Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-callback_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Callback Example code Example HTML Demo: Disabled Callback right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-changing.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled changing Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-changing_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled changing Example code Example HTML Demo: Disabled changing right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled-menu.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled menu Example code Example HTML right click me Enable Menu Example code Example HTML ================================================ FILE: test/integration/html/disabled.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/disabled_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Disabled Example code Example HTML Demo: Disabled right click me Example code Example HTML ================================================ FILE: test/integration/html/dynamic-create.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Adding new Context Menu Triggers Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/dynamic.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Adding new Context Menu Triggers Example code Example HTML jQuery.contextMenu allows you to define a <menu> before the trigger elements are available. Button Example code Example HTML ================================================ FILE: test/integration/html/html5-import.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Importing HTML5 <menu type="context"> Example code Example HTML jQuery.contextMenu allows you to import HTML5's <menu> structures to use in older browsers. right click me Example code Example HTML <menu id="html5menu" style="display:none" class="showcase"> <command label="rotate" icon="edit" onclick="alert('rotate')"> <command label="resize" onclick="alert('resize')"> <command label="twitter" onclick="alert('twitter')"> <hr> <command label="facebook" onclick="alert('facebook')"> </menu> </menu> ================================================ FILE: test/integration/html/html5-polyfill-firefox8.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: HTML5 Polyfill (Firefox) Example code Example HTML right click me Example code Example HTML <menu id="html5firefox8" type="context" > <menuitem label="rotate" onclick="alert('rotate')" hint="I'm a hint"></menuitem> <menuitem label="resize" onclick="alert('resize')"></menuitem> <menuitem label="disabled" onclick="alert('disabled')" disabled></menuitem> <menu label="share"> <menuitem label="twitter" onclick="alert('twitter')"></menuitem> <menuitem label="facebook" onclick="alert('facebook')"></menuitem> <hr> <menuitem type="checkbox" label="(checkbox) yes or no?" onclick="alert('checkbox: ' + (this.checked ? 'yep!' : 'nope'))"></menuitem> <hr> <menuitem type="radio" label="(radio) yes" radiogroup="alpha" checked onclick="alert('radio: yes')"></menuitem> <menuitem type="radio" label="(radio) no" radiogroup="alpha" onclick="alert('radio: no')"></menuitem> </menu> </menu> ================================================ FILE: test/integration/html/html5-polyfill.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: HTML5 Polyfill Example code Example HTML jQuery.contextMenu allows you to import HTML5's <menu> structures to use in older browsers. right click me Example code Example HTML <menu id="html5polyfill" type="context" style="display:none"> <command label="rotate" onclick="alert('rotate')" icon="images/cut.png"> <command label="resize" onclick="alert('resize')" icon="images/door.png"> <menu label="share"> <command label="twitter" onclick="alert('twitter')" icon="images/page_white_copy.png"> <hr> <command label="facebook" onclick="alert('facebook')" icon="images/page_white_edit.png"> <hr> <label>foo bar<input type="text" name="foo"></label> </menu> </menu> foo bar ================================================ FILE: test/integration/html/input.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Input Commands Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/keeping-contextmenu-open.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Keeping the Menu visible Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/menu-title.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Menu Title Example CSS Example code Example HTML right click me right click me right click me Example CSS Example code Example HTML <span class="context-menu-one btn btn-neutral">right click me</span> <span class="context-menu-two btn btn-neutral">right click me</span> <span class="context-menu-three btn btn-neutral">right click me</span> ================================================ FILE: test/integration/html/on-dom-element.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Context Menu on DOM Element Example code Example HTML right click me 1 right click me 2 right click me 3 right click me 4 Example code Example HTML <ul id="the-node"> <li><span class="context-menu-one btn btn-neutral">right click me 1</span></li> <li><span class="context-menu-one btn btn-neutral">right click me 2</span></li> <li>right click me 3</li> <li>right click me 4</li> </ul> ================================================ FILE: test/integration/html/sub-menus.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Submenus Example code Example HTML right click me Example code Example HTML ================================================ FILE: test/integration/html/sub-menus_test.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Submenus Example code Example HTML Demo: Submenus right click me Example code Example HTML ================================================ FILE: test/integration/html/trigger-custom.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Custom Activated Context Menu Example code Example HTML press that button Button Example code Example HTML ================================================ FILE: test/integration/html/trigger-hover-autohide.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Hover Activated Context Menu With Autohide Example code Example HTML hover over me Example code Example HTML ================================================ FILE: test/integration/html/trigger-hover.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Hover Activated Context Menu Example code Example HTML hover over me Example code Example HTML ================================================ FILE: test/integration/html/trigger-left-click.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Left-Click Trigger Example code Example HTML left click me Example code Example HTML ================================================ FILE: test/integration/html/trigger-swipe.html ================================================ jQuery contextMenu jQuery contextMenu Introduction Author Demo Documentation Options Defining menu items Plugin commands Customize icons FontAwesome icons Font Awesome support Runtime options Custom command types Events HTML5 polyfill Demo gallery Simple Context Menu FontAwesome icons Accesskeys Create Context Menu (asynchronous) Command's action (callbacks) Custom Command Types Disabled Command Disabled Callback Command Changing Command's disabled status Disabled Menu Adding new Context Menu Triggers Create Context Menu on demand Importing HTML5 menu HTML5 polyfill HTML5 polyfill (Firefox) Input Commands Keeping the context menu open Menus with titles Context Menu on DOM Element Submenus Custom Activated Context Menu Hover Activated Context Menu Hover Activated Context Menu With Autohide Left-Click Trigger Swipe Trigger Maintained by: Creative Digital Agency jQuery contextMenu Fork on GitHub Demo: Swipe Trigger Example code Example HTML This demo uses the (third party) TouchSwipe plugin. swype right Example code Example HTML ================================================ FILE: test/integration/input.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); var text1 = '.context-menu-root input[name="context-menu-input-name"]'; var textArea1 = '.context-menu-root textarea[name="context-menu-input-area1"]'; var textArea2 = '.context-menu-root textarea[name="context-menu-input-area2"]'; module.exports = { 'HTML5 input-based menu is shown correctly': function (test) { test .open('file://' + pwd + '/test/integration/html/input.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(14, '14 context menu items are shown') .assert.numberOfElements('.context-menu-root input') .is(6, '6 HTML input items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); }, 'HTML5 input-based menu stores state when closed': function (test) { test .open('file://' + pwd + '/test/integration/html/input.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .type(text1, 'lorem ipsum') .type(textArea1, 'test area with height') .type(textArea2, 'shots go off') .execute(helper.closeMenu, '.context-menu-one') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.val(text1, 'lorem ipsum', 'Input text should contain entered text') .assert.val(textArea1, 'test area with height', 'Text area 1 should contain entered text') .assert.val(textArea2, 'shots go off', 'Text area 2 should contain entered text') .done(); } }; ================================================ FILE: test/integration/keeping-contextmenu-open.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Ensure context menu is shown': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(2, '2 context menu items are shown') .done(); }, 'Close context menu after first menu item is clicked': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.visible('.context-menu-root li:first-child', 'Menu item is present') .click('.context-menu-root li:first-child') .assert.doesntExist('#context-menu-layer', 'It closes context menu') .done(); }, 'Keep context menu open after second menu item is clicked': function (test) { test .open('file://' + pwd + '/test/integration/html/keeping-contextmenu-open.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .wait(100) .assert.visible('.context-menu-root', 'Menu is present') .click('.context-menu-root li:last-child') .assert.exists('#context-menu-layer', 'It closes context menu') .done(); } }; ================================================ FILE: test/integration/on-dom-element.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Right-click on multiple DOM elements': function (test) { test .open('file://' + pwd + '/test/integration/html/on-dom-element.html') .execute(helper.rightClick, '#the-node li:first-child') .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure one context menu is open') // right click on the other DOM element .execute(helper.rightClick, '#the-node li:nth-child(3)') .wait(100) // wait for the old menu to close and new to reopen .waitForElement('#context-menu-layer') .assert.exists('.context-menu-root', 'It re-opens the same context menu') .assert.numberOfElements('.context-menu-active') .is(1, 'ensure still only one context menu is open') .done(); } }; ================================================ FILE: test/integration/trigger-custom.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Mouse hover opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/trigger-custom.html') .click('#activate-menu') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .click('#context-menu-layer') .wait(100) .assert.doesntExist('#context-menu-layer', 'Click outside closes context menu') .done(); } }; ================================================ FILE: test/integration/trigger-left-click.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Left-click opens context menu': function (test) { test .open('file://' + pwd + '/test/integration/html/trigger-left-click.html') .click('.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); } }; ================================================ FILE: test/integration/trigger-right-click.js ================================================ var pwd = process.cwd(); var helper = require('../integration_test_helper.js'); module.exports = { 'Right-click opens context menu': function (test) { test .open('file://' + pwd + '/demo.html') .execute(helper.rightClick, '.context-menu-one') .waitForElement('#context-menu-layer') .assert.visible('.context-menu-root', 'Menu is present') .assert.exists('.context-menu-root', 'It opens context menu') .assert.numberOfElements('.context-menu-root li') .is(7, '7 context menu items are shown') .assert.width('.context-menu-root').is.gt(100) .done(); } }; ================================================ FILE: test/specs/accesskeys.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test accesskeys', function() { it('should navigate to submenu 2 levels deep and see correct alert for charlie', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2') browser.click('span=charlie') assert.equal(browser.alertText(), 'clicked: fold2-key3'); browser.alertAccept(); }); it('Typing on keyboard triggers "edit" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('e'); assert.equal(browser.alertText(), 'clicked: edit'); browser.alertAccept(); }); it('Typing on keyboard triggers "cut" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('c'); assert.equal(browser.alertText(), 'clicked: cut'); browser.alertAccept(); }); it('Typing on keyboard triggers "copy" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('o'); assert.equal(browser.alertText(), 'clicked: copy'); browser.alertAccept(); }); it('Typing on keyboard triggers "paste" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('p'); assert.equal(browser.alertText(), 'clicked: paste'); browser.alertAccept(); }); }); ================================================ FILE: test/specs/aync-create.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test async create', function() { it('should render async created context menu', function () { browser.url('file://' + pwd + '/test/integration/html/async-create.html'); browser.rightClick('.context-menu-one'); browser.waitForExist('#context-menu-layer'); assert.equal(3, browser.elements('.context-menu-root li').value.length); }); }); ================================================ FILE: test/specs/callback.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test callback', function() { function openCallbackMenu() { browser.url('file://' + pwd + '/test/integration/html/callback.html'); browser.rightClick('.context-menu-one'); browser.waitForExist('#context-menu-layer'); assert.equal(true, browser.isVisible('.context-menu-root'), 'menu is visible'); } it('Ensure edit menu item triggers callback', function () { openCallbackMenu(); browser.leftClick('.context-menu-root li:nth-child(1)'); assert.equal('edit was clicked', browser.alertText()); browser.alertAccept(); assert.equal(false, browser.isVisible('£context-menu-layer'), 'menu is hidden'); }); it('Ensure cut menu item triggers global callback', function () { openCallbackMenu(); browser.leftClick('.context-menu-root li:nth-child(2)'); assert.equal('global: cut', browser.alertText()); browser.alertAccept(); assert.equal(false, browser.isVisible('£context-menu-layer'), 'menu is hidden'); }); }); ================================================ FILE: test/specs/submenu.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test submenus', function() { it('should navigate to submenu 2 levels deep and see correct alert for charlie', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2') browser.click('span=charlie') assert.equal(browser.alertText(), 'clicked: fold2-key3'); browser.alertAccept(); }); it('should navigate to submenu 2 levels deep and see first menu highlighted', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2'); var elements = browser.elements('.context-menu-hover'); assert.equal(2, elements.value.length); }); }); ================================================ FILE: test/unit/contextmenu.test.js ================================================ var menuOpenCounter = 0; var menuCloseCounter = 0; var itemSelectedCounter = 0; var itemSelectedStack = []; var menuRuntime = null; function testQUnit(name, itemClickEvent, triggerEvent) { QUnit.module(name, { afterEach: function(){ destroyContextMenuAndCleanup(); } }); // before each test function createContextMenu(items, classname) { if(typeof(classname) == 'undefined'){ classname = 'context-menu'; } var $fixture = $('#qunit-fixture'); // ensure `#qunit-fixture` exists when testing with karma runner if ($fixture.length === 0) { $('').appendTo("body"); $fixture = $('#qunit-fixture'); } $fixture.append("right click me!"); if(!items){ items = { copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }; } $.contextMenu({ selector: '.' + classname, events: { show: function(opt) { menuRuntime = opt; menuOpenCounter = menuOpenCounter + 1; }, hide: function() { menuCloseCounter = menuCloseCounter + 1; } }, callback: function(key, options) { itemSelectedCounter = itemSelectedCounter + 1; itemSelectedStack.push(key); }, items: items, itemClickEvent: itemClickEvent }); } // after each test function destroyContextMenuAndCleanup() { $.contextMenu('destroy'); // clean up `#qunit-fixture` when testing in karma runner var $fixture = $('#qunit-fixture'); if ($fixture.length) { $fixture.html(''); } // reset vars menuOpenCounter = 0; menuCloseCounter = 0; itemSelectedCounter = 0; itemSelectedStack = []; menuRuntime = null; } QUnit.test('$.contextMenu object exists', function(assert) { assert.ok($.contextMenu, '$.contextMenu plugin is loaded'); assert.notEqual($.contextMenu, undefined, '$.contextMenu is not undefined'); }); QUnit.test('open contextMenu', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); assert.equal(menuOpenCounter, 1, 'contextMenu was opened once'); }); QUnit.test('open contextMenu at 0,0', function(assert) { createContextMenu(); $(".context-menu").contextMenu({x: 0, y: 0}); assert.equal(menuOpenCounter, 1, 'contextMenu was opened once'); }); QUnit.test('close contextMenu', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); assert.equal(menuCloseCounter, 1, 'contextMenu was closed once'); }); QUnit.test('navigate contextMenu items', function(assert) { createContextMenu(); var itemWasFocused = 0; var itemWasBlurred = 0; // listen to focus and blur events $(document.body) .on("contextmenu:focus", ".context-menu-item", function(e) { itemWasFocused = itemWasFocused + 1; }) .on("contextmenu:blur", ".context-menu-item", function(e) { itemWasBlurred = itemWasBlurred + 1; }); $(".context-menu").contextMenu(); menuRuntime.$menu.trigger('nextcommand'); // triggers contextmenu:focus assert.equal(itemWasFocused, 1, 'first menu item was focused once'); itemWasFocused = 0; menuRuntime.$menu.trigger('nextcommand'); // triggers contextmenu:blur & contextmenu:focus assert.equal(itemWasFocused, 1, 'first menu item was blurred'); assert.equal(itemWasBlurred, 1, 'second menu item was focused'); }); QUnit.test('activate contextMenu item', function(assert) { createContextMenu(); $(".context-menu").contextMenu(); menuRuntime.$menu.trigger('nextcommand'); menuRuntime.$selected.trigger(triggerEvent); assert.equal(itemSelectedCounter, 1, 'selected menu item was clicked once'); }); QUnit.test('do not open destroyed context menu', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }); createContextMenu({ copy: {name: 'Copy', icon: 'copy'}, paste: {name: 'Paste', icon: 'paste'} }, 'context-menu-two'); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); $(".context-menu-two").contextMenu(); $(".context-menu-two").contextMenu('hide'); assert.equal(menuOpenCounter, 2, 'contextMenu was opened twice'); $(".context-menu-two").contextMenu('destroy'); $(".context-menu").contextMenu(); $(".context-menu").contextMenu('hide'); $(".context-menu-two").contextMenu(); $(".context-menu-two").contextMenu('hide'); assert.equal(menuOpenCounter, 3, 'destroyed contextMenu was not opened'); }); QUnit.test('do not open context menu with no visible items', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'copy', visible: function(){return false;}}, paste: {name: 'Paste', icon: 'paste', visible: function(){return false;}} }); $(".context-menu").contextMenu(); assert.equal($('.context-menu-item').is(':visible'), false, 'no menu items visible'); }); QUnit.test('visible function should only trigger once', function(assert) { var visibleTriggered = 0; createContextMenu({ copy: {name: 'Copy', icon: 'copy', visible: function(){visibleTriggered++; return true;}}, paste: {name: 'Paste', icon: 'paste', visible: function(){return false;}} }); $(".context-menu").contextMenu(); assert.equal(visibleTriggered, 1, 'selected menu wat not opened'); }); QUnit.test('items in seconds submenu to not override callbacks', function (assert) { var firstCallback = false, firstSubCallback = false, secondSubCallback = false; createContextMenu({ firstitem: { name: 'firstitem', icon: 'copy', callback : function(){ firstCallback = true; } }, firstsubmenu: { name: 'Copy', icon: 'copy', items: { firstitem : { name : "firstitem", icon : "copy", callback : function(){ firstSubCallback = true; } } } }, secondsubmenu: { name: 'Copy', icon: 'copy', items: { firstitem : { name : "firstitem", icon : "copy", callback : function(){ secondSubCallback = true; } } } } }); $('.context-menu-item').first().trigger(triggerEvent); $('.context-menu-submenu .context-menu-item').each(function(i,e){ $(e).trigger(triggerEvent) }); assert.equal(firstCallback, 1); assert.equal(firstSubCallback, 1); assert.equal(secondSubCallback, 1); }); QUnit.test('font-awesome creates icon elements', function(assert) { createContextMenu({ copy: {name: 'Copy', icon: 'fas fa-beer'} }); $(".context-menu").contextMenu(); assert.equal($('i.fas.fa-beer').length, 1, 'FontAwesome tag was not created'); }); } testQUnit('contextMenu events', '', 'mouseup'); testQUnit('contextMenu events - click handler', 'click', 'click'); ================================================ FILE: wdio.conf.js ================================================ exports.config = { // // ================= // Service Providers // ================= // WebdriverIO supports Sauce Labs, Browserstack, and Testing Bot (other cloud providers // should work too though). These services define specific user and key (or access key) // values you need to put in here in order to connect to these services. // user: process.env.SAUCE_USERNAME, key: process.env.SAUCE_ACCESS_KEY, // // ================== // Specify Test Files // ================== // Define which test specs should run. The pattern is relative to the directory // from which `wdio` was called. Notice that, if you are calling `wdio` from an // NPM script (see https://docs.npmjs.com/cli/run-script) then the current working // directory is where your package.json resides, so `wdio` will be called from there. // specs: [ './test/specs/**/*.js' ], // Patterns to exclude. exclude: [ // 'path/to/excluded/files' ], // // ============ // Capabilities // ============ // Define your capabilities here. WebdriverIO can run multiple capabilities at the same // time. Depending on the number of capabilities, WebdriverIO launches several test // sessions. Within your capabilities you can overwrite the spec and exclude options in // order to group specific specs to a specific capability. // // First, you can define how many instances should be started at the same time. Let's // say you have 3 different capabilities (Chrome, Firefox, and Safari) and you have // set maxInstances to 1; wdio will spawn 3 processes. Therefore, if you have 10 spec // files and you set maxInstances to 10, all spec files will get tested at the same time // and 30 processes will get spawned. The property handles how many capabilities // from the same test should run tests. // maxInstances: 10, // // If you have trouble getting all important capabilities together, check out the // Sauce Labs platform configurator - a great tool to configure your capabilities: // https://docs.saucelabs.com/reference/platforms-configurator // capabilities: [{ // maxInstances can get overwritten per capability. So if you have an in-house Selenium // grid with only 5 firefox instance available you can make sure that not more than // 5 instance gets started at a time. maxInstances: 5, // browserName: 'chrome' }], // // =================== // Test Configurations // =================== // Define all options that are relevant for the WebdriverIO instance here // // By default WebdriverIO commands are executed in a synchronous way using // the wdio-sync package. If you still want to run your tests in an async way // e.g. using promises you can set the sync option to false. sync: true, // // Level of logging verbosity: silent | verbose | command | data | result | error logLevel: 'error', // // Enables colors for log output. coloredLogs: true, // // Saves a screenshot to a given path if a command fails. screenshotPath: './errorShots/', // // Set a base URL in order to shorten url command calls. If your url parameter starts // with "/", then the base url gets prepended. baseUrl: 'http://localhost', // // Default timeout for all waitFor* commands. waitforTimeout: 10000, // // Default timeout in milliseconds for request // if Selenium Grid doesn't send response connectionRetryTimeout: 90000, // // Default request retries count connectionRetryCount: 3, // // Initialize the browser instance with a WebdriverIO plugin. The object should have the // plugin name as key and the desired plugin options as properties. Make sure you have // the plugin installed before running any tests. The following plugins are currently // available: // WebdriverCSS: https://github.com/webdriverio/webdrivercss // WebdriverRTC: https://github.com/webdriverio/webdriverrtc // Browserevent: https://github.com/webdriverio/browserevent // plugins: { // webdrivercss: { // screenshotRoot: 'my-shots', // failedComparisonsRoot: 'diffs', // misMatchTolerance: 0.05, // screenWidth: [320,480,640,1024] // }, // webdriverrtc: {}, // browserevent: {} // }, // // Test runner services // Services take over a specific job you don't want to take care of. They enhance // your test setup with almost no effort. Unlike plugins, they don't add new // commands. Instead, they hook themselves up into the test process. // services: ['selenium-standalone'], services: ['selenium-standalone', 'sauce'], // services: ['sauce'], // // Framework you want to run your specs with. // The following are supported: Mocha, Jasmine, and Cucumber // see also: http://webdriver.io/guide/testrunner/frameworks.html // // Make sure you have the wdio adapter package for the specific framework installed // before running any tests. framework: 'mocha', // // Test reporter for stdout. // The only one supported by default is 'dot' // see also: http://webdriver.io/guide/testrunner/reporters.html reporters: ['dot'], // // Options to be passed to Mocha. // See the full list at http://mochajs.org/ mochaOpts: { ui: 'bdd' }, // // ===== // Hooks // ===== // WebdriverIO provides several hooks you can use to interfere with the test process in order to enhance // it and to build services around it. You can either apply a single function or an array of // methods to it. If one of them returns with a promise, WebdriverIO will wait until that promise got // resolved to continue. // // Gets executed once before all workers get launched. // onPrepare: function (config, capabilities) { // }, // // Gets executed before test execution begins. At this point you can access all global // variables, such as `browser`. It is the perfect place to define custom commands. // before: function (capabilities, specs) { // }, // // Hook that gets executed before the suite starts // beforeSuite: function (suite) { // }, // // Hook that gets executed _before_ a hook within the suite starts (e.g. runs before calling // beforeEach in Mocha) // beforeHook: function () { // }, // // Hook that gets executed _after_ a hook within the suite starts (e.g. runs after calling // afterEach in Mocha) // afterHook: function () { // }, // // Function to be executed before a test (in Mocha/Jasmine) or a step (in Cucumber) starts. // beforeTest: function (test) { // }, // // Runs before a WebdriverIO command gets executed. // beforeCommand: function (commandName, args) { // }, // // Runs after a WebdriverIO command gets executed // afterCommand: function (commandName, args, result, error) { // }, // // Function to be executed after a test (in Mocha/Jasmine) or a step (in Cucumber) starts. // afterTest: function (test) { // }, // // Hook that gets executed after the suite has ended // afterSuite: function (suite) { // }, // // Gets executed after all tests are done. You still have access to all global variables from // the test. // after: function (result, capabilities, specs) { // }, // // Gets executed after all workers got shut down and the process is about to exit. It is not // possible to defer the end of the process using a promise. // onComplete: function(exitCode) { // } }
right click me
jQuery.contextMenu allows you to define a <menu> before the trigger elements are available.
jQuery.contextMenu
<menu type="context">
jQuery.contextMenu allows you to import HTML5's <menu> structures to use in older browsers.
<menu>
<menu id="html5menu" style="display:none" class="showcase"> <command label="rotate" icon="edit" onclick="alert('rotate')"> <command label="resize" onclick="alert('resize')"> <command label="twitter" onclick="alert('twitter')"> <hr> <command label="facebook" onclick="alert('facebook')"> </menu> </menu>
<menu id="html5firefox8" type="context" > <menuitem label="rotate" onclick="alert('rotate')" hint="I'm a hint"></menuitem> <menuitem label="resize" onclick="alert('resize')"></menuitem> <menuitem label="disabled" onclick="alert('disabled')" disabled></menuitem> <menu label="share"> <menuitem label="twitter" onclick="alert('twitter')"></menuitem> <menuitem label="facebook" onclick="alert('facebook')"></menuitem> <hr> <menuitem type="checkbox" label="(checkbox) yes or no?" onclick="alert('checkbox: ' + (this.checked ? 'yep!' : 'nope'))"></menuitem> <hr> <menuitem type="radio" label="(radio) yes" radiogroup="alpha" checked onclick="alert('radio: yes')"></menuitem> <menuitem type="radio" label="(radio) no" radiogroup="alpha" onclick="alert('radio: no')"></menuitem> </menu> </menu>
<menu id="html5polyfill" type="context" style="display:none"> <command label="rotate" onclick="alert('rotate')" icon="images/cut.png"> <command label="resize" onclick="alert('resize')" icon="images/door.png"> <menu label="share"> <command label="twitter" onclick="alert('twitter')" icon="images/page_white_copy.png"> <hr> <command label="facebook" onclick="alert('facebook')" icon="images/page_white_edit.png"> <hr> <label>foo bar<input type="text" name="foo"></label> </menu> </menu>
right click me right click me right click me
<span class="context-menu-one btn btn-neutral">right click me</span> <span class="context-menu-two btn btn-neutral">right click me</span> <span class="context-menu-three btn btn-neutral">right click me</span>
<ul id="the-node"> <li><span class="context-menu-one btn btn-neutral">right click me 1</span></li> <li><span class="context-menu-one btn btn-neutral">right click me 2</span></li> <li>right click me 3</li> <li>right click me 4</li> </ul>
press that button
hover over me
left click me
This demo uses the (third party) TouchSwipe plugin.
swype right
on keyboard triggers "paste" menu item callback', function () { browser.url('file://' + pwd + '/test/integration/html/accesskeys.html'); browser.rightClick('.context-menu-one'); browser.keys('p'); assert.equal(browser.alertText(), 'clicked: paste'); browser.alertAccept(); }); }); ================================================ FILE: test/specs/aync-create.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test async create', function() { it('should render async created context menu', function () { browser.url('file://' + pwd + '/test/integration/html/async-create.html'); browser.rightClick('.context-menu-one'); browser.waitForExist('#context-menu-layer'); assert.equal(3, browser.elements('.context-menu-root li').value.length); }); }); ================================================ FILE: test/specs/callback.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test callback', function() { function openCallbackMenu() { browser.url('file://' + pwd + '/test/integration/html/callback.html'); browser.rightClick('.context-menu-one'); browser.waitForExist('#context-menu-layer'); assert.equal(true, browser.isVisible('.context-menu-root'), 'menu is visible'); } it('Ensure edit menu item triggers callback', function () { openCallbackMenu(); browser.leftClick('.context-menu-root li:nth-child(1)'); assert.equal('edit was clicked', browser.alertText()); browser.alertAccept(); assert.equal(false, browser.isVisible('£context-menu-layer'), 'menu is hidden'); }); it('Ensure cut menu item triggers global callback', function () { openCallbackMenu(); browser.leftClick('.context-menu-root li:nth-child(2)'); assert.equal('global: cut', browser.alertText()); browser.alertAccept(); assert.equal(false, browser.isVisible('£context-menu-layer'), 'menu is hidden'); }); }); ================================================ FILE: test/specs/submenu.js ================================================ var assert = require('assert'); var pwd = process.cwd(); describe('Test submenus', function() { it('should navigate to submenu 2 levels deep and see correct alert for charlie', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2') browser.click('span=charlie') assert.equal(browser.alertText(), 'clicked: fold2-key3'); browser.alertAccept(); }); it('should navigate to submenu 2 levels deep and see first menu highlighted', function () { browser.url('file://' + pwd + '/test/integration/html/sub-menus.html'); browser.rightClick('.context-menu-one'); browser.moveToObject('span=Sub group') browser.moveToObject('span=Sub group 2'); var elements = browser.elements('.context-menu-hover'); assert.equal(2, elements.value.length); }); }); ================================================ FILE: test/unit/contextmenu.test.js ================================================ var menuOpenCounter = 0; var menuCloseCounter = 0; var itemSelectedCounter = 0; var itemSelectedStack = []; var menuRuntime = null; function testQUnit(name, itemClickEvent, triggerEvent) { QUnit.module(name, { afterEach: function(){ destroyContextMenuAndCleanup(); } }); // before each test function createContextMenu(items, classname) { if(typeof(classname) == 'undefined'){ classname = 'context-menu'; } var $fixture = $('#qunit-fixture'); // ensure `#qunit-fixture` exists when testing with karma runner if ($fixture.length === 0) { $('