├── .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 | 
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 [](https://travis-ci.org/enyo/opentip)
39 |
40 | 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
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
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
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
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 class name
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, "