├── .bowerrc ├── .editorconfig ├── .gitattributes ├── .gitignore ├── .jshintrc ├── .travis.yml ├── Gruntfile.js ├── LICENSE ├── README.md ├── app ├── .buildignore ├── .htaccess ├── 404.html ├── favicon.ico ├── images │ └── yeoman.png ├── index.html ├── robots.txt ├── scripts │ ├── app.js │ ├── controllers │ │ └── main.js │ └── directives │ │ └── wmwindow.js ├── styles │ ├── main.css │ └── wmwindow.css └── views │ └── main.html ├── bower.json ├── package.json └── test ├── .jshintrc ├── karma.conf.js └── spec ├── controllers └── main.js └── directives └── wmwindow.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # Change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # We recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | .sass-cache 5 | bower_components 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "undef": true, 16 | "unused": true, 17 | "strict": true, 18 | "trailing": true, 19 | "smarttabs": true, 20 | "globals": { 21 | "angular": false 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '0.10' 4 | before_script: 5 | - 'npm install -g bower grunt-cli' 6 | - 'bower install' 7 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | // Generated on 2015-01-06 using generator-angular 0.10.0 2 | 'use strict'; 3 | 4 | // # Globbing 5 | // for performance reasons we're only matching one level down: 6 | // 'test/spec/{,*/}*.js' 7 | // use this if you want to recursively match all subfolders: 8 | // 'test/spec/**/*.js' 9 | 10 | module.exports = function (grunt) { 11 | 12 | // Load grunt tasks automatically 13 | require('load-grunt-tasks')(grunt); 14 | 15 | // Time how long tasks take. Can help when optimizing build times 16 | require('time-grunt')(grunt); 17 | 18 | // Configurable paths for the application 19 | var appConfig = { 20 | app: require('./bower.json').appPath || 'app', 21 | dist: 'dist' 22 | }; 23 | 24 | // Define the configuration for all the tasks 25 | grunt.initConfig({ 26 | 27 | // Project settings 28 | yeoman: appConfig, 29 | 30 | // Watches files for changes and runs tasks based on the changed files 31 | watch: { 32 | bower: { 33 | files: ['bower.json'], 34 | tasks: ['wiredep'] 35 | }, 36 | js: { 37 | files: ['<%= yeoman.app %>/scripts/{,*/}*.js'], 38 | tasks: ['newer:jshint:all'], 39 | options: { 40 | livereload: '<%= connect.options.livereload %>' 41 | } 42 | }, 43 | jsTest: { 44 | files: ['test/spec/{,*/}*.js'], 45 | tasks: ['newer:jshint:test', 'karma'] 46 | }, 47 | styles: { 48 | files: ['<%= yeoman.app %>/styles/{,*/}*.css'], 49 | tasks: ['newer:copy:styles', 'autoprefixer'] 50 | }, 51 | gruntfile: { 52 | files: ['Gruntfile.js'] 53 | }, 54 | livereload: { 55 | options: { 56 | livereload: '<%= connect.options.livereload %>' 57 | }, 58 | files: [ 59 | '<%= yeoman.app %>/{,*/}*.html', 60 | '.tmp/styles/{,*/}*.css', 61 | '<%= yeoman.app %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}' 62 | ] 63 | } 64 | }, 65 | 66 | // The actual grunt server settings 67 | connect: { 68 | options: { 69 | port: 9000, 70 | // Change this to '0.0.0.0' to access the server from outside. 71 | hostname: 'localhost', 72 | livereload: 35729 73 | }, 74 | livereload: { 75 | options: { 76 | open: true, 77 | middleware: function (connect) { 78 | return [ 79 | connect.static('.tmp'), 80 | connect().use( 81 | '/bower_components', 82 | connect.static('./bower_components') 83 | ), 84 | connect.static(appConfig.app) 85 | ]; 86 | } 87 | } 88 | }, 89 | test: { 90 | options: { 91 | port: 9001, 92 | middleware: function (connect) { 93 | return [ 94 | connect.static('.tmp'), 95 | connect.static('test'), 96 | connect().use( 97 | '/bower_components', 98 | connect.static('./bower_components') 99 | ), 100 | connect.static(appConfig.app) 101 | ]; 102 | } 103 | } 104 | }, 105 | dist: { 106 | options: { 107 | open: true, 108 | base: '<%= yeoman.dist %>' 109 | } 110 | } 111 | }, 112 | 113 | // Make sure code styles are up to par and there are no obvious mistakes 114 | jshint: { 115 | options: { 116 | jshintrc: '.jshintrc', 117 | reporter: require('jshint-stylish') 118 | }, 119 | all: { 120 | src: [ 121 | 'Gruntfile.js', 122 | '<%= yeoman.app %>/scripts/{,*/}*.js' 123 | ] 124 | }, 125 | test: { 126 | options: { 127 | jshintrc: 'test/.jshintrc' 128 | }, 129 | src: ['test/spec/{,*/}*.js'] 130 | } 131 | }, 132 | 133 | // Empties folders to start fresh 134 | clean: { 135 | dist: { 136 | files: [{ 137 | dot: true, 138 | src: [ 139 | '.tmp', 140 | '<%= yeoman.dist %>/{,*/}*', 141 | '!<%= yeoman.dist %>/.git{,*/}*' 142 | ] 143 | }] 144 | }, 145 | server: '.tmp' 146 | }, 147 | 148 | // Add vendor prefixed styles 149 | autoprefixer: { 150 | options: { 151 | browsers: ['last 1 version'] 152 | }, 153 | dist: { 154 | files: [{ 155 | expand: true, 156 | cwd: '.tmp/styles/', 157 | src: '{,*/}*.css', 158 | dest: '.tmp/styles/' 159 | }] 160 | } 161 | }, 162 | 163 | // Automatically inject Bower components into the app 164 | wiredep: { 165 | app: { 166 | src: ['<%= yeoman.app %>/index.html'], 167 | ignorePath: /\.\.\// 168 | } 169 | }, 170 | 171 | // Renames files for browser caching purposes 172 | filerev: { 173 | dist: { 174 | src: [ 175 | '<%= yeoman.dist %>/scripts/{,*/}*.js', 176 | '<%= yeoman.dist %>/styles/{,*/}*.css', 177 | '<%= yeoman.dist %>/images/{,*/}*.{png,jpg,jpeg,gif,webp,svg}', 178 | '<%= yeoman.dist %>/styles/fonts/*' 179 | ] 180 | } 181 | }, 182 | 183 | // Reads HTML for usemin blocks to enable smart builds that automatically 184 | // concat, minify and revision files. Creates configurations in memory so 185 | // additional tasks can operate on them 186 | useminPrepare: { 187 | html: '<%= yeoman.app %>/index.html', 188 | options: { 189 | dest: '<%= yeoman.dist %>', 190 | flow: { 191 | html: { 192 | steps: { 193 | js: ['concat', 'uglifyjs'], 194 | css: ['cssmin'] 195 | }, 196 | post: {} 197 | } 198 | } 199 | } 200 | }, 201 | 202 | // Performs rewrites based on filerev and the useminPrepare configuration 203 | usemin: { 204 | html: ['<%= yeoman.dist %>/{,*/}*.html'], 205 | css: ['<%= yeoman.dist %>/styles/{,*/}*.css'], 206 | options: { 207 | assetsDirs: ['<%= yeoman.dist %>','<%= yeoman.dist %>/images'] 208 | } 209 | }, 210 | 211 | // The following *-min tasks will produce minified files in the dist folder 212 | // By default, your `index.html`'s will take care of 213 | // minification. These next options are pre-configured if you do not wish 214 | // to use the Usemin blocks. 215 | // cssmin: { 216 | // dist: { 217 | // files: { 218 | // '<%= yeoman.dist %>/styles/main.css': [ 219 | // '.tmp/styles/{,*/}*.css' 220 | // ] 221 | // } 222 | // } 223 | // }, 224 | // uglify: { 225 | // dist: { 226 | // files: { 227 | // '<%= yeoman.dist %>/scripts/scripts.js': [ 228 | // '<%= yeoman.dist %>/scripts/scripts.js' 229 | // ] 230 | // } 231 | // } 232 | // }, 233 | // concat: { 234 | // dist: {} 235 | // }, 236 | 237 | imagemin: { 238 | dist: { 239 | files: [{ 240 | expand: true, 241 | cwd: '<%= yeoman.app %>/images', 242 | src: '{,*/}*.{png,jpg,jpeg,gif}', 243 | dest: '<%= yeoman.dist %>/images' 244 | }] 245 | } 246 | }, 247 | 248 | svgmin: { 249 | dist: { 250 | files: [{ 251 | expand: true, 252 | cwd: '<%= yeoman.app %>/images', 253 | src: '{,*/}*.svg', 254 | dest: '<%= yeoman.dist %>/images' 255 | }] 256 | } 257 | }, 258 | 259 | htmlmin: { 260 | dist: { 261 | options: { 262 | collapseWhitespace: true, 263 | conservativeCollapse: true, 264 | collapseBooleanAttributes: true, 265 | removeCommentsFromCDATA: true, 266 | removeOptionalTags: true 267 | }, 268 | files: [{ 269 | expand: true, 270 | cwd: '<%= yeoman.dist %>', 271 | src: ['*.html', 'views/{,*/}*.html'], 272 | dest: '<%= yeoman.dist %>' 273 | }] 274 | } 275 | }, 276 | 277 | // ng-annotate tries to make the code safe for minification automatically 278 | // by using the Angular long form for dependency injection. 279 | ngAnnotate: { 280 | dist: { 281 | files: [{ 282 | expand: true, 283 | cwd: '.tmp/concat/scripts', 284 | src: ['*.js', '!oldieshim.js'], 285 | dest: '.tmp/concat/scripts' 286 | }] 287 | } 288 | }, 289 | 290 | // Replace Google CDN references 291 | cdnify: { 292 | dist: { 293 | html: ['<%= yeoman.dist %>/*.html'] 294 | } 295 | }, 296 | 297 | // Copies remaining files to places other tasks can use 298 | copy: { 299 | dist: { 300 | files: [{ 301 | expand: true, 302 | dot: true, 303 | cwd: '<%= yeoman.app %>', 304 | dest: '<%= yeoman.dist %>', 305 | src: [ 306 | '*.{ico,png,txt}', 307 | '.htaccess', 308 | '*.html', 309 | 'views/{,*/}*.html', 310 | 'images/{,*/}*.{webp}', 311 | 'fonts/{,*/}*.*' 312 | ] 313 | }, { 314 | expand: true, 315 | cwd: '.tmp/images', 316 | dest: '<%= yeoman.dist %>/images', 317 | src: ['generated/*'] 318 | }] 319 | }, 320 | styles: { 321 | expand: true, 322 | cwd: '<%= yeoman.app %>/styles', 323 | dest: '.tmp/styles/', 324 | src: '{,*/}*.css' 325 | } 326 | }, 327 | 328 | // Run some tasks in parallel to speed up the build process 329 | concurrent: { 330 | server: [ 331 | 'copy:styles' 332 | ], 333 | test: [ 334 | 'copy:styles' 335 | ], 336 | dist: [ 337 | 'copy:styles', 338 | 'imagemin', 339 | 'svgmin' 340 | ] 341 | }, 342 | 343 | // Test settings 344 | karma: { 345 | unit: { 346 | configFile: 'test/karma.conf.js', 347 | singleRun: true 348 | } 349 | } 350 | }); 351 | 352 | 353 | grunt.registerTask('serve', 'Compile then start a connect web server', function (target) { 354 | if (target === 'dist') { 355 | return grunt.task.run(['build', 'connect:dist:keepalive']); 356 | } 357 | 358 | grunt.task.run([ 359 | 'clean:server', 360 | 'wiredep', 361 | 'concurrent:server', 362 | 'autoprefixer', 363 | 'connect:livereload', 364 | 'watch' 365 | ]); 366 | }); 367 | 368 | grunt.registerTask('server', 'DEPRECATED TASK. Use the "serve" task instead', function (target) { 369 | grunt.log.warn('The `server` task has been deprecated. Use `grunt serve` to start a server.'); 370 | grunt.task.run(['serve:' + target]); 371 | }); 372 | 373 | grunt.registerTask('test', [ 374 | 'clean:server', 375 | 'concurrent:test', 376 | 'autoprefixer', 377 | 'connect:test', 378 | 'karma' 379 | ]); 380 | 381 | grunt.registerTask('build', [ 382 | 'clean:dist', 383 | 'wiredep', 384 | 'useminPrepare', 385 | 'concurrent:dist', 386 | 'autoprefixer', 387 | 'concat', 388 | 'ngAnnotate', 389 | 'copy:dist', 390 | 'cdnify', 391 | 'cssmin', 392 | 'uglify', 393 | 'filerev', 394 | 'usemin', 395 | 'htmlmin' 396 | ]); 397 | 398 | grunt.registerTask('default', [ 399 | 'newer:jshint', 400 | 'test', 401 | 'build' 402 | ]); 403 | }; 404 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 LooWID 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ng-window-manager 2 | Angular JS plugin to handle content inside windows, allowing user to move them close, minimize, ... 3 | 4 | That directive is inspired in the work of Ramón Lamana on Ventus https://github.com/rlamana/Ventus 5 | 6 | ## How to try it 7 | 8 | * npm install 9 | * bower install 10 | * grunt serve 11 | 12 | ###Try the library 13 | 14 | The app that runs with grunt is in the `app` folder. You will find the window manager directive in `app\scripts\wmwindow.js`. 15 | In the `app/views/main.html` you'll see the use of this directive. 16 | 17 | Some examples you'll find 18 | 19 | ``` 20 | My window content {{something_from_scope}} 21 | ``` 22 | You can also have multiple windows under an element from an array 23 | ``` 24 |

{{article.body}}

25 | ``` 26 | ### Parameters 27 | * _title_: Window title 28 | * _open_ : Function executed when window is opened. 29 | * _close_: Function executed when window is closed. 30 | * _maximize_: Function executed when window is maximized. 31 | * _restore_: Function executed when a window is restored. 32 | * _options_: Additional options to customize the window, see below. 33 | * _maximizable_: Boolean to determine if window can be maximized. (it must be set from $scope variable) 34 | * _closeable_: Boolean to determine if window can be closed. (it must be set from $scope variable) 35 | 36 | ### Options 37 | You can pass some options to a window to customize the behaviour: 38 | * _position_ {x, y} : Initial position of the window when it's created 39 | * _size_ {width, height}: Initial size of the window when it's created 40 | * _maximizeTo_: Element id where the windows will be maximized (when user push the button) 41 | * _windowContainer_: Element id where windows can move over. If not provided parent element would be the windowContainer. 42 | * _initialZIndex_ : Specify an initial z index where windows starts 43 | 44 | Options example: 45 | 46 | ``` 47 | $scope.options = { 48 | position: {x: 120, y:320}, 49 | size: {width: 300, height:300}, 50 | maximizeTo: 'contentArea', 51 | windowContainer: 'myMoveZone' 52 | }; 53 | ``` 54 | ## Executing actions 55 | 56 | When you define an action from list above to be executed, you must receive with a parameter windowHandler. 57 | ``` 58 | 59 | ``` 60 | 61 | ``` 62 | $scope.onopen = function (win){ 63 | 64 | //When the window is open then move to position 100 100 65 | win.windowHandler.move (100,100); 66 | ... 67 | }; 68 | ``` 69 | As you see you can use the `windowHandler` object of the window to execute the actions and access to the window's elements. 70 | 71 | 72 | 73 | ## Do you need a bower component? 74 | 75 | If you want to install the component in your project you can add it using the command: 76 | ``` 77 | bower install angular-window-manager 78 | ``` 79 | 80 | Take a look to the public repo https://github.com/sinmsinm/angular-window-manager to know more how to use it with your angular project. 81 | -------------------------------------------------------------------------------- /app/.buildignore: -------------------------------------------------------------------------------- 1 | *.coffee -------------------------------------------------------------------------------- /app/.htaccess: -------------------------------------------------------------------------------- 1 | # Apache Configuration File 2 | 3 | # (!) Using `.htaccess` files slows down Apache, therefore, if you have access 4 | # to the main server config file (usually called `httpd.conf`), you should add 5 | # this logic there: http://httpd.apache.org/docs/current/howto/htaccess.html. 6 | 7 | # ############################################################################## 8 | # # CROSS-ORIGIN RESOURCE SHARING (CORS) # 9 | # ############################################################################## 10 | 11 | # ------------------------------------------------------------------------------ 12 | # | Cross-domain AJAX requests | 13 | # ------------------------------------------------------------------------------ 14 | 15 | # Enable cross-origin AJAX requests. 16 | # http://code.google.com/p/html5security/wiki/CrossOriginRequestSecurity 17 | # http://enable-cors.org/ 18 | 19 | # 20 | # Header set Access-Control-Allow-Origin "*" 21 | # 22 | 23 | # ------------------------------------------------------------------------------ 24 | # | CORS-enabled images | 25 | # ------------------------------------------------------------------------------ 26 | 27 | # Send the CORS header for images when browsers request it. 28 | # https://developer.mozilla.org/en/CORS_Enabled_Image 29 | # http://blog.chromium.org/2011/07/using-cross-domain-images-in-webgl-and.html 30 | # http://hacks.mozilla.org/2011/11/using-cors-to-load-webgl-textures-from-cross-domain-images/ 31 | 32 | 33 | 34 | 35 | SetEnvIf Origin ":" IS_CORS 36 | Header set Access-Control-Allow-Origin "*" env=IS_CORS 37 | 38 | 39 | 40 | 41 | # ------------------------------------------------------------------------------ 42 | # | Web fonts access | 43 | # ------------------------------------------------------------------------------ 44 | 45 | # Allow access from all domains for web fonts 46 | 47 | 48 | 49 | Header set Access-Control-Allow-Origin "*" 50 | 51 | 52 | 53 | 54 | # ############################################################################## 55 | # # ERRORS # 56 | # ############################################################################## 57 | 58 | # ------------------------------------------------------------------------------ 59 | # | 404 error prevention for non-existing redirected folders | 60 | # ------------------------------------------------------------------------------ 61 | 62 | # Prevent Apache from returning a 404 error for a rewrite if a directory 63 | # with the same name does not exist. 64 | # http://httpd.apache.org/docs/current/content-negotiation.html#multiviews 65 | # http://www.webmasterworld.com/apache/3808792.htm 66 | 67 | Options -MultiViews 68 | 69 | # ------------------------------------------------------------------------------ 70 | # | Custom error messages / pages | 71 | # ------------------------------------------------------------------------------ 72 | 73 | # You can customize what Apache returns to the client in case of an error (see 74 | # http://httpd.apache.org/docs/current/mod/core.html#errordocument), e.g.: 75 | 76 | ErrorDocument 404 /404.html 77 | 78 | 79 | # ############################################################################## 80 | # # INTERNET EXPLORER # 81 | # ############################################################################## 82 | 83 | # ------------------------------------------------------------------------------ 84 | # | Better website experience | 85 | # ------------------------------------------------------------------------------ 86 | 87 | # Force IE to render pages in the highest available mode in the various 88 | # cases when it may not: http://hsivonen.iki.fi/doctype/ie-mode.pdf. 89 | 90 | 91 | Header set X-UA-Compatible "IE=edge" 92 | # `mod_headers` can't match based on the content-type, however, we only 93 | # want to send this header for HTML pages and not for the other resources 94 | 95 | Header unset X-UA-Compatible 96 | 97 | 98 | 99 | # ------------------------------------------------------------------------------ 100 | # | Cookie setting from iframes | 101 | # ------------------------------------------------------------------------------ 102 | 103 | # Allow cookies to be set from iframes in IE. 104 | 105 | # 106 | # Header set P3P "policyref=\"/w3c/p3p.xml\", CP=\"IDC DSP COR ADM DEVi TAIi PSA PSD IVAi IVDi CONi HIS OUR IND CNT\"" 107 | # 108 | 109 | # ------------------------------------------------------------------------------ 110 | # | Screen flicker | 111 | # ------------------------------------------------------------------------------ 112 | 113 | # Stop screen flicker in IE on CSS rollovers (this only works in 114 | # combination with the `ExpiresByType` directives for images from below). 115 | 116 | # BrowserMatch "MSIE" brokenvary=1 117 | # BrowserMatch "Mozilla/4.[0-9]{2}" brokenvary=1 118 | # BrowserMatch "Opera" !brokenvary 119 | # SetEnvIf brokenvary 1 force-no-vary 120 | 121 | 122 | # ############################################################################## 123 | # # MIME TYPES AND ENCODING # 124 | # ############################################################################## 125 | 126 | # ------------------------------------------------------------------------------ 127 | # | Proper MIME types for all files | 128 | # ------------------------------------------------------------------------------ 129 | 130 | 131 | 132 | # Audio 133 | AddType audio/mp4 m4a f4a f4b 134 | AddType audio/ogg oga ogg 135 | 136 | # JavaScript 137 | # Normalize to standard type (it's sniffed in IE anyways): 138 | # http://tools.ietf.org/html/rfc4329#section-7.2 139 | AddType application/javascript js jsonp 140 | AddType application/json json 141 | 142 | # Video 143 | AddType video/mp4 mp4 m4v f4v f4p 144 | AddType video/ogg ogv 145 | AddType video/webm webm 146 | AddType video/x-flv flv 147 | 148 | # Web fonts 149 | AddType application/font-woff woff 150 | AddType application/vnd.ms-fontobject eot 151 | 152 | # Browsers usually ignore the font MIME types and sniff the content, 153 | # however, Chrome shows a warning if other MIME types are used for the 154 | # following fonts. 155 | AddType application/x-font-ttf ttc ttf 156 | AddType font/opentype otf 157 | 158 | # Make SVGZ fonts work on iPad: 159 | # https://twitter.com/FontSquirrel/status/14855840545 160 | AddType image/svg+xml svg svgz 161 | AddEncoding gzip svgz 162 | 163 | # Other 164 | AddType application/octet-stream safariextz 165 | AddType application/x-chrome-extension crx 166 | AddType application/x-opera-extension oex 167 | AddType application/x-shockwave-flash swf 168 | AddType application/x-web-app-manifest+json webapp 169 | AddType application/x-xpinstall xpi 170 | AddType application/xml atom rdf rss xml 171 | AddType image/webp webp 172 | AddType image/x-icon ico 173 | AddType text/cache-manifest appcache manifest 174 | AddType text/vtt vtt 175 | AddType text/x-component htc 176 | AddType text/x-vcard vcf 177 | 178 | 179 | 180 | # ------------------------------------------------------------------------------ 181 | # | UTF-8 encoding | 182 | # ------------------------------------------------------------------------------ 183 | 184 | # Use UTF-8 encoding for anything served as `text/html` or `text/plain`. 185 | AddDefaultCharset utf-8 186 | 187 | # Force UTF-8 for certain file formats. 188 | 189 | AddCharset utf-8 .atom .css .js .json .rss .vtt .webapp .xml 190 | 191 | 192 | 193 | # ############################################################################## 194 | # # URL REWRITES # 195 | # ############################################################################## 196 | 197 | # ------------------------------------------------------------------------------ 198 | # | Rewrite engine | 199 | # ------------------------------------------------------------------------------ 200 | 201 | # Turning on the rewrite engine and enabling the `FollowSymLinks` option is 202 | # necessary for the following directives to work. 203 | 204 | # If your web host doesn't allow the `FollowSymlinks` option, you may need to 205 | # comment it out and use `Options +SymLinksIfOwnerMatch` but, be aware of the 206 | # performance impact: http://httpd.apache.org/docs/current/misc/perf-tuning.html#symlinks 207 | 208 | # Also, some cloud hosting services require `RewriteBase` to be set: 209 | # http://www.rackspace.com/knowledge_center/frequently-asked-question/why-is-mod-rewrite-not-working-on-my-site 210 | 211 | 212 | Options +FollowSymlinks 213 | # Options +SymLinksIfOwnerMatch 214 | RewriteEngine On 215 | # RewriteBase / 216 | 217 | 218 | # ------------------------------------------------------------------------------ 219 | # | Suppressing / Forcing the "www." at the beginning of URLs | 220 | # ------------------------------------------------------------------------------ 221 | 222 | # The same content should never be available under two different URLs especially 223 | # not with and without "www." at the beginning. This can cause SEO problems 224 | # (duplicate content), therefore, you should choose one of the alternatives and 225 | # redirect the other one. 226 | 227 | # By default option 1 (no "www.") is activated: 228 | # http://no-www.org/faq.php?q=class_b 229 | 230 | # If you'd prefer to use option 2, just comment out all the lines from option 1 231 | # and uncomment the ones from option 2. 232 | 233 | # IMPORTANT: NEVER USE BOTH RULES AT THE SAME TIME! 234 | 235 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 236 | 237 | # Option 1: rewrite www.example.com → example.com 238 | 239 | 240 | RewriteCond %{HTTPS} !=on 241 | RewriteCond %{HTTP_HOST} ^www\.(.+)$ [NC] 242 | RewriteRule ^ http://%1%{REQUEST_URI} [R=301,L] 243 | 244 | 245 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 246 | 247 | # Option 2: rewrite example.com → www.example.com 248 | 249 | # Be aware that the following might not be a good idea if you use "real" 250 | # subdomains for certain parts of your website. 251 | 252 | # 253 | # RewriteCond %{HTTPS} !=on 254 | # RewriteCond %{HTTP_HOST} !^www\..+$ [NC] 255 | # RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L] 256 | # 257 | 258 | 259 | # ############################################################################## 260 | # # SECURITY # 261 | # ############################################################################## 262 | 263 | # ------------------------------------------------------------------------------ 264 | # | Content Security Policy (CSP) | 265 | # ------------------------------------------------------------------------------ 266 | 267 | # You can mitigate the risk of cross-site scripting and other content-injection 268 | # attacks by setting a Content Security Policy which whitelists trusted sources 269 | # of content for your site. 270 | 271 | # The example header below allows ONLY scripts that are loaded from the current 272 | # site's origin (no inline scripts, no CDN, etc). This almost certainly won't 273 | # work as-is for your site! 274 | 275 | # To get all the details you'll need to craft a reasonable policy for your site, 276 | # read: http://html5rocks.com/en/tutorials/security/content-security-policy (or 277 | # see the specification: http://w3.org/TR/CSP). 278 | 279 | # 280 | # Header set Content-Security-Policy "script-src 'self'; object-src 'self'" 281 | # 282 | # Header unset Content-Security-Policy 283 | # 284 | # 285 | 286 | # ------------------------------------------------------------------------------ 287 | # | File access | 288 | # ------------------------------------------------------------------------------ 289 | 290 | # Block access to directories without a default document. 291 | # Usually you should leave this uncommented because you shouldn't allow anyone 292 | # to surf through every directory on your server (which may includes rather 293 | # private places like the CMS's directories). 294 | 295 | 296 | Options -Indexes 297 | 298 | 299 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 300 | 301 | # Block access to hidden files and directories. 302 | # This includes directories used by version control systems such as Git and SVN. 303 | 304 | 305 | RewriteCond %{SCRIPT_FILENAME} -d [OR] 306 | RewriteCond %{SCRIPT_FILENAME} -f 307 | RewriteRule "(^|/)\." - [F] 308 | 309 | 310 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 311 | 312 | # Block access to backup and source files. 313 | # These files may be left by some text editors and can pose a great security 314 | # danger when anyone has access to them. 315 | 316 | 317 | Order allow,deny 318 | Deny from all 319 | Satisfy All 320 | 321 | 322 | # ------------------------------------------------------------------------------ 323 | # | Secure Sockets Layer (SSL) | 324 | # ------------------------------------------------------------------------------ 325 | 326 | # Rewrite secure requests properly to prevent SSL certificate warnings, e.g.: 327 | # prevent `https://www.example.com` when your certificate only allows 328 | # `https://secure.example.com`. 329 | 330 | # 331 | # RewriteCond %{SERVER_PORT} !^443 332 | # RewriteRule ^ https://example-domain-please-change-me.com%{REQUEST_URI} [R=301,L] 333 | # 334 | 335 | # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 336 | 337 | # Force client-side SSL redirection. 338 | 339 | # If a user types "example.com" in his browser, the above rule will redirect him 340 | # to the secure version of the site. That still leaves a window of opportunity 341 | # (the initial HTTP connection) for an attacker to downgrade or redirect the 342 | # request. The following header ensures that browser will ONLY connect to your 343 | # server via HTTPS, regardless of what the users type in the address bar. 344 | # http://www.html5rocks.com/en/tutorials/security/transport-layer-security/ 345 | 346 | # 347 | # Header set Strict-Transport-Security max-age=16070400; 348 | # 349 | 350 | # ------------------------------------------------------------------------------ 351 | # | Server software information | 352 | # ------------------------------------------------------------------------------ 353 | 354 | # Avoid displaying the exact Apache version number, the description of the 355 | # generic OS-type and the information about Apache's compiled-in modules. 356 | 357 | # ADD THIS DIRECTIVE IN THE `httpd.conf` AS IT WILL NOT WORK IN THE `.htaccess`! 358 | 359 | # ServerTokens Prod 360 | 361 | 362 | # ############################################################################## 363 | # # WEB PERFORMANCE # 364 | # ############################################################################## 365 | 366 | # ------------------------------------------------------------------------------ 367 | # | Compression | 368 | # ------------------------------------------------------------------------------ 369 | 370 | 371 | 372 | # Force compression for mangled headers. 373 | # http://developer.yahoo.com/blogs/ydn/posts/2010/12/pushing-beyond-gzipping 374 | 375 | 376 | SetEnvIfNoCase ^(Accept-EncodXng|X-cept-Encoding|X{15}|~{15}|-{15})$ ^((gzip|deflate)\s*,?\s*)+|[X~-]{4,13}$ HAVE_Accept-Encoding 377 | RequestHeader append Accept-Encoding "gzip,deflate" env=HAVE_Accept-Encoding 378 | 379 | 380 | 381 | # Compress all output labeled with one of the following MIME-types 382 | # (for Apache versions below 2.3.7, you don't need to enable `mod_filter` 383 | # and can remove the `` and `` lines 384 | # as `AddOutputFilterByType` is still in the core directives). 385 | 386 | AddOutputFilterByType DEFLATE application/atom+xml \ 387 | application/javascript \ 388 | application/json \ 389 | application/rss+xml \ 390 | application/vnd.ms-fontobject \ 391 | application/x-font-ttf \ 392 | application/x-web-app-manifest+json \ 393 | application/xhtml+xml \ 394 | application/xml \ 395 | font/opentype \ 396 | image/svg+xml \ 397 | image/x-icon \ 398 | text/css \ 399 | text/html \ 400 | text/plain \ 401 | text/x-component \ 402 | text/xml 403 | 404 | 405 | 406 | 407 | # ------------------------------------------------------------------------------ 408 | # | Content transformations | 409 | # ------------------------------------------------------------------------------ 410 | 411 | # Prevent some of the mobile network providers from modifying the content of 412 | # your site: http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.5. 413 | 414 | # 415 | # Header set Cache-Control "no-transform" 416 | # 417 | 418 | # ------------------------------------------------------------------------------ 419 | # | ETag removal | 420 | # ------------------------------------------------------------------------------ 421 | 422 | # Since we're sending far-future expires headers (see below), ETags can 423 | # be removed: http://developer.yahoo.com/performance/rules.html#etags. 424 | 425 | # `FileETag None` is not enough for every server. 426 | 427 | Header unset ETag 428 | 429 | 430 | FileETag None 431 | 432 | # ------------------------------------------------------------------------------ 433 | # | Expires headers (for better cache control) | 434 | # ------------------------------------------------------------------------------ 435 | 436 | # The following expires headers are set pretty far in the future. If you don't 437 | # control versioning with filename-based cache busting, consider lowering the 438 | # cache time for resources like CSS and JS to something like 1 week. 439 | 440 | 441 | 442 | ExpiresActive on 443 | ExpiresDefault "access plus 1 month" 444 | 445 | # CSS 446 | ExpiresByType text/css "access plus 1 year" 447 | 448 | # Data interchange 449 | ExpiresByType application/json "access plus 0 seconds" 450 | ExpiresByType application/xml "access plus 0 seconds" 451 | ExpiresByType text/xml "access plus 0 seconds" 452 | 453 | # Favicon (cannot be renamed!) 454 | ExpiresByType image/x-icon "access plus 1 week" 455 | 456 | # HTML components (HTCs) 457 | ExpiresByType text/x-component "access plus 1 month" 458 | 459 | # HTML 460 | ExpiresByType text/html "access plus 0 seconds" 461 | 462 | # JavaScript 463 | ExpiresByType application/javascript "access plus 1 year" 464 | 465 | # Manifest files 466 | ExpiresByType application/x-web-app-manifest+json "access plus 0 seconds" 467 | ExpiresByType text/cache-manifest "access plus 0 seconds" 468 | 469 | # Media 470 | ExpiresByType audio/ogg "access plus 1 month" 471 | ExpiresByType image/gif "access plus 1 month" 472 | ExpiresByType image/jpeg "access plus 1 month" 473 | ExpiresByType image/png "access plus 1 month" 474 | ExpiresByType video/mp4 "access plus 1 month" 475 | ExpiresByType video/ogg "access plus 1 month" 476 | ExpiresByType video/webm "access plus 1 month" 477 | 478 | # Web feeds 479 | ExpiresByType application/atom+xml "access plus 1 hour" 480 | ExpiresByType application/rss+xml "access plus 1 hour" 481 | 482 | # Web fonts 483 | ExpiresByType application/font-woff "access plus 1 month" 484 | ExpiresByType application/vnd.ms-fontobject "access plus 1 month" 485 | ExpiresByType application/x-font-ttf "access plus 1 month" 486 | ExpiresByType font/opentype "access plus 1 month" 487 | ExpiresByType image/svg+xml "access plus 1 month" 488 | 489 | 490 | 491 | # ------------------------------------------------------------------------------ 492 | # | Filename-based cache busting | 493 | # ------------------------------------------------------------------------------ 494 | 495 | # If you're not using a build process to manage your filename version revving, 496 | # you might want to consider enabling the following directives to route all 497 | # requests such as `/css/style.12345.css` to `/css/style.css`. 498 | 499 | # To understand why this is important and a better idea than `*.css?v231`, read: 500 | # http://stevesouders.com/blog/2008/08/23/revving-filenames-dont-use-querystring 501 | 502 | # 503 | # RewriteCond %{REQUEST_FILENAME} !-f 504 | # RewriteCond %{REQUEST_FILENAME} !-d 505 | # RewriteRule ^(.+)\.(\d+)\.(js|css|png|jpg|gif)$ $1.$3 [L] 506 | # 507 | 508 | # ------------------------------------------------------------------------------ 509 | # | File concatenation | 510 | # ------------------------------------------------------------------------------ 511 | 512 | # Allow concatenation from within specific CSS and JS files, e.g.: 513 | # Inside of `script.combined.js` you could have 514 | # 515 | # 516 | # and they would be included into this single file. 517 | 518 | # 519 | # 520 | # Options +Includes 521 | # AddOutputFilterByType INCLUDES application/javascript application/json 522 | # SetOutputFilter INCLUDES 523 | # 524 | # 525 | # Options +Includes 526 | # AddOutputFilterByType INCLUDES text/css 527 | # SetOutputFilter INCLUDES 528 | # 529 | # 530 | 531 | # ------------------------------------------------------------------------------ 532 | # | Persistent connections | 533 | # ------------------------------------------------------------------------------ 534 | 535 | # Allow multiple requests to be sent over the same TCP connection: 536 | # http://httpd.apache.org/docs/current/en/mod/core.html#keepalive. 537 | 538 | # Enable if you serve a lot of static content but, be aware of the 539 | # possible disadvantages! 540 | 541 | # 542 | # Header set Connection Keep-Alive 543 | # 544 | -------------------------------------------------------------------------------- /app/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Page Not Found :( 6 | 141 | 142 | 143 |

144 |

Not found :(

145 |

Sorry, but the page you were trying to view does not exist.

146 |

It looks like this was the result of either:

147 | 151 | 154 | 155 |
156 | 157 | 158 | -------------------------------------------------------------------------------- /app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinmsinm/ng-window-manager/0b5f4d1d6e2f243df0fd26cb2a4127b572553db9/app/favicon.ico -------------------------------------------------------------------------------- /app/images/yeoman.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sinmsinm/ng-window-manager/0b5f4d1d6e2f243df0fd26cb2a4127b572553db9/app/images/yeoman.png -------------------------------------------------------------------------------- /app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 22 | 23 | 24 |
25 | 26 | 29 | 30 | 31 | 32 | 33 | 34 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org 2 | 3 | User-agent: * 4 | -------------------------------------------------------------------------------- /app/scripts/app.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc overview 5 | * @name winamnApp 6 | * @description 7 | * # winamnApp 8 | * 9 | * Main module of the application. 10 | */ 11 | 12 | 13 | 14 | angular 15 | .module('wmExampleApp', ['ngWindowManager' 16 | 17 | ]); 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/scripts/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc function 5 | * @name winamnApp.controller:MainCtrl 6 | * @description 7 | * # MainCtrl 8 | * Controller of the winamnApp 9 | */ 10 | angular.module('wmExampleApp') 11 | .controller('MainCtrl', function ($scope) { 12 | $scope.awesomeThings = [ 13 | 'One', 14 | 'Two', 15 | 'Three' 16 | ]; 17 | 18 | $scope.win ={}; 19 | $scope.win.newtitle = ''; 20 | $scope.maximizabe = false; 21 | 22 | $scope.myfunction = function (){ 23 | console.log ('Window'); 24 | }; 25 | 26 | $scope.options = { 27 | position: {x: 120, y:320}, 28 | size: {width: 300, height:300}, 29 | windowContainer: 'myMoveZone', 30 | maximizeTo: 'myMoveZone', 31 | 32 | }; 33 | 34 | $scope.add = function (){ 35 | $scope.awesomeThings.unshift($scope.win.newtitle); 36 | }; 37 | 38 | $scope.isMaximizable = function (){ 39 | return $scope.maximizabe; 40 | } 41 | 42 | $scope.isCloseable = function (){ 43 | return false; 44 | } 45 | 46 | }); 47 | -------------------------------------------------------------------------------- /app/scripts/directives/wmwindow.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /** 4 | * @ngdoc directive 5 | * @name ngWindowManager.directive:wmwindow 6 | * @description 7 | * # wmWindow 8 | */ 9 | 10 | angular.module('ngWindowManager',[]) 11 | .directive('wmwindow', function () { 12 | return { 13 | template: '
'+ 14 | '
'+ 15 | '
' + 16 | '
{{title}}
'+ 17 | '
' + 18 | '
'+ 22 | '
'+ 23 | '
'+ 24 | '
'+ 26 | '
', 27 | restrict: 'E', 28 | replace: true, 29 | transclude: true, 30 | scope: { 31 | title: '@', 32 | close: '=', 33 | open: '=', 34 | selectwindow: '=', 35 | maximize: '=', 36 | restore: '=', 37 | options: '@', 38 | maximizable: '@', 39 | closeable: '@', 40 | }, 41 | link: function (scope, element) { 42 | 43 | var parentWindow = element[0].parentElement; 44 | 45 | var titleBarElement = element[0].children[0].children[0]; 46 | var contentButtonElement = element[0].children[0].children[1]; 47 | var resizeButtonElement = element[0].children[0].children[2]; 48 | 49 | var buttonBar = titleBarElement.children[1]; 50 | 51 | var maximizeButton = buttonBar.children[0]; 52 | var closeButton = buttonBar.children[2]; 53 | 54 | //State variables 55 | var positionState = null; 56 | var sizeState = null; 57 | var maximizeState = null; 58 | 59 | var winHandler = {}; 60 | winHandler.elem = element; 61 | 62 | //Parse the options 63 | var options = scope.options ? JSON.parse(scope.options) :{}; 64 | 65 | //If it's defined a windowContainer zone we will use it to bind 66 | //all the listeners, that way we can fit windows under an element but move in other 67 | var windowArea = options.windowContainer===undefined ? parentWindow : document.getElementById(options.windowContainer); 68 | 69 | //Set some tricky controls to handle the layering 70 | parentWindow.topZ = parentWindow.topZ || options.initialZIndex || angular.element(parentWindow).css('z-index') || 100000; 71 | 72 | //This function is executed when close button is pushed 73 | winHandler.close = function (){ 74 | setTimeout (function (){ 75 | element.addClass ('closing'); 76 | setTimeout (function (){ 77 | element.removeClass ('closing'); 78 | element.remove(); 79 | },300); 80 | },50); 81 | 82 | if (scope.close){ 83 | scope.close(winHandler); 84 | } 85 | }; 86 | 87 | 88 | scope.isMaximizable = function (){ 89 | return (scope.maximizable === undefined || scope.maximizable === true || scope.maximizable === 'true') ? true : false ; 90 | }; 91 | 92 | scope.isCloseable = function (){ 93 | return (scope.closeable === undefined || scope.closeable === true || scope.closeable === 'true') ? true : false ; 94 | }; 95 | 96 | //Executed when touches or clicks in the title bar 97 | var startMoving = function (e){ 98 | var isTouch = (e.targetTouches && e.targetTouches.length === 1); 99 | var moveRef = isTouch ? e.targetTouches[0] : e; 100 | 101 | positionState = calculatePos({ 102 | x: moveRef.pageX, 103 | y: moveRef.pageY 104 | }); 105 | 106 | element.addClass('moving'); 107 | 108 | windowArea.addEventListener (isTouch ? 'touchmove' : 'mousemove',dragWindow); 109 | windowArea.addEventListener (isTouch ? 'touchend' : 'mouseup',dragWindowEnds); 110 | 111 | winHandler.selectWindow(); 112 | 113 | e.preventDefault(); 114 | }; 115 | 116 | //Executed when touches or clicks in the resize button 117 | var startResizing = function (e){ 118 | var isTouch = (e.targetTouches && e.targetTouches.length === 1); 119 | var moveRef = isTouch ? e.targetTouches[0] : e; 120 | 121 | 122 | sizeState = calculateSize ({ 123 | width: moveRef.pageX, 124 | height: moveRef.pageY 125 | }); 126 | 127 | element.addClass('resizing'); 128 | 129 | windowArea.addEventListener (isTouch ? 'touchmove' : 'mousemove',dragWindowCorner); 130 | windowArea.addEventListener (isTouch ? 'touchend' : 'mouseup', dragWindowCornerEnds); 131 | winHandler.selectWindow(); 132 | 133 | e.preventDefault(); 134 | 135 | }; 136 | 137 | //Execute when user moves the mouse after title is clicked 138 | var dragWindow = function(e) { 139 | var moveRef = (e.targetTouches && e.targetTouches.length === 1) ? e.targetTouches[0] : e; 140 | 141 | if (positionState){ 142 | winHandler.move( 143 | moveRef.pageX - positionState.x, 144 | moveRef.pageY - positionState.y 145 | ); 146 | } 147 | 148 | e.preventDefault(); 149 | }; 150 | 151 | //Execute when user moves the pointer after resize button is clicked 152 | var dragWindowCorner = function (e){ 153 | var moveRef = (e.targetTouches && e.targetTouches.length === 1) ? e.targetTouches[0] : e; 154 | 155 | if (sizeState){ 156 | winHandler.resize ( 157 | moveRef.pageX + sizeState.width, 158 | moveRef.pageY + sizeState.height 159 | ); 160 | } 161 | 162 | e.preventDefault(); 163 | }; 164 | 165 | //The user ends moving window when mouseup or touchends 166 | var dragWindowEnds = function (e){ 167 | var isTouch = (e.targetTouches && e.targetTouches.length === 1); 168 | 169 | if (positionState) { 170 | element.removeClass('moving'); 171 | positionState = null; 172 | } 173 | 174 | windowArea.removeEventListener (isTouch ? 'touchmove' : 'mousemove',dragWindow); 175 | windowArea.removeEventListener (isTouch ? 'touchend' : 'mouseup',dragWindowEnds); 176 | titleBarElement.removeEventListener ('click', winHandler.selectWindow); 177 | 178 | e.preventDefault(); 179 | }; 180 | 181 | 182 | //The user ends moving the resize button when mouseup or touchends 183 | var dragWindowCornerEnds = function (e){ 184 | var isTouch = (e.targetTouches && e.targetTouches.length === 1); 185 | 186 | if (sizeState){ 187 | element.removeClass ('resizing'); 188 | sizeState = null; 189 | } 190 | 191 | windowArea.removeEventListener (isTouch ? 'touchmove' : 'mousemove',dragWindowCorner); 192 | windowArea.removeEventListener (isTouch ? 'touchend' : 'mouseup',dragWindowCornerEnds); 193 | 194 | e.preventDefault(); 195 | }; 196 | 197 | 198 | //it just makes a postion calculation from the current positon reference passed 199 | var calculatePos = function(ref) { 200 | var winX = parseInt(element.prop('offsetLeft'), 10); 201 | var winY = parseInt(element.prop('offsetTop'), 10); 202 | 203 | return { 204 | x: ref.x - winX, 205 | y: ref.y - winY 206 | }; 207 | }; 208 | 209 | //it just makes a size calculation from the current positon reference passed 210 | var calculateSize = function (ref){ 211 | var winWidth = parseInt(element.prop('offsetWidth'), 10); 212 | var winHeight = parseInt(element.prop('offsetHeight'), 10); 213 | 214 | return { 215 | width: winWidth - ref.width, 216 | height: winHeight - ref.height 217 | }; 218 | }; 219 | 220 | //set the element in the specified position 221 | winHandler.move = function(x, y) { 222 | if (x) {element.css('left',x +'px');} 223 | if(y) {element.css('top',y + 'px');} 224 | 225 | }; 226 | 227 | //set the new size of the element 228 | winHandler.resize = function (width,height) { 229 | if (width) {element.css ('width', width + 'px');} 230 | if (height) {element.css ('height', height + 'px');} 231 | }; 232 | 233 | //Move the current window to the highest position 234 | winHandler.selectWindow = function (){ 235 | parentWindow.topZ = parentWindow.topZ +1; 236 | element.css ('z-index', parentWindow.topZ); 237 | if (scope.selectwindow) { scope.selectwindow(winHandler); } 238 | }; 239 | 240 | winHandler.getMaximizeToElement = function() { 241 | if (options.maximizeTo!=='window'){ 242 | var elementToMaximize = document.getElementById (options.maximizeTo) || document.getElementsByTagName (options.maximizeTo); 243 | return options.maximizeTo ? (elementToMaximize) : windowArea; 244 | } 245 | return window; 246 | }; 247 | 248 | //This functions is executed when maximize is executed 249 | winHandler.maximize = function (){ 250 | 251 | //Store the position and the size state 252 | maximizeState = { 253 | x: parseInt(element.prop('offsetLeft'), 10), 254 | y: parseInt(element.prop('offsetTop'), 10), 255 | width: parseInt(element.prop('offsetWidth'), 10), 256 | height: parseInt(element.prop('offsetHeight'), 10), 257 | z: element.css ('z-index') 258 | }; 259 | 260 | //Select the element where to maximize 261 | var maximizeToElement = winHandler.getMaximizeToElement(); 262 | 263 | var maximizeCoords = { 264 | x: parseInt(maximizeToElement.offsetLeft || 0, 10) , 265 | y: parseInt(maximizeToElement.offsetTop ||0, 10), 266 | width: parseInt(maximizeToElement.offsetWidth ||maximizeToElement.innerWidth, 10), 267 | height: parseInt(maximizeToElement.offsetHeight || maximizeToElement.innerHeight, 10) 268 | }; 269 | 270 | //move, set the effect and resize 271 | winHandler.move (maximizeCoords.x + 10,maximizeCoords.y +10 ); 272 | element.addClass ('maximizing'); 273 | element.addClass('maximized'); 274 | winHandler.resize (maximizeCoords.width -20, maximizeCoords.height - 20); 275 | 276 | //Set the apropiate listeners 277 | maximizeButton.removeEventListener ('click',winHandler.maximize); 278 | maximizeButton.removeEventListener ('touchstart',winHandler.maximize); 279 | 280 | maximizeButton.addEventListener ('click',winHandler.restore); 281 | maximizeButton.addEventListener ('touchstart',winHandler.restore); 282 | 283 | titleBarElement.removeEventListener ('dblclick', winHandler.maximize); 284 | titleBarElement.addEventListener ('dblclick', winHandler.restore); 285 | 286 | //Stop all the window listener (drag,resize...) 287 | stopWindowListeners(); 288 | 289 | //Program the effect extraction 290 | setTimeout (function (){ 291 | element.removeClass ('maximizing'); 292 | 293 | },500); 294 | 295 | if (scope.maximize){ 296 | scope.maximize(winHandler); 297 | } 298 | 299 | }; 300 | 301 | winHandler.restore = function (){ 302 | //move and resize to previus state 303 | element.addClass ('restoring'); 304 | element.removeClass ('maximized'); 305 | 306 | //move and resize to prior state 307 | winHandler.move (maximizeState.x,maximizeState.y); 308 | winHandler.resize(maximizeState.width,maximizeState.height); 309 | element.css ('z-index',maximizeState.z); 310 | //Restore the listeners 311 | maximizeButton.removeEventListener ('click',winHandler.restore); 312 | maximizeButton.removeEventListener ('touchstart',winHandler.restore); 313 | 314 | maximizeButton.addEventListener ('click',winHandler.maximize); 315 | maximizeButton.addEventListener ('touchstart',winHandler.maximize); 316 | 317 | 318 | titleBarElement.removeEventListener ('dblclick',winHandler.restore); 319 | titleBarElement.addEventListener ('dblclick', winHandler.maximize); 320 | 321 | //start all the window listener (drag,resize...) 322 | startWindowListeners(); 323 | 324 | //Execute restore method if it's provided 325 | if (scope.restore){ 326 | scope.restore(winHandler); 327 | } 328 | 329 | //Removes the element some time ago 330 | setTimeout (function (){ 331 | element.removeClass ('restoring'); 332 | },500); 333 | 334 | }; 335 | 336 | 337 | var startWindowListeners = function (){ 338 | titleBarElement.addEventListener ('mousedown', startMoving); 339 | titleBarElement.addEventListener ('touchstart', startMoving); 340 | 341 | resizeButtonElement.addEventListener ('mousedown',startResizing); 342 | resizeButtonElement.addEventListener ('touchstart',startResizing); 343 | contentButtonElement.addEventListener ('click', winHandler.selectWindow); 344 | 345 | }; 346 | 347 | var stopWindowListeners = function (){ 348 | titleBarElement.removeEventListener ('mousedown', startMoving); 349 | titleBarElement.removeEventListener ('touchstart', startMoving); 350 | 351 | resizeButtonElement.removeEventListener ('mousedown',startResizing); 352 | resizeButtonElement.removeEventListener ('touchstart',startResizing); 353 | contentButtonElement.removeEventListener ('click', winHandler.selectWindow); 354 | }; 355 | 356 | //Set the window in creatio 357 | 358 | //Set and start all the window listener (drag,resize...) 359 | startWindowListeners (); 360 | 361 | //Set buttons listener 362 | closeButton.addEventListener ('click',winHandler.close); 363 | closeButton.addEventListener ('touchstart',winHandler.close); 364 | maximizeButton.addEventListener ('click',winHandler.maximize); 365 | maximizeButton.addEventListener ('touchstart',winHandler.maximize); 366 | if (scope.maximizable) {titleBarElement.addEventListener ('dblclick', winHandler.maximize);} 367 | 368 | var applyWindowOptions = function(wh,opt) { 369 | if (opt.position){ 370 | var position = opt.position; 371 | wh.move (position.x,position.y); 372 | } 373 | if (opt.size){ 374 | var size = opt.size; 375 | wh.resize (size.width,size.height); 376 | } 377 | }; 378 | 379 | // apply the options for the window 380 | applyWindowOptions(winHandler,options); 381 | 382 | //To avoid adding transition listeners we remove tha clas after some time 383 | setTimeout (function (){ 384 | element.addClass ('active'); 385 | element.addClass ('opening'); 386 | winHandler.selectWindow(); 387 | 388 | setTimeout (function (){ 389 | element.removeClass ('opening'); 390 | if (scope.open) { 391 | scope.open(winHandler); 392 | } 393 | },400); 394 | },50); 395 | } 396 | }; 397 | }); 398 | -------------------------------------------------------------------------------- /app/styles/main.css: -------------------------------------------------------------------------------- 1 | /* Space out content a bit */ 2 | body { 3 | padding-top: 20px; 4 | padding-bottom: 20px; 5 | } 6 | .fullWindowZone{ 7 | width: 600px; 8 | height: 600px; 9 | background-color: #EAEAEA; 10 | position: relative; 11 | top: 0px; 12 | left: 0px; 13 | 14 | } 15 | 16 | /* Everything but the jumbotron gets side spac */ -------------------------------------------------------------------------------- /app/styles/wmwindow.css: -------------------------------------------------------------------------------- 1 | @-webkit-keyframes appear { 2 | 0% { 3 | -webkit-transform: scale(0,0); 4 | -moz-transform: scale(0,0); 5 | -ms-transform: scale(0,0); 6 | -o-transform: scale(0,0); 7 | transform: scale(0,0); 8 | } 9 | 80% { 10 | -webkit-transform: scale(1.2,1.2); 11 | -moz-transform: scale(1.2,1.2); 12 | -ms-transform: scale(1.2,1.2); 13 | -o-transform: scale(1.2,1.2); 14 | transform: scale(1.2,1.2); 15 | } 16 | 100% { 17 | -webkit-transform: rotate(1,1); 18 | -moz-transform: rotate(1,1); 19 | -ms-transform: rotate(1,1); 20 | -o-transform: rotate(1,1); 21 | transform: rotate(1,1); 22 | } 23 | } 24 | @-moz-keyframes appear { 25 | 0% { 26 | -webkit-transform: scale(0,0); 27 | -moz-transform: scale(0,0); 28 | -ms-transform: scale(0,0); 29 | -o-transform: scale(0,0); 30 | transform: scale(0,0); 31 | } 32 | 80% { 33 | -webkit-transform: scale(1.2,1.2); 34 | -moz-transform: scale(1.2,1.2); 35 | -ms-transform: scale(1.2,1.2); 36 | -o-transform: scale(1.2,1.2); 37 | transform: scale(1.2,1.2); 38 | } 39 | 100% { 40 | -webkit-transform: rotate(1,1); 41 | -moz-transform: rotate(1,1); 42 | -ms-transform: rotate(1,1); 43 | -o-transform: rotate(1,1); 44 | transform: rotate(1,1); 45 | } 46 | } 47 | @keyframes appear { 48 | 0% { 49 | -webkit-transform: scale(0,0); 50 | -moz-transform: scale(0,0); 51 | -ms-transform: scale(0,0); 52 | -o-transform: scale(0,0); 53 | transform: scale(0,0); 54 | } 55 | 80% { 56 | -webkit-transform: scale(1.2,1.2); 57 | -moz-transform: scale(1.2,1.2); 58 | -ms-transform: scale(1.2,1.2); 59 | -o-transform: scale(1.2,1.2); 60 | transform: scale(1.2,1.2); 61 | } 62 | 100% { 63 | -webkit-transform: rotate(1,1); 64 | -moz-transform: rotate(1,1); 65 | -ms-transform: rotate(1,1); 66 | -o-transform: rotate(1,1); 67 | transform: rotate(1,1); 68 | } 69 | } 70 | @-webkit-keyframes close { 71 | 0% { 72 | -webkit-transform: scale(1,1); 73 | -moz-transform: scale(1,1); 74 | -ms-transform: scale(1,1); 75 | -o-transform: scale(1,1); 76 | transform: scale(1,1); 77 | } 78 | 20% { 79 | -webkit-transform: scale(1.2,1.2); 80 | -moz-transform: scale(1.2,1.2); 81 | -ms-transform: scale(1.2,1.2); 82 | -o-transform: scale(1.2,1.2); 83 | transform: scale(1.2,1.2); 84 | } 85 | 100% { 86 | -webkit-transform: scale(0,0); 87 | -moz-transform: scale(0,0); 88 | -ms-transform: scale(0,0); 89 | -o-transform: scale(0,0); 90 | transform: scale(0,0); 91 | } 92 | } 93 | @-moz-keyframes close { 94 | 0% { 95 | -webkit-transform: scale(1,1); 96 | -moz-transform: scale(1,1); 97 | -ms-transform: scale(1,1); 98 | -o-transform: scale(1,1); 99 | transform: scale(1,1); 100 | } 101 | 20% { 102 | -webkit-transform: scale(1.2,1.2); 103 | -moz-transform: scale(1.2,1.2); 104 | -ms-transform: scale(1.2,1.2); 105 | -o-transform: scale(1.2,1.2); 106 | transform: scale(1.2,1.2); 107 | } 108 | 100% { 109 | -webkit-transform: scale(0,0); 110 | -moz-transform: scale(0,0); 111 | -ms-transform: scale(0,0); 112 | -o-transform: scale(0,0); 113 | transform: scale(0,0); 114 | } 115 | } 116 | @keyframes close { 117 | 0% { 118 | -webkit-transform: scale(1,1); 119 | -moz-transform: scale(1,1); 120 | -ms-transform: scale(1,1); 121 | -o-transform: scale(1,1); 122 | transform: scale(1,1); 123 | } 124 | 20% { 125 | -webkit-transform: scale(1.2,1.2); 126 | -moz-transform: scale(1.2,1.2); 127 | -ms-transform: scale(1.2,1.2); 128 | -o-transform: scale(1.2,1.2); 129 | transform: scale(1.2,1.2); 130 | } 131 | 100% { 132 | -webkit-transform: scale(0,0); 133 | -moz-transform: scale(0,0); 134 | -ms-transform: scale(0,0); 135 | -o-transform: scale(0,0); 136 | transform: scale(0,0); 137 | } 138 | } 139 | .wmWindow { 140 | opacity: 1; 141 | display: none; 142 | -webkit-touch-callout: none; 143 | user-select: none; 144 | -webkit-user-select: none; 145 | -moz-user-select: none; 146 | -ms-user-select: none; 147 | -o-user-select: none; 148 | overflow: hidden; 149 | position: absolute; 150 | min-width: 200px; 151 | min-height: 60px; 152 | background-color: #3c3c3c; 153 | -webkit-box-shadow: 0 0 5px rgba(0,0,0,0.25); 154 | -moz-box-shadow: 0 0 5px rgba(0,0,0,0.25); 155 | -ms-box-shadow: 0 0 5px rgba(0,0,0,0.25); 156 | -o-box-shadow: 0 0 5px rgba(0,0,0,0.25); 157 | box-shadow: 0 0 5px rgba(0,0,0,0.25); 158 | } 159 | .wmWindow .wmWindowOverlay { 160 | display: none; 161 | } 162 | 163 | .wmWindow .wmWindowBox { 164 | background-color: #3c3c3c; 165 | display: -webkit-box; 166 | display: -moz-box; 167 | display: -ms-box; 168 | display: -o-box; 169 | display: -webkit-flex; 170 | display: -moz-flex; 171 | display: -ms-flex; 172 | display: -o-flex; 173 | display: flex; 174 | -webkit-flex-direction: column; 175 | -moz-flex-direction: column; 176 | -ms-flex-direction: column; 177 | -o-flex-direction: column; 178 | flex-direction: column; 179 | -webkit-box-orient: vertical; 180 | -moz-box-orient: vertical; 181 | -ms-box-orient: vertical; 182 | -o-box-orient: vertical; 183 | height: 100%; 184 | min-height: 60px; 185 | width: 100%; 186 | } 187 | .wmWindow .wmWindowBox div.wmTitleBar { 188 | position: relative; 189 | display: -webkit-box; 190 | display: -moz-box; 191 | display: -ms-box; 192 | display: -o-box; 193 | display: -webkit-flex; 194 | display: -moz-flex; 195 | display: -ms-flex; 196 | display: -o-flex; 197 | display: flex; 198 | -webkit-flex-direction: row; 199 | -moz-flex-direction: row; 200 | -ms-flex-direction: row; 201 | -o-flex-direction: row; 202 | flex-direction: row; 203 | -webkit-box-orient: horizontal; 204 | -moz-box-orient: horizontal; 205 | -ms-box-orient: horizontal; 206 | -o-box-orient: horizontal; 207 | -webkit-box-align: center; 208 | -moz-box-align: center; 209 | -ms-box-align: center; 210 | -o-box-align: center; 211 | -webkit-align-items: center; 212 | -moz-align-items: center; 213 | -ms-align-items: center; 214 | -o-align-items: center; 215 | align-items: center; 216 | -webkit-touch-callout: none; 217 | user-select: none; 218 | -webkit-user-select: none; 219 | -moz-user-select: none; 220 | -ms-user-select: none; 221 | -o-user-select: none; 222 | height: 100%; 223 | padding: 0px; 224 | height: 30px; 225 | min-height: 30px; 226 | background-color: #b12e00; 227 | border: 0; 228 | overflow: hidden; 229 | } 230 | .wmWindow .wmWindowBox div.wmTitleBar .wmTitle { 231 | -webkit-flex: 1; 232 | -moz-flex: 1; 233 | -ms-flex: 1; 234 | -o-flex: 1; 235 | flex: 1; 236 | -webkit-box-flex: 1; 237 | -moz-box-flex: 1; 238 | -ms-box-flex: 1; 239 | -o-box-flex: 1; 240 | display: -webkit-box; 241 | display: -moz-box; 242 | display: -ms-box; 243 | display: -o-box; 244 | display: -webkit-flex; 245 | display: -moz-flex; 246 | display: -ms-flex; 247 | display: -o-flex; 248 | display: flex; 249 | -webkit-flex-direction: row; 250 | -moz-flex-direction: row; 251 | -ms-flex-direction: row; 252 | -o-flex-direction: row; 253 | flex-direction: row; 254 | -webkit-box-orient: horizontal; 255 | -moz-box-orient: horizontal; 256 | -ms-box-orient: horizontal; 257 | -o-box-orient: horizontal; 258 | -webkit-box-align: center; 259 | -moz-box-align: center; 260 | -ms-box-align: center; 261 | -o-box-align: center; 262 | -webkit-align-items: center; 263 | -moz-align-items: center; 264 | -ms-align-items: center; 265 | -o-align-items: center; 266 | align-items: center; 267 | -webkit-touch-callout: none; 268 | user-select: none; 269 | -webkit-user-select: none; 270 | -moz-user-select: none; 271 | -ms-user-select: none; 272 | -o-user-select: none; 273 | cursor: default; 274 | padding: 0; 275 | padding-left: 8px; 276 | margin: 0; 277 | font-size: 12px; 278 | font-weight: 400; 279 | color: #fff; 280 | } 281 | .wmWindow .wmWindowBox div.wmTitleBar .wmButtonBar { 282 | display: -webkit-box; 283 | display: -moz-box; 284 | display: -ms-box; 285 | display: -o-box; 286 | display: -webkit-flex; 287 | display: -moz-flex; 288 | display: -ms-flex; 289 | display: -o-flex; 290 | display: flex; 291 | -webkit-flex-direction: row; 292 | -moz-flex-direction: row; 293 | -ms-flex-direction: row; 294 | -o-flex-direction: row; 295 | flex-direction: row; 296 | -webkit-box-orient: horizontal; 297 | -moz-box-orient: horizontal; 298 | -ms-box-orient: horizontal; 299 | -o-box-orient: horizontal; 300 | -webkit-box-align: center; 301 | -moz-box-align: center; 302 | -ms-box-align: center; 303 | -o-box-align: center; 304 | -webkit-align-items: center; 305 | -moz-align-items: center; 306 | -ms-align-items: center; 307 | -o-align-items: center; 308 | align-items: center; 309 | padding-left: 2px; 310 | } 311 | .wmWindow .wmWindowBox div.wmTitleBar .wmButtonBar button { 312 | display: inline-block; 313 | border: 0; 314 | background-repeat: no-repeat; 315 | background-color: #365d98; 316 | color: #fff; 317 | margin: 0; 318 | margin-left: 3px; 319 | padding: 0; 320 | width: 15px; 321 | height: 15px; 322 | opacity: .7; 323 | } 324 | .wmWindow .wmWindowBox div.wmTitleBar .wmButtonBar button:hover { 325 | opacity: 1; 326 | } 327 | .wmWindow .wmWindowBox div.wmTitleBar .wmButtonBar button.wmClose { 328 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAYAAABi8KSDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QkIxOUQwNTEzMDM0MTFFMkI5MUFGMzlFMTgwOEI4ODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QkIxOUQwNTIzMDM0MTFFMkI5MUFGMzlFMTgwOEI4ODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpCQjE5RDA0RjMwMzQxMUUyQjkxQUYzOUUxODA4Qjg4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpCQjE5RDA1MDMwMzQxMUUyQjkxQUYzOUUxODA4Qjg4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PpFaWsQAAABxSURBVHjajJDRDcAgCERtJ2AER+oIjuZIHcER3IBCvDYX5KMklwg8lPNQ1fI3TjpfJgl9QX2F32yquuI2CWqCXNH/YFejgUpgexmGeUAjmMH+9AA4aKUN5h174qFkYEs8CMNuaMYdkc/sNySAW/0RYABjHiW8yydeWwAAAABJRU5ErkJggg==) no-repeat 1px 1px; 329 | } 330 | .wmWindow .wmWindowBox div.wmTitleBar .wmButtonBar button.wmMaximize { 331 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAAKCAYAAABi8KSDAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QkIxOUQwNTUzMDM0MTFFMkI5MUFGMzlFMTgwOEI4ODEiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QkIxOUQwNTYzMDM0MTFFMkI5MUFGMzlFMTgwOEI4ODEiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpCQjE5RDA1MzMwMzQxMUUyQjkxQUYzOUUxODA4Qjg4MSIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpCQjE5RDA1NDMwMzQxMUUyQjkxQUYzOUUxODA4Qjg4MSIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqAiG1YAAAA7SURBVHjaYvz//z8DsYAJSj8E4v948AdkxSSZDALyQMyIBQtgU0ySyQOomAWJ/RCPuo8ggpGUSAEIMACTWxDft/Hl3wAAAABJRU5ErkJggg==) no-repeat 1px 1px; 332 | } 333 | 334 | .wmWindow .wmWindowBox div.wmTitleBar.hide { 335 | display: none; 336 | } 337 | .wmWindow .wmWindowBox div.wmContent { 338 | display: block; 339 | -webkit-flex: 1; 340 | -moz-flex: 1; 341 | -ms-flex: 1; 342 | -o-flex: 1; 343 | flex: 1; 344 | -webkit-box-flex: 1; 345 | -moz-box-flex: 1; 346 | -ms-box-flex: 1; 347 | -o-box-flex: 1; 348 | min-height: 60px; 349 | overflow-x: hidden; 350 | overflow-y: hidden; 351 | position: relative; 352 | } 353 | .wmWindow .wmWindowBox button.wmResize { 354 | position: absolute; 355 | bottom: 0; 356 | right: 4px; 357 | background: transparent; 358 | border: 0; 359 | margin: 0; 360 | padding: 0; 361 | cursor: se-resize; 362 | -webkit-touch-callout: none; 363 | user-select: none; 364 | -webkit-user-select: none; 365 | -moz-user-select: none; 366 | -ms-user-select: none; 367 | -o-user-select: none; 368 | background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAALCAYAAACprHcmAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QzREODAwQzcyRjZDMTFFMjg5NkREMENBNjJERUE4Q0IiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QzREODAwQzgyRjZDMTFFMjg5NkREMENBNjJERUE4Q0IiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpDNEQ4MDBDNTJGNkMxMUUyODk2REQwQ0E2MkRFQThDQiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpDNEQ4MDBDNjJGNkMxMUUyODk2REQwQ0E2MkRFQThDQiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PuQy0VQAAACLSURBVHjaYpw9ezYDEUARiO8zEaHQHohPArEcCxEK1wGxPxA/wmeyDZLCIyABJjwKNwJxEFShIi7FyAoPArEZEB8DYi0mHFaHIikEaUwE4mtMWBRGAPE+NIU7kJ0BUxiNQyFInpMJKgFTuBuLQj8gXg3yJCicHyFZDQJfgDgOqhEE3gGxD8jNAAEGADlXJQUd3J75AAAAAElFTkSuQmCC) no-repeat; 369 | height: 15px; 370 | width: 10px; 371 | } 372 | .wmWindow.disabled * { 373 | -webkit-touch-callout: none; 374 | user-select: none; 375 | -webkit-user-select: none; 376 | -moz-user-select: none; 377 | -ms-user-select: none; 378 | -o-user-select: none; 379 | } 380 | .wmWindow.inactive * { 381 | -webkit-touch-callout: none; 382 | user-select: none; 383 | -webkit-user-select: none; 384 | -moz-user-select: none; 385 | -ms-user-select: none; 386 | -o-user-select: none; 387 | } 388 | .wmWindow.inactive div.wmTitleBar { 389 | background-color: #999; 390 | } 391 | .wmWindow.inactive div.wmTitleBar div.wmTitle { 392 | color: #ddd; 393 | } 394 | .wmWindow.active{ 395 | display: block; 396 | } 397 | 398 | .wmWindow.active iframe{ 399 | top: 0px !important; 400 | } 401 | 402 | .wmWindow.maximized{ 403 | position: fixed; 404 | z-index: 1000000 !important; 405 | } 406 | 407 | .wmWindow.active .wmContent * { 408 | user-select: text; 409 | -webkit-user-select: text; 410 | -moz-user-select: text; 411 | -ms-user-select: text; 412 | -o-user-select: text; 413 | } 414 | 415 | 416 | .wmWindow.resizing .wmWindowBox, .wmWindow.moving .wmWindowBox{ 417 | background-color: #4c4c4c; 418 | } 419 | 420 | .wmWindow.resizing .wmWindowBox div.wmTitleBar, .wmWindow.moving .wmWindowBox div.wmTitleBar{ 421 | background-color: #c13e10; 422 | } 423 | 424 | .wmWindow.opening { 425 | -webkit-animation: appear .4s 1 ease-out forwards; 426 | -moz-animation: appear .4s 1 ease-out forwards; 427 | animation: appear .4s 1 ease-out forwards; 428 | } 429 | 430 | .wmWindow.closing { 431 | -webkit-animation: close .3s 1 ease-in forwards; 432 | -moz-animation: close .3s 1 ease-in forwards; 433 | animation: close .3s 1 ease-in forwards; 434 | } 435 | 436 | .wmWindow.restoring, .wmWindow.maximizing { 437 | -webkit-transform: translate3d(0,0,0); 438 | -webkit-transition: all .5s ease-out; 439 | -moz-transition: all .5s ease-out; 440 | -ms-transition: all .5s ease-out; 441 | -o-transition: all .5s ease-out; 442 | transition: all .5s ease-out; 443 | } 444 | -------------------------------------------------------------------------------- /app/views/main.html: -------------------------------------------------------------------------------- 1 |
2 |

Window manager Example

3 |

4 | Get your app windofied 5 |

6 |
7 | 8 | 9 |
10 |
11 | hola 12 | Que tal 13 | Que tal 14 |

Window

{{elem}}

15 | 16 | 17 |
18 |
19 | 20 | 21 | 22 |
23 |
24 | 25 | Add new:
26 | 27 |
28 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "winamn", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "angular": "^1.3.0", 6 | "json3": "^3.3.0", 7 | "es5-shim": "^4.0.0", 8 | "angular-animate": "^1.3.0", 9 | "angular-touch": "^1.3.0" 10 | }, 11 | "devDependencies": { 12 | "angular-mocks": "~1.3.0", 13 | "angular-scenario": "~1.3.0" 14 | }, 15 | "appPath": "app" 16 | } 17 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "winamn", 3 | "version": "1.2.0", 4 | "dependencies": { 5 | }, 6 | "devDependencies": { 7 | "grunt": "~0.4.5", 8 | "grunt-autoprefixer": "^0.7.3", 9 | "grunt-concurrent": "^0.5.0", 10 | "grunt-contrib-clean": "^0.5.0", 11 | "grunt-contrib-concat": "^0.4.0", 12 | "grunt-contrib-connect": "^0.7.1", 13 | "grunt-contrib-less": "~1.0.0", 14 | "grunt-contrib-copy": "^0.5.0", 15 | "grunt-contrib-cssmin": "^0.9.0", 16 | "grunt-contrib-htmlmin": "^0.3.0", 17 | "grunt-contrib-imagemin": "^0.8.1", 18 | "grunt-contrib-jshint": "^0.10.0", 19 | "grunt-contrib-uglify": "^0.4.0", 20 | "grunt-contrib-watch": "^0.6.1", 21 | "grunt-filerev": "^0.2.1", 22 | "grunt-google-cdn": "^0.4.0", 23 | "grunt-newer": "^0.7.0", 24 | "grunt-ng-annotate": "^0.4.0", 25 | "grunt-svgmin": "^0.4.0", 26 | "grunt-usemin": "^2.1.1", 27 | "grunt-wiredep": "^1.7.0", 28 | "jshint-stylish": "^0.2.0", 29 | "load-grunt-tasks": "^0.4.0", 30 | "time-grunt": "^0.3.1", 31 | "jasmine-core": "~2.1.3", 32 | "karma": "~0.12.31", 33 | "karma-jasmine": "~0.3.4", 34 | "grunt-karma": "~0.9.0", 35 | "karma-phantomjs-launcher": "~0.1.4" 36 | }, 37 | "engines": { 38 | "node": ">=0.10.0" 39 | }, 40 | "scripts": { 41 | "test": "grunt test" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "node": true, 3 | "browser": true, 4 | "esnext": true, 5 | "bitwise": true, 6 | "camelcase": true, 7 | "curly": true, 8 | "eqeqeq": true, 9 | "immed": true, 10 | "indent": 2, 11 | "latedef": true, 12 | "newcap": true, 13 | "noarg": true, 14 | "quotmark": "single", 15 | "regexp": true, 16 | "undef": true, 17 | "unused": true, 18 | "strict": true, 19 | "trailing": true, 20 | "smarttabs": true, 21 | "globals": { 22 | "after": false, 23 | "afterEach": false, 24 | "angular": false, 25 | "before": false, 26 | "beforeEach": false, 27 | "browser": false, 28 | "describe": false, 29 | "expect": false, 30 | "inject": false, 31 | "it": false, 32 | "jasmine": false, 33 | "spyOn": false 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /test/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // http://karma-runner.github.io/0.12/config/configuration-file.html 3 | // Generated on 2015-01-06 using 4 | // generator-karma 0.8.3 5 | 6 | module.exports = function(config) { 7 | 'use strict'; 8 | 9 | config.set({ 10 | // enable / disable watching file and executing tests whenever any file changes 11 | autoWatch: true, 12 | 13 | // base path, that will be used to resolve files and exclude 14 | basePath: '../', 15 | 16 | // testing framework to use (jasmine/mocha/qunit/...) 17 | frameworks: ['jasmine'], 18 | 19 | // list of files / patterns to load in the browser 20 | files: [ 21 | 'bower_components/angular/angular.js', 22 | 'bower_components/angular-mocks/angular-mocks.js', 23 | 'bower_components/angular-animate/angular-animate.js', 24 | 'bower_components/angular-touch/angular-touch.js', 25 | 'app/scripts/**/*.js', 26 | 'test/mock/**/*.js', 27 | 'test/spec/**/*.js' 28 | ], 29 | 30 | // list of files / patterns to exclude 31 | exclude: [], 32 | 33 | // web server port 34 | port: 8080, 35 | 36 | // Start these browsers, currently available: 37 | // - Chrome 38 | // - ChromeCanary 39 | // - Firefox 40 | // - Opera 41 | // - Safari (only Mac) 42 | // - PhantomJS 43 | // - IE (only Windows) 44 | browsers: [ 45 | 'PhantomJS' 46 | ], 47 | 48 | // Which plugins to enable 49 | plugins: [ 50 | 'karma-phantomjs-launcher', 51 | 'karma-jasmine' 52 | ], 53 | 54 | // Continuous Integration mode 55 | // if true, it capture browsers, run tests and exit 56 | singleRun: false, 57 | 58 | colors: true, 59 | 60 | // level of logging 61 | // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG 62 | logLevel: config.LOG_INFO, 63 | 64 | // Uncomment the following lines if you are using grunt's server to run the tests 65 | // proxies: { 66 | // '/': 'http://localhost:9000/' 67 | // }, 68 | // URL root prevent conflicts with the site root 69 | // urlRoot: '_karma_' 70 | }); 71 | }; 72 | -------------------------------------------------------------------------------- /test/spec/controllers/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Controller: MainCtrl', function () { 4 | 5 | // load the controller's module 6 | beforeEach(module('wmExampleApp')); 7 | 8 | var MainCtrl, 9 | scope; 10 | 11 | // Initialize the controller and a mock scope 12 | beforeEach(inject(function ($controller, $rootScope) { 13 | scope = $rootScope.$new(); 14 | MainCtrl = $controller('MainCtrl', { 15 | $scope: scope 16 | }); 17 | })); 18 | 19 | it('should attach a list of awesomeThings to the scope', function () { 20 | expect(scope.awesomeThings.length).toBe(3); 21 | }); 22 | }); 23 | -------------------------------------------------------------------------------- /test/spec/directives/wmwindow.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | describe('Directive: wmwindow', function () { 4 | 5 | // load the directive's module 6 | beforeEach(module('ngWindowManager')); 7 | 8 | var element, 9 | scope; 10 | 11 | beforeEach(inject(function ($rootScope) { 12 | scope = $rootScope.$new(); 13 | })); 14 | 15 | it('should make hidden element visible', inject(function ($compile) { 16 | element = angular.element(''); 17 | element = $compile(element)(scope); 18 | expect(element.children.length).toBe(3); 19 | })); 20 | }); 21 | --------------------------------------------------------------------------------