├── .gitignore ├── .tagconfig.json ├── .travis.yml ├── CONTRIBUTING.md ├── Gruntfile.coffee ├── Makefile ├── README.md ├── bower.json ├── component.json ├── css ├── opentip.css └── stylus │ └── opentip.styl ├── docs ├── adapter.ender.html ├── adapter.jquery.html ├── adapter.native.html ├── adapter.prototype.html ├── docco.css └── opentip.html ├── downloads ├── opentip-jquery-excanvas.js ├── opentip-jquery-excanvas.min.js ├── opentip-jquery.js ├── opentip-jquery.min.js ├── opentip-native-excanvas.js ├── opentip-native-excanvas.min.js ├── opentip-native.js ├── opentip-native.min.js ├── opentip-prototype-excanvas.js ├── opentip-prototype-excanvas.min.js ├── opentip-prototype.js ├── opentip-prototype.min.js └── readme.md ├── files ├── close-button-angle.png ├── explanations.psd └── tests.png ├── index.js ├── lib ├── adapter-component.js ├── adapter-ender.js ├── adapter-jquery.js ├── adapter-native.js ├── adapter-prototype.js └── opentip.js ├── package.json ├── src ├── adapter-component.coffee ├── adapter-ender.coffee ├── adapter-jquery.coffee ├── adapter-native.coffee ├── adapter-prototype.coffee └── opentip.coffee └── test ├── .gitignore ├── ender.js ├── readme.md ├── src ├── 010-opentip.coffee ├── 020-opentip-startup.coffee ├── 030-opentip-show.coffee ├── 040-opentip-positioning.coffee ├── 050-opentip-draw.coffee ├── 060-opentip-ajax.coffee ├── 100-utils.coffee ├── 110-joint.coffee ├── 200-adapters.coffee ├── 210-adapter-native.coffee └── 220-adapter-ender.coffee ├── test.html ├── test.js └── vendor ├── jquery-1.9.1.js ├── prototype-1.7.1.js └── sinon-1.6.0.js /.gitignore: -------------------------------------------------------------------------------- 1 | components 2 | build 3 | .* 4 | !.gitignore 5 | !.travis.yml 6 | node_modules 7 | .tmp-* -------------------------------------------------------------------------------- /.tagconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "files": [ 3 | { 4 | "name": "README.md", 5 | "regexs": [ 6 | "Version ###" 7 | ] 8 | }, 9 | { 10 | "name": "src/opentip.coffee", 11 | "regexs": [ 12 | "Opentip v###", 13 | "Opentip\\.version = \"###\"" 14 | ] 15 | }, 16 | { 17 | "name": "lib/opentip.js", 18 | "regexs": [ 19 | "Opentip v###", 20 | "Opentip\\.version = \"###\"" 21 | ] 22 | }, 23 | { 24 | "name": "component.json", 25 | "regexs": [ 26 | "\"version\": \"###\"" 27 | ] 28 | }, 29 | { 30 | "name": "bower.json", 31 | "regexs": [ 32 | "\"version\": \"###\"" 33 | ] 34 | }, 35 | { 36 | "name": "package.json", 37 | "regexs": [ 38 | "\"version\": \"###\"" 39 | ] 40 | }, 41 | 42 | 43 | { 44 | "name": "downloads/opentip-jquery-excanvas.js", 45 | "regexs": [ 46 | "Opentip\\.version = \"###\"", 47 | "Opentip v###" 48 | ] 49 | }, 50 | { 51 | "name": "downloads/opentip-jquery.js", 52 | "regexs": [ 53 | "Opentip\\.version = \"###\"", 54 | "Opentip v###" 55 | ] 56 | }, 57 | { 58 | "name": "downloads/opentip-native-excanvas.js", 59 | "regexs": [ 60 | "Opentip\\.version = \"###\"", 61 | "Opentip v###" 62 | ] 63 | }, 64 | { 65 | "name": "downloads/opentip-native.js", 66 | "regexs": [ 67 | "Opentip\\.version = \"###\"", 68 | "Opentip v###" 69 | ] 70 | }, 71 | { 72 | "name": "downloads/opentip-prototype-excanvas.js", 73 | "regexs": [ 74 | "Opentip\\.version = \"###\"", 75 | "Opentip v###" 76 | ] 77 | }, 78 | { 79 | "name": "downloads/opentip-prototype.js", 80 | "regexs": [ 81 | "Opentip\\.version = \"###\"", 82 | "Opentip v###" 83 | ] 84 | }, 85 | 86 | { 87 | "name": "downloads/opentip-jquery-excanvas.min.js", 88 | "regexs": [ 89 | "Opentip\\.version=\"###\"", 90 | "Opentip v###" 91 | ] 92 | }, 93 | { 94 | "name": "downloads/opentip-jquery.min.js", 95 | "regexs": [ 96 | "Opentip\\.version=\"###\"", 97 | "Opentip v###" 98 | ] 99 | }, 100 | { 101 | "name": "downloads/opentip-native-excanvas.min.js", 102 | "regexs": [ 103 | "Opentip\\.version=\"###\"", 104 | "Opentip v###" 105 | ] 106 | }, 107 | { 108 | "name": "downloads/opentip-native.min.js", 109 | "regexs": [ 110 | "Opentip\\.version=\"###\"", 111 | "Opentip v###" 112 | ] 113 | }, 114 | { 115 | "name": "downloads/opentip-prototype-excanvas.min.js", 116 | "regexs": [ 117 | "Opentip\\.version=\"###\"", 118 | "Opentip v###" 119 | ] 120 | }, 121 | { 122 | "name": "downloads/opentip-prototype.min.js", 123 | "regexs": [ 124 | "Opentip\\.version=\"###\"", 125 | "Opentip v###" 126 | ] 127 | }, 128 | { 129 | "name": "Gruntfile.coffee", 130 | "regexs": [ 131 | "Opentip v###" 132 | ] 133 | } 134 | 135 | ] 136 | } 137 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - 0.10 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contribute 2 | ========== 3 | 4 | The latest stable version is always in the **[master](https://github.com/enyo/opentip)** branch (which always 5 | points at the latest version tag). 6 | 7 | The latest development version is in the **[develop](https://github.com/enyo/opentip/tree/develop)** branch. 8 | 9 | > Use the develop branch if you want to contribute or test features. 10 | 11 | Please do also **send pull requests to the `develop` branch**. 12 | I will **not** merge pull requests to the `master` branch. 13 | 14 | 15 | Make sure that changes pass all [tests](#testing). 16 | 17 | 18 | ### Coffeescript & Stylus (-> Javascript & CSS) 19 | 20 | Opentip is written in [Coffeescript](http://coffeescript.org) and 21 | [Stylus](http://learnboost.github.com/stylus/) so *do not* make 22 | changes to the Javascript or CSS files 23 | 24 | **I will not merge requests written in Javascript or CSS.** 25 | 26 | Getting started 27 | --------------- 28 | 29 | You need node to compile and test Opentip. So [install node](http://nodejs.org) 30 | first if you haven't done so already. 31 | 32 | 33 | ### Building Opentip 34 | 35 | 36 | First you have to setup the node modules to build Opentip. Simply run this in 37 | the Opentip directory: 38 | 39 | ```bash 40 | $ npm install 41 | ``` 42 | 43 | This will setup [Grunt](http://gruntjs.com) so you can compile Coffeescript and 44 | Stylus and generate the download files. 45 | 46 | To get a list of available commands use `grunt -h`. 47 | 48 | The most important command is 49 | 50 | ```bash 51 | $ grunt watch 52 | ``` 53 | 54 | This will observe any change to a coffeescript or stylus file and compile it 55 | immediately. 56 | 57 | 58 | > Please only submit commits with changed `.coffee` and `.stylus` files and do 59 | > *not* include the compiled JS or CSS files. 60 | 61 | 62 | ### Testing 63 | 64 | To test the library make sure that the source has been compiled with `grunt js` 65 | (as mentioned before, use `grunt watch` to always stay up to date) and then 66 | either type `npm test` to run the tests on the command line, or open the 67 | file `test/test.html` in a browser. 68 | 69 | It should look like this: 70 | 71 | ![Tests screenshot](https://raw.github.com/enyo/opentip/develop/files/tests.png) 72 | 73 | All tests are located in `test/src` and are written in coffeescript. 74 | 75 | If you add a change, please make sure that all tests pass! 76 | 77 | -------------------------------------------------------------------------------- /Gruntfile.coffee: -------------------------------------------------------------------------------- 1 | module.exports = (grunt) -> 2 | 3 | grunt.initConfig 4 | pkg: grunt.file.readJSON "package.json" 5 | 6 | 7 | stylus: 8 | options: 9 | compress: false 10 | default: 11 | files: [ 12 | "css/opentip.css": "css/stylus/opentip.styl" 13 | ] 14 | 15 | coffee: 16 | default: 17 | expand: true 18 | options: 19 | bare: true 20 | cwd: "src/" 21 | src: [ "*.coffee" ] 22 | dest: "lib/" 23 | ext: ".js" 24 | 25 | test: 26 | files: 27 | "test/test.js": "test/src/*.coffee" 28 | 29 | watch: 30 | css: 31 | files: "css/stylus/*.styl" 32 | tasks: [ "css" ] 33 | options: nospawn: on 34 | js: 35 | files: "src/*.coffee" 36 | tasks: [ "js" ] 37 | options: nospawn: on 38 | test: 39 | files: [ 40 | "test/src/*.coffee" 41 | ] 42 | tasks: [ "coffee:test" ] 43 | options: nospawn: on 44 | 45 | 46 | curl: 47 | ".tmp-excanvas.js": "https://raw.github.com/enyo/excanvas/master/index.js" 48 | ".tmp-classlist.js": "https://raw.github.com/eligrey/classList.js/master/classList.js" 49 | ".tmp-addeventlistener.js": "https://gist.github.com/raw/4684216/c58a272ef9d9e0f55ea5e90ac313e3a3b2f2b7b3/eventListener.polyfill.js" 50 | 51 | clean: 52 | tmp: ".tmp-*" 53 | 54 | concat: 55 | js: 56 | files: 57 | "downloads/opentip-jquery.js": [ "lib/opentip.js", "lib/adapter-jquery.js" ] 58 | "downloads/opentip-jquery-excanvas.js": [ "downloads/opentip-jquery.js", ".tmp-excanvas.js" ] 59 | 60 | "downloads/opentip-prototype.js": [ "lib/opentip.js", "lib/adapter-prototype.js" ] 61 | "downloads/opentip-prototype-excanvas.js": [ "downloads/opentip-prototype.js", ".tmp-excanvas.js" ] 62 | 63 | "downloads/opentip-native.js": [ "lib/opentip.js", "lib/adapter-native.js", ".tmp-classlist.js", ".tmp-addeventlistener.js" ] 64 | "downloads/opentip-native-excanvas.js": [ "downloads/opentip-native.js", ".tmp-excanvas.js" ] 65 | 66 | 67 | uglify: 68 | options: 69 | banner: """ 70 | // Opentip v2.4.6 71 | // Copyright (c) 2009-2012 72 | // www.opentip.org 73 | // MIT Licensed 74 | 75 | """ 76 | js: 77 | files: [ 78 | "downloads/opentip-jquery.min.js": "downloads/opentip-jquery.js" 79 | "downloads/opentip-jquery-excanvas.min.js": "downloads/opentip-jquery-excanvas.js" 80 | "downloads/opentip-prototype.min.js": "downloads/opentip-prototype.js" 81 | "downloads/opentip-prototype-excanvas.min.js": "downloads/opentip-prototype-excanvas.js" 82 | "downloads/opentip-native.min.js": "downloads/opentip-native.js" 83 | "downloads/opentip-native-excanvas.min.js": "downloads/opentip-native-excanvas.js" 84 | ] 85 | 86 | 87 | grunt.loadNpmTasks "grunt-contrib-coffee" 88 | grunt.loadNpmTasks "grunt-contrib-stylus" 89 | grunt.loadNpmTasks "grunt-contrib-concat" 90 | grunt.loadNpmTasks "grunt-contrib-watch" 91 | grunt.loadNpmTasks "grunt-contrib-uglify" 92 | grunt.loadNpmTasks "grunt-contrib-clean" 93 | grunt.loadNpmTasks "grunt-curl" 94 | 95 | # Default tasks 96 | grunt.registerTask "default", [ "downloads" ] 97 | 98 | grunt.registerTask "css", "Compile the stylus files to css", [ "stylus" ] 99 | 100 | grunt.registerTask "js", "Compile coffeescript and create all download files", [ "coffee" ] 101 | 102 | grunt.registerTask "downloads", [ "css", "js", "curl", "concat", "clean", "uglify" ] 103 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: index.js components 2 | @component build 3 | 4 | rebuild: index.js components 5 | rm -fr build 6 | make build 7 | 8 | components: 9 | @component install 10 | 11 | clean: 12 | rm -fr build components 13 | 14 | downloads: 15 | ./downloads/generate.coffee 16 | 17 | release: 18 | cake build 19 | cake css 20 | make downloads 21 | 22 | all: 23 | clear 24 | make clean 25 | make build 26 | 27 | .PHONY: clean, downloads 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Opentip 2 | ======= 3 | 4 | [Opentip][opentip] is a free opensource Java-Script tooltip class. 5 | 6 | 7 | Features 8 | -------- 9 | 10 | It supports: 11 | 12 | - Stems (little pointers) 13 | - Automatic content download with [AJAX][ajax] 14 | - Different styles 15 | - Automatic repositioning of the tooltip if it's not in the viewport of the browser anymore 16 | - All kind of triggers (The tooltip can be triggered by mouse over, click, form submit,... everything you can think of really) 17 | - CSS3 Animations 18 | - Well tested, with over 200 unit tests 19 | 20 | As of Version 2.0 Opentip does **no longer depend on [Prototype]**. You can choose 21 | *any* adapter you want so you can work with the framework of your choice. 22 | 23 | Supported frameworks are: 24 | 25 | - Native. You can use this one if you don't use any framework. 26 | - [Ender] 27 | - [Component] 28 | - [jQuery] 29 | - [Prototype] 30 | 31 | 32 | > If you want to contribute, please read on in the [contribute](https://github.com/enyo/opentip/blob/master/CONTRIBUTING.md) 33 | > file. If you are migrating from version **1.x** please refer to the 34 | > [migration section](#migrating-from-opentip-1x-to-2x) 35 | 36 | ### Build status 37 | 38 | Master [![Build Status](https://travis-ci.org/enyo/opentip.png?branch=master)](https://travis-ci.org/enyo/opentip) 39 | 40 | Develop [![Build Status](https://travis-ci.org/enyo/opentip.png?branch=develop)](https://travis-ci.org/enyo/opentip) 41 | 42 | 43 | Installation 44 | ------------ 45 | 46 | ### jQuery, Prototype, Native 47 | 48 | Just download `lib/opentip.js` and `lib/adapter.FRAMEWORK.js` and include them 49 | in this order. You can also take the already minified and combined files in the 50 | `downloads/` folder. 51 | 52 | ### Component 53 | 54 | The easiest and recommended way to install *opentip* is with [component]. Just 55 | add `enyo/opentip` as dependency in your `component.json` and rebuild it. 56 | 57 | Simply requiring opentip then activates the tooltips: `require "opentip";` 58 | 59 | 60 | ### Ender 61 | 62 | If you prefer [ender] as package manager just install it like this: 63 | 64 | ```bash 65 | $ ender build opentip 66 | ``` 67 | 68 | ### Bower 69 | 70 | Another package manager supported is [bower]: 71 | 72 | ```bash 73 | $ bower install opentip 74 | ``` 75 | 76 | * * * 77 | 78 | You should include opentip's CSS as well. It's in `css/opentip.css`. (Except 79 | for [component] of course which automatically bundles the css.) 80 | 81 | * * * 82 | 83 | If you want to work it with <=IE8, you have to include excanvas as well. Please 84 | refer to the [installation guide](http://www.opentip.org/installation.html). 85 | 86 | Usage 87 | ----- 88 | 89 | *Version 2.4.6* 90 | 91 | With HTML data attributes: 92 | 93 | ```html 94 |
Click me
95 | ``` 96 | 97 | or with the Javascript API: 98 | 99 | ```js 100 | $('elementId').opentip('Content', { showOn: "click", ...options... }); 101 | ``` 102 | 103 | For the complete documentation please visit [www.opentip.org][opentip]. 104 | 105 | 106 | Future plans 107 | ------------ 108 | 109 | - ~~Become library independant. I'm currently working on 110 | extracting all prototype functionality, so I can switch library easily. The 111 | next library I'll support will be jquery, and then mootools.~~ 112 | 113 | - Add more skins. 114 | 115 | - ~~Add cooler loading animation.~~ 116 | 117 | - ~~Implement unit tests.~~ 118 | 119 | 120 | If you have ideas, please contact me! 121 | 122 | 123 | Contribute 124 | ---------- 125 | 126 | Please refer to the [CONTRIBUTING](https://github.com/enyo/opentip/blob/develop/CONTRIBUTING.md) readme. 127 | 128 | 129 | 130 | Migrating from Opentip 1.x to 2.x 131 | --------------------------------- 132 | 133 | Those are the major changes you should look out for when migrating from 1.x to 2.x: 134 | 135 | - There's no `Tip` or `Tips` object anymore. Everything is done through 136 | `Opentip` 137 | 138 | - The recommend way to create opentips now is to call 139 | `new Opentip(element, content, title, options)`, or with the framework of 140 | your choice (eg, [ender]: `$("#my-div").opentip(content, title options)`) 141 | 142 | - The instantiation of new tips inside an event (eg: `onclick`, `onmouseover`) 143 | is no longer supported! This would create new opentips everytime the event 144 | is fired. 145 | 146 | - `Opentip.debugging = true;` does no longer exist. Use `Opentip.debug = true;` 147 | 148 | - Positions are no longer of the weird form `[ "left", "top" ]` but simply 149 | strings like `"top left"` or `"right"` 150 | 151 | - `stem.size` has been dropped in favor of `stem.length` and `stem.base` 152 | 153 | - Most of the design is now done in JS since the whole thing is a canvas now. 154 | 155 | - The way close buttons are defined has completely changed. Please refer to the 156 | docs for more information. 157 | 158 | Tagging 159 | ------- 160 | 161 | Tagging in this project is done with my [tag script](http://github.com/enyo/tag). 162 | 163 | 164 | Authors 165 | ------- 166 | 167 | Opentip is written by Matias Meno.
168 | Original graphics by Tjandra Mayerhold. 169 | 170 | ### Contributors 171 | 172 | Thanks to the following people for providing bug reports, feature requests and fixes: 173 | 174 | - Torsten Saam 175 | - Aaron Peckham 176 | - Oguri 177 | - MaxKirillov 178 | - Nick Daugherty 179 | 180 | If I forgot somebody, please just tell me. 181 | 182 | ### Related projects 183 | 184 | You might also be interested in my [formwatcher](http://www.formwatcher.org/) or 185 | [dropzone](http://www.dropzonejs.com/). 186 | 187 | License 188 | ------- 189 | (The MIT License) 190 | 191 | Copyright (c) 2012 Matias Meno <m@tias.me>
192 | 193 | Permission is hereby granted, free of charge, to any person obtaining a copy of 194 | this software and associated documentation files (the "Software"), to deal in 195 | the Software without restriction, including without limitation the rights to 196 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 197 | of the Software, and to permit persons to whom the Software is furnished to do 198 | so, subject to the following conditions: 199 | 200 | The above copyright notice and this permission notice shall be included in all 201 | copies or substantial portions of the Software. 202 | 203 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 204 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 205 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 206 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 207 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 208 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 209 | SOFTWARE. 210 | 211 | [opentip]: http://www.opentip.org/ 212 | [prototype]: http://www.prototypejs.org/ 213 | [jquery]: http://jquery.com/ 214 | [ajax]: http://en.wikipedia.org/wiki/Ajax_(programming) 215 | [excanvas]: https://github.com/enyo/excanvas 216 | [ender]: http://ender.no.de 217 | [component]: https://github.com/component 218 | [bower]: https://github.com/twitter/bower#readme 219 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opentip", 3 | "version": "2.4.6", 4 | "main": [ 5 | "css/opentip.css", 6 | "lib/opentip.js", 7 | "lib/adapter-component.js" 8 | ], 9 | "dependencies": { 10 | "jquery": "~1.9.0" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opentip", 3 | "repo": "enyo/opentip", 4 | "version": "2.4.6", 5 | "description": "Free opensource tooltip class.", 6 | "keywords": [ "tooltip" ], 7 | "dependencies": { 8 | "component/jquery": "*" 9 | }, 10 | "styles": [ "css/opentip.css" ], 11 | "scripts": [ "index.js", "lib/opentip.js", "lib/adapter-component.js" ] 12 | } 13 | -------------------------------------------------------------------------------- /css/opentip.css: -------------------------------------------------------------------------------- 1 | .opentip-container, 2 | .opentip-container * { 3 | -webkit-box-sizing: border-box; 4 | -moz-box-sizing: border-box; 5 | box-sizing: border-box; 6 | } 7 | .opentip-container { 8 | position: absolute; 9 | max-width: 300px; 10 | z-index: 100; 11 | -webkit-transition: -webkit-transform 1s ease-in-out; 12 | -moz-transition: -moz-transform 1s ease-in-out; 13 | -o-transition: -o-transform 1s ease-in-out; 14 | -ms-transition: -ms-transform 1s ease-in-out; 15 | transition: transform 1s ease-in-out; 16 | pointer-events: none; 17 | -webkit-transform: translateX(0) translateY(0); 18 | -moz-transform: translateX(0) translateY(0); 19 | -o-transform: translateX(0) translateY(0); 20 | -ms-transform: translateX(0) translateY(0); 21 | transform: translateX(0) translateY(0); 22 | } 23 | .opentip-container.ot-fixed.ot-hidden.stem-top.stem-center, 24 | .opentip-container.ot-fixed.ot-going-to-show.stem-top.stem-center, 25 | .opentip-container.ot-fixed.ot-hiding.stem-top.stem-center { 26 | -webkit-transform: translateY(-5px); 27 | -moz-transform: translateY(-5px); 28 | -o-transform: translateY(-5px); 29 | -ms-transform: translateY(-5px); 30 | transform: translateY(-5px); 31 | } 32 | .opentip-container.ot-fixed.ot-hidden.stem-top.stem-right, 33 | .opentip-container.ot-fixed.ot-going-to-show.stem-top.stem-right, 34 | .opentip-container.ot-fixed.ot-hiding.stem-top.stem-right { 35 | -webkit-transform: translateY(-5px) translateX(5px); 36 | -moz-transform: translateY(-5px) translateX(5px); 37 | -o-transform: translateY(-5px) translateX(5px); 38 | -ms-transform: translateY(-5px) translateX(5px); 39 | transform: translateY(-5px) translateX(5px); 40 | } 41 | .opentip-container.ot-fixed.ot-hidden.stem-middle.stem-right, 42 | .opentip-container.ot-fixed.ot-going-to-show.stem-middle.stem-right, 43 | .opentip-container.ot-fixed.ot-hiding.stem-middle.stem-right { 44 | -webkit-transform: translateX(5px); 45 | -moz-transform: translateX(5px); 46 | -o-transform: translateX(5px); 47 | -ms-transform: translateX(5px); 48 | transform: translateX(5px); 49 | } 50 | .opentip-container.ot-fixed.ot-hidden.stem-bottom.stem-right, 51 | .opentip-container.ot-fixed.ot-going-to-show.stem-bottom.stem-right, 52 | .opentip-container.ot-fixed.ot-hiding.stem-bottom.stem-right { 53 | -webkit-transform: translateY(5px) translateX(5px); 54 | -moz-transform: translateY(5px) translateX(5px); 55 | -o-transform: translateY(5px) translateX(5px); 56 | -ms-transform: translateY(5px) translateX(5px); 57 | transform: translateY(5px) translateX(5px); 58 | } 59 | .opentip-container.ot-fixed.ot-hidden.stem-bottom.stem-center, 60 | .opentip-container.ot-fixed.ot-going-to-show.stem-bottom.stem-center, 61 | .opentip-container.ot-fixed.ot-hiding.stem-bottom.stem-center { 62 | -webkit-transform: translateY(5px); 63 | -moz-transform: translateY(5px); 64 | -o-transform: translateY(5px); 65 | -ms-transform: translateY(5px); 66 | transform: translateY(5px); 67 | } 68 | .opentip-container.ot-fixed.ot-hidden.stem-bottom.stem-left, 69 | .opentip-container.ot-fixed.ot-going-to-show.stem-bottom.stem-left, 70 | .opentip-container.ot-fixed.ot-hiding.stem-bottom.stem-left { 71 | -webkit-transform: translateY(5px) translateX(-5px); 72 | -moz-transform: translateY(5px) translateX(-5px); 73 | -o-transform: translateY(5px) translateX(-5px); 74 | -ms-transform: translateY(5px) translateX(-5px); 75 | transform: translateY(5px) translateX(-5px); 76 | } 77 | .opentip-container.ot-fixed.ot-hidden.stem-middle.stem-left, 78 | .opentip-container.ot-fixed.ot-going-to-show.stem-middle.stem-left, 79 | .opentip-container.ot-fixed.ot-hiding.stem-middle.stem-left { 80 | -webkit-transform: translateX(-5px); 81 | -moz-transform: translateX(-5px); 82 | -o-transform: translateX(-5px); 83 | -ms-transform: translateX(-5px); 84 | transform: translateX(-5px); 85 | } 86 | .opentip-container.ot-fixed.ot-hidden.stem-top.stem-left, 87 | .opentip-container.ot-fixed.ot-going-to-show.stem-top.stem-left, 88 | .opentip-container.ot-fixed.ot-hiding.stem-top.stem-left { 89 | -webkit-transform: translateY(-5px) translateX(-5px); 90 | -moz-transform: translateY(-5px) translateX(-5px); 91 | -o-transform: translateY(-5px) translateX(-5px); 92 | -ms-transform: translateY(-5px) translateX(-5px); 93 | transform: translateY(-5px) translateX(-5px); 94 | } 95 | .opentip-container.ot-fixed .opentip { 96 | pointer-events: auto; 97 | } 98 | .opentip-container.ot-hidden { 99 | display: none; 100 | } 101 | .opentip-container .opentip { 102 | position: relative; 103 | font-size: 13px; 104 | line-height: 120%; 105 | padding: 9px 14px; 106 | color: #4f4b47; 107 | text-shadow: -1px -1px 0px rgba(255,255,255,0.2); 108 | } 109 | .opentip-container .opentip .header { 110 | margin: 0; 111 | padding: 0; 112 | } 113 | .opentip-container .opentip .ot-close { 114 | pointer-events: auto; 115 | display: block; 116 | position: absolute; 117 | top: -12px; 118 | left: 60px; 119 | color: rgba(0,0,0,0.5); 120 | background: rgba(0,0,0,0); 121 | text-decoration: none; 122 | } 123 | .opentip-container .opentip .ot-close span { 124 | display: none; 125 | } 126 | .opentip-container .opentip .ot-loading-indicator { 127 | display: none; 128 | } 129 | .opentip-container.ot-loading .ot-loading-indicator { 130 | width: 30px; 131 | height: 30px; 132 | font-size: 30px; 133 | line-height: 30px; 134 | font-weight: bold; 135 | display: block; 136 | } 137 | .opentip-container.ot-loading .ot-loading-indicator span { 138 | display: block; 139 | -webkit-animation: otloading 2s linear infinite; 140 | -moz-animation: otloading 2s linear infinite; 141 | -o-animation: otloading 2s linear infinite; 142 | -ms-animation: otloading 2s linear infinite; 143 | animation: otloading 2s linear infinite; 144 | text-align: center; 145 | } 146 | .opentip-container.style-dark .opentip, 147 | .opentip-container.style-alert .opentip { 148 | color: #f8f8f8; 149 | text-shadow: 1px 1px 0px rgba(0,0,0,0.2); 150 | } 151 | .opentip-container.style-glass .opentip { 152 | padding: 15px 25px; 153 | color: #317cc5; 154 | text-shadow: 1px 1px 8px rgba(0,94,153,0.3); 155 | } 156 | .opentip-container.ot-hide-effect-fade { 157 | -webkit-transition: -webkit-transform 0.5s ease-in-out, opacity 1s ease-in-out; 158 | -moz-transition: -moz-transform 0.5s ease-in-out, opacity 1s ease-in-out; 159 | -o-transition: -o-transform 0.5s ease-in-out, opacity 1s ease-in-out; 160 | -ms-transition: -ms-transform 0.5s ease-in-out, opacity 1s ease-in-out; 161 | transition: transform 0.5s ease-in-out, opacity 1s ease-in-out; 162 | opacity: 1; 163 | -ms-filter: none; 164 | filter: none; 165 | } 166 | .opentip-container.ot-hide-effect-fade.ot-hiding { 167 | opacity: 0; 168 | filter: alpha(opacity=0); 169 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 170 | } 171 | .opentip-container.ot-show-effect-appear.ot-going-to-show, 172 | .opentip-container.ot-show-effect-appear.ot-showing { 173 | -webkit-transition: -webkit-transform 0.5s ease-in-out, opacity 1s ease-in-out; 174 | -moz-transition: -moz-transform 0.5s ease-in-out, opacity 1s ease-in-out; 175 | -o-transition: -o-transform 0.5s ease-in-out, opacity 1s ease-in-out; 176 | -ms-transition: -ms-transform 0.5s ease-in-out, opacity 1s ease-in-out; 177 | transition: transform 0.5s ease-in-out, opacity 1s ease-in-out; 178 | } 179 | .opentip-container.ot-show-effect-appear.ot-going-to-show { 180 | opacity: 0; 181 | filter: alpha(opacity=0); 182 | -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)"; 183 | } 184 | .opentip-container.ot-show-effect-appear.ot-showing { 185 | opacity: 1; 186 | -ms-filter: none; 187 | filter: none; 188 | } 189 | .opentip-container.ot-show-effect-appear.ot-visible { 190 | opacity: 1; 191 | -ms-filter: none; 192 | filter: none; 193 | } 194 | @-moz-keyframes otloading { 195 | 0% { 196 | -webkit-transform: rotate(0deg); 197 | -moz-transform: rotate(0deg); 198 | -o-transform: rotate(0deg); 199 | -ms-transform: rotate(0deg); 200 | transform: rotate(0deg); 201 | } 202 | 203 | 100% { 204 | -webkit-transform: rotate(360deg); 205 | -moz-transform: rotate(360deg); 206 | -o-transform: rotate(360deg); 207 | -ms-transform: rotate(360deg); 208 | transform: rotate(360deg); 209 | } 210 | } 211 | @-webkit-keyframes otloading { 212 | 0% { 213 | -webkit-transform: rotate(0deg); 214 | -moz-transform: rotate(0deg); 215 | -o-transform: rotate(0deg); 216 | -ms-transform: rotate(0deg); 217 | transform: rotate(0deg); 218 | } 219 | 220 | 100% { 221 | -webkit-transform: rotate(360deg); 222 | -moz-transform: rotate(360deg); 223 | -o-transform: rotate(360deg); 224 | -ms-transform: rotate(360deg); 225 | transform: rotate(360deg); 226 | } 227 | } 228 | @-o-keyframes otloading { 229 | 0% { 230 | -webkit-transform: rotate(0deg); 231 | -moz-transform: rotate(0deg); 232 | -o-transform: rotate(0deg); 233 | -ms-transform: rotate(0deg); 234 | transform: rotate(0deg); 235 | } 236 | 237 | 100% { 238 | -webkit-transform: rotate(360deg); 239 | -moz-transform: rotate(360deg); 240 | -o-transform: rotate(360deg); 241 | -ms-transform: rotate(360deg); 242 | transform: rotate(360deg); 243 | } 244 | } 245 | @-ms-keyframes otloading { 246 | 0% { 247 | -webkit-transform: rotate(0deg); 248 | -moz-transform: rotate(0deg); 249 | -o-transform: rotate(0deg); 250 | -ms-transform: rotate(0deg); 251 | transform: rotate(0deg); 252 | } 253 | 254 | 100% { 255 | -webkit-transform: rotate(360deg); 256 | -moz-transform: rotate(360deg); 257 | -o-transform: rotate(360deg); 258 | -ms-transform: rotate(360deg); 259 | transform: rotate(360deg); 260 | } 261 | } 262 | @keyframes otloading { 263 | 0% { 264 | -webkit-transform: rotate(0deg); 265 | -moz-transform: rotate(0deg); 266 | -o-transform: rotate(0deg); 267 | -ms-transform: rotate(0deg); 268 | transform: rotate(0deg); 269 | } 270 | 271 | 100% { 272 | -webkit-transform: rotate(360deg); 273 | -moz-transform: rotate(360deg); 274 | -o-transform: rotate(360deg); 275 | -ms-transform: rotate(360deg); 276 | transform: rotate(360deg); 277 | } 278 | } 279 | -------------------------------------------------------------------------------- /css/stylus/opentip.styl: -------------------------------------------------------------------------------- 1 | @import "nib" 2 | 3 | 4 | @keyframes otloading 5 | from 6 | transform rotate(0deg) 7 | to 8 | transform rotate(360deg) 9 | 10 | 11 | .opentip-container, .opentip-container * 12 | box-sizing border-box 13 | 14 | .opentip-container 15 | position absolute 16 | max-width 300px 17 | z-index 100 18 | transition transform 1s ease-in-out 19 | pointer-events none 20 | transform translateX(0) translateY(0) 21 | 22 | 23 | 24 | &.ot-fixed 25 | &.ot-hidden 26 | &.ot-going-to-show 27 | &.ot-hiding 28 | &.stem-top.stem-center 29 | transform translateY(-5px) 30 | &.stem-top.stem-right 31 | transform translateY(-5px) translateX(5px) 32 | &.stem-middle.stem-right 33 | transform translateX(5px) 34 | &.stem-bottom.stem-right 35 | transform translateY(5px) translateX(5px) 36 | &.stem-bottom.stem-center 37 | transform translateY(5px) 38 | &.stem-bottom.stem-left 39 | transform translateY(5px) translateX(-5px) 40 | &.stem-middle.stem-left 41 | transform translateX(-5px) 42 | &.stem-top.stem-left 43 | transform translateY(-5px) translateX(-5px) 44 | 45 | &.ot-fixed 46 | // When it's fixed, the close button, and links inside the tooltip should 47 | // be clickable 48 | .opentip 49 | pointer-events auto 50 | 51 | &.ot-hidden 52 | display none 53 | 54 | .opentip 55 | position relative 56 | font-size 13px 57 | line-height 120% 58 | padding 9px 14px 59 | color #4F4B47 60 | text-shadow -1px -1px 0px rgba(255, 255, 255, 0.2) 61 | 62 | .header 63 | margin 0 64 | padding 0 65 | 66 | .ot-close 67 | pointer-events auto 68 | display block 69 | absolute top -12px left 60px 70 | color rgba(0, 0, 0, 0.5) 71 | background rgba(0, 0, 0, 0) // So IE9 renders this as clickable 72 | text-decoration none 73 | 74 | span 75 | display none 76 | 77 | .ot-loading-indicator 78 | display none 79 | 80 | &.ot-loading 81 | .ot-loading-indicator 82 | width 30px 83 | height @width 84 | font-size @width 85 | line-height @width 86 | font-weight bold 87 | display block 88 | span 89 | display block 90 | animation otloading 2s linear infinite 91 | text-align center 92 | 93 | 94 | 95 | // Styles 96 | // ====== 97 | &.style-dark 98 | &.style-alert 99 | .opentip 100 | color #f8f8f8 101 | text-shadow 1px 1px 0px rgba(0, 0, 0, 0.2) 102 | 103 | &.style-glass 104 | .opentip 105 | padding 15px 25px 106 | color #317CC5 107 | text-shadow 1px 1px 8px rgba(0, 94, 153, 0.3) 108 | 109 | 110 | // Effects 111 | // ======= 112 | &.ot-hide-effect-fade 113 | transition transform 0.5s ease-in-out, opacity 1s ease-in-out 114 | opacity 1 115 | &.ot-hiding 116 | opacity 0 117 | 118 | &.ot-show-effect-appear 119 | &.ot-going-to-show 120 | &.ot-showing 121 | transition transform 0.5s ease-in-out, opacity 1s ease-in-out 122 | &.ot-going-to-show 123 | opacity 0 124 | &.ot-showing 125 | opacity 1 126 | &.ot-visible 127 | opacity 1 128 | -------------------------------------------------------------------------------- /docs/adapter.jquery.html: -------------------------------------------------------------------------------- 1 | adapter.jquery.coffee
Jump To …

adapter.jquery.coffee

jQuery Opentip Adapter

2 | 3 |

Uses jQuery

Because $ is my favorite character

(($) ->

Augment jQuery

  $.fn.opentip = (content, title, options) ->
 4 |     new Opentip this, content, title, options

And now the class

  class Adapter
 5 | 
 6 |     name: "jquery"

Simply using $.domReady

    domReady: (callback) -> $ callback

DOM

Using bonzo to create html

    create: (html) -> $ html

Element handling

Wraps the element in ender

    wrap: (element) ->
 7 |       element = $ element
 8 |       throw new Error "Multiple elements provided." if element.length > 1
 9 |       element

Returns the unwrapped element

    unwrap: (element) -> $(element)[0]

Returns the tag name of the element

    tagName: (element) -> @unwrap(element).tagName

Returns or sets the given attribute of element

10 | 11 |

It's important not to simply forward name and value because the value 12 | is set whether or not the value argument is present

    attr: (element, args...) -> $(element).attr args...

Returns or sets the given data of element 13 | It's important not to simply forward name and value because the value 14 | is set whether or not the value argument is present

    data: (element, args...) -> $(element).data args...

Finds elements by selector

    find: (element, selector) -> $(element).find selector

Finds all elements by selector

    findAll: -> @find.apply @, arguments

Updates the content of the element

    update: (element, content, escape) ->
15 |       element = $ element
16 |       if escape
17 |         element.text content
18 |       else
19 |         element.html content

Appends given child to element

    append: (element, child) -> $(element).append child

Add a class

    addClass: (element, className) -> $(element).addClass className

Remove a class

    removeClass: (element, className) -> $(element).removeClass className

Set given css properties

    css: (element, properties) -> $(element).css properties

Returns an object with given dimensions

    dimensions: (element) ->
20 |       {
21 |         width: $(element).outerWidth()
22 |         height: $(element).outerHeight()
23 |       }

Returns the scroll offsets of current document

    scrollOffset: ->
24 |       [
25 |         window.pageXOffset or document.documentElement.scrollLeft or document.body.scrollLeft
26 |         window.pageYOffset or document.documentElement.scrollTop or document.body.scrollTop
27 |       ]

Returns the dimensions of the viewport (currently visible browser area)

    viewportDimensions: ->
28 |       {
29 |         width: document.documentElement.clientWidth
30 |         height: document.documentElement.clientHeight
31 |       }

Returns an object with x and y

    mousePosition: (e) ->
32 |       return null unless e?
33 |       x: e.pageX, y: e.pageY

Returns the offset of the element

    offset: (element) ->
34 |       offset = $(element).offset()
35 |       {
36 |         left: offset.left
37 |         top: offset.top
38 |       }

Observe given eventName

    observe: (element, eventName, observer) -> $(element).bind eventName, observer

Stop observing event

    stopObserving: (element, eventName, observer) -> $(element).unbind eventName, observer

Perform an AJAX request and call the appropriate callbacks.

    ajax: (options) ->
39 |       throw new Error "No url provided" unless options.url?
40 |       $.ajax(
41 |         url: options.url
42 |         type: options.method?.toUpperCase() ? "GET"
43 |       )
44 |         .done((content) -> options.onSuccess? content)
45 |         .fail((request) -> options.onError? "Server responded with status #{request.status}")
46 |         .always(-> options.onComplete?())

Utility functions

Creates a shallow copy of the object

    clone: (object) -> $.extend { }, object

Copies all properties from sources to target

    extend: (target, sources...) -> $.extend target, sources...

Add the adapter to the list

  Opentip.addAdapter new Adapter
47 | 
48 | )(jQuery)
49 | 
50 | 
-------------------------------------------------------------------------------- /docs/adapter.prototype.html: -------------------------------------------------------------------------------- 1 | adapter.prototype.coffee
Jump To …

adapter.prototype.coffee

$ = ender
 2 | 
 3 | 
 4 | class Adapter
 5 | 
 6 |   name: "prototype"

Simply using $.domReady

  domReady: (callback) -> Event.observe window, "dom:loaded", callback

Using bonzo to create html

  create: (html) -> $ html

Mimics scriptaculous Builder.node behaviour 7 | element: (tagName, attributes, children) -> 8 | if Object.isArray(attributes) or Object.isString(attributes) or Object.isElement(attributes) 9 | children = attributes 10 | attributes = null 11 | element = new Element(tagName, attributes or {})

    

# This is a prototype 1.6 bug, that doesn't apply the className to IE8 elements. 12 | # Thanks to Alexander Shakhnovsky for finding the bug, and pinpointing the problem. 13 | if attributes and attributes["className"] 14 | attributes["className"].split(" ").each (classname) -> 15 | element.addClassName classname

if children 16 | if Object.isArray(children) 17 | children.each (child) -> 18 | element.insert bottom: child

else
19 |   element.insert bottom: children
20 | 
21 | 22 |

element

adapter = new Adapter
23 | 
24 | Opentip.addAdapter adapter
25 | 
26 | 
-------------------------------------------------------------------------------- /docs/docco.css: -------------------------------------------------------------------------------- 1 | /*--------------------- Layout and Typography ----------------------------*/ 2 | body { 3 | font-family: 'Palatino Linotype', 'Book Antiqua', Palatino, FreeSerif, serif; 4 | font-size: 15px; 5 | line-height: 22px; 6 | color: #252519; 7 | margin: 0; padding: 0; 8 | } 9 | a { 10 | color: #261a3b; 11 | } 12 | a:visited { 13 | color: #261a3b; 14 | } 15 | p { 16 | margin: 0 0 15px 0; 17 | } 18 | h1, h2, h3, h4, h5, h6 { 19 | margin: 0px 0 15px 0; 20 | } 21 | h1 { 22 | margin-top: 40px; 23 | } 24 | #container { 25 | position: relative; 26 | } 27 | #background { 28 | position: fixed; 29 | top: 0; left: 525px; right: 0; bottom: 0; 30 | background: #f5f5ff; 31 | border-left: 1px solid #e5e5ee; 32 | z-index: -1; 33 | } 34 | #jump_to, #jump_page { 35 | background: white; 36 | -webkit-box-shadow: 0 0 25px #777; -moz-box-shadow: 0 0 25px #777; 37 | -webkit-border-bottom-left-radius: 5px; -moz-border-radius-bottomleft: 5px; 38 | font: 10px Arial; 39 | text-transform: uppercase; 40 | cursor: pointer; 41 | text-align: right; 42 | } 43 | #jump_to, #jump_wrapper { 44 | position: fixed; 45 | right: 0; top: 0; 46 | padding: 5px 10px; 47 | } 48 | #jump_wrapper { 49 | padding: 0; 50 | display: none; 51 | } 52 | #jump_to:hover #jump_wrapper { 53 | display: block; 54 | } 55 | #jump_page { 56 | padding: 5px 0 3px; 57 | margin: 0 0 25px 25px; 58 | } 59 | #jump_page .source { 60 | display: block; 61 | padding: 5px 10px; 62 | text-decoration: none; 63 | border-top: 1px solid #eee; 64 | } 65 | #jump_page .source:hover { 66 | background: #f5f5ff; 67 | } 68 | #jump_page .source:first-child { 69 | } 70 | table td { 71 | border: 0; 72 | outline: 0; 73 | } 74 | td.docs, th.docs { 75 | max-width: 450px; 76 | min-width: 450px; 77 | min-height: 5px; 78 | padding: 10px 25px 1px 50px; 79 | overflow-x: hidden; 80 | vertical-align: top; 81 | text-align: left; 82 | } 83 | .docs pre { 84 | margin: 15px 0 15px; 85 | padding-left: 15px; 86 | } 87 | .docs p tt, .docs p code { 88 | background: #f8f8ff; 89 | border: 1px solid #dedede; 90 | font-size: 12px; 91 | padding: 0 0.2em; 92 | } 93 | .pilwrap { 94 | position: relative; 95 | } 96 | .pilcrow { 97 | font: 12px Arial; 98 | text-decoration: none; 99 | color: #454545; 100 | position: absolute; 101 | top: 3px; left: -20px; 102 | padding: 1px 2px; 103 | opacity: 0; 104 | -webkit-transition: opacity 0.2s linear; 105 | } 106 | td.docs:hover .pilcrow { 107 | opacity: 1; 108 | } 109 | td.code, th.code { 110 | padding: 14px 15px 16px 25px; 111 | width: 100%; 112 | vertical-align: top; 113 | background: #f5f5ff; 114 | border-left: 1px solid #e5e5ee; 115 | } 116 | pre, tt, code { 117 | font-size: 12px; line-height: 18px; 118 | font-family: Monaco, Consolas, "Lucida Console", monospace; 119 | margin: 0; padding: 0; 120 | } 121 | 122 | 123 | /*---------------------- Syntax Highlighting -----------------------------*/ 124 | td.linenos { background-color: #f0f0f0; padding-right: 10px; } 125 | span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; } 126 | body .hll { background-color: #ffffcc } 127 | body .c { color: #408080; font-style: italic } /* Comment */ 128 | body .err { border: 1px solid #FF0000 } /* Error */ 129 | body .k { color: #954121 } /* Keyword */ 130 | body .o { color: #666666 } /* Operator */ 131 | body .cm { color: #408080; font-style: italic } /* Comment.Multiline */ 132 | body .cp { color: #BC7A00 } /* Comment.Preproc */ 133 | body .c1 { color: #408080; font-style: italic } /* Comment.Single */ 134 | body .cs { color: #408080; font-style: italic } /* Comment.Special */ 135 | body .gd { color: #A00000 } /* Generic.Deleted */ 136 | body .ge { font-style: italic } /* Generic.Emph */ 137 | body .gr { color: #FF0000 } /* Generic.Error */ 138 | body .gh { color: #000080; font-weight: bold } /* Generic.Heading */ 139 | body .gi { color: #00A000 } /* Generic.Inserted */ 140 | body .go { color: #808080 } /* Generic.Output */ 141 | body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ 142 | body .gs { font-weight: bold } /* Generic.Strong */ 143 | body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ 144 | body .gt { color: #0040D0 } /* Generic.Traceback */ 145 | body .kc { color: #954121 } /* Keyword.Constant */ 146 | body .kd { color: #954121; font-weight: bold } /* Keyword.Declaration */ 147 | body .kn { color: #954121; font-weight: bold } /* Keyword.Namespace */ 148 | body .kp { color: #954121 } /* Keyword.Pseudo */ 149 | body .kr { color: #954121; font-weight: bold } /* Keyword.Reserved */ 150 | body .kt { color: #B00040 } /* Keyword.Type */ 151 | body .m { color: #666666 } /* Literal.Number */ 152 | body .s { color: #219161 } /* Literal.String */ 153 | body .na { color: #7D9029 } /* Name.Attribute */ 154 | body .nb { color: #954121 } /* Name.Builtin */ 155 | body .nc { color: #0000FF; font-weight: bold } /* Name.Class */ 156 | body .no { color: #880000 } /* Name.Constant */ 157 | body .nd { color: #AA22FF } /* Name.Decorator */ 158 | body .ni { color: #999999; font-weight: bold } /* Name.Entity */ 159 | body .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ 160 | body .nf { color: #0000FF } /* Name.Function */ 161 | body .nl { color: #A0A000 } /* Name.Label */ 162 | body .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ 163 | body .nt { color: #954121; font-weight: bold } /* Name.Tag */ 164 | body .nv { color: #19469D } /* Name.Variable */ 165 | body .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ 166 | body .w { color: #bbbbbb } /* Text.Whitespace */ 167 | body .mf { color: #666666 } /* Literal.Number.Float */ 168 | body .mh { color: #666666 } /* Literal.Number.Hex */ 169 | body .mi { color: #666666 } /* Literal.Number.Integer */ 170 | body .mo { color: #666666 } /* Literal.Number.Oct */ 171 | body .sb { color: #219161 } /* Literal.String.Backtick */ 172 | body .sc { color: #219161 } /* Literal.String.Char */ 173 | body .sd { color: #219161; font-style: italic } /* Literal.String.Doc */ 174 | body .s2 { color: #219161 } /* Literal.String.Double */ 175 | body .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ 176 | body .sh { color: #219161 } /* Literal.String.Heredoc */ 177 | body .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ 178 | body .sx { color: #954121 } /* Literal.String.Other */ 179 | body .sr { color: #BB6688 } /* Literal.String.Regex */ 180 | body .s1 { color: #219161 } /* Literal.String.Single */ 181 | body .ss { color: #19469D } /* Literal.String.Symbol */ 182 | body .bp { color: #954121 } /* Name.Builtin.Pseudo */ 183 | body .vc { color: #19469D } /* Name.Variable.Class */ 184 | body .vg { color: #19469D } /* Name.Variable.Global */ 185 | body .vi { color: #19469D } /* Name.Variable.Instance */ 186 | body .il { color: #666666 } /* Literal.Number.Integer.Long */ -------------------------------------------------------------------------------- /downloads/readme.md: -------------------------------------------------------------------------------- 1 | # Downloads 2 | 3 | Choose the **one** file that suits your needs. If you don't intend to support IE8 4 | (or include excanvas yourself) you don't need the `-excanvas` version. 5 | 6 | You'll probably want the `.min.js` version unless you count on debugging Opentip. 7 | 8 | ## Generate downloads 9 | 10 | To generate downloads you need to run `npm install` once from within the 11 | root folder, and then `grunt js`. -------------------------------------------------------------------------------- /files/close-button-angle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyo/opentip/4913cbf1f543609a009d36f9bbc72f3f110d1ab9/files/close-button-angle.png -------------------------------------------------------------------------------- /files/explanations.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyo/opentip/4913cbf1f543609a009d36f9bbc72f3f110d1ab9/files/explanations.psd -------------------------------------------------------------------------------- /files/tests.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enyo/opentip/4913cbf1f543609a009d36f9bbc72f3f110d1ab9/files/tests.png -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2012 Matias Meno 2 | 3 | 4 | // The index.js file for component 5 | var Opentip = require("./lib/opentip.js"); 6 | 7 | 8 | var Adapter = require("./lib/adapter-component.js"); 9 | 10 | // Add the adapter to the list 11 | Opentip.addAdapter(new Adapter()); 12 | 13 | 14 | // Exposing the Opentip class 15 | module.exports = Opentip; -------------------------------------------------------------------------------- /lib/adapter-component.js: -------------------------------------------------------------------------------- 1 | var $, Adapter, _ref, 2 | __slice = [].slice; 3 | 4 | $ = (_ref = window.jQuery) != null ? _ref : require("jquery"); 5 | 6 | module.exports = Adapter = (function() { 7 | function Adapter() {} 8 | 9 | Adapter.prototype.name = "component"; 10 | 11 | Adapter.prototype.domReady = function(callback) { 12 | return $(callback); 13 | }; 14 | 15 | Adapter.prototype.create = function(html) { 16 | return $(html); 17 | }; 18 | 19 | Adapter.prototype.wrap = function(element) { 20 | element = $(element); 21 | if (element.length > 1) { 22 | throw new Error("Multiple elements provided."); 23 | } 24 | return element; 25 | }; 26 | 27 | Adapter.prototype.unwrap = function(element) { 28 | return $(element)[0]; 29 | }; 30 | 31 | Adapter.prototype.tagName = function(element) { 32 | return this.unwrap(element).tagName; 33 | }; 34 | 35 | Adapter.prototype.attr = function() { 36 | var args, element, _ref1; 37 | 38 | element = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 39 | return (_ref1 = $(element)).attr.apply(_ref1, args); 40 | }; 41 | 42 | Adapter.prototype.data = function() { 43 | var args, element, _ref1; 44 | 45 | element = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 46 | return (_ref1 = $(element)).data.apply(_ref1, args); 47 | }; 48 | 49 | Adapter.prototype.find = function(element, selector) { 50 | return $(element).find(selector)[0]; 51 | }; 52 | 53 | Adapter.prototype.findAll = function(element, selector) { 54 | return $(element).find(selector); 55 | }; 56 | 57 | Adapter.prototype.update = function(element, content, escape) { 58 | element = $(element); 59 | if (escape) { 60 | return element.text(content); 61 | } else { 62 | return element.html(content); 63 | } 64 | }; 65 | 66 | Adapter.prototype.append = function(element, child) { 67 | return $(element).append(child); 68 | }; 69 | 70 | Adapter.prototype.remove = function(element) { 71 | return $(element).remove(); 72 | }; 73 | 74 | Adapter.prototype.addClass = function(element, className) { 75 | return $(element).addClass(className); 76 | }; 77 | 78 | Adapter.prototype.removeClass = function(element, className) { 79 | return $(element).removeClass(className); 80 | }; 81 | 82 | Adapter.prototype.css = function(element, properties) { 83 | return $(element).css(properties); 84 | }; 85 | 86 | Adapter.prototype.dimensions = function(element) { 87 | return { 88 | width: $(element).outerWidth(), 89 | height: $(element).outerHeight() 90 | }; 91 | }; 92 | 93 | Adapter.prototype.scrollOffset = function() { 94 | return [window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop]; 95 | }; 96 | 97 | Adapter.prototype.viewportDimensions = function() { 98 | return { 99 | width: document.documentElement.clientWidth, 100 | height: document.documentElement.clientHeight 101 | }; 102 | }; 103 | 104 | Adapter.prototype.mousePosition = function(e) { 105 | if (e == null) { 106 | return null; 107 | } 108 | return { 109 | x: e.pageX, 110 | y: e.pageY 111 | }; 112 | }; 113 | 114 | Adapter.prototype.offset = function(element) { 115 | var offset; 116 | 117 | offset = $(element).offset(); 118 | return { 119 | left: offset.left, 120 | top: offset.top 121 | }; 122 | }; 123 | 124 | Adapter.prototype.observe = function(element, eventName, observer) { 125 | return $(element).bind(eventName, observer); 126 | }; 127 | 128 | Adapter.prototype.stopObserving = function(element, eventName, observer) { 129 | return $(element).unbind(eventName, observer); 130 | }; 131 | 132 | Adapter.prototype.ajax = function(options) { 133 | var _ref1, _ref2; 134 | 135 | if (options.url == null) { 136 | throw new Error("No url provided"); 137 | } 138 | return $.ajax({ 139 | url: options.url, 140 | type: (_ref1 = (_ref2 = options.method) != null ? _ref2.toUpperCase() : void 0) != null ? _ref1 : "GET" 141 | }).done(function(content) { 142 | return typeof options.onSuccess === "function" ? options.onSuccess(content) : void 0; 143 | }).fail(function(request) { 144 | return typeof options.onError === "function" ? options.onError("Server responded with status " + request.status) : void 0; 145 | }).always(function() { 146 | return typeof options.onComplete === "function" ? options.onComplete() : void 0; 147 | }); 148 | }; 149 | 150 | Adapter.prototype.clone = function(object) { 151 | return $.extend({}, object); 152 | }; 153 | 154 | Adapter.prototype.extend = function() { 155 | var sources, target; 156 | 157 | target = arguments[0], sources = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 158 | return $.extend.apply($, [target].concat(__slice.call(sources))); 159 | }; 160 | 161 | return Adapter; 162 | 163 | })(); 164 | -------------------------------------------------------------------------------- /lib/adapter-ender.js: -------------------------------------------------------------------------------- 1 | var __slice = [].slice, 2 | __hasProp = {}.hasOwnProperty; 3 | 4 | (function($) { 5 | var Adapter, bean, reqwest; 6 | 7 | bean = require("bean"); 8 | reqwest = require("reqwest"); 9 | $.ender({ 10 | opentip: function(content, title, options) { 11 | return new Opentip(this, content, title, options); 12 | } 13 | }, true); 14 | Adapter = (function() { 15 | function Adapter() {} 16 | 17 | Adapter.prototype.name = "ender"; 18 | 19 | Adapter.prototype.domReady = function(callback) { 20 | return $.domReady(callback); 21 | }; 22 | 23 | Adapter.prototype.create = function(html) { 24 | return $(html); 25 | }; 26 | 27 | Adapter.prototype.wrap = function(element) { 28 | element = $(element); 29 | if (element.length > 1) { 30 | throw new Error("Multiple elements provided."); 31 | } 32 | return element; 33 | }; 34 | 35 | Adapter.prototype.unwrap = function(element) { 36 | return $(element).get(0); 37 | }; 38 | 39 | Adapter.prototype.tagName = function(element) { 40 | return this.unwrap(element).tagName; 41 | }; 42 | 43 | Adapter.prototype.attr = function() { 44 | var args, element, _ref; 45 | 46 | element = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 47 | return (_ref = $(element)).attr.apply(_ref, args); 48 | }; 49 | 50 | Adapter.prototype.data = function() { 51 | var args, element, _ref; 52 | 53 | element = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 54 | return (_ref = $(element)).data.apply(_ref, args); 55 | }; 56 | 57 | Adapter.prototype.find = function(element, selector) { 58 | return $(element).find(selector)[0]; 59 | }; 60 | 61 | Adapter.prototype.findAll = function(element, selector) { 62 | return $(element).find(selector); 63 | }; 64 | 65 | Adapter.prototype.update = function(element, content, escape) { 66 | element = $(element); 67 | if (escape) { 68 | return element.text(content); 69 | } else { 70 | return element.html(content); 71 | } 72 | }; 73 | 74 | Adapter.prototype.append = function(element, child) { 75 | return $(element).append(child); 76 | }; 77 | 78 | Adapter.prototype.remove = function(element) { 79 | return $(element).remove(); 80 | }; 81 | 82 | Adapter.prototype.addClass = function(element, className) { 83 | return $(element).addClass(className); 84 | }; 85 | 86 | Adapter.prototype.removeClass = function(element, className) { 87 | return $(element).removeClass(className); 88 | }; 89 | 90 | Adapter.prototype.css = function(element, properties) { 91 | return $(element).css(properties); 92 | }; 93 | 94 | Adapter.prototype.dimensions = function(element) { 95 | return $(element).dim(); 96 | }; 97 | 98 | Adapter.prototype.scrollOffset = function() { 99 | return [window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop]; 100 | }; 101 | 102 | Adapter.prototype.viewportDimensions = function() { 103 | return { 104 | width: document.documentElement.clientWidth, 105 | height: document.documentElement.clientHeight 106 | }; 107 | }; 108 | 109 | Adapter.prototype.mousePosition = function(e) { 110 | var pos; 111 | 112 | pos = { 113 | x: 0, 114 | y: 0 115 | }; 116 | if (e == null) { 117 | e = window.event; 118 | } 119 | if (e == null) { 120 | return; 121 | } 122 | if (e.pageX || e.pageY) { 123 | pos.x = e.pageX; 124 | pos.y = e.pageY; 125 | } else if (e.clientX || e.clientY) { 126 | pos.x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 127 | pos.y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 128 | } 129 | return pos; 130 | }; 131 | 132 | Adapter.prototype.offset = function(element) { 133 | var offset; 134 | 135 | offset = $(element).offset(); 136 | return { 137 | top: offset.top, 138 | left: offset.left 139 | }; 140 | }; 141 | 142 | Adapter.prototype.observe = function(element, eventName, observer) { 143 | return $(element).on(eventName, observer); 144 | }; 145 | 146 | Adapter.prototype.stopObserving = function(element, eventName, observer) { 147 | return $(element).unbind(eventName, observer); 148 | }; 149 | 150 | Adapter.prototype.ajax = function(options) { 151 | var _ref, _ref1; 152 | 153 | if (options.url == null) { 154 | throw new Error("No url provided"); 155 | } 156 | return reqwest({ 157 | url: options.url, 158 | type: 'html', 159 | method: (_ref = (_ref1 = options.method) != null ? _ref1.toUpperCase() : void 0) != null ? _ref : "GET", 160 | error: function(resp) { 161 | return typeof options.onError === "function" ? options.onError("Server responded with status " + resp.status) : void 0; 162 | }, 163 | success: function(resp) { 164 | return typeof options.onSuccess === "function" ? options.onSuccess(resp) : void 0; 165 | }, 166 | complete: function() { 167 | return typeof options.onComplete === "function" ? options.onComplete() : void 0; 168 | } 169 | }); 170 | }; 171 | 172 | Adapter.prototype.clone = function(object) { 173 | var key, newObject, val; 174 | 175 | newObject = {}; 176 | for (key in object) { 177 | if (!__hasProp.call(object, key)) continue; 178 | val = object[key]; 179 | newObject[key] = val; 180 | } 181 | return newObject; 182 | }; 183 | 184 | Adapter.prototype.extend = function() { 185 | var key, source, sources, target, val, _i, _len; 186 | 187 | target = arguments[0], sources = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 188 | for (_i = 0, _len = sources.length; _i < _len; _i++) { 189 | source = sources[_i]; 190 | for (key in source) { 191 | if (!__hasProp.call(source, key)) continue; 192 | val = source[key]; 193 | target[key] = val; 194 | } 195 | } 196 | return target; 197 | }; 198 | 199 | return Adapter; 200 | 201 | })(); 202 | return Opentip.addAdapter(new Adapter); 203 | })(ender); 204 | -------------------------------------------------------------------------------- /lib/adapter-jquery.js: -------------------------------------------------------------------------------- 1 | var __slice = [].slice; 2 | 3 | (function($) { 4 | var Adapter; 5 | 6 | $.fn.opentip = function(content, title, options) { 7 | return new Opentip(this, content, title, options); 8 | }; 9 | Adapter = (function() { 10 | function Adapter() {} 11 | 12 | Adapter.prototype.name = "jquery"; 13 | 14 | Adapter.prototype.domReady = function(callback) { 15 | return $(callback); 16 | }; 17 | 18 | Adapter.prototype.create = function(html) { 19 | return $(html); 20 | }; 21 | 22 | Adapter.prototype.wrap = function(element) { 23 | element = $(element); 24 | if (element.length > 1) { 25 | throw new Error("Multiple elements provided."); 26 | } 27 | return element; 28 | }; 29 | 30 | Adapter.prototype.unwrap = function(element) { 31 | return $(element)[0]; 32 | }; 33 | 34 | Adapter.prototype.tagName = function(element) { 35 | return this.unwrap(element).tagName; 36 | }; 37 | 38 | Adapter.prototype.attr = function() { 39 | var args, element, _ref; 40 | 41 | element = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 42 | return (_ref = $(element)).attr.apply(_ref, args); 43 | }; 44 | 45 | Adapter.prototype.data = function() { 46 | var args, element, _ref; 47 | 48 | element = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 49 | return (_ref = $(element)).data.apply(_ref, args); 50 | }; 51 | 52 | Adapter.prototype.find = function(element, selector) { 53 | return $(element).find(selector).get(0); 54 | }; 55 | 56 | Adapter.prototype.findAll = function(element, selector) { 57 | return $(element).find(selector); 58 | }; 59 | 60 | Adapter.prototype.update = function(element, content, escape) { 61 | element = $(element); 62 | if (escape) { 63 | return element.text(content); 64 | } else { 65 | return element.html(content); 66 | } 67 | }; 68 | 69 | Adapter.prototype.append = function(element, child) { 70 | return $(element).append(child); 71 | }; 72 | 73 | Adapter.prototype.remove = function(element) { 74 | return $(element).remove(); 75 | }; 76 | 77 | Adapter.prototype.addClass = function(element, className) { 78 | return $(element).addClass(className); 79 | }; 80 | 81 | Adapter.prototype.removeClass = function(element, className) { 82 | return $(element).removeClass(className); 83 | }; 84 | 85 | Adapter.prototype.css = function(element, properties) { 86 | return $(element).css(properties); 87 | }; 88 | 89 | Adapter.prototype.dimensions = function(element) { 90 | return { 91 | width: $(element).outerWidth(), 92 | height: $(element).outerHeight() 93 | }; 94 | }; 95 | 96 | Adapter.prototype.scrollOffset = function() { 97 | return [window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop]; 98 | }; 99 | 100 | Adapter.prototype.viewportDimensions = function() { 101 | return { 102 | width: document.documentElement.clientWidth, 103 | height: document.documentElement.clientHeight 104 | }; 105 | }; 106 | 107 | Adapter.prototype.mousePosition = function(e) { 108 | if (e == null) { 109 | return null; 110 | } 111 | return { 112 | x: e.pageX, 113 | y: e.pageY 114 | }; 115 | }; 116 | 117 | Adapter.prototype.offset = function(element) { 118 | var offset; 119 | 120 | offset = $(element).offset(); 121 | return { 122 | left: offset.left, 123 | top: offset.top 124 | }; 125 | }; 126 | 127 | Adapter.prototype.observe = function(element, eventName, observer) { 128 | return $(element).bind(eventName, observer); 129 | }; 130 | 131 | Adapter.prototype.stopObserving = function(element, eventName, observer) { 132 | return $(element).unbind(eventName, observer); 133 | }; 134 | 135 | Adapter.prototype.ajax = function(options) { 136 | var _ref, _ref1; 137 | 138 | if (options.url == null) { 139 | throw new Error("No url provided"); 140 | } 141 | return $.ajax({ 142 | url: options.url, 143 | type: (_ref = (_ref1 = options.method) != null ? _ref1.toUpperCase() : void 0) != null ? _ref : "GET" 144 | }).done(function(content) { 145 | return typeof options.onSuccess === "function" ? options.onSuccess(content) : void 0; 146 | }).fail(function(request) { 147 | return typeof options.onError === "function" ? options.onError("Server responded with status " + request.status) : void 0; 148 | }).always(function() { 149 | return typeof options.onComplete === "function" ? options.onComplete() : void 0; 150 | }); 151 | }; 152 | 153 | Adapter.prototype.clone = function(object) { 154 | return $.extend({}, object); 155 | }; 156 | 157 | Adapter.prototype.extend = function() { 158 | var sources, target; 159 | 160 | target = arguments[0], sources = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 161 | return $.extend.apply($, [target].concat(__slice.call(sources))); 162 | }; 163 | 164 | return Adapter; 165 | 166 | })(); 167 | return Opentip.addAdapter(new Adapter); 168 | })(jQuery); 169 | -------------------------------------------------------------------------------- /lib/adapter-native.js: -------------------------------------------------------------------------------- 1 | var Adapter, 2 | __hasProp = {}.hasOwnProperty, 3 | __slice = [].slice; 4 | 5 | Adapter = (function() { 6 | var dataValues, lastDataId; 7 | 8 | function Adapter() {} 9 | 10 | Adapter.prototype.name = "native"; 11 | 12 | Adapter.prototype.domReady = function(callback) { 13 | var add, doc, done, init, poll, pre, rem, root, top, win, _ref; 14 | 15 | done = false; 16 | top = true; 17 | win = window; 18 | doc = document; 19 | if ((_ref = doc.readyState) === "complete" || _ref === "loaded") { 20 | return callback(); 21 | } 22 | root = doc.documentElement; 23 | add = (doc.addEventListener ? "addEventListener" : "attachEvent"); 24 | rem = (doc.addEventListener ? "removeEventListener" : "detachEvent"); 25 | pre = (doc.addEventListener ? "" : "on"); 26 | init = function(e) { 27 | if (e.type === "readystatechange" && doc.readyState !== "complete") { 28 | return; 29 | } 30 | (e.type === "load" ? win : doc)[rem](pre + e.type, init, false); 31 | if (!done) { 32 | done = true; 33 | return callback(); 34 | } 35 | }; 36 | poll = function() { 37 | var e; 38 | 39 | try { 40 | root.doScroll("left"); 41 | } catch (_error) { 42 | e = _error; 43 | setTimeout(poll, 50); 44 | return; 45 | } 46 | return init("poll"); 47 | }; 48 | if (doc.readyState !== "complete") { 49 | if (doc.createEventObject && root.doScroll) { 50 | try { 51 | top = !win.frameElement; 52 | } catch (_error) {} 53 | if (top) { 54 | poll(); 55 | } 56 | } 57 | doc[add](pre + "DOMContentLoaded", init, false); 58 | doc[add](pre + "readystatechange", init, false); 59 | return win[add](pre + "load", init, false); 60 | } 61 | }; 62 | 63 | Adapter.prototype.create = function(htmlString) { 64 | var div; 65 | 66 | div = document.createElement("div"); 67 | div.innerHTML = htmlString; 68 | return this.wrap(div.childNodes); 69 | }; 70 | 71 | Adapter.prototype.wrap = function(element) { 72 | var el; 73 | 74 | if (!element) { 75 | element = []; 76 | } else if (typeof element === "string") { 77 | element = this.find(document.body, element); 78 | element = element ? [element] : []; 79 | } else if (element instanceof NodeList) { 80 | element = (function() { 81 | var _i, _len, _results; 82 | 83 | _results = []; 84 | for (_i = 0, _len = element.length; _i < _len; _i++) { 85 | el = element[_i]; 86 | _results.push(el); 87 | } 88 | return _results; 89 | })(); 90 | } else if (!(element instanceof Array)) { 91 | element = [element]; 92 | } 93 | return element; 94 | }; 95 | 96 | Adapter.prototype.unwrap = function(element) { 97 | return this.wrap(element)[0]; 98 | }; 99 | 100 | Adapter.prototype.tagName = function(element) { 101 | return this.unwrap(element).tagName; 102 | }; 103 | 104 | Adapter.prototype.attr = function(element, attr, value) { 105 | if (arguments.length === 3) { 106 | return this.unwrap(element).setAttribute(attr, value); 107 | } else { 108 | return this.unwrap(element).getAttribute(attr); 109 | } 110 | }; 111 | 112 | lastDataId = 0; 113 | 114 | dataValues = {}; 115 | 116 | Adapter.prototype.data = function(element, name, value) { 117 | var dataId; 118 | 119 | dataId = this.attr(element, "data-id"); 120 | if (!dataId) { 121 | dataId = ++lastDataId; 122 | this.attr(element, "data-id", dataId); 123 | dataValues[dataId] = {}; 124 | } 125 | if (arguments.length === 3) { 126 | return dataValues[dataId][name] = value; 127 | } else { 128 | value = dataValues[dataId][name]; 129 | if (value != null) { 130 | return value; 131 | } 132 | value = this.attr(element, "data-" + (Opentip.prototype.dasherize(name))); 133 | if (value) { 134 | dataValues[dataId][name] = value; 135 | } 136 | return value; 137 | } 138 | }; 139 | 140 | Adapter.prototype.find = function(element, selector) { 141 | return this.unwrap(element).querySelector(selector); 142 | }; 143 | 144 | Adapter.prototype.findAll = function(element, selector) { 145 | return this.unwrap(element).querySelectorAll(selector); 146 | }; 147 | 148 | Adapter.prototype.update = function(element, content, escape) { 149 | element = this.unwrap(element); 150 | if (escape) { 151 | element.innerHTML = ""; 152 | return element.appendChild(document.createTextNode(content)); 153 | } else { 154 | return element.innerHTML = content; 155 | } 156 | }; 157 | 158 | Adapter.prototype.append = function(element, child) { 159 | var unwrappedChild, unwrappedElement; 160 | 161 | unwrappedChild = this.unwrap(child); 162 | unwrappedElement = this.unwrap(element); 163 | return unwrappedElement.appendChild(unwrappedChild); 164 | }; 165 | 166 | Adapter.prototype.remove = function(element) { 167 | var parentNode; 168 | 169 | element = this.unwrap(element); 170 | parentNode = element.parentNode; 171 | if (parentNode != null) { 172 | return parentNode.removeChild(element); 173 | } 174 | }; 175 | 176 | Adapter.prototype.addClass = function(element, className) { 177 | return this.unwrap(element).classList.add(className); 178 | }; 179 | 180 | Adapter.prototype.removeClass = function(element, className) { 181 | return this.unwrap(element).classList.remove(className); 182 | }; 183 | 184 | Adapter.prototype.css = function(element, properties) { 185 | var key, value, _results; 186 | 187 | element = this.unwrap(this.wrap(element)); 188 | _results = []; 189 | for (key in properties) { 190 | if (!__hasProp.call(properties, key)) continue; 191 | value = properties[key]; 192 | _results.push(element.style[key] = value); 193 | } 194 | return _results; 195 | }; 196 | 197 | Adapter.prototype.dimensions = function(element) { 198 | var dimensions, revert; 199 | 200 | element = this.unwrap(this.wrap(element)); 201 | dimensions = { 202 | width: element.offsetWidth, 203 | height: element.offsetHeight 204 | }; 205 | if (!(dimensions.width && dimensions.height)) { 206 | revert = { 207 | position: element.style.position || '', 208 | visibility: element.style.visibility || '', 209 | display: element.style.display || '' 210 | }; 211 | this.css(element, { 212 | position: "absolute", 213 | visibility: "hidden", 214 | display: "block" 215 | }); 216 | dimensions = { 217 | width: element.offsetWidth, 218 | height: element.offsetHeight 219 | }; 220 | this.css(element, revert); 221 | } 222 | return dimensions; 223 | }; 224 | 225 | Adapter.prototype.scrollOffset = function() { 226 | return [window.pageXOffset || document.documentElement.scrollLeft || document.body.scrollLeft, window.pageYOffset || document.documentElement.scrollTop || document.body.scrollTop]; 227 | }; 228 | 229 | Adapter.prototype.viewportDimensions = function() { 230 | return { 231 | width: document.documentElement.clientWidth, 232 | height: document.documentElement.clientHeight 233 | }; 234 | }; 235 | 236 | Adapter.prototype.mousePosition = function(e) { 237 | var pos; 238 | 239 | pos = { 240 | x: 0, 241 | y: 0 242 | }; 243 | if (e == null) { 244 | e = window.event; 245 | } 246 | if (e == null) { 247 | return; 248 | } 249 | try { 250 | if (e.pageX || e.pageY) { 251 | pos.x = e.pageX; 252 | pos.y = e.pageY; 253 | } else if (e.clientX || e.clientY) { 254 | pos.x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft; 255 | pos.y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop; 256 | } 257 | } catch (_error) { 258 | e = _error; 259 | } 260 | return pos; 261 | }; 262 | 263 | Adapter.prototype.offset = function(element) { 264 | var offset; 265 | 266 | element = this.unwrap(element); 267 | offset = { 268 | top: element.offsetTop, 269 | left: element.offsetLeft 270 | }; 271 | while (element = element.offsetParent) { 272 | offset.top += element.offsetTop; 273 | offset.left += element.offsetLeft; 274 | if (element !== document.body) { 275 | offset.top -= element.scrollTop; 276 | offset.left -= element.scrollLeft; 277 | } 278 | } 279 | return offset; 280 | }; 281 | 282 | Adapter.prototype.observe = function(element, eventName, observer) { 283 | return this.unwrap(element).addEventListener(eventName, observer, false); 284 | }; 285 | 286 | Adapter.prototype.stopObserving = function(element, eventName, observer) { 287 | return this.unwrap(element).removeEventListener(eventName, observer, false); 288 | }; 289 | 290 | Adapter.prototype.ajax = function(options) { 291 | var e, request, _ref, _ref1; 292 | 293 | if (options.url == null) { 294 | throw new Error("No url provided"); 295 | } 296 | if (window.XMLHttpRequest) { 297 | request = new XMLHttpRequest; 298 | } else if (window.ActiveXObject) { 299 | try { 300 | request = new ActiveXObject("Msxml2.XMLHTTP"); 301 | } catch (_error) { 302 | e = _error; 303 | try { 304 | request = new ActiveXObject("Microsoft.XMLHTTP"); 305 | } catch (_error) { 306 | e = _error; 307 | } 308 | } 309 | } 310 | if (!request) { 311 | throw new Error("Can't create XMLHttpRequest"); 312 | } 313 | request.onreadystatechange = function() { 314 | if (request.readyState === 4) { 315 | try { 316 | if (request.status === 200) { 317 | if (typeof options.onSuccess === "function") { 318 | options.onSuccess(request.responseText); 319 | } 320 | } else { 321 | if (typeof options.onError === "function") { 322 | options.onError("Server responded with status " + request.status); 323 | } 324 | } 325 | } catch (_error) { 326 | e = _error; 327 | if (typeof options.onError === "function") { 328 | options.onError(e.message); 329 | } 330 | } 331 | return typeof options.onComplete === "function" ? options.onComplete() : void 0; 332 | } 333 | }; 334 | request.open((_ref = (_ref1 = options.method) != null ? _ref1.toUpperCase() : void 0) != null ? _ref : "GET", options.url); 335 | return request.send(); 336 | }; 337 | 338 | Adapter.prototype.clone = function(object) { 339 | var key, newObject, val; 340 | 341 | newObject = {}; 342 | for (key in object) { 343 | if (!__hasProp.call(object, key)) continue; 344 | val = object[key]; 345 | newObject[key] = val; 346 | } 347 | return newObject; 348 | }; 349 | 350 | Adapter.prototype.extend = function() { 351 | var key, source, sources, target, val, _i, _len; 352 | 353 | target = arguments[0], sources = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 354 | for (_i = 0, _len = sources.length; _i < _len; _i++) { 355 | source = sources[_i]; 356 | for (key in source) { 357 | if (!__hasProp.call(source, key)) continue; 358 | val = source[key]; 359 | target[key] = val; 360 | } 361 | } 362 | return target; 363 | }; 364 | 365 | return Adapter; 366 | 367 | })(); 368 | 369 | Opentip.addAdapter(new Adapter); 370 | -------------------------------------------------------------------------------- /lib/adapter-prototype.js: -------------------------------------------------------------------------------- 1 | var __slice = [].slice; 2 | 3 | (function() { 4 | var Adapter, isArrayOrNodeList; 5 | 6 | Element.addMethods({ 7 | addTip: function(element, content, title, options) { 8 | return new Opentip(element, content, title, options); 9 | } 10 | }); 11 | isArrayOrNodeList = function(element) { 12 | if ((element instanceof Array) || ((element != null) && typeof element.length === 'number' && typeof element.item === 'function' && typeof element.nextNode === 'function' && typeof element.reset === 'function')) { 13 | return true; 14 | } 15 | return false; 16 | }; 17 | Adapter = (function() { 18 | function Adapter() {} 19 | 20 | Adapter.prototype.name = "prototype"; 21 | 22 | Adapter.prototype.domReady = function(callback) { 23 | if (document.loaded) { 24 | return callback(); 25 | } else { 26 | return $(document).observe("dom:loaded", callback); 27 | } 28 | }; 29 | 30 | Adapter.prototype.create = function(html) { 31 | return new Element('div').update(html).childElements(); 32 | }; 33 | 34 | Adapter.prototype.wrap = function(element) { 35 | if (isArrayOrNodeList(element)) { 36 | if (element.length > 1) { 37 | throw new Error("Multiple elements provided."); 38 | } 39 | element = this.unwrap(element); 40 | } else if (typeof element === "string") { 41 | element = $$(element)[0]; 42 | } 43 | return $(element); 44 | }; 45 | 46 | Adapter.prototype.unwrap = function(element) { 47 | if (isArrayOrNodeList(element)) { 48 | return element[0]; 49 | } else { 50 | return element; 51 | } 52 | }; 53 | 54 | Adapter.prototype.tagName = function(element) { 55 | return this.unwrap(element).tagName; 56 | }; 57 | 58 | Adapter.prototype.attr = function() { 59 | var args, element, _ref; 60 | 61 | element = arguments[0], args = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 62 | if (args.length === 1) { 63 | return this.wrap(element).readAttribute(args[0]); 64 | } else { 65 | return (_ref = this.wrap(element)).writeAttribute.apply(_ref, args); 66 | } 67 | }; 68 | 69 | Adapter.prototype.data = function(element, name, value) { 70 | var arg; 71 | 72 | this.wrap(element); 73 | if (arguments.length > 2) { 74 | return element.store(name, value); 75 | } else { 76 | arg = element.readAttribute("data-" + (name.underscore().dasherize())); 77 | if (arg != null) { 78 | return arg; 79 | } 80 | return element.retrieve(name); 81 | } 82 | }; 83 | 84 | Adapter.prototype.find = function(element, selector) { 85 | return this.wrap(element).select(selector)[0]; 86 | }; 87 | 88 | Adapter.prototype.findAll = function(element, selector) { 89 | return this.wrap(element).select(selector); 90 | }; 91 | 92 | Adapter.prototype.update = function(element, content, escape) { 93 | return this.wrap(element).update(escape ? content.escapeHTML() : content); 94 | }; 95 | 96 | Adapter.prototype.append = function(element, child) { 97 | return this.wrap(element).insert(this.wrap(child)); 98 | }; 99 | 100 | Adapter.prototype.remove = function(element) { 101 | return this.wrap(element).remove(); 102 | }; 103 | 104 | Adapter.prototype.addClass = function(element, className) { 105 | return this.wrap(element).addClassName(className); 106 | }; 107 | 108 | Adapter.prototype.removeClass = function(element, className) { 109 | return this.wrap(element).removeClassName(className); 110 | }; 111 | 112 | Adapter.prototype.css = function(element, properties) { 113 | return this.wrap(element).setStyle(properties); 114 | }; 115 | 116 | Adapter.prototype.dimensions = function(element) { 117 | return this.wrap(element).getDimensions(); 118 | }; 119 | 120 | Adapter.prototype.scrollOffset = function() { 121 | var offsets; 122 | 123 | offsets = document.viewport.getScrollOffsets(); 124 | return [offsets.left, offsets.top]; 125 | }; 126 | 127 | Adapter.prototype.viewportDimensions = function() { 128 | return document.viewport.getDimensions(); 129 | }; 130 | 131 | Adapter.prototype.mousePosition = function(e) { 132 | if (e == null) { 133 | return null; 134 | } 135 | return { 136 | x: Event.pointerX(e), 137 | y: Event.pointerY(e) 138 | }; 139 | }; 140 | 141 | Adapter.prototype.offset = function(element) { 142 | var offset; 143 | 144 | offset = this.wrap(element).cumulativeOffset(); 145 | return { 146 | left: offset.left, 147 | top: offset.top 148 | }; 149 | }; 150 | 151 | Adapter.prototype.observe = function(element, eventName, observer) { 152 | return Event.observe(this.wrap(element), eventName, observer); 153 | }; 154 | 155 | Adapter.prototype.stopObserving = function(element, eventName, observer) { 156 | return Event.stopObserving(this.wrap(element), eventName, observer); 157 | }; 158 | 159 | Adapter.prototype.ajax = function(options) { 160 | var _ref, _ref1; 161 | 162 | if (options.url == null) { 163 | throw new Error("No url provided"); 164 | } 165 | return new Ajax.Request(options.url, { 166 | method: (_ref = (_ref1 = options.method) != null ? _ref1.toUpperCase() : void 0) != null ? _ref : "GET", 167 | onSuccess: function(response) { 168 | return typeof options.onSuccess === "function" ? options.onSuccess(response.responseText) : void 0; 169 | }, 170 | onFailure: function(response) { 171 | return typeof options.onError === "function" ? options.onError("Server responded with status " + response.status) : void 0; 172 | }, 173 | onComplete: function() { 174 | return typeof options.onComplete === "function" ? options.onComplete() : void 0; 175 | } 176 | }); 177 | }; 178 | 179 | Adapter.prototype.clone = function(object) { 180 | return Object.clone(object); 181 | }; 182 | 183 | Adapter.prototype.extend = function() { 184 | var source, sources, target, _i, _len; 185 | 186 | target = arguments[0], sources = 2 <= arguments.length ? __slice.call(arguments, 1) : []; 187 | for (_i = 0, _len = sources.length; _i < _len; _i++) { 188 | source = sources[_i]; 189 | Object.extend(target, source); 190 | } 191 | return target; 192 | }; 193 | 194 | return Adapter; 195 | 196 | })(); 197 | return Opentip.addAdapter(new Adapter); 198 | })(); 199 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "opentip", 3 | "version": "2.4.6", 4 | "description": "Free opensource tooltip class.", 5 | "keywords": [ "tooltip" ], 6 | "homepage": "http://www.opentip.org", 7 | "main": [ 8 | "./lib/opentip.js", 9 | "./lib/adapter.ender.js" 10 | ], 11 | "maintainers": [ 12 | { 13 | "name": "Matias Meno", 14 | "email": "m@tias.me", 15 | "web": "http://www.matiasmeno.com" 16 | } 17 | ], 18 | "contributors": [ 19 | { 20 | "name": "Matias Meno", 21 | "email": "m@tias.me", 22 | "web": "http://www.matiasmeno.com" 23 | } 24 | ], 25 | "bugs": { 26 | "mail": "m@tias.me" 27 | }, 28 | "licenses": [ 29 | { 30 | "type": "MIT", 31 | "url": "http://www.opensource.org/licenses/MIT" 32 | } 33 | ], 34 | "repositories": [ 35 | { 36 | "type": "git", 37 | "url": "https://github.com/enyo/opentip.git" 38 | } 39 | ], 40 | "scripts": { 41 | "test": "node_modules/mocha-phantomjs/bin/mocha-phantomjs test/test.html" 42 | }, 43 | "dependencies": { 44 | "qwery": "3.x", 45 | "domready": "0.x", 46 | "bean": "0.x", 47 | "bonzo": "1.x", 48 | "reqwest": "0.x" 49 | }, 50 | "devDependencies": { 51 | "grunt": "*", 52 | "grunt-contrib-stylus": "*", 53 | "grunt-contrib-watch": "*", 54 | "grunt-contrib-coffee": "*", 55 | "grunt-contrib-concat": "*", 56 | "grunt-contrib-uglify": "*", 57 | "grunt-contrib-clean": "*", 58 | "grunt-curl": "*", 59 | "mocha": "*", 60 | "expect.js": "*", 61 | "mocha-phantomjs": "*", 62 | "uglify-js2": "*", 63 | "request": "*" 64 | }, 65 | "ender": "noop" 66 | } 67 | -------------------------------------------------------------------------------- /src/adapter-component.coffee: -------------------------------------------------------------------------------- 1 | # Component Opentip Adapter 2 | # ====================== 3 | # 4 | # Uses github.com/component components 5 | 6 | $ = window.jQuery ? require "jquery" 7 | 8 | # The adapter class 9 | module.exports = class Adapter 10 | 11 | name: "component" 12 | 13 | # Simply using $.domReady 14 | domReady: (callback) -> $ callback 15 | 16 | 17 | # DOM 18 | # === 19 | 20 | # Using bonzo to create html 21 | create: (html) -> $ html 22 | 23 | 24 | # Element handling 25 | # ---------------- 26 | 27 | # Wraps the element in ender 28 | wrap: (element) -> 29 | element = $ element 30 | throw new Error "Multiple elements provided." if element.length > 1 31 | element 32 | 33 | # Returns the unwrapped element 34 | unwrap: (element) -> $(element)[0] 35 | 36 | # Returns the tag name of the element 37 | tagName: (element) -> @unwrap(element).tagName 38 | 39 | # Returns or sets the given attribute of element 40 | # 41 | # It's important not to simply forward name and value because the value 42 | # is set whether or not the value argument is present 43 | attr: (element, args...) -> $(element).attr args... 44 | 45 | # Returns or sets the given data of element 46 | # It's important not to simply forward name and value because the value 47 | # is set whether or not the value argument is present 48 | data: (element, args...) -> $(element).data args... 49 | 50 | # Finds elements by selector 51 | find: (element, selector) -> $(element).find(selector)[0] 52 | 53 | # Finds all elements by selector 54 | findAll: (element, selector) -> $(element).find selector 55 | 56 | # Updates the content of the element 57 | update: (element, content, escape) -> 58 | element = $ element 59 | if escape 60 | element.text content 61 | else 62 | element.html content 63 | 64 | # Appends given child to element 65 | append: (element, child) -> $(element).append child 66 | 67 | # Removes element 68 | remove: (element) -> $(element).remove() 69 | 70 | # Add a class 71 | addClass: (element, className) -> $(element).addClass className 72 | 73 | # Remove a class 74 | removeClass: (element, className) -> $(element).removeClass className 75 | 76 | # Set given css properties 77 | css: (element, properties) -> $(element).css properties 78 | 79 | # Returns an object with given dimensions 80 | dimensions: (element) -> 81 | { 82 | width: $(element).outerWidth() 83 | height: $(element).outerHeight() 84 | } 85 | 86 | # Returns the scroll offsets of current document 87 | scrollOffset: -> 88 | [ 89 | window.pageXOffset or document.documentElement.scrollLeft or document.body.scrollLeft 90 | window.pageYOffset or document.documentElement.scrollTop or document.body.scrollTop 91 | ] 92 | 93 | # Returns the dimensions of the viewport (currently visible browser area) 94 | viewportDimensions: -> 95 | { 96 | width: document.documentElement.clientWidth 97 | height: document.documentElement.clientHeight 98 | } 99 | 100 | # Returns an object with x and y 101 | mousePosition: (e) -> 102 | return null unless e? 103 | x: e.pageX, y: e.pageY 104 | 105 | 106 | # Returns the offset of the element 107 | offset: (element) -> 108 | offset = $(element).offset() 109 | { 110 | left: offset.left 111 | top: offset.top 112 | } 113 | 114 | # Observe given eventName 115 | observe: (element, eventName, observer) -> $(element).bind eventName, observer 116 | 117 | # Stop observing event 118 | stopObserving: (element, eventName, observer) -> $(element).unbind eventName, observer 119 | 120 | # Perform an AJAX request and call the appropriate callbacks. 121 | ajax: (options) -> 122 | throw new Error "No url provided" unless options.url? 123 | $.ajax( 124 | url: options.url 125 | type: options.method?.toUpperCase() ? "GET" 126 | ) 127 | .done((content) -> options.onSuccess? content) 128 | .fail((request) -> options.onError? "Server responded with status #{request.status}") 129 | .always(-> options.onComplete?()) 130 | 131 | 132 | # Utility functions 133 | # ================= 134 | 135 | # Creates a shallow copy of the object 136 | clone: (object) -> $.extend { }, object 137 | 138 | # Copies all properties from sources to target 139 | extend: (target, sources...) -> $.extend target, sources... 140 | 141 | 142 | -------------------------------------------------------------------------------- /src/adapter-ender.coffee: -------------------------------------------------------------------------------- 1 | # Ender Opentip Adapter 2 | # ===================== 3 | # 4 | # Uses ender packages 5 | 6 | # Because $ is my favorite character 7 | (($) -> 8 | 9 | # Using bean as event handler 10 | bean = require "bean" 11 | 12 | # Using reqwest as AJAX lib 13 | reqwest = require "reqwest" 14 | 15 | # Augment ender 16 | $.ender { 17 | opentip: (content, title, options) -> new Opentip this, content, title, options 18 | }, true 19 | 20 | 21 | # And now the class 22 | class Adapter 23 | 24 | name: "ender" 25 | 26 | # Simply using $.domReady 27 | domReady: (callback) -> $.domReady callback 28 | 29 | 30 | # DOM 31 | # === 32 | 33 | # Using bonzo to create html 34 | create: (html) -> $ html 35 | 36 | 37 | # Element handling 38 | # ---------------- 39 | 40 | # Wraps the element in ender 41 | wrap: (element) -> 42 | element = $ element 43 | throw new Error "Multiple elements provided." if element.length > 1 44 | element 45 | 46 | # Returns the unwrapped element 47 | unwrap: (element) -> $(element).get 0 48 | 49 | # Returns the tag name of the element 50 | tagName: (element) -> @unwrap(element).tagName 51 | 52 | # Returns or sets the given attribute of element 53 | # It's important not to simply forward name and value because the value 54 | # is set whether or not the value argument is present 55 | attr: (element, args...) -> $(element).attr args... 56 | 57 | # Returns or sets the given data of element 58 | # It's important not to simply forward name and value because the value 59 | # is set whether or not the value argument is present 60 | data: (element, args...) -> $(element).data args... 61 | 62 | # Finds elements by selector 63 | find: (element, selector) -> $(element).find(selector)[0] 64 | 65 | # Finds all elements by selector 66 | findAll: (element, selector) -> $(element).find selector 67 | 68 | # Updates the content of the element 69 | update: (element, content, escape) -> 70 | element = $ element 71 | if escape 72 | element.text content 73 | else 74 | element.html content 75 | 76 | # Appends given child to element 77 | append: (element, child) -> $(element).append child 78 | 79 | # Removes element 80 | remove: (element) -> $(element).remove() 81 | 82 | # Add a class 83 | addClass: (element, className) -> $(element).addClass className 84 | 85 | # Remove a class 86 | removeClass: (element, className) -> $(element).removeClass className 87 | 88 | # Set given css properties 89 | css: (element, properties) -> $(element).css properties 90 | 91 | # Returns an object with given dimensions 92 | dimensions: (element) -> $(element).dim() 93 | 94 | # Returns the scroll offsets of current document 95 | scrollOffset: -> 96 | [ 97 | window.pageXOffset or document.documentElement.scrollLeft or document.body.scrollLeft 98 | window.pageYOffset or document.documentElement.scrollTop or document.body.scrollTop 99 | ] 100 | 101 | # Returns the dimensions of the viewport (currently visible browser area) 102 | viewportDimensions: -> 103 | { 104 | width: document.documentElement.clientWidth 105 | height: document.documentElement.clientHeight 106 | } 107 | 108 | # Returns an object with x and y 109 | mousePosition: (e) -> 110 | pos = x: 0, y: 0 111 | 112 | e ?= window.event 113 | 114 | return unless e? 115 | 116 | if e.pageX or e.pageY 117 | pos.x = e.pageX 118 | pos.y = e.pageY 119 | else if e.clientX or e.clientY 120 | pos.x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft 121 | pos.y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop 122 | 123 | pos 124 | 125 | # Returns the offset of the element 126 | offset: (element) -> 127 | offset = $(element).offset() 128 | { 129 | top: offset.top 130 | left: offset.left 131 | } 132 | 133 | # Observe given eventName 134 | observe: (element, eventName, observer) -> 135 | $(element).on eventName, observer 136 | 137 | # Stop observing event 138 | stopObserving: (element, eventName, observer) -> $(element).unbind eventName, observer 139 | 140 | # Perform an AJAX request and call the appropriate callbacks. 141 | ajax: (options) -> 142 | throw new Error "No url provided" unless options.url? 143 | reqwest 144 | url: options.url 145 | type: 'html' 146 | method: options.method?.toUpperCase() ? "GET" 147 | error: (resp) -> options.onError? "Server responded with status #{resp.status}" 148 | success: (resp) -> options.onSuccess? resp 149 | complete: -> options.onComplete?() 150 | 151 | 152 | # Utility functions 153 | # ================= 154 | 155 | # Creates a shallow copy of the object 156 | clone: (object) -> 157 | newObject = { } 158 | for own key, val of object 159 | newObject[key] = val 160 | newObject 161 | 162 | # Copies all properties from sources to target 163 | extend: (target, sources...) -> 164 | for source in sources 165 | for own key, val of source 166 | target[key] = val 167 | target 168 | 169 | # Add the adapter to the list 170 | Opentip.addAdapter new Adapter 171 | 172 | )(ender) 173 | -------------------------------------------------------------------------------- /src/adapter-jquery.coffee: -------------------------------------------------------------------------------- 1 | # jQuery Opentip Adapter 2 | # ====================== 3 | # 4 | # Uses jQuery 5 | 6 | # Because $ is my favorite character 7 | (($) -> 8 | 9 | 10 | # Augment jQuery 11 | $.fn.opentip = (content, title, options) -> 12 | new Opentip this, content, title, options 13 | 14 | 15 | # And now the class 16 | class Adapter 17 | 18 | name: "jquery" 19 | 20 | # Simply using $.domReady 21 | domReady: (callback) -> $ callback 22 | 23 | 24 | # DOM 25 | # === 26 | 27 | # Using bonzo to create html 28 | create: (html) -> $ html 29 | 30 | 31 | # Element handling 32 | # ---------------- 33 | 34 | # Wraps the element in ender 35 | wrap: (element) -> 36 | element = $ element 37 | throw new Error "Multiple elements provided." if element.length > 1 38 | element 39 | 40 | # Returns the unwrapped element 41 | unwrap: (element) -> $(element)[0] 42 | 43 | # Returns the tag name of the element 44 | tagName: (element) -> @unwrap(element).tagName 45 | 46 | # Returns or sets the given attribute of element 47 | # 48 | # It's important not to simply forward name and value because the value 49 | # is set whether or not the value argument is present 50 | attr: (element, args...) -> $(element).attr args... 51 | 52 | # Returns or sets the given data of element 53 | # It's important not to simply forward name and value because the value 54 | # is set whether or not the value argument is present 55 | data: (element, args...) -> $(element).data args... 56 | 57 | # Finds elements by selector 58 | find: (element, selector) -> $(element).find(selector).get(0) 59 | 60 | # Finds all elements by selector 61 | findAll: (element, selector) -> $(element).find selector 62 | 63 | # Updates the content of the element 64 | update: (element, content, escape) -> 65 | element = $ element 66 | if escape 67 | element.text content 68 | else 69 | element.html content 70 | 71 | # Appends given child to element 72 | append: (element, child) -> $(element).append child 73 | 74 | # Removes element 75 | remove: (element) -> $(element).remove() 76 | 77 | # Add a class 78 | addClass: (element, className) -> $(element).addClass className 79 | 80 | # Remove a class 81 | removeClass: (element, className) -> $(element).removeClass className 82 | 83 | # Set given css properties 84 | css: (element, properties) -> $(element).css properties 85 | 86 | # Returns an object with given dimensions 87 | dimensions: (element) -> 88 | { 89 | width: $(element).outerWidth() 90 | height: $(element).outerHeight() 91 | } 92 | 93 | # Returns the scroll offsets of current document 94 | scrollOffset: -> 95 | [ 96 | window.pageXOffset or document.documentElement.scrollLeft or document.body.scrollLeft 97 | window.pageYOffset or document.documentElement.scrollTop or document.body.scrollTop 98 | ] 99 | 100 | # Returns the dimensions of the viewport (currently visible browser area) 101 | viewportDimensions: -> 102 | { 103 | width: document.documentElement.clientWidth 104 | height: document.documentElement.clientHeight 105 | } 106 | 107 | # Returns an object with x and y 108 | mousePosition: (e) -> 109 | return null unless e? 110 | x: e.pageX, y: e.pageY 111 | 112 | 113 | # Returns the offset of the element 114 | offset: (element) -> 115 | offset = $(element).offset() 116 | { 117 | left: offset.left 118 | top: offset.top 119 | } 120 | 121 | # Observe given eventName 122 | observe: (element, eventName, observer) -> $(element).bind eventName, observer 123 | 124 | # Stop observing event 125 | stopObserving: (element, eventName, observer) -> $(element).unbind eventName, observer 126 | 127 | # Perform an AJAX request and call the appropriate callbacks. 128 | ajax: (options) -> 129 | throw new Error "No url provided" unless options.url? 130 | $.ajax( 131 | url: options.url 132 | type: options.method?.toUpperCase() ? "GET" 133 | ) 134 | .done((content) -> options.onSuccess? content) 135 | .fail((request) -> options.onError? "Server responded with status #{request.status}") 136 | .always(-> options.onComplete?()) 137 | 138 | 139 | # Utility functions 140 | # ================= 141 | 142 | # Creates a shallow copy of the object 143 | clone: (object) -> $.extend { }, object 144 | 145 | # Copies all properties from sources to target 146 | extend: (target, sources...) -> $.extend target, sources... 147 | 148 | # Add the adapter to the list 149 | Opentip.addAdapter new Adapter 150 | 151 | )(jQuery) 152 | -------------------------------------------------------------------------------- /src/adapter-native.coffee: -------------------------------------------------------------------------------- 1 | 2 | # Native Opentip Adapter 3 | # ====================== 4 | # 5 | # Use this adapter if you don't use a framework like jQuery and you don't 6 | # really care about oldschool browser compatibility. 7 | class Adapter 8 | 9 | name: "native" 10 | 11 | # Invoke callback as soon as dom is ready 12 | # Source: https://github.com/dperini/ContentLoaded/blob/master/src/contentloaded.js 13 | domReady: (callback) -> 14 | done = no 15 | top = true 16 | win = window 17 | doc = document 18 | 19 | return callback() if doc.readyState in [ "complete", "loaded" ] 20 | 21 | root = doc.documentElement 22 | add = (if doc.addEventListener then "addEventListener" else "attachEvent") 23 | rem = (if doc.addEventListener then "removeEventListener" else "detachEvent") 24 | pre = (if doc.addEventListener then "" else "on") 25 | 26 | init = (e) -> 27 | return if e.type is "readystatechange" and doc.readyState isnt "complete" 28 | (if e.type is "load" then win else doc)[rem] pre + e.type, init, false 29 | unless done 30 | done = yes 31 | callback() 32 | 33 | poll = -> 34 | try 35 | root.doScroll "left" 36 | catch e 37 | setTimeout poll, 50 38 | return 39 | init "poll" 40 | 41 | unless doc.readyState is "complete" 42 | if doc.createEventObject and root.doScroll 43 | try 44 | top = not win.frameElement 45 | poll() if top 46 | doc[add] pre + "DOMContentLoaded", init, false 47 | doc[add] pre + "readystatechange", init, false 48 | win[add] pre + "load", init, false 49 | 50 | 51 | # DOM 52 | # === 53 | 54 | 55 | # Create the HTML passed as string 56 | create: (htmlString) -> 57 | div = document.createElement "div" 58 | div.innerHTML = htmlString 59 | @wrap div.childNodes 60 | 61 | 62 | 63 | # Element handling 64 | # ---------------- 65 | 66 | # Wrap the element in the framework 67 | wrap: (element) -> 68 | if !element 69 | element = [ ] 70 | else if typeof element == "string" 71 | element = @find document.body, element 72 | element = if element then [ element ] else [ ] 73 | else if element instanceof NodeList 74 | element = (el for el in element) 75 | else if element not instanceof Array 76 | element = [ element ] 77 | element 78 | 79 | # Returns the unwrapped element 80 | unwrap: (element) -> @wrap(element)[0] 81 | 82 | # Returns the tag name of the element 83 | tagName: (element) -> @unwrap(element).tagName 84 | 85 | # Returns or sets the given attribute of element 86 | attr: (element, attr, value) -> 87 | if arguments.length == 3 88 | @unwrap(element).setAttribute attr, value 89 | else 90 | @unwrap(element).getAttribute attr 91 | 92 | 93 | lastDataId = 0 94 | dataValues = { } 95 | # Returns or sets the given data of element 96 | data: (element, name, value) -> 97 | dataId = @attr element, "data-id" 98 | unless dataId 99 | dataId = ++lastDataId 100 | @attr element, "data-id", dataId 101 | dataValues[dataId] = { } 102 | 103 | if arguments.length == 3 104 | # Setter 105 | dataValues[dataId][name] = value 106 | else 107 | value = dataValues[dataId][name] 108 | return value if value? 109 | 110 | value = @attr element, "data-#{Opentip::dasherize name}" 111 | if value 112 | dataValues[dataId][name] = value 113 | return value 114 | 115 | 116 | 117 | # Finds elements by selector 118 | find: (element, selector) -> @unwrap(element).querySelector selector 119 | 120 | # Finds all elements by selector 121 | findAll: (element, selector) -> @unwrap(element).querySelectorAll selector 122 | 123 | # Updates the content of the element 124 | update: (element, content, escape) -> 125 | element = @unwrap element 126 | if escape 127 | element.innerHTML = "" # Clearing the content 128 | element.appendChild document.createTextNode content 129 | else 130 | element.innerHTML = content 131 | 132 | # Appends given child to element 133 | append: (element, child) -> 134 | unwrappedChild = @unwrap child 135 | unwrappedElement = @unwrap element 136 | unwrappedElement.appendChild unwrappedChild 137 | 138 | # Removes element 139 | remove: (element) -> 140 | element = @unwrap element 141 | parentNode = element.parentNode 142 | parentNode.removeChild element if parentNode? 143 | 144 | # Add a class 145 | addClass: (element, className) -> @unwrap(element).classList.add className 146 | 147 | # Remove a class 148 | removeClass: (element, className) -> @unwrap(element).classList.remove className 149 | 150 | # Set given css properties 151 | css: (element, properties) -> 152 | element = @unwrap @wrap element 153 | for own key, value of properties 154 | element.style[key] = value 155 | 156 | # Returns an object with given dimensions 157 | dimensions: (element) -> 158 | element = @unwrap @wrap element 159 | dimensions = 160 | width: element.offsetWidth 161 | height: element.offsetHeight 162 | 163 | unless dimensions.width and dimensions.height 164 | # The element is probably invisible. So make it visible 165 | revert = 166 | position: element.style.position || '' 167 | visibility: element.style.visibility || '' 168 | display: element.style.display || '' 169 | 170 | @css element, 171 | position: "absolute" 172 | visibility: "hidden" 173 | display: "block" 174 | 175 | dimensions = 176 | width: element.offsetWidth 177 | height: element.offsetHeight 178 | 179 | @css element, revert 180 | 181 | dimensions 182 | 183 | # Returns the scroll offsets of current document 184 | scrollOffset: -> 185 | [ 186 | window.pageXOffset or document.documentElement.scrollLeft or document.body.scrollLeft 187 | window.pageYOffset or document.documentElement.scrollTop or document.body.scrollTop 188 | ] 189 | 190 | # Returns the dimensions of the viewport (currently visible browser area) 191 | viewportDimensions: -> 192 | { 193 | width: document.documentElement.clientWidth 194 | height: document.documentElement.clientHeight 195 | } 196 | 197 | # Returns an object with x and y 198 | mousePosition: (e) -> 199 | pos = x: 0, y: 0 200 | 201 | e ?= window.event 202 | 203 | return unless e? 204 | 205 | try 206 | if e.pageX or e.pageY 207 | pos.x = e.pageX 208 | pos.y = e.pageY 209 | else if e.clientX or e.clientY 210 | pos.x = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft 211 | pos.y = e.clientY + document.body.scrollTop + document.documentElement.scrollTop 212 | catch e 213 | pos 214 | 215 | # Returns the offset of the element 216 | offset: (element) -> 217 | element = @unwrap element 218 | 219 | offset = { 220 | top: element.offsetTop 221 | left: element.offsetLeft 222 | } 223 | 224 | while element = element.offsetParent 225 | offset.top += element.offsetTop 226 | offset.left += element.offsetLeft 227 | 228 | if element != document.body 229 | offset.top -= element.scrollTop 230 | offset.left -= element.scrollLeft 231 | 232 | offset 233 | 234 | # Observe given eventName 235 | observe: (element, eventName, observer) -> 236 | # Firefox <= 3.6 needs the last optional parameter `useCapture` 237 | @unwrap(element).addEventListener eventName, observer, false 238 | 239 | # Stop observing event 240 | stopObserving: (element, eventName, observer) -> 241 | # Firefox <= 3.6 needs the last optional parameter `useCapture` 242 | @unwrap(element).removeEventListener eventName, observer, false 243 | 244 | 245 | # Perform an AJAX request and call the appropriate callbacks. 246 | ajax: (options) -> 247 | throw new Error "No url provided" unless options.url? 248 | 249 | if window.XMLHttpRequest 250 | # Mozilla, Safari, ... 251 | request = new XMLHttpRequest 252 | else if window.ActiveXObject 253 | # IE 254 | try 255 | request = new ActiveXObject "Msxml2.XMLHTTP" 256 | catch e 257 | try 258 | request = new ActiveXObject "Microsoft.XMLHTTP" 259 | catch e 260 | 261 | throw new Error "Can't create XMLHttpRequest" unless request 262 | 263 | request.onreadystatechange = -> 264 | if request.readyState == 4 265 | try 266 | if request.status == 200 267 | options.onSuccess? request.responseText 268 | else 269 | options.onError? "Server responded with status #{request.status}" 270 | catch e 271 | options.onError? e.message 272 | 273 | options.onComplete?() 274 | 275 | 276 | request.open options.method?.toUpperCase() ? "GET", options.url 277 | request.send() 278 | 279 | # Utility functions 280 | # ================= 281 | 282 | # Creates a shallow copy of the object 283 | clone: (object) -> 284 | newObject = { } 285 | for own key, val of object 286 | newObject[key] = val 287 | newObject 288 | 289 | # Copies all properties from sources to target 290 | extend: (target, sources...) -> 291 | for source in sources 292 | for own key, val of source 293 | target[key] = val 294 | target 295 | 296 | 297 | 298 | 299 | 300 | 301 | # Add the adapter to the list 302 | Opentip.addAdapter new Adapter 303 | -------------------------------------------------------------------------------- /src/adapter-prototype.coffee: -------------------------------------------------------------------------------- 1 | # Prototype Opentip Adapter 2 | # ====================== 3 | # 4 | # Uses the prototype framework 5 | 6 | do -> 7 | 8 | Element.addMethods 9 | addTip: (element, content, title, options) -> 10 | new Opentip element, content, title, options 11 | 12 | 13 | # Needs this function because of IE8 14 | isArrayOrNodeList = (element) -> 15 | if (element instanceof Array) or (element? and typeof element.length == 'number' and typeof element.item == 'function' and typeof element.nextNode == 'function' and typeof element.reset == 'function') 16 | return yes 17 | return no 18 | 19 | # And now the class 20 | class Adapter 21 | 22 | name: "prototype" 23 | 24 | domReady: (callback) -> 25 | if document.loaded 26 | callback() 27 | else 28 | $(document).observe "dom:loaded", callback 29 | 30 | 31 | # DOM 32 | # === 33 | 34 | # Using bonzo to create html 35 | create: (html) -> new Element('div').update(html).childElements() 36 | 37 | 38 | # Element handling 39 | # ---------------- 40 | 41 | # Wraps the element 42 | wrap: (element) -> 43 | if isArrayOrNodeList element 44 | throw new Error "Multiple elements provided." if element.length > 1 45 | element = @unwrap element 46 | else if typeof element == "string" 47 | element = $$(element)[0] 48 | $ element 49 | 50 | # Returns the unwrapped element 51 | unwrap: (element) -> 52 | if isArrayOrNodeList element 53 | element[0] 54 | else 55 | element 56 | 57 | # Returns the tag name of the element 58 | tagName: (element) -> @unwrap(element).tagName 59 | 60 | # Returns or sets the given attribute of element 61 | # 62 | # It's important not to simply forward name and value because the value 63 | # is set whether or not the value argument is present 64 | attr: (element, args...) -> 65 | if args.length == 1 66 | @wrap(element).readAttribute args[0] 67 | else 68 | @wrap(element).writeAttribute args... 69 | 70 | # Returns or sets the given data of element 71 | # It's important not to simply forward name and value because the value 72 | # is set whether or not the value argument is present 73 | data: (element, name, value) -> 74 | @wrap(element) 75 | if arguments.length > 2 76 | element.store name, value 77 | else 78 | arg = element.readAttribute "data-#{name.underscore().dasherize()}" 79 | return arg if arg? 80 | element.retrieve name 81 | 82 | # Finds elements by selector 83 | find: (element, selector) -> @wrap(element).select(selector)[0] 84 | 85 | # Finds all elements by selector 86 | findAll: (element, selector) -> @wrap(element).select selector 87 | 88 | # Updates the content of the element 89 | update: (element, content, escape) -> 90 | @wrap(element).update if escape then content.escapeHTML() else content 91 | 92 | # Appends given child to element 93 | append: (element, child) -> @wrap(element).insert @wrap child 94 | 95 | # Removes element 96 | remove: (element) -> @wrap(element).remove() 97 | 98 | # Add a class 99 | addClass: (element, className) -> @wrap(element).addClassName className 100 | 101 | # Remove a class 102 | removeClass: (element, className) -> @wrap(element).removeClassName className 103 | 104 | # Set given css properties 105 | css: (element, properties) -> @wrap(element).setStyle properties 106 | 107 | # Returns an object with given dimensions 108 | dimensions: (element) -> @wrap(element).getDimensions() 109 | 110 | # Returns the scroll offsets of current document 111 | scrollOffset: -> 112 | offsets = document.viewport.getScrollOffsets() 113 | [ offsets.left, offsets.top ] 114 | 115 | # Returns the dimensions of the viewport (currently visible browser area) 116 | viewportDimensions: -> document.viewport.getDimensions() 117 | 118 | # Returns an object with x and y 119 | mousePosition: (e) -> 120 | return null unless e? 121 | x: Event.pointerX(e), y: Event.pointerY(e) 122 | 123 | 124 | # Returns the offset of the element 125 | offset: (element) -> 126 | offset = @wrap(element).cumulativeOffset() 127 | left: offset.left, top: offset.top 128 | 129 | # Observe given eventName 130 | observe: (element, eventName, observer) -> Event.observe @wrap(element), eventName, observer 131 | 132 | # Stop observing event 133 | stopObserving: (element, eventName, observer) -> Event.stopObserving @wrap(element), eventName, observer 134 | 135 | # Perform an AJAX request and call the appropriate callbacks. 136 | ajax: (options) -> 137 | throw new Error "No url provided" unless options.url? 138 | 139 | new Ajax.Request options.url, { 140 | method: options.method?.toUpperCase() ? "GET" 141 | onSuccess: (response) -> options.onSuccess? response.responseText 142 | onFailure: (response) -> options.onError? "Server responded with status #{response.status}" 143 | onComplete: -> options.onComplete?() 144 | } 145 | 146 | 147 | # Utility functions 148 | # ================= 149 | 150 | # Creates a shallow copy of the object 151 | clone: (object) -> Object.clone(object) 152 | 153 | # Copies all properties from sources to target 154 | extend: (target, sources...) -> 155 | for source in sources 156 | Object.extend target, source 157 | target 158 | 159 | # Add the adapter to the list 160 | Opentip.addAdapter new Adapter 161 | 162 | -------------------------------------------------------------------------------- /test/.gitignore: -------------------------------------------------------------------------------- 1 | ender.min.js 2 | -------------------------------------------------------------------------------- /test/readme.md: -------------------------------------------------------------------------------- 1 | # Tests 2 | 3 | The tests are writte in coffeescript and are in the `src/` folder. 4 | 5 | To compile them, launch `grunt js` in the project root folder. 6 | 7 | To execute the tests, simply open the `test.html` file in a browser, or run 8 | `npm test` in the root folder. -------------------------------------------------------------------------------- /test/src/010-opentip.coffee: -------------------------------------------------------------------------------- 1 | 2 | $ = jQuery 3 | 4 | describe "Opentip", -> 5 | adapter = null 6 | beforeEach -> 7 | adapter = Opentip.adapter 8 | 9 | afterEach -> 10 | elements = $(".opentip-container") 11 | elements.remove() 12 | 13 | 14 | describe "constructor()", -> 15 | before -> 16 | sinon.stub Opentip::, "_setup" 17 | after -> 18 | Opentip::_setup.restore() 19 | it "arguments should be optional", -> 20 | element = adapter.create "
" 21 | opentip = new Opentip element, "content" 22 | expect(opentip.content).to.equal "content" 23 | expect(adapter.unwrap(opentip.triggerElement)).to.equal adapter.unwrap element 24 | 25 | opentip = new Opentip element, "content", "title", { hideOn: "click" } 26 | expect(opentip.content).to.equal "content" 27 | expect(adapter.unwrap opentip.triggerElement).to.equal adapter.unwrap element 28 | expect(opentip.options.hideOn).to.equal "click" 29 | expect(opentip.options.title).to.equal "title" 30 | 31 | opentip = new Opentip element, { hideOn: "click" } 32 | expect(adapter.unwrap opentip.triggerElement).to.equal adapter.unwrap element 33 | expect(opentip.options.hideOn).to.equal "click" 34 | expect(opentip.content).to.equal "" 35 | expect(opentip.options.title).to.equal undefined 36 | 37 | it "should always use the next tip id", -> 38 | element = document.createElement "div" 39 | Opentip.lastId = 0 40 | opentip = new Opentip element, "Test" 41 | opentip2 = new Opentip element, "Test" 42 | opentip3 = new Opentip element, "Test" 43 | expect(opentip.id).to.be 1 44 | expect(opentip2.id).to.be 2 45 | expect(opentip3.id).to.be 3 46 | 47 | it "should use the href attribute if AJAX and an A element", -> 48 | element = $("""link""")[0] 49 | opentip = new Opentip element, ajax: on 50 | expect(opentip.options.ajax).to.equal "http://testlink" 51 | 52 | it "should disable AJAX if neither URL or a link HREF is provided", -> 53 | element = $("""
text
""")[0] 54 | opentip = new Opentip element, ajax: on 55 | expect(opentip.options.ajax).to.be false 56 | 57 | it "should disable a link if the event is onClick", -> 58 | sinon.stub adapter, "observe" 59 | element = $("""link""")[0] 60 | sinon.stub Opentip::, "_setupObservers" 61 | opentip = new Opentip element, showOn: "click" 62 | 63 | 64 | expect(adapter.observe.calledOnce).to.be.ok() 65 | expect(adapter.observe.getCall(0).args[1]).to.equal "click" 66 | 67 | 68 | Opentip::_setupObservers.restore() 69 | adapter.observe.restore() 70 | 71 | it "should take all options from selected style", -> 72 | element = document.createElement "div" 73 | opentip = new Opentip element, style: "glass", showOn: "click" 74 | 75 | # Should have been set by the options 76 | expect(opentip.options.showOn).to.equal "click" 77 | # Should have been set by the glass theme 78 | expect(opentip.options.className).to.equal "glass" 79 | # Should have been set by the standard theme 80 | expect(opentip.options.stemLength).to.equal 5 81 | 82 | it "the property 'style' should be handled the same as 'extends'", -> 83 | element = document.createElement "div" 84 | opentip = new Opentip element, extends: "glass", showOn: "click" 85 | 86 | # Should have been set by the options 87 | expect(opentip.options.showOn).to.equal "click" 88 | # Should have been set by the glass theme 89 | expect(opentip.options.className).to.equal "glass" 90 | # Should have been set by the standard theme 91 | expect(opentip.options.stemLength).to.equal 5 92 | 93 | it "chaining incorrect styles should throw an exception", -> 94 | element = document.createElement "div" 95 | expect(-> new Opentip element, { extends: "invalidstyle" }).to.throwException /Invalid style\: invalidstyle/ 96 | 97 | it "chaining styles should work", -> 98 | element = document.createElement "div" 99 | 100 | Opentip.styles.test1 = stemLength: 40 101 | Opentip.styles.test2 = extends: "test1", title: "overwritten title" 102 | Opentip.styles.test3 = extends: "test2", className: "test5", title: "some title" 103 | 104 | opentip = new Opentip element, { extends: "test3", stemBase: 20 } 105 | 106 | expect(opentip.options.className).to.equal "test5" 107 | expect(opentip.options.title).to.equal "some title" 108 | expect(opentip.options.stemLength).to.equal 40 109 | expect(opentip.options.stemBase).to.equal 20 110 | 111 | it "should set the options to fixed if a target is provided", -> 112 | element = document.createElement "div" 113 | opentip = new Opentip element, target: yes, fixed: no 114 | expect(opentip.options.fixed).to.be.ok() 115 | 116 | it "should use provided stem", -> 117 | element = document.createElement "div" 118 | opentip = new Opentip element, stem: "bottom", tipJoin: "topLeft" 119 | expect(opentip.options.stem.toString()).to.eql "bottom" 120 | 121 | it "should take the tipJoint as stem if stem is just true", -> 122 | element = document.createElement "div" 123 | opentip = new Opentip element, stem: yes, tipJoint: "top left" 124 | expect(opentip.options.stem.toString()).to.eql "top left" 125 | 126 | it "should use provided target", -> 127 | element = adapter.create "
" 128 | element2 = adapter.create "
" 129 | opentip = new Opentip element, target: element2 130 | expect(adapter.unwrap opentip.options.target).to.equal adapter.unwrap element2 131 | 132 | it "should take the triggerElement as target if target is just true", -> 133 | element = adapter.create "
" 134 | opentip = new Opentip element, target: yes 135 | expect(adapter.unwrap opentip.options.target).to.equal adapter.unwrap element 136 | 137 | it "currentStemPosition should be set to inital stemPosition", -> 138 | element = adapter.create "
" 139 | opentip = new Opentip element, stem: "topLeft" 140 | expect(opentip.currentStem.toString()).to.eql "top left" 141 | 142 | it "delay should be automatically set if none provided", -> 143 | element = document.createElement "div" 144 | opentip = new Opentip element, delay: null, showOn: "click" 145 | expect(opentip.options.delay).to.equal 0 146 | opentip = new Opentip element, delay: null, showOn: "mouseover" 147 | expect(opentip.options.delay).to.equal 0.2 148 | 149 | it "the targetJoint should be the inverse of the tipJoint if none provided", -> 150 | element = document.createElement "div" 151 | opentip = new Opentip element, tipJoint: "left" 152 | expect(opentip.options.targetJoint.toString()).to.eql "right" 153 | opentip = new Opentip element, tipJoint: "top" 154 | expect(opentip.options.targetJoint.toString()).to.eql "bottom" 155 | opentip = new Opentip element, tipJoint: "bottom right" 156 | expect(opentip.options.targetJoint.toString()).to.eql "top left" 157 | 158 | 159 | it "should setup all trigger elements", -> 160 | element = adapter.create "
" 161 | opentip = new Opentip element, showOn: "click" 162 | expect(opentip.showTriggers[0].event).to.eql "click" 163 | expect(adapter.unwrap opentip.showTriggers[0].element).to.equal adapter.unwrap element 164 | expect(opentip.showTriggersWhenVisible).to.eql [ ] 165 | expect(opentip.hideTriggers).to.eql [ ] 166 | opentip = new Opentip element, showOn: "creation" 167 | expect(opentip.showTriggers).to.eql [ ] 168 | expect(opentip.showTriggersWhenVisible).to.eql [ ] 169 | expect(opentip.hideTriggers).to.eql [ ] 170 | 171 | it "should copy options.hideTrigger onto options.hideTriggers", -> 172 | element = adapter.create "
" 173 | opentip = new Opentip element, hideTrigger: "closeButton", hideTriggers: [ ] 174 | expect(opentip.options.hideTriggers).to.eql [ "closeButton"] 175 | 176 | it "should NOT copy options.hideTrigger onto options.hideTriggers when hideTriggers are set", -> 177 | element = adapter.create "
" 178 | opentip = new Opentip element, hideTrigger: "closeButton", hideTriggers: [ "tip", "trigger" ] 179 | expect(opentip.options.hideTriggers).to.eql [ "tip", "trigger" ] 180 | 181 | it "should attach itself to the elements `data-opentips` property", -> 182 | element = $("
")[0] 183 | expect(adapter.data element, "opentips").to.not.be.ok() 184 | opentip = new Opentip element 185 | expect(adapter.data element, "opentips").to.eql [ opentip ] 186 | opentip2 = new Opentip element 187 | opentip3 = new Opentip element 188 | expect(adapter.data element, "opentips").to.eql [ opentip, opentip2, opentip3 ] 189 | 190 | it "should add itself to the Opentip.tips list", -> 191 | element = $("
")[0] 192 | Opentip.tips = [ ] 193 | opentip1 = new Opentip element 194 | opentip2 = new Opentip element 195 | expect(Opentip.tips.length).to.equal 2 196 | expect(Opentip.tips[0]).to.equal opentip1 197 | expect(Opentip.tips[1]).to.equal opentip2 198 | 199 | it "should rename ajaxCache to cache for backwards compatibility", -> 200 | element = $("
")[0] 201 | opentip1 = new Opentip element, ajaxCache: off 202 | opentip2 = new Opentip element, ajaxCache: on 203 | 204 | expect(opentip1.options.ajaxCache == opentip2.options.ajaxCache == undefined).to.be.ok() 205 | expect(opentip1.options.cache).to.not.be.ok() 206 | expect(opentip2.options.cache).to.be.ok() 207 | 208 | describe "init()", -> 209 | describe "showOn == creation", -> 210 | element = document.createElement "div" 211 | beforeEach -> sinon.stub Opentip::, "prepareToShow" 212 | afterEach -> Opentip::prepareToShow.restore() 213 | it "should immediately call prepareToShow()", -> 214 | opentip = new Opentip element, showOn: "creation" 215 | expect(opentip.prepareToShow.callCount).to.equal 1 216 | 217 | describe "setContent()", -> 218 | it "should update the content if tooltip currently visible", -> 219 | element = document.createElement "div" 220 | opentip = new Opentip element, showOn: "click" 221 | sinon.stub opentip, "_updateElementContent" 222 | opentip.visible = no 223 | opentip.setContent "TEST" 224 | expect(opentip.content).to.equal "TEST" 225 | opentip.visible = yes 226 | opentip.setContent "TEST2" 227 | expect(opentip.content).to.equal "TEST2" 228 | expect(opentip._updateElementContent.callCount).to.equal 1 229 | opentip._updateElementContent.restore() 230 | 231 | it "should not set the content directly if function", -> 232 | element = document.createElement "div" 233 | opentip = new Opentip element, showOn: "click" 234 | sinon.stub opentip, "_updateElementContent" 235 | opentip.setContent -> "TEST" 236 | expect(opentip.content).to.equal "" 237 | 238 | 239 | describe "_updateElementContent()", -> 240 | 241 | it "should escape the content if @options.escapeContent", -> 242 | element = document.createElement "div" 243 | opentip = new Opentip element, "
", escapeContent: yes 244 | sinon.stub opentip, "_triggerElementExists", -> yes 245 | opentip.show() 246 | expect($(opentip.container).find(".ot-content").html()).to.be """<div><span></span></div>""" 247 | 248 | it "should not escape the content if not @options.escapeContent", -> 249 | element = document.createElement "div" 250 | opentip = new Opentip element, "
", escapeContent: no 251 | sinon.stub opentip, "_triggerElementExists", -> yes 252 | opentip.show() 253 | expect($(opentip.container).find(".ot-content > div > span").length).to.be 1 254 | 255 | it "should storeAndLock dimensions and reposition the element", -> 256 | element = document.createElement "div" 257 | opentip = new Opentip element, showOn: "click" 258 | sinon.stub opentip, "_storeAndLockDimensions" 259 | sinon.stub opentip, "reposition" 260 | opentip.visible = yes 261 | opentip._updateElementContent() 262 | expect(opentip._storeAndLockDimensions.callCount).to.equal 1 263 | expect(opentip.reposition.callCount).to.equal 1 264 | 265 | it "should execute the content function", -> 266 | element = document.createElement "div" 267 | opentip = new Opentip element, showOn: "click" 268 | sinon.stub opentip.adapter, "find", -> "element" 269 | opentip.visible = yes 270 | opentip.setContent -> "BLA TEST" 271 | expect(opentip.content).to.be "BLA TEST" 272 | opentip.adapter.find.restore() 273 | 274 | it "should only execute the content function once if cache:true", -> 275 | element = document.createElement "div" 276 | opentip = new Opentip element, showOn: "click", cache: yes 277 | sinon.stub opentip.adapter, "find", -> "element" 278 | opentip.visible = yes 279 | counter = 0 280 | opentip.setContent -> "count#{counter++}" 281 | expect(opentip.content).to.be "count0" 282 | opentip._updateElementContent() 283 | opentip._updateElementContent() 284 | expect(opentip.content).to.be "count0" 285 | opentip.adapter.find.restore() 286 | 287 | it "should execute the content function multiple times if cache:false", -> 288 | element = document.createElement "div" 289 | opentip = new Opentip element, showOn: "click", cache: no 290 | sinon.stub opentip.adapter, "find", -> "element" 291 | opentip.visible = yes 292 | counter = 0 293 | opentip.setContent -> "count#{counter++}" 294 | expect(opentip.content).to.be "count0" 295 | opentip._updateElementContent() 296 | opentip._updateElementContent() 297 | expect(opentip.content).to.be "count2" 298 | opentip.adapter.find.restore() 299 | 300 | it "should only update the HTML elements if the content has been changed", -> 301 | element = document.createElement "div" 302 | opentip = new Opentip element, showOn: "click" 303 | sinon.stub opentip.adapter, "find", -> "element" 304 | sinon.stub opentip.adapter, "update", -> 305 | opentip.visible = yes 306 | 307 | opentip.setContent "TEST" 308 | expect(opentip.adapter.update.callCount).to.be 1 309 | opentip._updateElementContent() 310 | opentip._updateElementContent() 311 | expect(opentip.adapter.update.callCount).to.be 1 312 | opentip.setContent "TEST2" 313 | expect(opentip.adapter.update.callCount).to.be 2 314 | opentip.adapter.find.restore() 315 | opentip.adapter.update.restore() 316 | 317 | 318 | describe "_buildContainer()", -> 319 | element = document.createElement "div" 320 | opentip = null 321 | beforeEach -> 322 | opentip = new Opentip element, 323 | style: "glass" 324 | showEffect: "appear" 325 | hideEffect: "fade" 326 | opentip._setup() 327 | 328 | it "should set the id", -> 329 | expect(adapter.attr opentip.container, "id").to.equal "opentip-" + opentip.id 330 | it "should set the classes", -> 331 | enderElement = $ adapter.unwrap opentip.container 332 | expect(enderElement.hasClass "opentip-container").to.be.ok() 333 | expect(enderElement.hasClass "ot-hidden").to.be.ok() 334 | expect(enderElement.hasClass "style-glass").to.be.ok() 335 | expect(enderElement.hasClass "ot-show-effect-appear").to.be.ok() 336 | expect(enderElement.hasClass "ot-hide-effect-fade").to.be.ok() 337 | 338 | describe "_buildElements()", -> 339 | element = opentip = null 340 | 341 | beforeEach -> 342 | element = document.createElement "div" 343 | opentip = new Opentip element, "the content", "the title", hideTrigger: "closeButton", stem: "top left", ajax: "bla" 344 | opentip._setup() 345 | opentip._buildElements() 346 | 347 | it "should add a h1 if title is provided", -> 348 | enderElement = $ adapter.unwrap opentip.container 349 | headerElement = enderElement.find "> .opentip > .ot-header > h1" 350 | expect(headerElement.length).to.be.ok() 351 | expect(headerElement.html()).to.be "the title" 352 | 353 | it "should add a loading indicator if ajax", -> 354 | enderElement = $ adapter.unwrap opentip.container 355 | loadingElement = enderElement.find "> .opentip > .ot-loading-indicator > span" 356 | expect(loadingElement.length).to.be.ok() 357 | expect(loadingElement.html()).to.be "↻" 358 | 359 | it "should add a close button if hideTrigger = close", -> 360 | enderElement = $ adapter.unwrap opentip.container 361 | closeButton = enderElement.find "> .opentip > .ot-header > a.ot-close > span" 362 | expect(closeButton.length).to.be.ok() 363 | expect(closeButton.html()).to.be "Close" 364 | 365 | describe "addAdapter()", -> 366 | it "should set the current adapter, and add the adapter to the list", -> 367 | expect(Opentip.adapters.testa).to.not.be.ok() 368 | testAdapter = { name: "testa" } 369 | Opentip.addAdapter testAdapter 370 | expect(Opentip.adapters.testa).to.equal testAdapter 371 | 372 | it "should use adapter.domReady to call findElements() with it" 373 | 374 | describe "_setupObservers()", -> 375 | it "should never setup the same observers twice" 376 | 377 | describe "_searchAndActivateCloseButtons()", -> 378 | it "should do what it says" 379 | 380 | describe "_activateFirstInput()", -> 381 | it "should do what it says", -> 382 | element = document.createElement "div" 383 | opentip = new Opentip element, "