├── README.md ├── enjoyhint ├── .bowerrc ├── .gitignore ├── Gruntfile.js ├── README.md ├── bower.json ├── enjoyhint.css ├── enjoyhint.js ├── enjoyhint.min.js ├── package.json └── src │ ├── Casino_Hand │ ├── casino_hand-webfont.eot │ ├── casino_hand-webfont.svg │ ├── casino_hand-webfont.ttf │ └── casino_hand-webfont.woff │ ├── enjoyhint.js │ ├── jquery.enjoyhint.css │ └── jquery.enjoyhint.js ├── hint-sequence.js ├── index.html └── style.css /README.md: -------------------------------------------------------------------------------- 1 | ### Demo of EnjoyHint library 2 | 3 | Check https://github.com/xbsoftware/enjoyhint 4 | -------------------------------------------------------------------------------- /enjoyhint/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory":"lib" 3 | } -------------------------------------------------------------------------------- /enjoyhint/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/* 2 | lib/* -------------------------------------------------------------------------------- /enjoyhint/Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt){ 2 | grunt.initConfig({ 3 | pkg: grunt.file.readJSON('bower.json'), 4 | concat:{ 5 | options: { 6 | separator: ';' 7 | }, 8 | dist:{ 9 | src:['src/*.js', 'lib/kineticjs/kinetic.min.js', 'lib/jquery.scrollTo/jquery.scrollTo.min.js'], 10 | dest: '<%= pkg.name %>.js' 11 | } 12 | }, 13 | uglify: { 14 | main: { 15 | files: { 16 | '<%= pkg.name %>.min.js': ['<%= concat.dist.dest %>'] 17 | } 18 | } 19 | }, 20 | jshint: { 21 | files: ['Gruntfile.js', 'src/*.js'], 22 | options: { 23 | "eqnull": true, 24 | "globals": { 25 | jQuery: true, 26 | console: true, 27 | module: true 28 | } 29 | } 30 | }, 31 | cssmin: { 32 | combine: { 33 | files: { 34 | 'enjoyhint.css': ['src/jquery.enjoyhint.css'] 35 | } 36 | } 37 | } 38 | }); 39 | 40 | grunt.loadNpmTasks('grunt-contrib-uglify'); 41 | grunt.loadNpmTasks('grunt-contrib-jshint'); 42 | grunt.loadNpmTasks('grunt-contrib-concat'); 43 | grunt.loadNpmTasks('grunt-contrib-cssmin'); 44 | 45 | grunt.registerTask("default", ["concat", "uglify", "cssmin"]) 46 | 47 | }; 48 | -------------------------------------------------------------------------------- /enjoyhint/README.md: -------------------------------------------------------------------------------- 1 | EnjoyHint 2 | ========= 3 | **EnjoyHint** is a web-tool that provides the simplest way to create interactive tutorials and hints for your site or web-application. It can also be used to highlight and sign application elements. 4 | 5 | EnjoyHint is free software distributed under the terms of MIT license. 6 | 7 | #### Demo 8 | * [TODO app demo](http://xbsoftware.github.io/enjoyhint/) ([downloadable package](http://xbsoftware.github.io/enjoyhint/enjoyhint_todo_demo.zip)) 9 | * [A small guide on EnjoyHint](http://xbsoftware.github.io/enjoyhint/example1.html) 10 | 11 | #### Dependencies 12 | EnjoyHint require the following plugins and libs: 13 | 14 | * jQuery > 1.7 15 | * KineticJS v5.1.0 (included into js file) 16 | 17 | #### Installation 18 | You can install it through `bower` package manager: 19 | ``` 20 | bower install enjoyhint 21 | ``` 22 | Alternative way: 23 | - Download the latest version of EnjoyHint 24 | - Extract the archive with EnjoyHint. 25 | - Move the EnjoyHint directory to somewhere on your webserver 26 | - Insert next lines into your page's \ tag: 27 | ```html 28 | 29 | 30 | ``` 31 | 32 | #### Initialization and configuration: 33 | ```javascript 34 | //initialize instance 35 | var enjoyhint_instance = new EnjoyHint({}); 36 | 37 | //simple config. 38 | //Only one step - highlighting(with description) "New" button 39 | //hide EnjoyHint after a click on the button. 40 | var enjoyhint_script_steps = [ 41 | { 42 | 'click .new_btn' : 'Click the "New" button to start creating your project' 43 | } 44 | ]; 45 | 46 | //set script config 47 | enjoyhint_instance.set(enjoyhint_script_steps); 48 | 49 | //run Enjoyhint script 50 | enjoyhint_instance.run(); 51 | ``` 52 | 53 | #### Script Configuration 54 | The sequence of steps can be only linear for now. So, the script config is an array. Every element of this array is the config for some step. 55 | 56 | #### Example of script configuration 57 | Highlight some button and after you click on it, highlight some panel: 58 | ```javascript 59 | var enjoyhint_script_steps = [ 60 | { 61 | 'click .some_btn' : 'Click on this btn' 62 | }, 63 | { 64 | 'click .some_panel' : 'Click on this panel' 65 | } 66 | ]; 67 | ``` 68 | 69 | #### Properties of the step configuration 70 | * `"event selector" : "description"` - to describe a step you should set an event type, selecte element and add description for this element (hint) 71 | * `keyCode` - the code of a button, which triggers the next EnjoyHint step upon a click. Defined by the “key” event. (“key #block” : “hello”). 72 | * `event_selector` - if you need to attach an event (that was set in "event" property) to other selector, you can use this one 73 | * `timeout` - delay before the moment, when an element is highlighted 74 | * `shape` - shape for highlighting (circle|rect) 75 | * `radius` - if the shape of "circle" is specified, we can set the radius. 76 | * `margin` - margin for the highlight shape (for Ex.:10) 77 | * `top` - top margin for the shape of "rect" type 78 | * `right` - right margin for the shape of "rect" type 79 | * `bottom` - bottom margin for the shape of "rect" type 80 | * `left` - left margin for the shape of "rect" type 81 | * `scrollAnimationSpeed` - sets the auto scroll speed (ms). 82 | * `nextButton` - allows applying its classes and names for the button Nеxt. 83 | * `skipButton` - allows applying its classes and names for the button Skip. For the example : 84 | ```javascript 85 | var options = { 86 | "next #block": 'Hello.', 87 | "nextButton" : {className: "myNext", text: "NEXT"}, 88 | "skipButton" : {className: "mySkip", text: "SKIP"}, 89 | 90 | } 91 | ``` 92 | * `showSkip` - shows or hides the Skip button (true|false) 93 | * `showNext` - shows or hides the Next button (true|false) 94 | 95 | 96 | 97 | 98 | #### Non-standard events: 99 | **auto** - for example, you need to click on the same button on the second step imediatelly after the first step and go to the next step after it. Then you can use "auto" in the "event_type" property and "click" in "event" property. 100 | * `custom` - this value is very usefull if you need to go to the next step by event in your app code. For example, you want to go to the next step only after some data have been loaded in your application. Then you should use the "custom" event_type and the "trigger" method of the EnjoyHint instance. 101 | ```javascript 102 | //Example of using custom event_type 103 | $.get('/load/some_data', function(data){ 104 | //trigger method has only one argument: event_name.(equal to the value of event property in step config) 105 | enjoyhint_instance.trigger('custom_event_name'); 106 | }); 107 | ``` 108 | * `next` - when you set value of event_type to "next", you will see the "Next" btn on this step. 109 | * `key` - tells EnjoyHint to go to the next step when you click on the button defined by the keyCode 110 | 111 | 112 | #### Methods 113 | * `set` - set current steps configuration. Arguments: config 114 | * `run` - run the current script. Has no arguments 115 | * `resume` - resume the script from the step where it was stopped. Has no arguments 116 | * `getCurrentStep` - returns the current step index 117 | * `trigger` - After writing this code you can either move to the next step or finish with EnjoyHint (next|skip) 118 | 119 | #### Events 120 | **Script Events**: 121 | * `onStart` - fires on the first step. 122 | * `onEnd` - fires after the last step in script. 123 | ```javascript 124 | var enjoyhint_instance = new EnjoyHint({ 125 | onStart:function(){ 126 | //do something 127 | } 128 | }); 129 | ``` 130 | **Step Events**: 131 | * `onBeforeStart` - fires before the step is started. 132 | ```javascript 133 | var enjoyhint_script_steps = [ 134 | { 135 | selector:'.some_btn',//jquery selector 136 | event:'click', 137 | description:'Click on this btn', 138 | onBeforeStart:function(){ 139 | //do something 140 | } 141 | } 142 | ]; 143 | ``` 144 | 145 | #### Release notes 146 | 147 | ##### v.3 148 | 149 | * New and simplified description of EnjoyHint steps 150 | * Auto scroll to the element 151 | * Possibility to hide or display the buttons showNext, showSkip. 152 | * HTML usage allowed in description 153 | * Destructor 154 | * Simplified property names 155 | * Grunt to compress and merge files 156 | * New examples 157 | * You can learn the step you are on by the class enjoyhint-step-* ( where * stands for the step number). 158 | 159 | 160 | 161 | 162 | -------------------------------------------------------------------------------- /enjoyhint/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enjoyhint", 3 | "main": [ 4 | "enjoyhint.js", 5 | "enjoyhint.css" 6 | ], 7 | "version": "3.1.0", 8 | "homepage": "https://github.com/xbsoftware/enjoyhint", 9 | "authors": [ 10 | "XB Software" 11 | ], 12 | "description": "Web-tool that provides the simplest way to create interactive tutorials and hints.", 13 | "keywords": [ 14 | "enjoyhint", 15 | "hint", 16 | "xbs", 17 | "tutorials" 18 | ], 19 | "license": "MIT", 20 | "ignore": [ 21 | "**/.*", 22 | "node_modules", 23 | "bower_components", 24 | "test", 25 | "tests" 26 | ], 27 | "dependencies": { 28 | "jquery": "~2.1.3", 29 | "jquery.scrollTo": "~1.4.14", 30 | "kineticjs": "~5.1.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /enjoyhint/enjoyhint.css: -------------------------------------------------------------------------------- 1 | .enjoyhint_close_btn,.enjoyhint_close_btn::after,.enjoyhint_close_btn::before,.enjoyhint_next_btn,.enjoyhint_skip_btn{-o-text-overflow:clip;text-overflow:clip}.enjoyhint_next_btn,.enjoyhint_skip_btn{font:normal normal normal 17px/40px "Advent Pro",Helvetica,sans-serif;-webkit-border-radius:40px;border-radius:40px}.enjoyhint_btn,.enjoyhint_close_btn,.enjoyhint_close_btn::after,.enjoyhint_close_btn::before,.enjoyhint_next_btn,.enjoyhint_skip_btn{-webkit-box-sizing:content-box}.enjoyhint_btn,.enjoyhint_close_btn,.enjoyhint_close_btn::after,.enjoyhint_next_btn,.enjoyhint_skip_btn{-moz-box-sizing:content-box}@font-face{font-family:casino_handregular;src:url(Casino_Hand/casino_hand-webfont.eot);src:url(Casino_Hand/casino_hand-webfont.eot?#iefix)format('embedded-opentype'),url(Casino_Hand/casino_hand-webfont.woff)format('woff'),url(Casino_Hand/casino_hand-webfont.ttf)format('truetype'),url(Casino_Hand/casino_hand-webfont.svg#casino_handregular)format('svg');font-weight:400;font-style:normal}.enjoyhint{position:fixed;width:100%;height:100%;top:0;left:0;z-index:1010;pointer-events:none;overflow:hidden}.enjoyhint_close_btn,.enjoyhint_next_btn,.enjoyhint_skip_btn{z-index:1012;pointer-events:all}.enjoyhint_hide{display:none}.enjoyhint_close_btn::after,.enjoyhint_close_btn::before{background:#fff;border:none;color:rgba(0,0,0,1);display:inline-block}.enjoyhint_disable_events{position:absolute;width:2000px;height:1500px;z-index:1011;pointer-events:all}.enjoyhint_next_btn{position:absolute;box-sizing:content-box;width:100px;height:40px;cursor:pointer;margin:0 auto;border:2px solid #1ecd97;color:#1ecd97;text-align:center;letter-spacing:1px;background:0 0;-webkit-transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0);-moz-transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0);-o-transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0);transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0)}.enjoyhint_next_btn:hover{color:rgba(255,255,255,1);background:#1ecd97}.enjoyhint_next_btn:active{border:2px solid rgba(33,224,163,1);background:rgba(33,224,163,1);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.enjoyhint_btn,.enjoyhint_skip_btn{-o-transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0)}.enjoyhint_skip_btn{position:absolute;box-sizing:content-box;width:100px;height:40px;cursor:pointer;margin:0 auto;border:2px solid #1ecd97;color:#1ecd97;text-align:center;letter-spacing:1px;background:0 0;-webkit-transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0);-moz-transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0);transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0)}.enjoyhint_close_btn:active,.enjoyhint_skip_btn:active{transition:none;-webkit-transition:none;-moz-transition:none}.enjoyhint_skip_btn:hover{color:rgba(255,255,255,1);background:#1ecd97}.enjoyhint_skip_btn:active{border:2px solid rgba(33,224,163,1);background:rgba(33,224,163,1);-o-transition:none}.enjoyhint_close_btn{display:inline-block;position:absolute;box-sizing:content-box;width:.3em;height:.3em;border:none;-webkit-border-radius:1em;border-radius:1em;font:400 8em/normal Arial,Helvetica,sans-serif;color:rgba(0,0,0,1);background:0 0;border:2px solid rgba(33,224,163,1)}.enjoyhint_close_btn::after,.enjoyhint_close_btn::before{width:73%;height:2px;font:normal 100%/normal Arial,Helvetica,sans-serif}.enjoyhint_close_btn::before{-moz-box-sizing:content-box;box-sizing:content-box;position:absolute;content:"";top:48%;left:14%;text-shadow:none;-webkit-transform:rotateZ(45deg);transform:rotateZ(45deg)}.enjoyhint_close_btn::after{box-sizing:content-box;position:absolute;content:"";top:46%;left:15%;text-shadow:none;-webkit-transform:rotateZ(-45deg);transform:rotateZ(-45deg)}#kinetic_container,.enjoyhint_svg_wrapper,.enjoyhint_svg_wrapper svg{width:100%;height:100%;top:0;left:0}.enjoyhint_close_btn:hover{color:rgba(255,255,255,1);background:#1ecd97;cursor:pointer}.enjoyhint_close_btn:active{border:2px solid rgba(33,224,163,1);background:rgba(33,224,163,1);-o-transition:none}.enjoyhint_btn{box-sizing:content-box;width:150px;height:40px;cursor:pointer;margin:0 auto;border:2px solid #1ecd97;-webkit-border-radius:40px;border-radius:40px;font:normal normal normal 17px/40px "Advent Pro",Helvetica,sans-serif;color:#1ecd97;text-align:center;-o-text-overflow:clip;text-overflow:clip;letter-spacing:1px;background:0 0;-webkit-transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0);-moz-transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0);transition:background-color .3s cubic-bezier(0,0,0,0),color .3s cubic-bezier(0,0,0,0),width .3s cubic-bezier(0,0,0,0),border-width .3s cubic-bezier(0,0,0,0),border-color .3s cubic-bezier(0,0,0,0)}.enjoyhint_btn:hover{color:rgba(255,255,255,1);background:#1ecd97}.enjoyhint_btn:active{border:2px solid rgba(33,224,163,1);background:rgba(33,224,163,1);-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.enjoy_hint_label,.enjoyhint_svg_wrapper{-moz-transition:opacity 400ms cubic-bezier(.42,0,.58,1);transition:opacity 400ms cubic-bezier(.42,0,.58,1);-webkit-transition:opacity 400ms cubic-bezier(.42,0,.58,1)}.enjoyhint div.canvas-container{position:absolute}.enjoyhint_canvas{position:absolute;z-index:100;width:100%;height:100%;pointer-events:none}#kinetic_container{pointer-events:none;position:absolute}.enjoyhint_svg_wrapper{position:absolute;z-index:100}.enjoyhint_svg_wrapper svg{position:absolute}.enjoyhint_svg_transparent .enjoy_hint_label,.enjoyhint_svg_transparent .enjoyhint_svg_wrapper{opacity:0}.enjoy_hint_label{position:absolute;color:#fff;z-index:107;font-size:22px;font-family:casino_handregular,Arial}div.kineticjs-content{position:absolute!important} -------------------------------------------------------------------------------- /enjoyhint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enjoyhint", 3 | "version": "1.0.0", 4 | "description": "Web-tool that provides the simplest way to create interactive tutorials and hints.", 5 | "main": "enjoyhint.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/xbsoftware/enjoyhint.git" 12 | }, 13 | "keywords": [ 14 | "enjoyhint", 15 | "hint", 16 | "xbs", 17 | "tutorials" 18 | ], 19 | "author": "XB Software", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/xbsoftware/enjoyhint/issues" 23 | }, 24 | "homepage": "https://github.com/xbsoftware/enjoyhint", 25 | "devDependencies": { 26 | "grunt-contrib-concat": "^0.5.1", 27 | "grunt-contrib-cssmin": "^0.11.0", 28 | "grunt-contrib-jshint": "^0.11.2", 29 | "grunt-contrib-uglify": "^0.9.1" 30 | }, 31 | "dependencies": { 32 | "grunt": "^0.4.5", 33 | "kinetic": "^5.2.0" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /enjoyhint/src/Casino_Hand/casino_hand-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/enjoyhint-demo/41754c52a73df8e826ab4c817bb839575298e055/enjoyhint/src/Casino_Hand/casino_hand-webfont.eot -------------------------------------------------------------------------------- /enjoyhint/src/Casino_Hand/casino_hand-webfont.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | -------------------------------------------------------------------------------- /enjoyhint/src/Casino_Hand/casino_hand-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/enjoyhint-demo/41754c52a73df8e826ab4c817bb839575298e055/enjoyhint/src/Casino_Hand/casino_hand-webfont.ttf -------------------------------------------------------------------------------- /enjoyhint/src/Casino_Hand/casino_hand-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sitepoint-editors/enjoyhint-demo/41754c52a73df8e826ab4c817bb839575298e055/enjoyhint/src/Casino_Hand/casino_hand-webfont.woff -------------------------------------------------------------------------------- /enjoyhint/src/enjoyhint.js: -------------------------------------------------------------------------------- 1 | var EnjoyHint = function (_options) { 2 | var that = this; 3 | // Some options 4 | var defaults = { 5 | onStart: function () { 6 | 7 | }, 8 | onEnd: function () { 9 | 10 | } 11 | }; 12 | var options = $.extend(defaults, _options); 13 | 14 | 15 | var data = []; 16 | var current_step = 0; 17 | 18 | $body = $('body'); 19 | 20 | /********************* PRIVAT METHODS ***************************************/ 21 | var init = function () { 22 | if ($('.enjoyhint')) 23 | $('.enjoyhint').remove(); 24 | $('body').css({'overflow':'hidden'}); 25 | $(document).on("touchmove",lockTouch); 26 | 27 | $body.enjoyhint({ 28 | onNextClick: function () { 29 | nextStep(); 30 | }, 31 | onSkipClick: function () { 32 | skipAll(); 33 | } 34 | }); 35 | }; 36 | 37 | var lockTouch = function(e) { 38 | e.preventDefault(); 39 | }; 40 | 41 | var destroyEnjoy = function () { 42 | $body = $('body'); 43 | $('.enjoyhint').remove(); 44 | $("body").css({'overflow':'auto'}); 45 | $(document).off("touchmove", lockTouch); 46 | 47 | }; 48 | 49 | that.clear = function(){ 50 | //(Remove userClass and set default text) 51 | $(".enjoyhint_next_btn").removeClass(that.nextUserClass); 52 | $(".enjoyhint_next_btn").text("Next"); 53 | $(".enjoyhint_skip_btn").removeClass(that.skipUserClass); 54 | $(".enjoyhint_skip_btn").text("Skip"); 55 | } 56 | 57 | var $body = $('body'); 58 | 59 | var stepAction = function () { 60 | if (data && data[current_step]) { 61 | $(".enjoyhint").removeClass("enjoyhint-step-"+current_step); 62 | $(".enjoyhint").addClass("enjoyhint-step-"+(current_step+1)); 63 | var step_data = data[current_step]; 64 | if (step_data.onBeforeStart && typeof step_data.onBeforeStart === 'function') { 65 | step_data.onBeforeStart(); 66 | } 67 | var timeout = step_data.timeout || 0; 68 | setTimeout(function () { 69 | if (!step_data.selector) { 70 | for (var prop in step_data) { 71 | if (step_data.hasOwnProperty(prop) && prop.split(" ")[1]) { 72 | var space_index = prop.indexOf(" "); 73 | step_data.event = prop.slice(0, space_index); 74 | step_data.selector = prop.slice(space_index + 1); 75 | if (step_data.event == 'next' || step_data.event == 'auto' || step_data.event == 'custom') { 76 | step_data.event_type = step_data.event; 77 | } 78 | step_data.description = step_data[prop]; 79 | } 80 | } 81 | } 82 | setTimeout(function(){ 83 | that.clear(); 84 | }, 250); 85 | $(document.body).scrollTo(step_data.selector, step_data.scrollAnimationSpeed || 250, {offset: -100}); 86 | setTimeout(function () { 87 | var $element = $(step_data.selector); 88 | var event = makeEventName(step_data.event); 89 | 90 | $body.enjoyhint('show'); 91 | $body.enjoyhint('hide_next'); 92 | var $event_element = $element; 93 | if (step_data.event_selector) { 94 | $event_element = $(step_data.event_selector); 95 | } 96 | if (!step_data.event_type && step_data.event == "key"){ 97 | $element.keydown(function( event ) { 98 | if ( event.which == step_data.keyCode ) { 99 | current_step++; 100 | stepAction(); 101 | } 102 | }); 103 | } 104 | if (step_data.showNext == true){ 105 | $body.enjoyhint('show_next'); 106 | } 107 | if (step_data.showSkip == false){ 108 | $body.enjoyhint('hide_skip'); 109 | }else{ 110 | $body.enjoyhint('show_skip'); 111 | } 112 | if (step_data.showSkip == true){ 113 | 114 | } 115 | 116 | 117 | if (step_data.nextButton){ 118 | $(".enjoyhint_next_btn").addClass(step_data.nextButton.className || ""); 119 | $(".enjoyhint_next_btn").text(step_data.nextButton.text || "Next"); 120 | that.nextUserClass = step_data.nextButton.className 121 | } 122 | 123 | if (step_data.skipButton){ 124 | $(".enjoyhint_skip_btn").addClass(step_data.skipButton.className || ""); 125 | $(".enjoyhint_skip_btn").text(step_data.skipButton.text || "Skip"); 126 | that.skipUserClass = step_data.skipButton.className 127 | } 128 | 129 | if (step_data.event_type) { 130 | switch (step_data.event_type) { 131 | case 'auto': 132 | $element[step_data.event](); 133 | switch (step_data.event) { 134 | case 'click': 135 | break; 136 | } 137 | current_step++; 138 | stepAction(); 139 | return; 140 | break; 141 | case 'custom': 142 | on(step_data.event, function () { 143 | current_step++; 144 | off(step_data.event); 145 | stepAction(); 146 | }); 147 | break; 148 | case 'next': 149 | $body.enjoyhint('show_next'); 150 | break; 151 | 152 | } 153 | 154 | } else { 155 | $event_element.on(event, function (e) { 156 | if (step_data.keyCode && e.keyCode != step_data.keyCode) { 157 | return; 158 | } 159 | current_step++; 160 | $(this).off(event); 161 | 162 | stepAction(); 163 | }); 164 | 165 | } 166 | var max_habarites = Math.max($element.outerWidth(), $element.outerHeight()); 167 | var radius = step_data.radius || Math.round(max_habarites / 2) + 5; 168 | var offset = $element.offset(); 169 | var w = $element.outerWidth(); 170 | var h = $element.outerHeight(); 171 | var shape_margin = (step_data.margin !== undefined) ? step_data.margin : 10; 172 | var coords = { 173 | x: offset.left + Math.round(w / 2) , 174 | y: offset.top + Math.round(h / 2) - $(document).scrollTop() 175 | }; 176 | var shape_data = { 177 | center_x: coords.x, 178 | center_y: coords.y, 179 | text: step_data.description, 180 | top: step_data.top, 181 | bottom: step_data.bottom, 182 | left: step_data.left, 183 | right: step_data.right, 184 | margin: step_data.margin, 185 | scroll: step_data.scroll 186 | }; 187 | 188 | if (step_data.shape && step_data.shape == 'circle') { 189 | shape_data.shape = 'circle'; 190 | shape_data.radius = radius; 191 | } else { 192 | shape_data.radius = 0; 193 | shape_data.width = w + shape_margin; 194 | shape_data.height = h + shape_margin; 195 | } 196 | $body.enjoyhint('render_label_with_shape', shape_data); 197 | }, step_data.scrollAnimationSpeed + 20 || 270); 198 | }, timeout); 199 | } else { 200 | $body.enjoyhint('hide'); 201 | options.onEnd(); 202 | destroyEnjoy(); 203 | } 204 | 205 | }; 206 | 207 | var nextStep = function(){ 208 | current_step++; 209 | stepAction(); 210 | }; 211 | var skipAll = function(){ 212 | var step_data = data[current_step]; 213 | var $element = $(step_data.selector); 214 | off(step_data.event); 215 | $element.off(makeEventName(step_data.event)); 216 | destroyEnjoy(); 217 | }; 218 | 219 | var makeEventName = function (name, is_custom) { 220 | return name + (is_custom ? 'custom' : '') + '.enjoy_hint'; 221 | }; 222 | 223 | var on = function (event_name, callback) { 224 | $body.on(makeEventName(event_name, true), callback); 225 | }; 226 | var off = function (event_name) { 227 | $body.off(makeEventName(event_name, true)); 228 | }; 229 | 230 | /********************* PUBLIC METHODS ***************************************/ 231 | that.runScript = function () { 232 | current_step = 0; 233 | options.onStart(); 234 | stepAction(); 235 | }; 236 | 237 | that.resumeScript = function () { 238 | stepAction(); 239 | }; 240 | 241 | that.getCurrentStep = function () { 242 | return current_step; 243 | }; 244 | 245 | that.trigger = function (event_name) { 246 | switch (event_name) { 247 | case 'next': 248 | nextStep(); 249 | break 250 | case 'skip': 251 | skipAll(); 252 | break 253 | } 254 | }; 255 | 256 | that.setScript = function (_data) { 257 | if (_data) { 258 | data = _data; 259 | } 260 | }; 261 | 262 | //support deprecated API methods 263 | that.set = function (_data) { 264 | that.setScript(_data); 265 | }; 266 | 267 | that.setSteps = function (_data) { 268 | that.setScript(_data); 269 | }; 270 | 271 | that.run = function () { 272 | that.runScript(); 273 | }; 274 | 275 | that.resume = function () { 276 | that.resumeScript(); 277 | }; 278 | 279 | 280 | init(); 281 | }; 282 | -------------------------------------------------------------------------------- /enjoyhint/src/jquery.enjoyhint.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'casino_handregular'; 3 | src: url('Casino_Hand/casino_hand-webfont.eot'); 4 | src: url('Casino_Hand/casino_hand-webfont.eot?#iefix') format('embedded-opentype'), 5 | url('Casino_Hand/casino_hand-webfont.woff') format('woff'), 6 | url('Casino_Hand/casino_hand-webfont.ttf') format('truetype'), 7 | url('Casino_Hand/casino_hand-webfont.svg#casino_handregular') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | 11 | } 12 | .enjoyhint{ 13 | position: fixed; 14 | width: 100%; 15 | height: 100%; 16 | top:0; 17 | left: 0; 18 | z-index: 1010; 19 | pointer-events: none; 20 | overflow: hidden; 21 | } 22 | 23 | 24 | .enjoyhint_hide{ 25 | display: none; 26 | } 27 | 28 | .enjoyhint_disable_events{ 29 | position:absolute; 30 | width: 2000px; 31 | height: 1500px; 32 | z-index: 1011; 33 | /*display: none;*/ 34 | pointer-events: all; 35 | } 36 | 37 | .enjoyhint_next_btn{ 38 | position:absolute; 39 | z-index: 1012; 40 | /*display: none;*/ 41 | pointer-events: all; 42 | -webkit-box-sizing: content-box; 43 | -moz-box-sizing: content-box; 44 | box-sizing: content-box; 45 | width: 100px; 46 | height: 40px; 47 | cursor: pointer; 48 | margin: 0 auto; 49 | border: 2px solid rgb(30,205,151); 50 | -webkit-border-radius: 40px; 51 | border-radius: 40px; 52 | font: normal normal normal 17px/40px "Advent Pro", Helvetica, sans-serif; 53 | color: rgb(30, 205, 151); 54 | text-align: center; 55 | -o-text-overflow: clip; 56 | text-overflow: clip; 57 | letter-spacing: 1px; 58 | background: rgba(0,0,0,0); 59 | -webkit-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 60 | -moz-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 61 | -o-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 62 | transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 63 | } 64 | 65 | .enjoyhint_next_btn:hover{ 66 | color: rgba(255,255,255,1); 67 | background: rgb(30, 205, 151); 68 | } 69 | 70 | .enjoyhint_next_btn:active{ 71 | border: 2px solid rgba(33,224,163,1); 72 | background: rgba(33,224,163,1); 73 | -webkit-transition: none; 74 | -moz-transition: none; 75 | -o-transition: none; 76 | transition: none; 77 | } 78 | 79 | .enjoyhint_skip_btn{ 80 | position:absolute; 81 | position: absolute; 82 | z-index: 1012; 83 | /*display: none;*/ 84 | pointer-events: all; 85 | -webkit-box-sizing: content-box; 86 | -moz-box-sizing: content-box; 87 | box-sizing: content-box; 88 | width: 100px; 89 | height: 40px; 90 | cursor: pointer; 91 | margin: 0 auto; 92 | border: 2px solid rgb(30,205,151); 93 | -webkit-border-radius: 40px; 94 | border-radius: 40px; 95 | font: normal normal normal 17px/40px "Advent Pro", Helvetica, sans-serif; 96 | color: rgb(30, 205, 151); 97 | text-align: center; 98 | -o-text-overflow: clip; 99 | text-overflow: clip; 100 | letter-spacing: 1px; 101 | background: rgba(0,0,0,0); 102 | -webkit-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 103 | -moz-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 104 | -o-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 105 | transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 106 | } 107 | 108 | .enjoyhint_skip_btn:hover{ 109 | color: rgba(255,255,255,1); 110 | background: rgb(30, 205, 151); 111 | } 112 | 113 | .enjoyhint_skip_btn:active{ 114 | border: 2px solid rgba(33,224,163,1); 115 | background: rgba(33,224,163,1); 116 | -webkit-transition: none; 117 | -moz-transition: none; 118 | -o-transition: none; 119 | transition: none; 120 | } 121 | 122 | .enjoyhint_close_btn { 123 | display: inline-block; 124 | position: absolute; 125 | z-index: 1012; 126 | pointer-events: all; 127 | -webkit-box-sizing: content-box; 128 | -moz-box-sizing: content-box; 129 | box-sizing: content-box; 130 | width: .3em; 131 | height: .3em; 132 | border: none; 133 | -webkit-border-radius: 1em; 134 | border-radius: 1em; 135 | font: 400 8em/normal Arial,Helvetica,sans-serif; 136 | color: rgba(0,0,0,1); 137 | -o-text-overflow: clip; 138 | text-overflow: clip; 139 | background: rgba(0,0,0,0); 140 | border: 2px solid rgba(33,224,163,1); 141 | } 142 | 143 | .enjoyhint_close_btn::before { 144 | display: inline-block; 145 | -webkit-box-sizing: content-box; 146 | -moz-box-sizing: content-box; 147 | box-sizing: content-box; 148 | width: 73%; 149 | height: 2px; 150 | position: absolute; 151 | content: ""; 152 | top: 48%; 153 | left: 14%; 154 | border: none; 155 | font: normal 100%/normal Arial,Helvetica,sans-serif; 156 | color: rgba(0,0,0,1); 157 | -o-text-overflow: clip; 158 | text-overflow: clip; 159 | background: #fff; 160 | text-shadow: none; 161 | -webkit-transform: rotateZ(45deg); 162 | transform: rotateZ(45deg); 163 | } 164 | 165 | .enjoyhint_close_btn::after { 166 | display: inline-block; 167 | -webkit-box-sizing: content-box; 168 | -moz-box-sizing: content-box; 169 | box-sizing: content-box; 170 | width: 73%; 171 | height: 2px; 172 | position: absolute; 173 | content: ""; 174 | top: 46%; 175 | left: 15%; 176 | border: none; 177 | font: normal 100%/normal Arial,Helvetica,sans-serif; 178 | color: rgba(0,0,0,1); 179 | -o-text-overflow: clip; 180 | text-overflow: clip; 181 | background: #fff; 182 | text-shadow: none; 183 | -webkit-transform: rotateZ(-45deg); 184 | transform: rotateZ(-45deg); 185 | } 186 | 187 | 188 | .enjoyhint_close_btn:hover{ 189 | color: rgba(255,255,255,1); 190 | background: rgb(30, 205, 151); 191 | cursor: pointer; 192 | } 193 | 194 | .enjoyhint_close_btn:active{ 195 | border: 2px solid rgba(33,224,163,1); 196 | background: rgba(33,224,163,1); 197 | -webkit-transition: none; 198 | -moz-transition: none; 199 | -o-transition: none; 200 | transition: none; 201 | } 202 | 203 | .enjoyhint_btn{ 204 | -webkit-box-sizing: content-box; 205 | -moz-box-sizing: content-box; 206 | box-sizing: content-box; 207 | width: 150px; 208 | height: 40px; 209 | cursor: pointer; 210 | margin: 0 auto; 211 | border: 2px solid rgb(30,205,151); 212 | -webkit-border-radius: 40px; 213 | border-radius: 40px; 214 | font: normal normal normal 17px/40px "Advent Pro", Helvetica, sans-serif; 215 | color: rgb(30, 205, 151); 216 | text-align: center; 217 | -o-text-overflow: clip; 218 | text-overflow: clip; 219 | letter-spacing: 1px; 220 | background: rgba(0,0,0,0); 221 | -webkit-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 222 | -moz-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 223 | -o-transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 224 | transition: background-color 0.3s cubic-bezier(0, 0, 0, 0), color 0.3s cubic-bezier(0, 0, 0, 0), width 0.3s cubic-bezier(0, 0, 0, 0), border-width 0.3s cubic-bezier(0, 0, 0, 0), border-color 0.3s cubic-bezier(0, 0, 0, 0); 225 | } 226 | 227 | .enjoyhint_btn:hover{ 228 | color: rgba(255,255,255,1); 229 | background: rgb(30, 205, 151); 230 | } 231 | 232 | .enjoyhint_btn:active { 233 | border: 2px solid rgba(33,224,163,1); 234 | background: rgba(33,224,163,1); 235 | -webkit-transition: none; 236 | -moz-transition: none; 237 | -o-transition: none; 238 | transition: none; 239 | } 240 | 241 | 242 | .enjoyhint div.canvas-container{ 243 | position: absolute; 244 | } 245 | 246 | .enjoyhint_canvas{ 247 | position: absolute; 248 | z-index: 100; 249 | width: 100%; 250 | height: 100%; 251 | pointer-events: none; 252 | } 253 | 254 | #kinetic_container{ 255 | pointer-events: none; 256 | position: absolute; 257 | width: 100%; 258 | height: 100%; 259 | top:0; 260 | left: 0; 261 | } 262 | 263 | .enjoyhint_svg{ 264 | } 265 | .enjoyhint_svg_wrapper{ 266 | position: absolute; 267 | width: 100%; 268 | height: 100%; 269 | top:0; 270 | left: 0; 271 | z-index: 100; 272 | -webkit-transition: opacity 400ms cubic-bezier(0.42, 0, 0.58, 1); 273 | -moz-transition: opacity 400ms cubic-bezier(0.42, 0, 0.58, 1); 274 | transition: opacity 400ms cubic-bezier(0.42, 0, 0.58, 1); 275 | } 276 | .enjoyhint_svg_wrapper svg{ 277 | position: absolute; 278 | width: 100%; 279 | height: 100%; 280 | top:0; 281 | left: 0; 282 | } 283 | 284 | .enjoyhint_svg_transparent .enjoyhint_svg_wrapper,.enjoyhint_svg_transparent .enjoy_hint_label{ 285 | opacity:0; 286 | } 287 | 288 | 289 | .enjoy_hint_label{ 290 | position: absolute; 291 | color: white; 292 | z-index: 107; 293 | font-size: 22px; 294 | font-family: 'casino_handregular', 'Arial'; 295 | -webkit-transition: opacity 400ms cubic-bezier(0.42, 0, 0.58, 1); 296 | -moz-transition: opacity 400ms cubic-bezier(0.42, 0, 0.58, 1); 297 | transition: opacity 400ms cubic-bezier(0.42, 0, 0.58, 1); 298 | } 299 | 300 | 301 | 302 | div.kineticjs-content{ 303 | position: absolute !important; 304 | } -------------------------------------------------------------------------------- /enjoyhint/src/jquery.enjoyhint.js: -------------------------------------------------------------------------------- 1 | CanvasRenderingContext2D.prototype.roundRect = function (x, y, w, h, r) { 2 | if (w < 2 * r) r = w / 2; 3 | if (h < 2 * r) r = h / 2; 4 | this.beginPath(); 5 | this.moveTo(x + r, y); 6 | this.arcTo(x + w, y, x + w, y + h, r); 7 | this.arcTo(x + w, y + h, x, y + h, r); 8 | this.arcTo(x, y + h, x, y, r); 9 | this.arcTo(x, y, x + w, y, r); 10 | this.closePath(); 11 | return this; 12 | }; 13 | 14 | (function ($) { 15 | var methods = { 16 | init: function (options) { 17 | return this.each(function () { 18 | var defaults = { 19 | onNextClick: function () { 20 | }, 21 | onSkipClick: function () { 22 | }, 23 | animation_time: 800 24 | }; 25 | 26 | 27 | this.enjoyhint_obj = {}; 28 | var that = this.enjoyhint_obj; 29 | var $that = $(this); 30 | var $body = $('body'); 31 | that.options = jQuery.extend(defaults, options); 32 | 33 | //general classes 34 | that.gcl = { 35 | chooser: 'enjoyhint' 36 | }; 37 | 38 | // classes 39 | that.cl = { 40 | enjoy_hint: 'enjoyhint', 41 | hide: 'enjoyhint_hide', 42 | disable_events_element: 'enjoyhint_disable_events', 43 | btn: 'enjoyhint_btn', 44 | skip_btn: 'enjoyhint_skip_btn', 45 | close_btn: 'enjoyhint_close_btn', 46 | next_btn: 'enjoyhint_next_btn', 47 | main_canvas: 'enjoyhint_canvas', 48 | main_svg: 'enjoyhint_svg', 49 | svg_wrapper: 'enjoyhint_svg_wrapper', 50 | svg_transparent: 'enjoyhint_svg_transparent', 51 | kinetic_container: 'kinetic_container' 52 | }; 53 | function makeSVG(tag, attrs) { 54 | var el = document.createElementNS('http://www.w3.org/2000/svg', tag); 55 | for (var k in attrs) 56 | el.setAttribute(k, attrs[k]); 57 | return el; 58 | } 59 | 60 | // ======================================================================= 61 | // ========================---- enjoyhint ----============================== 62 | // ======================================================================= 63 | that.canvas_size = { 64 | w: $(window).width()*1.4, 65 | h: $(window).height()*1.4 66 | }; 67 | var canvas_id = "enj_canvas"; 68 | 69 | that.enjoyhint = $('
', {'class': that.cl.enjoy_hint + ' ' + that.cl.svg_transparent}).appendTo($that); 70 | that.enjoyhint_svg_wrapper = $('
', {'class': that.cl.svg_wrapper + ' ' + that.cl.svg_transparent}).appendTo(that.enjoyhint); 71 | that.$stage_container = $('
').appendTo(that.enjoyhint); 72 | that.$canvas = $('').appendTo(that.enjoyhint); 73 | that.$svg = $('').appendTo(that.enjoyhint_svg_wrapper); 74 | var defs = $(makeSVG('defs')); 75 | var marker = $(makeSVG('marker', {id: "arrowMarker", viewBox: "0 0 36 21", refX: "21", refY: "10", markerUnits: "strokeWidth", orient: "auto", markerWidth: "16", markerHeight: "12"})); 76 | var polilyne = $(makeSVG('path', {style: "fill:none; stroke:rgb(255,255,255); stroke-width:2", d: "M0,0 c30,11 30,9 0,20"})); 77 | defs.append(marker.append(polilyne)).appendTo(that.$svg); 78 | that.kinetic_stage = new Kinetic.Stage({ 79 | container: that.cl.kinetic_container, 80 | width: that.canvas_size.w, 81 | height: that.canvas_size.h 82 | }); 83 | 84 | that.layer = new Kinetic.Layer(); 85 | that.rect = new Kinetic.Rect({ 86 | // x: 0, 87 | // y: 0, 88 | fill: 'rgba(0,0,0,0.6)', 89 | width: that.canvas_size.w, 90 | height: that.canvas_size.h 91 | }); 92 | 93 | var $top_dis_events = $('
', {'class': that.cl.disable_events_element}).appendTo(that.enjoyhint); 94 | var $bottom_dis_events = $top_dis_events.clone().appendTo(that.enjoyhint); 95 | var $left_dis_events = $top_dis_events.clone().appendTo(that.enjoyhint); 96 | var $right_dis_events = $top_dis_events.clone().appendTo(that.enjoyhint); 97 | 98 | that.$skip_btn = $('
', {'class': that.cl.skip_btn}).appendTo(that.enjoyhint).html('Skip').click(function (e) { 99 | that.hide(); 100 | that.options.onSkipClick(); 101 | }); 102 | that.$next_btn = $('
', {'class': that.cl.next_btn}).appendTo(that.enjoyhint).html('Next').click(function (e) { 103 | that.options.onNextClick(); 104 | }); 105 | 106 | that.$close_btn = $('
', {'class': that.cl.close_btn}).appendTo(that.enjoyhint).html('').click(function (e){ 107 | that.hide(); 108 | that.options.onSkipClick(); 109 | }); 110 | 111 | that.$canvas.mousedown(function (e) { 112 | $('canvas').css({left: '4000px'}); 113 | 114 | var BottomElement = document.elementFromPoint(e.clientX, e.clientY); 115 | $('canvas').css({left: '0px'}); 116 | 117 | $(BottomElement).click(); 118 | // that.$canvas.show(); 119 | return false; 120 | }); 121 | 122 | 123 | var circle_r = 0; 124 | var shape_init_shift = 130; 125 | that.shape = new Kinetic.Shape({ 126 | radius: circle_r, 127 | center_x: -shape_init_shift, 128 | center_y: -shape_init_shift, 129 | width: 0, 130 | height: 0, 131 | sceneFunc: function (context) { 132 | var ctx = this.getContext("2d")._context; 133 | var pos = this.pos; 134 | var def_comp = ctx.globalCompositeOperation; 135 | ctx.globalCompositeOperation = 'destination-out'; 136 | ctx.beginPath(); 137 | 138 | var x = this.attrs.center_x - Math.round(this.attrs.width / 2); 139 | var y = this.attrs.center_y - Math.round(this.attrs.height / 2); 140 | ctx.roundRect(x, y, this.attrs.width, this.attrs.height, this.attrs.radius); 141 | ctx.fillStyle = "red"; 142 | ctx.fill(); 143 | 144 | ctx.globalCompositeOperation = def_comp; 145 | } 146 | }); 147 | that.shape.radius = circle_r; 148 | that.layer.add(that.rect); 149 | that.layer.add(that.shape); 150 | that.kinetic_stage.add(that.layer); 151 | 152 | var enjoyhint_elements = [ 153 | that.enjoyhint, 154 | $top_dis_events, 155 | $bottom_dis_events, 156 | $left_dis_events, 157 | $right_dis_events 158 | ]; 159 | 160 | that.show = function () { 161 | that.enjoyhint.removeClass(that.cl.hide); 162 | }; 163 | 164 | that.hide = function () { 165 | that.enjoyhint.addClass(that.cl.hide); 166 | var tween = new Kinetic.Tween({ 167 | node: that.shape, 168 | duration: 0.002, 169 | center_x: -shape_init_shift, 170 | center_y: -shape_init_shift 171 | }); 172 | tween.play(); 173 | }; 174 | 175 | that.hide(); 176 | 177 | that.hideNextBtn = function () { 178 | that.$next_btn.addClass(that.cl.hide); 179 | that.nextBtn = "hide"; 180 | }; 181 | that.showNextBtn = function () { 182 | that.$next_btn.removeClass(that.cl.hide); 183 | that.nextBtn = "show"; 184 | }; 185 | 186 | that.hideSkipBtn = function () { 187 | that.$skip_btn.addClass(that.cl.hide); 188 | }; 189 | that.showSkipBtn = function () { 190 | that.$skip_btn.removeClass(that.cl.hide); 191 | }; 192 | 193 | 194 | 195 | 196 | 197 | that.renderCircle = function (data) { 198 | var r = data.r || 0; 199 | var x = data.x || 0; 200 | var y = data.y || 0; 201 | 202 | var tween = new Kinetic.Tween({ 203 | node: that.shape, 204 | duration: 0.2, 205 | center_x: x, 206 | center_y: y, 207 | width: r * 2, 208 | height: r * 2, 209 | radius: r 210 | }); 211 | tween.play(); 212 | 213 | var left = x - r; 214 | var right = x + r; 215 | var top = y - r; 216 | var bottom = y + r; 217 | var margin = 20; 218 | return { 219 | x: x, 220 | y: y, 221 | left: left, 222 | right: right, 223 | top: top, 224 | bottom: bottom, 225 | conn: { 226 | left: { 227 | x: left - margin, 228 | y: y 229 | }, 230 | right: { 231 | x: right + margin, 232 | y: y 233 | }, 234 | top: { 235 | x: x, 236 | y: top - margin 237 | }, 238 | bottom: { 239 | x: x, 240 | y: bottom + margin 241 | } 242 | } 243 | }; 244 | 245 | }; 246 | 247 | 248 | that.renderRect = function (data) { 249 | var r = data.r || 0; 250 | var x = data.x || 0; 251 | var y = data.y || 0; 252 | var w = data.w || 0; 253 | var h = data.h || 0; 254 | var margin = 20; 255 | var tween = new Kinetic.Tween({ 256 | node: that.shape, 257 | duration: 0.2, 258 | center_x: x, 259 | center_y: y, 260 | width: w, 261 | height: h, 262 | radius: r 263 | }); 264 | tween.play(); 265 | var half_w = Math.round(w / 2); 266 | var half_h = Math.round(h / 2); 267 | var left = x - half_w; 268 | var right = x + half_w; 269 | var top = y - half_h; 270 | var bottom = y + half_h; 271 | return { 272 | x: x, 273 | y: y, 274 | left: left, 275 | right: right, 276 | top: top, 277 | bottom: bottom, 278 | conn: { 279 | left: { 280 | x: left - margin, 281 | y: y 282 | }, 283 | right: { 284 | x: right + margin, 285 | y: y 286 | }, 287 | top: { 288 | x: x, 289 | y: top - margin 290 | }, 291 | bottom: { 292 | x: x, 293 | y: bottom + margin 294 | } 295 | } 296 | }; 297 | 298 | }; 299 | that.renderLabel = function (data) { 300 | var x = data.x || 0; 301 | var y = data.y || 0; 302 | var text = data.text || 0; 303 | 304 | var label = that.getLabelElement({ 305 | x: x, 306 | y: y, 307 | text: data.text 308 | }); 309 | var label_w = label.width(); 310 | var label_h = label.height(); 311 | var label_left = label.offset().left; 312 | var label_right = label.offset().left + label_w; 313 | var label_top = label.offset().top - $(document).scrollTop();; 314 | var label_bottom = label.offset().top + label_h; 315 | 316 | var margin = 10; 317 | var conn_left = { 318 | x: label_left - margin, 319 | y: label_top + Math.round(label_h / 2) 320 | }; 321 | var conn_right = { 322 | x: label_right + margin, 323 | y: label_top + Math.round(label_h / 2) 324 | }; 325 | var conn_top = { 326 | x: label_left + Math.round(label_w / 2), 327 | y: label_top - margin 328 | }; 329 | var conn_bottom = { 330 | x: label_left + Math.round(label_w / 2), 331 | y: label_bottom + margin 332 | }; 333 | label.detach(); 334 | setTimeout(function () { 335 | $('#enjoyhint_label').remove(); 336 | label.appendTo(that.enjoyhint); 337 | 338 | }, that.options.animation_time / 2); 339 | return { 340 | label: label, 341 | left: label_left, 342 | right: label_right, 343 | top: label_top, 344 | bottom: label_bottom, 345 | conn: { 346 | left: conn_left, 347 | right: conn_right, 348 | top: conn_top, 349 | bottom: conn_bottom 350 | } 351 | 352 | }; 353 | }; 354 | that.renderArrow = function (data) { 355 | var x_from = data.x_from || 0; 356 | var y_from = data.y_from || 0; 357 | var x_to = data.x_to || 0; 358 | var y_to = data.y_to || 0; 359 | var by_top_side = data.by_top_side; 360 | var control_point_x = 0; 361 | var control_point_y = 0; 362 | if (by_top_side) { 363 | if (y_from >= y_to) { 364 | control_point_y = y_to; 365 | control_point_x = x_from; 366 | } else { 367 | control_point_y = y_from; 368 | control_point_x = x_to; 369 | } 370 | } else { 371 | if (y_from >= y_to) { 372 | control_point_y = y_from; 373 | control_point_x = x_to; 374 | } else { 375 | control_point_y = y_to; 376 | control_point_x = x_from; 377 | } 378 | } 379 | 380 | var text = data.text || ''; 381 | that.enjoyhint.addClass(that.cl.svg_transparent); 382 | setTimeout(function () { 383 | $('#enjoyhint_arrpw_line').remove(); 384 | var d = 'M' + x_from + ',' + y_from + ' Q' + control_point_x + ',' + control_point_y + ' ' + x_to + ',' + y_to; 385 | that.$svg.append(makeSVG('path', {style: "fill:none; stroke:rgb(255,255,255); stroke-width:3", 'marker-end': "url(#arrowMarker)", d: d, id: 'enjoyhint_arrpw_line'})); 386 | that.enjoyhint.removeClass(that.cl.svg_transparent); 387 | 388 | }, that.options.animation_time / 2); 389 | }; 390 | 391 | 392 | that.getLabelElement = function (data) { 393 | return $('
', {"class": 'enjoy_hint_label', id: 'enjoyhint_label'}) 394 | .css({ 395 | 'top': data.y + 'px', 396 | 'left': data.x + 'px' 397 | }) 398 | .html(data.text).appendTo(that.enjoyhint); 399 | 400 | }; 401 | 402 | 403 | that.disableEventsNearRect = function (rect) { 404 | $top_dis_events.css({ 405 | top: '0', 406 | left: '0' 407 | }).height(rect.top); 408 | $bottom_dis_events.css({ 409 | top: rect.bottom + 'px', 410 | left: '0' 411 | }); 412 | $left_dis_events.css({ 413 | top: '0', 414 | left: 0 + 'px' 415 | }).width(rect.left); 416 | $right_dis_events.css({ 417 | top: '0', 418 | left: rect.right + 'px' 419 | }); 420 | }; 421 | 422 | 423 | that.renderLabelWithShape = function (data) { 424 | var shape_type = data.shape || 'rect'; 425 | var shape_data = {}; 426 | 427 | 428 | var half_w = 0; 429 | var half_h = 0; 430 | 431 | var shape_offsets = { 432 | top: data.top || 0, 433 | bottom: data.bottom || 0, 434 | left: data.left || 0, 435 | right: data.right || 0 436 | }; 437 | 438 | switch (shape_type) { 439 | case 'circle': 440 | half_w = half_h = data.radius; 441 | var sides_pos = { 442 | top: data.center_y - half_h + shape_offsets.top, 443 | bottom: data.center_y + half_h - shape_offsets.bottom, 444 | left: data.center_x - half_w + shape_offsets.left, 445 | right: data.center_x + half_w - shape_offsets.right 446 | }; 447 | var width = sides_pos.right - sides_pos.left; 448 | var height = sides_pos.bottom - sides_pos.top; 449 | data.radius = Math.round(Math.min(width, height) / 2); 450 | //new half habarites 451 | half_w = half_h = Math.round(data.radius / 2); 452 | 453 | var new_half_w = Math.round(width / 2); 454 | var new_half_h = Math.round(height / 2); 455 | //new center_x and center_y 456 | data.center_x = sides_pos.left + new_half_w; 457 | data.center_y = sides_pos.top + new_half_h; 458 | 459 | shape_data = that.renderCircle({ 460 | x: data.center_x, 461 | y: data.center_y, 462 | r: data.radius 463 | }); 464 | 465 | break; 466 | case 'rect': 467 | half_w = Math.round(data.width / 2); 468 | half_h = Math.round(data.height / 2); 469 | 470 | var sides_pos = { 471 | top: data.center_y - half_h + shape_offsets.top, 472 | bottom: data.center_y + half_h - shape_offsets.bottom, 473 | left: data.center_x - half_w + shape_offsets.left, 474 | right: data.center_x + half_w - shape_offsets.right 475 | }; 476 | data.width = sides_pos.right - sides_pos.left; 477 | data.height = sides_pos.bottom - sides_pos.top; 478 | 479 | half_w = Math.round(data.width / 2); 480 | half_h = Math.round(data.height / 2); 481 | //new center_x and center_y 482 | data.center_x = sides_pos.left + half_w; 483 | data.center_y = sides_pos.top + half_h; 484 | shape_data = that.renderRect({ 485 | x: data.center_x, 486 | y: data.center_y, 487 | w: data.width, 488 | h: data.height, 489 | r: data.radius, 490 | }); 491 | break; 492 | } 493 | 494 | 495 | var body_size = { 496 | w: that.enjoyhint.width(), 497 | h: that.enjoyhint.height() 498 | }; 499 | 500 | 501 | var label = that.getLabelElement({ 502 | x: 0, 503 | y: 0, 504 | text: data.text 505 | }); 506 | var label_width = label.outerWidth(); 507 | var label_height = label.outerHeight(); 508 | label.remove(); 509 | var top_offset = data.center_y - half_h; 510 | var bottom_offset = body_size.h - (data.center_y + half_h); 511 | var left_offset = data.center_x - half_w; 512 | var right_offset = body_size.w - (data.center_x + half_w); 513 | 514 | var label_hor_side = (body_size.w - data.center_x) < data.center_x ? 'left' : 'right'; 515 | var label_ver_side = (body_size.h - data.center_y) < data.center_y ? 'top' : 'bottom'; 516 | var label_shift = 150; 517 | var label_margin = 40; 518 | var label_shift_with_label_width = label_shift + label_width + label_margin; 519 | var label_shift_with_label_height = label_shift + label_height + label_margin; 520 | var label_hor_offset = half_w + label_shift; 521 | var label_ver_offset = half_h + label_shift; 522 | 523 | var label_x = (label_hor_side == 'left') ? data.center_x - label_hor_offset - label_width : data.center_x + label_hor_offset; 524 | var label_y = (label_ver_side == 'top') ? data.center_y - label_ver_offset - label_height : data.center_y + label_ver_offset; 525 | if (top_offset < label_shift_with_label_height && bottom_offset < label_shift_with_label_height) { 526 | label_y = data.center_y + label_margin; 527 | } 528 | if (left_offset < label_shift_with_label_width && right_offset < label_shift_with_label_width) { 529 | label_x = data.center_x; 530 | } 531 | 532 | var label_data = that.renderLabel({ 533 | x: label_x, 534 | y: label_y, 535 | text: data.text 536 | }); 537 | 538 | that.$next_btn.css({ 539 | left: label_x, 540 | top: label_y + label_height + 20 541 | }); 542 | var left_skip = label_x + that.$next_btn.width() + 10; 543 | if (that.nextBtn == "hide"){ 544 | left_skip = label_x; 545 | } 546 | 547 | that.$skip_btn.css({ 548 | left: left_skip, 549 | top: label_y + label_height + 20 550 | }); 551 | that.$close_btn.css({ 552 | right : 10, 553 | top: 10 554 | }); 555 | 556 | 557 | that.disableEventsNearRect({ 558 | top: shape_data.top, 559 | bottom: shape_data.bottom, 560 | left: shape_data.left, 561 | right: shape_data.right 562 | }); 563 | 564 | 565 | var x_to = 0; 566 | var y_to = 0; 567 | var arrow_side = false; 568 | var conn_label_side = 'left'; 569 | var conn_circle_side = 'left'; 570 | 571 | var is_center = (label_data.left <= shape_data.x && label_data.right >= shape_data.x); 572 | var is_left = (label_data.right < shape_data.x); 573 | var is_right = (label_data.left > shape_data.x); 574 | 575 | var is_abs_left = (label_data.right < shape_data.left); 576 | var is_abs_right = (label_data.left > shape_data.right); 577 | 578 | var is_top = (label_data.bottom < shape_data.top); 579 | var is_bottom = (label_data.top > shape_data.bottom); 580 | var is_mid = (label_data.bottom >= shape_data.y && label_data.top <= shape_data.y); 581 | var is_mid_top = (label_data.bottom <= shape_data.y && !is_top); 582 | var is_mid_bottom = (label_data.top >= shape_data.y && !is_bottom); 583 | 584 | 585 | function setArrowData(l_s, c_s, a_s) { 586 | conn_label_side = l_s; 587 | conn_circle_side = c_s; 588 | arrow_side = a_s; 589 | } 590 | 591 | function sideStatements(top_s, mid_top_s, mid_s, mid_bottom_s, bottom_s) { 592 | var statement = []; 593 | if (is_top) { 594 | statement = top_s; 595 | } else if (is_mid_top) { 596 | statement = mid_top_s; 597 | } else if (is_mid) { 598 | statement = mid_s; 599 | } else if (is_mid_bottom) { 600 | statement = mid_bottom_s; 601 | } else {//bottom 602 | statement = bottom_s; 603 | } 604 | if (!statement) { 605 | return; 606 | } else { 607 | setArrowData(statement[0], statement[1], statement[2]); 608 | } 609 | } 610 | 611 | 612 | if (is_center) { 613 | if (is_top) { 614 | setArrowData('bottom', 'top', 'top'); 615 | } else if (is_bottom) { 616 | setArrowData('top', 'bottom', 'bottom'); 617 | } else { 618 | return; 619 | } 620 | } else if (is_left) { 621 | sideStatements( 622 | ['right', 'top', 'top'],//top 623 | ['bottom', 'left', 'bottom'],//mid_top 624 | ['right', 'left', 'top'],//mid 625 | ['top', 'left', 'top'],//mid_bot 626 | ['right', 'bottom', 'bottom']//bot 627 | ); 628 | } else {//right 629 | sideStatements( 630 | ['left', 'top', 'top'],//top 631 | ['bottom', 'right', 'bottom'],//mid_top 632 | ['left', 'right', 'top'],//mid 633 | ['top', 'right', 'top'],//mid_bot 634 | ['left', 'bottom', 'bottom']//bot 635 | ); 636 | } 637 | 638 | var label_conn_coordinates = label_data.conn[conn_label_side]; 639 | var circle_conn_coordinates = shape_data.conn[conn_circle_side]; 640 | var by_top_side = (arrow_side == 'top') ? true : false; 641 | that.renderArrow({ 642 | x_from: label_conn_coordinates.x, 643 | y_from: label_conn_coordinates.y, 644 | x_to: circle_conn_coordinates.x, 645 | y_to: circle_conn_coordinates.y, 646 | by_top_side: by_top_side 647 | }); 648 | 649 | }; 650 | 651 | that.clear = function () { 652 | that.ctx.clearRect(0, 0, 3000, 2000); 653 | }; 654 | 655 | return this; 656 | }); 657 | }, 658 | 659 | set: function (val) { 660 | this.each(function () { 661 | this.enjoyhint_obj.setValue(val); 662 | }); 663 | return this; 664 | }, 665 | 666 | show: function () { 667 | this.each(function () { 668 | this.enjoyhint_obj.show(); 669 | }); 670 | return this; 671 | }, 672 | 673 | hide: function () { 674 | this.each(function () { 675 | this.enjoyhint_obj.hide(); 676 | }); 677 | return this; 678 | }, 679 | 680 | hide_next: function () { 681 | this.each(function () { 682 | this.enjoyhint_obj.hideNextBtn(); 683 | }); 684 | return this; 685 | }, 686 | 687 | show_next: function () { 688 | this.each(function () { 689 | this.enjoyhint_obj.showNextBtn(); 690 | }); 691 | return this; 692 | }, 693 | 694 | hide_skip: function () { 695 | this.each(function () { 696 | this.enjoyhint_obj.hideSkipBtn(); 697 | }); 698 | return this; 699 | }, 700 | 701 | show_skip: function () { 702 | this.each(function () { 703 | this.enjoyhint_obj.showSkipBtn(); 704 | }); 705 | return this; 706 | }, 707 | 708 | render_circle: function (x, y, r) { 709 | this.each(function () { 710 | this.enjoyhint_obj.renderCircle(x, y, r); 711 | }); 712 | return this; 713 | }, 714 | 715 | render_label: function (x, y, r) { 716 | this.each(function () { 717 | this.enjoyhint_obj.renderLabel(x, y, r); 718 | }); 719 | return this; 720 | }, 721 | 722 | render_label_with_shape: function (data) { 723 | this.each(function () { 724 | this.enjoyhint_obj.renderLabelWithShape(data); 725 | }); 726 | return this; 727 | }, 728 | 729 | clear: function () { 730 | this.each(function () { 731 | this.enjoyhint_obj.clear(); 732 | }); 733 | return this; 734 | }, 735 | 736 | close: function (val) { 737 | this.each(function () { 738 | this.enjoyhint_obj.closePopdown(); 739 | }); 740 | return this; 741 | } 742 | }; 743 | 744 | $.fn.enjoyhint = function (method) { 745 | if (methods[method]) { 746 | return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); 747 | } else if (typeof method === 'object' || !method) { 748 | return methods.init.apply(this, arguments); 749 | } else { 750 | $.error('Method ' + method + ' does not exist on $.numinput'); 751 | } 752 | return this; 753 | }; 754 | })(window.jQuery); 755 | 756 | -------------------------------------------------------------------------------- /hint-sequence.js: -------------------------------------------------------------------------------- 1 | var enjoyhint_instance = new EnjoyHint({}); 2 | 3 | var enjoyhint_script_steps = [ 4 | { 5 | 'next .navbar-brand' : 'Welcome to Turbo Search! Let me guide you through its features.', 6 | 'nextButton' : {className: "myNext", text: "Sure"}, 7 | 'skipButton' : {className: "mySkip", text: "Nope!"} 8 | }, 9 | { 10 | 'key #mySearchButton' : "Insert your search request and press 'Enter'", 11 | 'keyCode' : 13 12 | }, 13 | { 14 | 'click .btn' : 'This button allows you to switch between the search results' 15 | }, 16 | { 17 | 'next .about' : 'Check the list of all the features available', 18 | 'shape': 'circle' 19 | }, 20 | { 21 | 'next .contact' : 'Your feedback will be appreciated', 22 | 'shape': 'circle', 23 | 'radius': 70, 24 | 'showSkip' : false, 25 | 'nextButton' : {className: "myNext", text: "Got it!"} 26 | } 27 | 28 | ]; 29 | 30 | enjoyhint_instance.set(enjoyhint_script_steps); 31 | enjoyhint_instance.run(); -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | EnjoyHint Demo 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 33 | 34 |
35 |
36 |
37 | 38 | 39 | 40 | 47 | 48 |
49 |
50 |
51 | 52 |
53 |
54 |
55 |
56 | 57 | 58 | -------------------------------------------------------------------------------- /style.css: -------------------------------------------------------------------------------- 1 | html { 2 | position: relative; 3 | min-height: 100%; 4 | } 5 | body { 6 | margin-bottom: 60px; 7 | } 8 | .footer { 9 | position: absolute; 10 | bottom: 0; 11 | width: 100%; 12 | height: 50px; 13 | background-color: #f5f5f5; 14 | } 15 | 16 | body > .container { 17 | padding: 50px 15px 0; 18 | } 19 | .footer > .container { 20 | padding-right: 15px; 21 | padding-left: 15px; 22 | } 23 | .featurette { 24 | display: table; 25 | width: 100%; 26 | height: 30%; 27 | vertical-align: middle; 28 | color: #fff; 29 | position: absolute; 30 | top: 40%; 31 | } 32 | .featurette-inner { 33 | display: table-cell; 34 | vertical-align: middle; 35 | } 36 | .featurette .input-group { 37 | padding: 3%; 38 | max-width: 750px; 39 | margin: 0 auto; 40 | } 41 | --------------------------------------------------------------------------------