diff --git a/app/assets/stylesheets/tags.sass b/app/assets/stylesheets/tags.sass index 47854c44..76d15a92 100644 --- a/app/assets/stylesheets/tags.sass +++ b/app/assets/stylesheets/tags.sass @@ -6,13 +6,13 @@ p.tags a:last-child:after content: '' .css1 - font-size: 1.0em + font-size: 0.8em .css2 - font-size: 1.2em + font-size: 1.0em .css3 font-size: 1.4em .css4 - font-size: 1.6em + font-size: 1.8em .tagsinput border: none !important diff --git a/public/assets/.sprockets-manifest-15ceda6913c644f60925b0cd3079d44e.json b/public/assets/.sprockets-manifest-15ceda6913c644f60925b0cd3079d44e.json index 185dae4c..fc8676e5 100644 --- a/public/assets/.sprockets-manifest-15ceda6913c644f60925b0cd3079d44e.json +++ b/public/assets/.sprockets-manifest-15ceda6913c644f60925b0cd3079d44e.json @@ -1 +1 @@ -{"files":{"active_admin-1a6505fe97404d4518ab3540e631c4ba837fd413398b4e75ad4abbcd110398f3.css":{"logical_path":"active_admin.css","mtime":"2016-12-10T11:36:51+01:00","size":78849,"digest":"1a6505fe97404d4518ab3540e631c4ba837fd413398b4e75ad4abbcd110398f3","integrity":"sha256-GmUF/pdATUUYqzVA5jHEuoN/1BM5i051rUq7zREDmPM="},"active_admin/nested_menu_arrow-15084d93c65c1964d7077700ea748bd2d70cfa2d4c19707c58a9c64e232dd442.gif":{"logical_path":"active_admin/nested_menu_arrow.gif","mtime":"2016-12-10T11:36:51+01:00","size":70,"digest":"15084d93c65c1964d7077700ea748bd2d70cfa2d4c19707c58a9c64e232dd442","integrity":"sha256-FQhNk8ZcGWTXB3cA6nSL0tcM+i1MGXB8WKnGTiMt1EI="},"active_admin/nested_menu_arrow_dark-7c43b8e0a5f8823875f49a093c9d7a6b374f885b6f9cc248ae9cd7e6e9b29034.gif":{"logical_path":"active_admin/nested_menu_arrow_dark.gif","mtime":"2016-12-10T11:36:51+01:00","size":70,"digest":"7c43b8e0a5f8823875f49a093c9d7a6b374f885b6f9cc248ae9cd7e6e9b29034","integrity":"sha256-fEO44KX4gjh19JoJPJ16azdPiFtvnMJIrpzX5umykDQ="},"active_admin/datepicker/datepicker-input-icon-d9c2bb73769af777c8a71720d29741f3a499aebd5a043e9a119bd0d9597aed47.png":{"logical_path":"active_admin/datepicker/datepicker-input-icon.png","mtime":"2016-12-10T11:36:51+01:00","size":1535,"digest":"d9c2bb73769af777c8a71720d29741f3a499aebd5a043e9a119bd0d9597aed47","integrity":"sha256-2cK7c3aa93fIpxcg0pdB86SZrr1aBD6aEZvQ2Vl67Uc="},"active_admin/orderable-29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617.png":{"logical_path":"active_admin/orderable.png","mtime":"2016-12-10T11:36:51+01:00","size":220,"digest":"29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617","integrity":"sha256-KTdNu1WwAS14o3xhTVc7s0dPB3mEm0eKFH0PGEXKZhc="},"active_admin/print-87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68.css":{"logical_path":"active_admin/print.css","mtime":"2016-12-10T11:36:51+01:00","size":5494,"digest":"87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68","integrity":"sha256-h8X/wdhpqRkSO8wdxexRsgvHn9muq57td+NDjGrNT2g="},"active_admin-5d8dda85fd2e9296c4f9682a0c4441e4dfc6c2779454e8144be78befe3ff05bb.js":{"logical_path":"active_admin.js","mtime":"2016-12-17T16:47:53+01:00","size":692380,"digest":"5d8dda85fd2e9296c4f9682a0c4441e4dfc6c2779454e8144be78befe3ff05bb","integrity":"sha256-XY3ahf0ukpbE+WgqDERB5N/GwneUVOgUS+eL7+P/Bbs="},"layers-2x-0c02a2388f637d21f86e6d4b314ec9a968e7b05ad4c3a005280a3f76c0fd3cb8.png":{"logical_path":"layers-2x.png","mtime":"2016-10-29T16:45:43+02:00","size":2898,"digest":"0c02a2388f637d21f86e6d4b314ec9a968e7b05ad4c3a005280a3f76c0fd3cb8","integrity":"sha256-DAKiOI9jfSH4bm1LMU7JqWjnsFrUw6AFKAo/dsD9PLg="},"layers-0908aa2a72a082fb2563a2427a5e4fb247571862b448b80fb6f720af1109923e.png":{"logical_path":"layers.png","mtime":"2016-10-29T16:45:43+02:00","size":1502,"digest":"0908aa2a72a082fb2563a2427a5e4fb247571862b448b80fb6f720af1109923e","integrity":"sha256-CQiqKnKggvslY6JCel5PskdXGGK0SLgPtvcgrxEJkj4="},"marker-icon-2x-454dc479e82b487529b6b93d6a9b29ac69ca7b4f5a9d5fdf8e01871f6d216113.png":{"logical_path":"marker-icon-2x.png","mtime":"2016-10-29T16:45:43+02:00","size":4033,"digest":"454dc479e82b487529b6b93d6a9b29ac69ca7b4f5a9d5fdf8e01871f6d216113","integrity":"sha256-RU3EeegrSHUptrk9apsprGnKe09anV/fjgGHH20hYRM="},"marker-icon-915e83a6fc798c599e5c9e3f759d6bc065d65151019acd0410d1f4731bcaaf72.png":{"logical_path":"marker-icon.png","mtime":"2016-10-29T16:45:43+02:00","size":1747,"digest":"915e83a6fc798c599e5c9e3f759d6bc065d65151019acd0410d1f4731bcaaf72","integrity":"sha256-kV6Dpvx5jFmeXJ4/dZ1rwGXWUVEBms0EENH0cxvKr3I="},"marker-shadow-4f340d2d61746333dffe056e074ce1704ae4e47fec5a7de98322fbdbcfcb2b6d.png":{"logical_path":"marker-shadow.png","mtime":"2016-10-29T16:45:43+02:00","size":797,"digest":"4f340d2d61746333dffe056e074ce1704ae4e47fec5a7de98322fbdbcfcb2b6d","integrity":"sha256-TzQNLWF0YzPf/gVuB0zhcErk5H/sWn3pgyL728/LK20="},"tinymce-e760fd8b6f08323d2b85a1a7a149a483d75d4bf23628ffb9340280f59b2d359d.js":{"logical_path":"tinymce.js","mtime":"2016-12-17T16:47:53+01:00","size":971,"digest":"e760fd8b6f08323d2b85a1a7a149a483d75d4bf23628ffb9340280f59b2d359d","integrity":"sha256-52D9i28IMj0rhaGnoUmkg9ddS/I2KP+5NAKA9ZstNZ0="},"application-943a3e70ac370e883a0394cbddf7c60a6ec281cd561d3c6817cfa97974531bc8.css":{"logical_path":"application.css","mtime":"2016-12-17T16:47:53+01:00","size":170905,"digest":"943a3e70ac370e883a0394cbddf7c60a6ec281cd561d3c6817cfa97974531bc8","integrity":"sha256-lDo+cKw3Dog6A5TL3ffGCm7Cgc1WHTxoF8+peXRTG8g="},"jquery-ui/ui-icons_444444_256x240-31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d.png":{"logical_path":"jquery-ui/ui-icons_444444_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d","integrity":"sha256-MdmIdltOb1ZVPClYjFADgdw+bwqimAyCEiAuVkSu/V0="},"jquery-ui/ui-icons_555555_256x240-32175261daee76c82bb0edf0eea16a56421866fbc31e94f3c1d570aa114502f5.png":{"logical_path":"jquery-ui/ui-icons_555555_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"32175261daee76c82bb0edf0eea16a56421866fbc31e94f3c1d570aa114502f5","integrity":"sha256-MhdSYdrudsgrsO3w7qFqVkIYZvvDHpTzwdVwqhFFAvU="},"jquery-ui/ui-icons_ffffff_256x240-350df1b7131037de20e83c5c0f3a41a770d2ac48b5762ea772b3f4a8a7b9d47a.png":{"logical_path":"jquery-ui/ui-icons_ffffff_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"350df1b7131037de20e83c5c0f3a41a770d2ac48b5762ea772b3f4a8a7b9d47a","integrity":"sha256-NQ3xtxMQN94g6DxcDzpBp3DSrEi1di6ncrP0qKe51Ho="},"jquery-ui/ui-icons_777620_256x240-0b020fc6e696d88d296e7bb1f61f1eb2ad827848e2c7382a4c3e0999e702dd9b.png":{"logical_path":"jquery-ui/ui-icons_777620_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"0b020fc6e696d88d296e7bb1f61f1eb2ad827848e2c7382a4c3e0999e702dd9b","integrity":"sha256-CwIPxuaW2I0pbnux9h8esq2CeEjixzgqTD4JmecC3Zs="},"jquery-ui/ui-icons_cc0000_256x240-40985a64b4d5dd213fba27fcd862a1bd1b337a97674f6ff0b9ec20abcee4bc69.png":{"logical_path":"jquery-ui/ui-icons_cc0000_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"40985a64b4d5dd213fba27fcd862a1bd1b337a97674f6ff0b9ec20abcee4bc69","integrity":"sha256-QJhaZLTV3SE/uif82GKhvRszepdnT2/wuewgq87kvGk="},"jquery-ui/ui-icons_777777_256x240-faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f.png":{"logical_path":"jquery-ui/ui-icons_777777_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f","integrity":"sha256-+vMgB64SDDAiE1V2JuZg3RDnEcXdTxET018m3AW3jS8="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2016-10-29T16:46:55+02:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2016-10-29T16:46:55+02:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2016-10-29T16:46:55+02:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2016-10-29T16:46:55+02:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2016-10-29T16:46:55+02:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"markers-soft-e78784e4ed70aaffddd73c315fab590233cc4e7b72388d7dd47a14796fc7c739.png":{"logical_path":"markers-soft.png","mtime":"2016-05-21T23:41:15+02:00","size":41226,"digest":"e78784e4ed70aaffddd73c315fab590233cc4e7b72388d7dd47a14796fc7c739","integrity":"sha256-54eE5O1wqv/d1zwxX6tZAjPMTntyOI191HoUeW/Hxzk="},"markers-shadow-8703a2262710f5e3d29e65d2acdf90d6512e159e119d27b8234731d8a6208a20.png":{"logical_path":"markers-shadow.png","mtime":"2016-05-21T23:41:15+02:00","size":535,"digest":"8703a2262710f5e3d29e65d2acdf90d6512e159e119d27b8234731d8a6208a20","integrity":"sha256-hwOiJicQ9ePSnmXSrN+Q1lEuFZ4RnSe4I0cx2KYgiiA="},"markers-soft@2x-c1e77253a8bfbe30cec24885d7046f443b76ebb66f4c961f77083b03f4a5cbaf.png":{"logical_path":"markers-soft@2x.png","mtime":"2016-05-21T23:41:15+02:00","size":66408,"digest":"c1e77253a8bfbe30cec24885d7046f443b76ebb66f4c961f77083b03f4a5cbaf","integrity":"sha256-wedyU6i/vjDOwkiF1wRvRDt267ZvTJYfdwg7A/Sly68="},"markers-shadow@2x-b21a536be27313fb504f69f5899ff0b1245b276571769ac08d6c32c35676e47a.png":{"logical_path":"markers-shadow@2x.png","mtime":"2016-05-21T23:41:15+02:00","size":1469,"digest":"b21a536be27313fb504f69f5899ff0b1245b276571769ac08d6c32c35676e47a","integrity":"sha256-shpTa+JzE/tQT2n1iZ/wsSRbJ2VxdprAjWwyw1Z25Ho="},"france-f4341a7ec8331161a9c8d5298f808014c3fc9c799b5a29ed95eb56a7f3ccd0df.png":{"logical_path":"france.png","mtime":"2014-07-04T14:08:56+02:00","size":276,"digest":"f4341a7ec8331161a9c8d5298f808014c3fc9c799b5a29ed95eb56a7f3ccd0df","integrity":"sha256-9DQafsgzEWGpyNUpj4CAFMP8nHmbWintletWp/PM0N8="},"quebec-776d563b6a4ac4312cae9f0bfe630c20711346e8dbddd41040998eba79f4b588.png":{"logical_path":"quebec.png","mtime":"2015-10-22T22:54:30+02:00","size":567,"digest":"776d563b6a4ac4312cae9f0bfe630c20711346e8dbddd41040998eba79f4b588","integrity":"sha256-d21WO2pKxDEsrp8L/mMMIHETRujb3dQQQJmOunn0tYg="},"belgique-3b8b772a522de2cbae7714b35a956faf2c394419b532a14bba982fed3f341091.png":{"logical_path":"belgique.png","mtime":"2014-07-04T14:08:56+02:00","size":187,"digest":"3b8b772a522de2cbae7714b35a956faf2c394419b532a14bba982fed3f341091","integrity":"sha256-O4t3KlIt4suudxSzWpVvryw5RBm1MqFLupgv7T80EJE="},"suisse-58d067f1c3fcdc4000fa13e95896cd5369a2b91aafd314475aa5e29da0b543d1.png":{"logical_path":"suisse.png","mtime":"2015-10-22T22:54:30+02:00","size":299,"digest":"58d067f1c3fcdc4000fa13e95896cd5369a2b91aafd314475aa5e29da0b543d1","integrity":"sha256-WNBn8cP83EAA+hPpWJbNU2miuRqv0xRHWqXinaC1Q9E="},"modernizr-654222debe8018b12f1993ceddff30dc163a7d5008d79869c399d6d167321f97.js":{"logical_path":"modernizr.js","mtime":"2016-10-29T16:45:47+02:00","size":51365,"digest":"654222debe8018b12f1993ceddff30dc163a7d5008d79869c399d6d167321f97","integrity":"sha256-ZUIi3r6AGLEvGZPO3f8w3BY6fVAI15hpw5nW0WcyH5c="},"agendadescommuns-cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6.png":{"logical_path":"agendadescommuns.png","mtime":"2015-10-22T22:54:30+02:00","size":2760,"digest":"cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6","integrity":"sha256-zUDjQgJL4Fh/jnoOOQLTLPZwCdNJtnwA9ofgSZ/en/Y="},"alert-762ace9479328243a44061346b64c4d6b997e963c68dfc6bddd9e4d241192906.png":{"logical_path":"alert.png","mtime":"2014-07-04T14:08:56+02:00","size":47876,"digest":"762ace9479328243a44061346b64c4d6b997e963c68dfc6bddd9e4d241192906","integrity":"sha256-dirOlHkygkOkQGE0a2TE1rmX6WPGjfxr3dnk0kEZKQY="},"baby_gnu_adl-232caf355c30740d5d9b30491887cd546b8849b33ca9bdb6cc71f8a47ea61815.png":{"logical_path":"baby_gnu_adl.png","mtime":"2016-09-11T17:42:49+02:00","size":10155,"digest":"232caf355c30740d5d9b30491887cd546b8849b33ca9bdb6cc71f8a47ea61815","integrity":"sha256-IyyvNVwwdA1dmzBJGIfNVGuISbM8qb22zHH4pH6mGBU="},"baby_gnu_adl-97251005d3225cf1d58b8c497d6b7905dbc9560cc8acd50118fcce60d0a2679e.svg":{"logical_path":"baby_gnu_adl.svg","mtime":"2016-09-11T17:42:49+02:00","size":109635,"digest":"97251005d3225cf1d58b8c497d6b7905dbc9560cc8acd50118fcce60d0a2679e","integrity":"sha256-lyUQBdMiXPHVi4xJfWt5BdvJVgzIrNUBGPzOYNCiZ54="},"communs-cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6.png":{"logical_path":"communs.png","mtime":"2015-10-22T22:54:30+02:00","size":2760,"digest":"cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6","integrity":"sha256-zUDjQgJL4Fh/jnoOOQLTLPZwCdNJtnwA9ofgSZ/en/Y="},"lef-small-160cf5b883add60c9c0f4361bd8425c75f6fb23b0e551a0b941fa0491c70e0c9.png":{"logical_path":"lef-small.png","mtime":"2015-03-29T11:07:15+02:00","size":1089,"digest":"160cf5b883add60c9c0f4361bd8425c75f6fb23b0e551a0b941fa0491c70e0c9","integrity":"sha256-Fgz1uIOt1gycD0NhvYQlx19vsjsOVRoLlB+gSRxw4Mk="},"lef-bec4081a11fbef165216827cf72c7a292ad772a77af6b8132e5bc0bbf83cb2d9.png":{"logical_path":"lef.png","mtime":"2016-01-24T11:38:05+01:00","size":8415,"digest":"bec4081a11fbef165216827cf72c7a292ad772a77af6b8132e5bc0bbf83cb2d9","integrity":"sha256-vsQIGhH77xZSFoJ89yx6KSrXcqd69rgTLlvAu/g8stk="},"priorite-logiciel-libre-je-soutiens-april_2_m-6442e454e96ed45cc1ebc40673a6c50bd286b9c28ea6a8b58572e94f7d6459fc.png":{"logical_path":"priorite-logiciel-libre-je-soutiens-april_2_m.png","mtime":"2015-10-22T22:54:30+02:00","size":16952,"digest":"6442e454e96ed45cc1ebc40673a6c50bd286b9c28ea6a8b58572e94f7d6459fc","integrity":"sha256-ZELkVOlu1FzB68QGc6bFC9KGucKOpqi1hXLpT31kWfw="},"team-cb04c7a311f7160c4eb6a281eae68be84f26991dde5d415bb4e205e6726ae275.png":{"logical_path":"team.png","mtime":"2014-07-04T14:08:56+02:00","size":3586,"digest":"cb04c7a311f7160c4eb6a281eae68be84f26991dde5d415bb4e205e6726ae275","integrity":"sha256-ywTHoxH3FgxOtqKB6uaL6E8mmR3eXUFbtOIF5nJq4nU="},"application-224c876758d567479b1ebc1da354a712a64d27a100fb0ba5b8f1c0ca5188d55c.js":{"logical_path":"application.js","mtime":"2016-12-17T16:47:53+01:00","size":2158066,"digest":"224c876758d567479b1ebc1da354a712a64d27a100fb0ba5b8f1c0ca5188d55c","integrity":"sha256-IkyHZ1jVZ0ebHrwdo1SnEqZNJ6EA+wuluPHAylGI1Vw="},"markers-matte-497826545a90e09a240504d14530eba45823b19fd44175e09e27c47cd822ddb9.png":{"logical_path":"markers-matte.png","mtime":"2016-05-21T23:41:15+02:00","size":14323,"digest":"497826545a90e09a240504d14530eba45823b19fd44175e09e27c47cd822ddb9","integrity":"sha256-SXgmVFqQ4JokBQTRRTDrpFgjsZ/UQXXgnifEfNgi3bk="},"markers-matte@2x-948fc8c4426f04f60964ed20394247f45b0b60e575d02398b9b6810e7a29a823.png":{"logical_path":"markers-matte@2x.png","mtime":"2016-05-21T23:41:15+02:00","size":31113,"digest":"948fc8c4426f04f60964ed20394247f45b0b60e575d02398b9b6810e7a29a823","integrity":"sha256-lI/IxEJvBPYJZO0gOUJH9FsLYOV10COYubaBDnopqCM="},"markers-plain-cf233423aa44e75ac0031e77b8ba571cd3331010517e1197e63fb7b06856c1ff.png":{"logical_path":"markers-plain.png","mtime":"2016-05-21T23:41:15+02:00","size":7946,"digest":"cf233423aa44e75ac0031e77b8ba571cd3331010517e1197e63fb7b06856c1ff","integrity":"sha256-zyM0I6pE51rAAx53uLpXHNMzEBBRfhGX5j+3sGhWwf8="},"jquery-ui/ui-bg_flat_0_aaaaaa_40x100-ae65a7ae22c4c23115948fdeb5c05c9137dbd13ca2d426b3c4c3c4183451e410.png":{"logical_path":"jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png","mtime":"2016-12-10T10:15:35+01:00","size":86,"digest":"ae65a7ae22c4c23115948fdeb5c05c9137dbd13ca2d426b3c4c3c4183451e410","integrity":"sha256-rmWnriLEwjEVlI/etcBckTfb0Tyi1CazxMPEGDRR5BA="},"tinymce-a6d3b77f66eb3e1971dc7bd18d9cefefd2947a846683d2a189a092f94280acf4.js":{"logical_path":"tinymce.js","mtime":"2016-12-17T22:42:28+01:00","size":971,"digest":"a6d3b77f66eb3e1971dc7bd18d9cefefd2947a846683d2a189a092f94280acf4","integrity":"sha256-ptO3f2brPhlx3HvRjZzv79KUeoRmg9KhiaCS+UKArPQ="},"application-6054b0bc1c370f1c7d66e9d0c3d21aa9fa886547e9c7732570440a4f42618a99.css":{"logical_path":"application.css","mtime":"2016-12-17T22:42:28+01:00","size":170862,"digest":"6054b0bc1c370f1c7d66e9d0c3d21aa9fa886547e9c7732570440a4f42618a99","integrity":"sha256-YFSwvBw3Dxx9ZunQw9IaqfqIZUfpx3MlcEQKT0Jhipk="},"application-97155f8afdbec83bd10d2feeaf475014a2d2efa855a16491aa7b922d8c1cbf58.js":{"logical_path":"application.js","mtime":"2016-12-17T22:42:28+01:00","size":2158066,"digest":"97155f8afdbec83bd10d2feeaf475014a2d2efa855a16491aa7b922d8c1cbf58","integrity":"sha256-lxVfiv2+yDvRDS/ur0dQFKLS76hVoWSRqnuSLYwcv1g="},"tinymce/plugins/emoticons/img/smiley-cool.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-cool.gif","mtime":"2016-12-10T10:15:36+01:00","size":354,"digest":null},"tinymce/plugins/emoticons/img/smiley-cry.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-cry.gif","mtime":"2016-12-10T10:15:36+01:00","size":329,"digest":null},"tinymce/plugins/emoticons/img/smiley-embarassed.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-embarassed.gif","mtime":"2016-12-10T10:15:36+01:00","size":331,"digest":null},"tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif","mtime":"2016-12-10T10:15:36+01:00","size":342,"digest":null},"tinymce/plugins/emoticons/img/smiley-frown.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-frown.gif","mtime":"2016-12-10T10:15:36+01:00","size":340,"digest":null},"tinymce/plugins/emoticons/img/smiley-innocent.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-innocent.gif","mtime":"2016-12-10T10:15:36+01:00","size":336,"digest":null},"tinymce/plugins/emoticons/img/smiley-kiss.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-kiss.gif","mtime":"2016-12-10T10:15:36+01:00","size":338,"digest":null},"tinymce/plugins/emoticons/img/smiley-laughing.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-laughing.gif","mtime":"2016-12-10T10:15:36+01:00","size":343,"digest":null},"tinymce/plugins/emoticons/img/smiley-money-mouth.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-money-mouth.gif","mtime":"2016-12-10T10:15:36+01:00","size":321,"digest":null},"tinymce/plugins/emoticons/img/smiley-sealed.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-sealed.gif","mtime":"2016-12-10T10:15:36+01:00","size":323,"digest":null},"tinymce/plugins/emoticons/img/smiley-smile.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-smile.gif","mtime":"2016-12-10T10:15:36+01:00","size":344,"digest":null},"tinymce/plugins/emoticons/img/smiley-surprised.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-surprised.gif","mtime":"2016-12-10T10:15:36+01:00","size":338,"digest":null},"tinymce/plugins/emoticons/img/smiley-tongue-out.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-tongue-out.gif","mtime":"2016-12-10T10:15:36+01:00","size":328,"digest":null},"tinymce/plugins/emoticons/img/smiley-undecided.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-undecided.gif","mtime":"2016-12-10T10:15:36+01:00","size":337,"digest":null},"tinymce/plugins/emoticons/img/smiley-wink.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-wink.gif","mtime":"2016-12-10T10:15:36+01:00","size":350,"digest":null},"tinymce/plugins/emoticons/img/smiley-yell.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-yell.gif","mtime":"2016-12-10T10:15:36+01:00","size":336,"digest":null},"tinymce/skins/lightgray/img/anchor.gif":{"logical_path":"tinymce/skins/lightgray/img/anchor.gif","mtime":"2016-12-10T10:15:36+01:00","size":53,"digest":null},"tinymce/skins/lightgray/img/loader.gif":{"logical_path":"tinymce/skins/lightgray/img/loader.gif","mtime":"2016-12-10T10:15:36+01:00","size":2608,"digest":null},"tinymce/skins/lightgray/img/object.gif":{"logical_path":"tinymce/skins/lightgray/img/object.gif","mtime":"2016-12-10T10:15:36+01:00","size":152,"digest":null},"tinymce/skins/lightgray/img/trans.gif":{"logical_path":"tinymce/skins/lightgray/img/trans.gif","mtime":"2016-12-10T10:15:36+01:00","size":43,"digest":null},"tinymce/jquery.tinymce.js":{"logical_path":"tinymce/jquery.tinymce.js","mtime":"2016-12-10T10:15:36+01:00","size":3591,"digest":null},"tinymce/langs/readme.md":{"logical_path":"tinymce/langs/readme.md","mtime":"2016-12-10T10:15:36+01:00","size":151,"digest":null},"tinymce/license.txt":{"logical_path":"tinymce/license.txt","mtime":"2016-12-10T10:15:36+01:00","size":26427,"digest":null},"tinymce/plugins/advlist/plugin.js":{"logical_path":"tinymce/plugins/advlist/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2048,"digest":null},"tinymce/plugins/anchor/plugin.js":{"logical_path":"tinymce/plugins/anchor/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":957,"digest":null},"tinymce/plugins/autolink/plugin.js":{"logical_path":"tinymce/plugins/autolink/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2060,"digest":null},"tinymce/plugins/autoresize/plugin.js":{"logical_path":"tinymce/plugins/autoresize/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1903,"digest":null},"tinymce/plugins/autosave/plugin.js":{"logical_path":"tinymce/plugins/autosave/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2187,"digest":null},"tinymce/plugins/bbcode/plugin.js":{"logical_path":"tinymce/plugins/bbcode/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":3136,"digest":null},"tinymce/plugins/charmap/plugin.js":{"logical_path":"tinymce/plugins/charmap/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":8199,"digest":null},"tinymce/plugins/code/plugin.js":{"logical_path":"tinymce/plugins/code/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":721,"digest":null},"tinymce/plugins/codesample/css/prism.css":{"logical_path":"tinymce/plugins/codesample/css/prism.css","mtime":"2016-12-10T10:15:36+01:00","size":2289,"digest":null},"tinymce/plugins/codesample/plugin.dev.js":{"logical_path":"tinymce/plugins/codesample/plugin.dev.js","mtime":"2016-12-10T10:15:36+01:00","size":3168,"digest":null},"tinymce/plugins/codesample/plugin.js":{"logical_path":"tinymce/plugins/codesample/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":20264,"digest":null},"tinymce/plugins/colorpicker/plugin.js":{"logical_path":"tinymce/plugins/colorpicker/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1222,"digest":null},"tinymce/plugins/contextmenu/plugin.js":{"logical_path":"tinymce/plugins/contextmenu/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1131,"digest":null},"tinymce/plugins/directionality/plugin.js":{"logical_path":"tinymce/plugins/directionality/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":726,"digest":null},"tinymce/plugins/emoticons/plugin.js":{"logical_path":"tinymce/plugins/emoticons/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":911,"digest":null},"tinymce/plugins/example/dialog.html":{"logical_path":"tinymce/plugins/example/dialog.html","mtime":"2016-12-10T10:15:36+01:00","size":213,"digest":null},"tinymce/plugins/example/plugin.js":{"logical_path":"tinymce/plugins/example/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":658,"digest":null},"tinymce/plugins/example_dependency/plugin.js":{"logical_path":"tinymce/plugins/example_dependency/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":73,"digest":null},"tinymce/plugins/fullpage/plugin.js":{"logical_path":"tinymce/plugins/fullpage/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":6308,"digest":null},"tinymce/plugins/fullscreen/plugin.js":{"logical_path":"tinymce/plugins/fullscreen/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1675,"digest":null},"tinymce/plugins/hr/plugin.js":{"logical_path":"tinymce/plugins/hr/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":322,"digest":null},"tinymce/plugins/image/plugin.js":{"logical_path":"tinymce/plugins/image/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":8195,"digest":null},"tinymce/plugins/imagetools/plugin.js":{"logical_path":"tinymce/plugins/imagetools/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":30900,"digest":null},"tinymce/plugins/importcss/plugin.js":{"logical_path":"tinymce/plugins/importcss/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2746,"digest":null},"tinymce/plugins/insertdatetime/plugin.js":{"logical_path":"tinymce/plugins/insertdatetime/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1971,"digest":null},"tinymce/plugins/layer/plugin.js":{"logical_path":"tinymce/plugins/layer/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2857,"digest":null},"tinymce/plugins/legacyoutput/plugin.js":{"logical_path":"tinymce/plugins/legacyoutput/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":3263,"digest":null},"tinymce/plugins/link/plugin.js":{"logical_path":"tinymce/plugins/link/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":6903,"digest":null},"tinymce/plugins/lists/plugin.js":{"logical_path":"tinymce/plugins/lists/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":10331,"digest":null},"tinymce/plugins/media/plugin.js":{"logical_path":"tinymce/plugins/media/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":14999,"digest":null},"tinymce/plugins/nonbreaking/plugin.js":{"logical_path":"tinymce/plugins/nonbreaking/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":655,"digest":null},"tinymce/plugins/noneditable/plugin.js":{"logical_path":"tinymce/plugins/noneditable/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1270,"digest":null},"tinymce/plugins/pagebreak/plugin.js":{"logical_path":"tinymce/plugins/pagebreak/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1230,"digest":null},"tinymce/plugins/paste/plugin.dev.js":{"logical_path":"tinymce/plugins/paste/plugin.dev.js","mtime":"2016-12-10T10:15:36+01:00","size":3130,"digest":null},"tinymce/plugins/paste/plugin.js":{"logical_path":"tinymce/plugins/paste/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":17676,"digest":null},"tinymce/plugins/preview/plugin.js":{"logical_path":"tinymce/plugins/preview/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1602,"digest":null},"tinymce/plugins/print/plugin.js":{"logical_path":"tinymce/plugins/print/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":293,"digest":null},"tinymce/plugins/save/plugin.js":{"logical_path":"tinymce/plugins/save/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1150,"digest":null},"tinymce/plugins/searchreplace/plugin.js":{"logical_path":"tinymce/plugins/searchreplace/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":6493,"digest":null},"tinymce/plugins/spellchecker/plugin.dev.js":{"logical_path":"tinymce/plugins/spellchecker/plugin.dev.js","mtime":"2016-12-10T10:15:36+01:00","size":3032,"digest":null},"tinymce/plugins/spellchecker/plugin.js":{"logical_path":"tinymce/plugins/spellchecker/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":10044,"digest":null},"tinymce/plugins/tabfocus/plugin.js":{"logical_path":"tinymce/plugins/tabfocus/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1264,"digest":null},"tinymce/plugins/table/plugin.dev.js":{"logical_path":"tinymce/plugins/table/plugin.dev.js","mtime":"2016-12-10T10:15:36+01:00","size":3152,"digest":null},"tinymce/plugins/table/plugin.js":{"logical_path":"tinymce/plugins/table/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":45825,"digest":null},"tinymce/plugins/template/plugin.js":{"logical_path":"tinymce/plugins/template/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":4520,"digest":null},"tinymce/plugins/textcolor/plugin.js":{"logical_path":"tinymce/plugins/textcolor/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":4145,"digest":null},"tinymce/plugins/textpattern/plugin.js":{"logical_path":"tinymce/plugins/textpattern/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2745,"digest":null},"tinymce/plugins/toc/plugin.js":{"logical_path":"tinymce/plugins/toc/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2771,"digest":null},"tinymce/plugins/visualblocks/css/visualblocks.css":{"logical_path":"tinymce/plugins/visualblocks/css/visualblocks.css","mtime":"2016-12-10T10:15:36+01:00","size":5092,"digest":null},"tinymce/plugins/visualblocks/plugin.js":{"logical_path":"tinymce/plugins/visualblocks/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1153,"digest":null},"tinymce/plugins/visualchars/plugin.js":{"logical_path":"tinymce/plugins/visualchars/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1178,"digest":null},"tinymce/plugins/wordcount/plugin.js":{"logical_path":"tinymce/plugins/wordcount/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":11674,"digest":null},"tinymce/skins/lightgray/AbsoluteLayout.less":{"logical_path":"tinymce/skins/lightgray/AbsoluteLayout.less","mtime":"2016-12-10T10:15:36+01:00","size":265,"digest":null},"tinymce/skins/lightgray/Animations.less":{"logical_path":"tinymce/skins/lightgray/Animations.less","mtime":"2016-12-10T10:15:36+01:00","size":119,"digest":null},"tinymce/skins/lightgray/Arrows.less":{"logical_path":"tinymce/skins/lightgray/Arrows.less","mtime":"2016-12-10T10:15:36+01:00","size":2437,"digest":null},"tinymce/skins/lightgray/Button.less":{"logical_path":"tinymce/skins/lightgray/Button.less","mtime":"2016-12-10T10:15:36+01:00","size":3853,"digest":null},"tinymce/skins/lightgray/ButtonGroup.less":{"logical_path":"tinymce/skins/lightgray/ButtonGroup.less","mtime":"2016-12-10T10:15:36+01:00","size":1681,"digest":null},"tinymce/skins/lightgray/Checkbox.less":{"logical_path":"tinymce/skins/lightgray/Checkbox.less","mtime":"2016-12-10T10:15:36+01:00","size":1096,"digest":null},"tinymce/skins/lightgray/ColorBox.less":{"logical_path":"tinymce/skins/lightgray/ColorBox.less","mtime":"2016-12-10T10:15:36+01:00","size":103,"digest":null},"tinymce/skins/lightgray/ColorButton.less":{"logical_path":"tinymce/skins/lightgray/ColorButton.less","mtime":"2016-12-10T10:15:36+01:00","size":1301,"digest":null},"tinymce/skins/lightgray/ColorPicker.less":{"logical_path":"tinymce/skins/lightgray/ColorPicker.less","mtime":"2016-12-10T10:15:36+01:00","size":1864,"digest":null},"tinymce/skins/lightgray/ComboBox.less":{"logical_path":"tinymce/skins/lightgray/ComboBox.less","mtime":"2016-12-10T10:15:36+01:00","size":1729,"digest":null},"tinymce/skins/lightgray/Container.less":{"logical_path":"tinymce/skins/lightgray/Container.less","mtime":"2016-12-10T10:15:36+01:00","size":129,"digest":null},"tinymce/skins/lightgray/Content.Inline.less":{"logical_path":"tinymce/skins/lightgray/Content.Inline.less","mtime":"2016-12-10T10:15:36+01:00","size":82,"digest":null},"tinymce/skins/lightgray/Content.Objects.less":{"logical_path":"tinymce/skins/lightgray/Content.Objects.less","mtime":"2016-12-10T10:15:36+01:00","size":3210,"digest":null},"tinymce/skins/lightgray/Content.less":{"logical_path":"tinymce/skins/lightgray/Content.less","mtime":"2016-12-10T10:15:36+01:00","size":592,"digest":null},"tinymce/skins/lightgray/CropRect.less":{"logical_path":"tinymce/skins/lightgray/CropRect.less","mtime":"2016-12-10T10:15:36+01:00","size":1108,"digest":null},"tinymce/skins/lightgray/FieldSet.less":{"logical_path":"tinymce/skins/lightgray/FieldSet.less","mtime":"2016-12-10T10:15:36+01:00","size":233,"digest":null},"tinymce/skins/lightgray/FitLayout.less":{"logical_path":"tinymce/skins/lightgray/FitLayout.less","mtime":"2016-12-10T10:15:36+01:00","size":111,"digest":null},"tinymce/skins/lightgray/FloatPanel.less":{"logical_path":"tinymce/skins/lightgray/FloatPanel.less","mtime":"2016-12-10T10:15:36+01:00","size":1477,"digest":null},"tinymce/skins/lightgray/FlowLayout.less":{"logical_path":"tinymce/skins/lightgray/FlowLayout.less","mtime":"2016-12-10T10:15:36+01:00","size":694,"digest":null},"tinymce/skins/lightgray/Icons.Ie7.less":{"logical_path":"tinymce/skins/lightgray/Icons.Ie7.less","mtime":"2016-12-10T10:15:36+01:00","size":5621,"digest":null},"tinymce/skins/lightgray/Icons.less":{"logical_path":"tinymce/skins/lightgray/Icons.less","mtime":"2016-12-10T10:15:36+01:00","size":9372,"digest":null},"tinymce/skins/lightgray/Iframe.less":{"logical_path":"tinymce/skins/lightgray/Iframe.less","mtime":"2016-12-10T10:15:36+01:00","size":94,"digest":null},"tinymce/skins/lightgray/ImagePanel.less":{"logical_path":"tinymce/skins/lightgray/ImagePanel.less","mtime":"2016-12-10T10:15:36+01:00","size":476,"digest":null},"tinymce/skins/lightgray/InfoBox.less":{"logical_path":"tinymce/skins/lightgray/InfoBox.less","mtime":"2016-12-10T10:15:36+01:00","size":1087,"digest":null},"tinymce/skins/lightgray/Label.less":{"logical_path":"tinymce/skins/lightgray/Label.less","mtime":"2016-12-10T10:15:36+01:00","size":554,"digest":null},"tinymce/skins/lightgray/ListBox.less":{"logical_path":"tinymce/skins/lightgray/ListBox.less","mtime":"2016-12-10T10:15:36+01:00","size":388,"digest":null},"tinymce/skins/lightgray/Menu.less":{"logical_path":"tinymce/skins/lightgray/Menu.less","mtime":"2016-12-10T10:15:36+01:00","size":821,"digest":null},"tinymce/skins/lightgray/MenuBar.less":{"logical_path":"tinymce/skins/lightgray/MenuBar.less","mtime":"2016-12-10T10:15:36+01:00","size":689,"digest":null},"tinymce/skins/lightgray/MenuButton.less":{"logical_path":"tinymce/skins/lightgray/MenuButton.less","mtime":"2016-12-10T10:15:36+01:00","size":607,"digest":null},"tinymce/skins/lightgray/MenuItem.less":{"logical_path":"tinymce/skins/lightgray/MenuItem.less","mtime":"2016-12-10T10:15:36+01:00","size":4782,"digest":null},"tinymce/skins/lightgray/Mixins.less":{"logical_path":"tinymce/skins/lightgray/Mixins.less","mtime":"2016-12-10T10:15:36+01:00","size":1799,"digest":null},"tinymce/skins/lightgray/Notification.less":{"logical_path":"tinymce/skins/lightgray/Notification.less","mtime":"2016-12-10T10:15:36+01:00","size":3628,"digest":null},"tinymce/skins/lightgray/Panel.less":{"logical_path":"tinymce/skins/lightgray/Panel.less","mtime":"2016-12-10T10:15:36+01:00","size":220,"digest":null},"tinymce/skins/lightgray/Path.less":{"logical_path":"tinymce/skins/lightgray/Path.less","mtime":"2016-12-10T10:15:36+01:00","size":650,"digest":null},"tinymce/skins/lightgray/Progress.less":{"logical_path":"tinymce/skins/lightgray/Progress.less","mtime":"2016-12-10T10:15:36+01:00","size":619,"digest":null},"tinymce/skins/lightgray/Radio.less":{"logical_path":"tinymce/skins/lightgray/Radio.less","mtime":"2016-12-10T10:15:36+01:00","size":31,"digest":null},"tinymce/skins/lightgray/Reset.less":{"logical_path":"tinymce/skins/lightgray/Reset.less","mtime":"2016-12-10T10:15:36+01:00","size":905,"digest":null},"tinymce/skins/lightgray/ResizeHandle.less":{"logical_path":"tinymce/skins/lightgray/ResizeHandle.less","mtime":"2016-12-10T10:15:36+01:00","size":301,"digest":null},"tinymce/skins/lightgray/Scrollable.less":{"logical_path":"tinymce/skins/lightgray/Scrollable.less","mtime":"2016-12-10T10:15:36+01:00","size":687,"digest":null},"tinymce/skins/lightgray/SelectBox.less":{"logical_path":"tinymce/skins/lightgray/SelectBox.less","mtime":"2016-12-10T10:15:36+01:00","size":105,"digest":null},"tinymce/skins/lightgray/Sidebar.less":{"logical_path":"tinymce/skins/lightgray/Sidebar.less","mtime":"2016-12-10T10:15:36+01:00","size":956,"digest":null},"tinymce/skins/lightgray/Slider.less":{"logical_path":"tinymce/skins/lightgray/Slider.less","mtime":"2016-12-10T10:15:36+01:00","size":579,"digest":null},"tinymce/skins/lightgray/Spacer.less":{"logical_path":"tinymce/skins/lightgray/Spacer.less","mtime":"2016-12-10T10:15:36+01:00","size":54,"digest":null},"tinymce/skins/lightgray/SplitButton.less":{"logical_path":"tinymce/skins/lightgray/SplitButton.less","mtime":"2016-12-10T10:15:36+01:00","size":980,"digest":null},"tinymce/skins/lightgray/StackLayout.less":{"logical_path":"tinymce/skins/lightgray/StackLayout.less","mtime":"2016-12-10T10:15:36+01:00","size":67,"digest":null},"tinymce/skins/lightgray/TabPanel.less":{"logical_path":"tinymce/skins/lightgray/TabPanel.less","mtime":"2016-12-10T10:15:36+01:00","size":706,"digest":null},"tinymce/skins/lightgray/TextBox.less":{"logical_path":"tinymce/skins/lightgray/TextBox.less","mtime":"2016-12-10T10:15:36+01:00","size":870,"digest":null},"tinymce/skins/lightgray/Throbber.less":{"logical_path":"tinymce/skins/lightgray/Throbber.less","mtime":"2016-12-10T10:15:36+01:00","size":303,"digest":null},"tinymce/skins/lightgray/TinyMCE.less":{"logical_path":"tinymce/skins/lightgray/TinyMCE.less","mtime":"2016-12-10T10:15:36+01:00","size":2449,"digest":null},"tinymce/skins/lightgray/ToolTip.less":{"logical_path":"tinymce/skins/lightgray/ToolTip.less","mtime":"2016-12-10T10:15:36+01:00","size":2533,"digest":null},"tinymce/skins/lightgray/Variables.less":{"logical_path":"tinymce/skins/lightgray/Variables.less","mtime":"2016-12-10T10:15:36+01:00","size":8371,"digest":null},"tinymce/skins/lightgray/Window.less":{"logical_path":"tinymce/skins/lightgray/Window.less","mtime":"2016-12-10T10:15:36+01:00","size":2217,"digest":null},"tinymce/skins/lightgray/content.inline.min.css":{"logical_path":"tinymce/skins/lightgray/content.inline.min.css","mtime":"2016-12-10T10:15:36+01:00","size":2769,"digest":null},"tinymce/skins/lightgray/content.min.css":{"logical_path":"tinymce/skins/lightgray/content.min.css","mtime":"2016-12-10T10:15:36+01:00","size":3193,"digest":null},"tinymce/skins/lightgray/fonts/tinymce-small.eot":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.eot","mtime":"2016-12-10T10:15:36+01:00","size":9492,"digest":null},"tinymce/skins/lightgray/fonts/tinymce-small.svg":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.svg","mtime":"2016-12-10T10:15:36+01:00","size":24727,"digest":null},"tinymce/skins/lightgray/fonts/tinymce-small.ttf":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.ttf","mtime":"2016-12-10T10:15:36+01:00","size":9304,"digest":null},"tinymce/skins/lightgray/fonts/tinymce-small.woff":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.woff","mtime":"2016-12-10T10:15:36+01:00","size":9380,"digest":null},"tinymce/skins/lightgray/fonts/tinymce.eot":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.eot","mtime":"2016-12-10T10:15:36+01:00","size":17572,"digest":null},"tinymce/skins/lightgray/fonts/tinymce.svg":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.svg","mtime":"2016-12-10T10:15:36+01:00","size":45991,"digest":null},"tinymce/skins/lightgray/fonts/tinymce.ttf":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.ttf","mtime":"2016-12-10T10:15:36+01:00","size":17408,"digest":null},"tinymce/skins/lightgray/fonts/tinymce.woff":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.woff","mtime":"2016-12-10T10:15:36+01:00","size":17484,"digest":null},"tinymce/skins/lightgray/skin.dev.less":{"logical_path":"tinymce/skins/lightgray/skin.dev.less","mtime":"2016-12-10T10:15:36+01:00","size":1201,"digest":null},"tinymce/skins/lightgray/skin.ie7.dev.less":{"logical_path":"tinymce/skins/lightgray/skin.ie7.dev.less","mtime":"2016-12-10T10:15:36+01:00","size":1181,"digest":null},"tinymce/skins/lightgray/skin.ie7.less":{"logical_path":"tinymce/skins/lightgray/skin.ie7.less","mtime":"2016-12-10T10:15:36+01:00","size":61487,"digest":null},"tinymce/skins/lightgray/skin.ie7.min.css":{"logical_path":"tinymce/skins/lightgray/skin.ie7.min.css","mtime":"2016-12-10T10:15:36+01:00","size":34904,"digest":null},"tinymce/skins/lightgray/skin.less":{"logical_path":"tinymce/skins/lightgray/skin.less","mtime":"2016-12-10T10:15:36+01:00","size":66196,"digest":null},"tinymce/skins/lightgray/skin.min.css":{"logical_path":"tinymce/skins/lightgray/skin.min.css","mtime":"2016-12-10T10:15:36+01:00","size":38232,"digest":null},"tinymce/themes/inlite/theme.js":{"logical_path":"tinymce/themes/inlite/theme.js","mtime":"2016-12-10T10:15:36+01:00","size":16364,"digest":null},"tinymce/themes/modern/theme.js":{"logical_path":"tinymce/themes/modern/theme.js","mtime":"2016-12-10T10:15:36+01:00","size":13146,"digest":null},"tinymce/tinymce.js":{"logical_path":"tinymce/tinymce.js","mtime":"2016-12-10T10:15:36+01:00","size":401179,"digest":null},"tinymce/langs/ar.js":{"logical_path":"tinymce/langs/ar.js","mtime":"2016-10-29T16:47:26+02:00","size":17775,"digest":null},"tinymce/langs/ar_SA.js":{"logical_path":"tinymce/langs/ar_SA.js","mtime":"2016-10-29T16:47:26+02:00","size":10002,"digest":null},"tinymce/langs/az.js":{"logical_path":"tinymce/langs/az.js","mtime":"2016-10-29T16:47:26+02:00","size":9679,"digest":null},"tinymce/langs/be.js":{"logical_path":"tinymce/langs/be.js","mtime":"2016-10-29T16:47:26+02:00","size":21063,"digest":null},"tinymce/langs/bg_BG.js":{"logical_path":"tinymce/langs/bg_BG.js","mtime":"2016-10-29T16:47:26+02:00","size":23944,"digest":null},"tinymce/langs/bn_BD.js":{"logical_path":"tinymce/langs/bn_BD.js","mtime":"2016-10-29T16:47:26+02:00","size":6845,"digest":null},"tinymce/langs/bs.js":{"logical_path":"tinymce/langs/bs.js","mtime":"2016-10-29T16:47:26+02:00","size":6969,"digest":null},"tinymce/langs/ca.js":{"logical_path":"tinymce/langs/ca.js","mtime":"2016-10-29T16:47:26+02:00","size":7786,"digest":null},"tinymce/langs/cs.js":{"logical_path":"tinymce/langs/cs.js","mtime":"2016-10-29T16:47:26+02:00","size":8750,"digest":null},"tinymce/langs/cs_CZ.js":{"logical_path":"tinymce/langs/cs_CZ.js","mtime":"2016-10-29T16:47:26+02:00","size":8398,"digest":null},"tinymce/langs/cy.js":{"logical_path":"tinymce/langs/cy.js","mtime":"2016-10-29T16:47:26+02:00","size":6141,"digest":null},"tinymce/langs/da.js":{"logical_path":"tinymce/langs/da.js","mtime":"2016-10-29T16:47:26+02:00","size":7512,"digest":null},"tinymce/langs/de.js":{"logical_path":"tinymce/langs/de.js","mtime":"2016-10-29T16:47:26+02:00","size":8279,"digest":null},"tinymce/langs/de_AT.js":{"logical_path":"tinymce/langs/de_AT.js","mtime":"2016-10-29T16:47:26+02:00","size":8307,"digest":null},"tinymce/langs/dv.js":{"logical_path":"tinymce/langs/dv.js","mtime":"2016-10-29T16:47:26+02:00","size":18099,"digest":null},"tinymce/langs/el.js":{"logical_path":"tinymce/langs/el.js","mtime":"2016-10-29T16:47:26+02:00","size":22972,"digest":null},"tinymce/langs/en_CA.js":{"logical_path":"tinymce/langs/en_CA.js","mtime":"2016-10-29T16:47:26+02:00","size":6915,"digest":null},"tinymce/langs/en_GB.js":{"logical_path":"tinymce/langs/en_GB.js","mtime":"2016-10-29T16:47:26+02:00","size":5906,"digest":null},"tinymce/langs/eo.js":{"logical_path":"tinymce/langs/eo.js","mtime":"2016-10-29T16:47:26+02:00","size":7465,"digest":null},"tinymce/langs/es.js":{"logical_path":"tinymce/langs/es.js","mtime":"2016-10-29T16:47:26+02:00","size":7808,"digest":null},"tinymce/langs/es_MX.js":{"logical_path":"tinymce/langs/es_MX.js","mtime":"2016-10-29T16:47:26+02:00","size":7893,"digest":null},"tinymce/langs/et.js":{"logical_path":"tinymce/langs/et.js","mtime":"2016-10-29T16:47:26+02:00","size":7783,"digest":null},"tinymce/langs/eu.js":{"logical_path":"tinymce/langs/eu.js","mtime":"2016-10-29T16:47:26+02:00","size":7010,"digest":null},"tinymce/langs/fa.js":{"logical_path":"tinymce/langs/fa.js","mtime":"2016-10-29T16:47:26+02:00","size":16461,"digest":null},"tinymce/langs/fa_IR.js":{"logical_path":"tinymce/langs/fa_IR.js","mtime":"2016-10-29T16:47:26+02:00","size":17724,"digest":null},"tinymce/langs/fi.js":{"logical_path":"tinymce/langs/fi.js","mtime":"2016-10-29T16:47:26+02:00","size":8142,"digest":null},"tinymce/langs/fo.js":{"logical_path":"tinymce/langs/fo.js","mtime":"2016-10-29T16:47:26+02:00","size":8043,"digest":null},"tinymce/langs/fr_CH.js":{"logical_path":"tinymce/langs/fr_CH.js","mtime":"2016-10-29T16:47:26+02:00","size":8073,"digest":null},"tinymce/langs/fr_FR.js":{"logical_path":"tinymce/langs/fr_FR.js","mtime":"2016-10-29T16:47:26+02:00","size":8008,"digest":null},"tinymce/langs/ga.js":{"logical_path":"tinymce/langs/ga.js","mtime":"2016-10-29T16:47:26+02:00","size":8590,"digest":null},"tinymce/langs/gd.js":{"logical_path":"tinymce/langs/gd.js","mtime":"2016-10-29T16:47:26+02:00","size":8807,"digest":null},"tinymce/langs/gl.js":{"logical_path":"tinymce/langs/gl.js","mtime":"2016-10-29T16:47:26+02:00","size":7065,"digest":null},"tinymce/langs/he_IL.js":{"logical_path":"tinymce/langs/he_IL.js","mtime":"2016-10-29T16:47:26+02:00","size":16080,"digest":null},"tinymce/langs/hi_IN.js":{"logical_path":"tinymce/langs/hi_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":18457,"digest":null},"tinymce/langs/hr.js":{"logical_path":"tinymce/langs/hr.js","mtime":"2016-10-29T16:47:26+02:00","size":7576,"digest":null},"tinymce/langs/hu_HU.js":{"logical_path":"tinymce/langs/hu_HU.js","mtime":"2016-10-29T16:47:26+02:00","size":9409,"digest":null},"tinymce/langs/hy.js":{"logical_path":"tinymce/langs/hy.js","mtime":"2016-10-29T16:47:26+02:00","size":20374,"digest":null},"tinymce/langs/id.js":{"logical_path":"tinymce/langs/id.js","mtime":"2016-10-29T16:47:26+02:00","size":7140,"digest":null},"tinymce/langs/is_IS.js":{"logical_path":"tinymce/langs/is_IS.js","mtime":"2016-10-29T16:47:26+02:00","size":7927,"digest":null},"tinymce/langs/it.js":{"logical_path":"tinymce/langs/it.js","mtime":"2016-10-29T16:47:26+02:00","size":7597,"digest":null},"tinymce/langs/ja.js":{"logical_path":"tinymce/langs/ja.js","mtime":"2016-10-29T16:47:26+02:00","size":12289,"digest":null},"tinymce/langs/ka_GE.js":{"logical_path":"tinymce/langs/ka_GE.js","mtime":"2016-10-29T16:47:26+02:00","size":21283,"digest":null},"tinymce/langs/kab.js":{"logical_path":"tinymce/langs/kab.js","mtime":"2016-10-29T16:47:26+02:00","size":7390,"digest":null},"tinymce/langs/kk.js":{"logical_path":"tinymce/langs/kk.js","mtime":"2016-10-29T16:47:26+02:00","size":16680,"digest":null},"tinymce/langs/km_KH.js":{"logical_path":"tinymce/langs/km_KH.js","mtime":"2016-10-29T16:47:26+02:00","size":21395,"digest":null},"tinymce/langs/ko.js":{"logical_path":"tinymce/langs/ko.js","mtime":"2016-10-29T16:47:26+02:00","size":10104,"digest":null},"tinymce/langs/ko_KR.js":{"logical_path":"tinymce/langs/ko_KR.js","mtime":"2016-10-29T16:47:26+02:00","size":10004,"digest":null},"tinymce/langs/ku.js":{"logical_path":"tinymce/langs/ku.js","mtime":"2016-10-29T16:47:26+02:00","size":18273,"digest":null},"tinymce/langs/ku_IQ.js":{"logical_path":"tinymce/langs/ku_IQ.js","mtime":"2016-10-29T16:47:26+02:00","size":18363,"digest":null},"tinymce/langs/lb.js":{"logical_path":"tinymce/langs/lb.js","mtime":"2016-10-29T16:47:26+02:00","size":7466,"digest":null},"tinymce/langs/lt.js":{"logical_path":"tinymce/langs/lt.js","mtime":"2016-10-29T16:47:26+02:00","size":8898,"digest":null},"tinymce/langs/lv.js":{"logical_path":"tinymce/langs/lv.js","mtime":"2016-10-29T16:47:26+02:00","size":8281,"digest":null},"tinymce/langs/mk_MK.js":{"logical_path":"tinymce/langs/mk_MK.js","mtime":"2016-10-29T16:47:26+02:00","size":18732,"digest":null},"tinymce/langs/ml.js":{"logical_path":"tinymce/langs/ml.js","mtime":"2016-10-29T16:47:26+02:00","size":7338,"digest":null},"tinymce/langs/ml_IN.js":{"logical_path":"tinymce/langs/ml_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":20018,"digest":null},"tinymce/langs/mn_MN.js":{"logical_path":"tinymce/langs/mn_MN.js","mtime":"2016-10-29T16:47:26+02:00","size":6876,"digest":null},"tinymce/langs/nb_NO.js":{"logical_path":"tinymce/langs/nb_NO.js","mtime":"2016-10-29T16:47:26+02:00","size":7527,"digest":null},"tinymce/langs/nl.js":{"logical_path":"tinymce/langs/nl.js","mtime":"2016-10-29T16:47:26+02:00","size":7234,"digest":null},"tinymce/langs/pl.js":{"logical_path":"tinymce/langs/pl.js","mtime":"2016-10-29T16:47:26+02:00","size":8128,"digest":null},"tinymce/langs/pt_BR.js":{"logical_path":"tinymce/langs/pt_BR.js","mtime":"2016-10-29T16:47:26+02:00","size":8001,"digest":null},"tinymce/langs/pt_PT.js":{"logical_path":"tinymce/langs/pt_PT.js","mtime":"2016-10-29T16:47:26+02:00","size":8144,"digest":null},"tinymce/langs/ro.js":{"logical_path":"tinymce/langs/ro.js","mtime":"2016-10-29T16:47:26+02:00","size":8274,"digest":null},"tinymce/langs/ru.js":{"logical_path":"tinymce/langs/ru.js","mtime":"2016-10-29T16:47:26+02:00","size":22289,"digest":null},"tinymce/langs/ru_RU.js":{"logical_path":"tinymce/langs/ru_RU.js","mtime":"2016-10-29T16:47:26+02:00","size":4954,"digest":null},"tinymce/langs/si_LK.js":{"logical_path":"tinymce/langs/si_LK.js","mtime":"2016-10-29T16:47:26+02:00","size":15855,"digest":null},"tinymce/langs/sk.js":{"logical_path":"tinymce/langs/sk.js","mtime":"2016-10-29T16:47:26+02:00","size":8957,"digest":null},"tinymce/langs/sl_SI.js":{"logical_path":"tinymce/langs/sl_SI.js","mtime":"2016-10-29T16:47:26+02:00","size":7038,"digest":null},"tinymce/langs/sr.js":{"logical_path":"tinymce/langs/sr.js","mtime":"2016-10-29T16:47:26+02:00","size":6335,"digest":null},"tinymce/langs/sv_SE.js":{"logical_path":"tinymce/langs/sv_SE.js","mtime":"2016-10-29T16:47:26+02:00","size":7495,"digest":null},"tinymce/langs/ta.js":{"logical_path":"tinymce/langs/ta.js","mtime":"2016-10-29T16:47:26+02:00","size":22616,"digest":null},"tinymce/langs/ta_IN.js":{"logical_path":"tinymce/langs/ta_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":22619,"digest":null},"tinymce/langs/tg.js":{"logical_path":"tinymce/langs/tg.js","mtime":"2016-10-29T16:47:26+02:00","size":18182,"digest":null},"tinymce/langs/th_TH.js":{"logical_path":"tinymce/langs/th_TH.js","mtime":"2016-10-29T16:47:26+02:00","size":17296,"digest":null},"tinymce/langs/tr.js":{"logical_path":"tinymce/langs/tr.js","mtime":"2016-10-29T16:47:26+02:00","size":8704,"digest":null},"tinymce/langs/tr_TR.js":{"logical_path":"tinymce/langs/tr_TR.js","mtime":"2016-10-29T16:47:26+02:00","size":8140,"digest":null},"tinymce/langs/tt.js":{"logical_path":"tinymce/langs/tt.js","mtime":"2016-10-29T16:47:26+02:00","size":19034,"digest":null},"tinymce/langs/ug.js":{"logical_path":"tinymce/langs/ug.js","mtime":"2016-10-29T16:47:26+02:00","size":16887,"digest":null},"tinymce/langs/uk.js":{"logical_path":"tinymce/langs/uk.js","mtime":"2016-10-29T16:47:26+02:00","size":22263,"digest":null},"tinymce/langs/uk_UA.js":{"logical_path":"tinymce/langs/uk_UA.js","mtime":"2016-10-29T16:47:26+02:00","size":21690,"digest":null},"tinymce/langs/vi.js":{"logical_path":"tinymce/langs/vi.js","mtime":"2016-10-29T16:47:26+02:00","size":9900,"digest":null},"tinymce/langs/vi_VN.js":{"logical_path":"tinymce/langs/vi_VN.js","mtime":"2016-10-29T16:47:26+02:00","size":9898,"digest":null},"tinymce/langs/zh_CN.js":{"logical_path":"tinymce/langs/zh_CN.js","mtime":"2016-10-29T16:47:26+02:00","size":8482,"digest":null},"tinymce/langs/zh_TW.js":{"logical_path":"tinymce/langs/zh_TW.js","mtime":"2016-10-29T16:47:26+02:00","size":9378,"digest":null}},"assets":{"active_admin.css":"active_admin-1a6505fe97404d4518ab3540e631c4ba837fd413398b4e75ad4abbcd110398f3.css","active_admin/nested_menu_arrow.gif":"active_admin/nested_menu_arrow-15084d93c65c1964d7077700ea748bd2d70cfa2d4c19707c58a9c64e232dd442.gif","active_admin/nested_menu_arrow_dark.gif":"active_admin/nested_menu_arrow_dark-7c43b8e0a5f8823875f49a093c9d7a6b374f885b6f9cc248ae9cd7e6e9b29034.gif","active_admin/datepicker/datepicker-input-icon.png":"active_admin/datepicker/datepicker-input-icon-d9c2bb73769af777c8a71720d29741f3a499aebd5a043e9a119bd0d9597aed47.png","active_admin/orderable.png":"active_admin/orderable-29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617.png","active_admin/print.css":"active_admin/print-87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68.css","active_admin.js":"active_admin-5d8dda85fd2e9296c4f9682a0c4441e4dfc6c2779454e8144be78befe3ff05bb.js","layers-2x.png":"layers-2x-0c02a2388f637d21f86e6d4b314ec9a968e7b05ad4c3a005280a3f76c0fd3cb8.png","layers.png":"layers-0908aa2a72a082fb2563a2427a5e4fb247571862b448b80fb6f720af1109923e.png","marker-icon-2x.png":"marker-icon-2x-454dc479e82b487529b6b93d6a9b29ac69ca7b4f5a9d5fdf8e01871f6d216113.png","marker-icon.png":"marker-icon-915e83a6fc798c599e5c9e3f759d6bc065d65151019acd0410d1f4731bcaaf72.png","marker-shadow.png":"marker-shadow-4f340d2d61746333dffe056e074ce1704ae4e47fec5a7de98322fbdbcfcb2b6d.png","tinymce.js":"tinymce-a6d3b77f66eb3e1971dc7bd18d9cefefd2947a846683d2a189a092f94280acf4.js","application.css":"application-6054b0bc1c370f1c7d66e9d0c3d21aa9fa886547e9c7732570440a4f42618a99.css","jquery-ui/ui-icons_444444_256x240.png":"jquery-ui/ui-icons_444444_256x240-31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d.png","jquery-ui/ui-icons_555555_256x240.png":"jquery-ui/ui-icons_555555_256x240-32175261daee76c82bb0edf0eea16a56421866fbc31e94f3c1d570aa114502f5.png","jquery-ui/ui-icons_ffffff_256x240.png":"jquery-ui/ui-icons_ffffff_256x240-350df1b7131037de20e83c5c0f3a41a770d2ac48b5762ea772b3f4a8a7b9d47a.png","jquery-ui/ui-icons_777620_256x240.png":"jquery-ui/ui-icons_777620_256x240-0b020fc6e696d88d296e7bb1f61f1eb2ad827848e2c7382a4c3e0999e702dd9b.png","jquery-ui/ui-icons_cc0000_256x240.png":"jquery-ui/ui-icons_cc0000_256x240-40985a64b4d5dd213fba27fcd862a1bd1b337a97674f6ff0b9ec20abcee4bc69.png","jquery-ui/ui-icons_777777_256x240.png":"jquery-ui/ui-icons_777777_256x240-faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f.png","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","markers-soft.png":"markers-soft-e78784e4ed70aaffddd73c315fab590233cc4e7b72388d7dd47a14796fc7c739.png","markers-shadow.png":"markers-shadow-8703a2262710f5e3d29e65d2acdf90d6512e159e119d27b8234731d8a6208a20.png","markers-soft@2x.png":"markers-soft@2x-c1e77253a8bfbe30cec24885d7046f443b76ebb66f4c961f77083b03f4a5cbaf.png","markers-shadow@2x.png":"markers-shadow@2x-b21a536be27313fb504f69f5899ff0b1245b276571769ac08d6c32c35676e47a.png","france.png":"france-f4341a7ec8331161a9c8d5298f808014c3fc9c799b5a29ed95eb56a7f3ccd0df.png","quebec.png":"quebec-776d563b6a4ac4312cae9f0bfe630c20711346e8dbddd41040998eba79f4b588.png","belgique.png":"belgique-3b8b772a522de2cbae7714b35a956faf2c394419b532a14bba982fed3f341091.png","suisse.png":"suisse-58d067f1c3fcdc4000fa13e95896cd5369a2b91aafd314475aa5e29da0b543d1.png","modernizr.js":"modernizr-654222debe8018b12f1993ceddff30dc163a7d5008d79869c399d6d167321f97.js","agendadescommuns.png":"agendadescommuns-cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6.png","alert.png":"alert-762ace9479328243a44061346b64c4d6b997e963c68dfc6bddd9e4d241192906.png","baby_gnu_adl.png":"baby_gnu_adl-232caf355c30740d5d9b30491887cd546b8849b33ca9bdb6cc71f8a47ea61815.png","baby_gnu_adl.svg":"baby_gnu_adl-97251005d3225cf1d58b8c497d6b7905dbc9560cc8acd50118fcce60d0a2679e.svg","communs.png":"communs-cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6.png","lef-small.png":"lef-small-160cf5b883add60c9c0f4361bd8425c75f6fb23b0e551a0b941fa0491c70e0c9.png","lef.png":"lef-bec4081a11fbef165216827cf72c7a292ad772a77af6b8132e5bc0bbf83cb2d9.png","priorite-logiciel-libre-je-soutiens-april_2_m.png":"priorite-logiciel-libre-je-soutiens-april_2_m-6442e454e96ed45cc1ebc40673a6c50bd286b9c28ea6a8b58572e94f7d6459fc.png","team.png":"team-cb04c7a311f7160c4eb6a281eae68be84f26991dde5d415bb4e205e6726ae275.png","application.js":"application-97155f8afdbec83bd10d2feeaf475014a2d2efa855a16491aa7b922d8c1cbf58.js","markers-matte.png":"markers-matte-497826545a90e09a240504d14530eba45823b19fd44175e09e27c47cd822ddb9.png","markers-matte@2x.png":"markers-matte@2x-948fc8c4426f04f60964ed20394247f45b0b60e575d02398b9b6810e7a29a823.png","markers-plain.png":"markers-plain-cf233423aa44e75ac0031e77b8ba571cd3331010517e1197e63fb7b06856c1ff.png","tinymce/plugins/emoticons/img/smiley-cool.gif":"tinymce/plugins/emoticons/img/smiley-cool.gif","tinymce/plugins/emoticons/img/smiley-cry.gif":"tinymce/plugins/emoticons/img/smiley-cry.gif","tinymce/plugins/emoticons/img/smiley-embarassed.gif":"tinymce/plugins/emoticons/img/smiley-embarassed.gif","tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif":"tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif","tinymce/plugins/emoticons/img/smiley-frown.gif":"tinymce/plugins/emoticons/img/smiley-frown.gif","tinymce/plugins/emoticons/img/smiley-innocent.gif":"tinymce/plugins/emoticons/img/smiley-innocent.gif","tinymce/plugins/emoticons/img/smiley-kiss.gif":"tinymce/plugins/emoticons/img/smiley-kiss.gif","tinymce/plugins/emoticons/img/smiley-laughing.gif":"tinymce/plugins/emoticons/img/smiley-laughing.gif","tinymce/plugins/emoticons/img/smiley-money-mouth.gif":"tinymce/plugins/emoticons/img/smiley-money-mouth.gif","tinymce/plugins/emoticons/img/smiley-sealed.gif":"tinymce/plugins/emoticons/img/smiley-sealed.gif","tinymce/plugins/emoticons/img/smiley-smile.gif":"tinymce/plugins/emoticons/img/smiley-smile.gif","tinymce/plugins/emoticons/img/smiley-surprised.gif":"tinymce/plugins/emoticons/img/smiley-surprised.gif","tinymce/plugins/emoticons/img/smiley-tongue-out.gif":"tinymce/plugins/emoticons/img/smiley-tongue-out.gif","tinymce/plugins/emoticons/img/smiley-undecided.gif":"tinymce/plugins/emoticons/img/smiley-undecided.gif","tinymce/plugins/emoticons/img/smiley-wink.gif":"tinymce/plugins/emoticons/img/smiley-wink.gif","tinymce/plugins/emoticons/img/smiley-yell.gif":"tinymce/plugins/emoticons/img/smiley-yell.gif","tinymce/skins/lightgray/img/anchor.gif":"tinymce/skins/lightgray/img/anchor.gif","tinymce/skins/lightgray/img/loader.gif":"tinymce/skins/lightgray/img/loader.gif","tinymce/skins/lightgray/img/object.gif":"tinymce/skins/lightgray/img/object.gif","tinymce/skins/lightgray/img/trans.gif":"tinymce/skins/lightgray/img/trans.gif","jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png":"jquery-ui/ui-bg_flat_0_aaaaaa_40x100-ae65a7ae22c4c23115948fdeb5c05c9137dbd13ca2d426b3c4c3c4183451e410.png","tinymce/jquery.tinymce.js":"tinymce/jquery.tinymce.js","tinymce/langs/readme.md":"tinymce/langs/readme.md","tinymce/license.txt":"tinymce/license.txt","tinymce/plugins/advlist/plugin.js":"tinymce/plugins/advlist/plugin.js","tinymce/plugins/anchor/plugin.js":"tinymce/plugins/anchor/plugin.js","tinymce/plugins/autolink/plugin.js":"tinymce/plugins/autolink/plugin.js","tinymce/plugins/autoresize/plugin.js":"tinymce/plugins/autoresize/plugin.js","tinymce/plugins/autosave/plugin.js":"tinymce/plugins/autosave/plugin.js","tinymce/plugins/bbcode/plugin.js":"tinymce/plugins/bbcode/plugin.js","tinymce/plugins/charmap/plugin.js":"tinymce/plugins/charmap/plugin.js","tinymce/plugins/code/plugin.js":"tinymce/plugins/code/plugin.js","tinymce/plugins/codesample/css/prism.css":"tinymce/plugins/codesample/css/prism.css","tinymce/plugins/codesample/plugin.dev.js":"tinymce/plugins/codesample/plugin.dev.js","tinymce/plugins/codesample/plugin.js":"tinymce/plugins/codesample/plugin.js","tinymce/plugins/colorpicker/plugin.js":"tinymce/plugins/colorpicker/plugin.js","tinymce/plugins/contextmenu/plugin.js":"tinymce/plugins/contextmenu/plugin.js","tinymce/plugins/directionality/plugin.js":"tinymce/plugins/directionality/plugin.js","tinymce/plugins/emoticons/plugin.js":"tinymce/plugins/emoticons/plugin.js","tinymce/plugins/example/dialog.html":"tinymce/plugins/example/dialog.html","tinymce/plugins/example/plugin.js":"tinymce/plugins/example/plugin.js","tinymce/plugins/example_dependency/plugin.js":"tinymce/plugins/example_dependency/plugin.js","tinymce/plugins/fullpage/plugin.js":"tinymce/plugins/fullpage/plugin.js","tinymce/plugins/fullscreen/plugin.js":"tinymce/plugins/fullscreen/plugin.js","tinymce/plugins/hr/plugin.js":"tinymce/plugins/hr/plugin.js","tinymce/plugins/image/plugin.js":"tinymce/plugins/image/plugin.js","tinymce/plugins/imagetools/plugin.js":"tinymce/plugins/imagetools/plugin.js","tinymce/plugins/importcss/plugin.js":"tinymce/plugins/importcss/plugin.js","tinymce/plugins/insertdatetime/plugin.js":"tinymce/plugins/insertdatetime/plugin.js","tinymce/plugins/layer/plugin.js":"tinymce/plugins/layer/plugin.js","tinymce/plugins/legacyoutput/plugin.js":"tinymce/plugins/legacyoutput/plugin.js","tinymce/plugins/link/plugin.js":"tinymce/plugins/link/plugin.js","tinymce/plugins/lists/plugin.js":"tinymce/plugins/lists/plugin.js","tinymce/plugins/media/plugin.js":"tinymce/plugins/media/plugin.js","tinymce/plugins/nonbreaking/plugin.js":"tinymce/plugins/nonbreaking/plugin.js","tinymce/plugins/noneditable/plugin.js":"tinymce/plugins/noneditable/plugin.js","tinymce/plugins/pagebreak/plugin.js":"tinymce/plugins/pagebreak/plugin.js","tinymce/plugins/paste/plugin.dev.js":"tinymce/plugins/paste/plugin.dev.js","tinymce/plugins/paste/plugin.js":"tinymce/plugins/paste/plugin.js","tinymce/plugins/preview/plugin.js":"tinymce/plugins/preview/plugin.js","tinymce/plugins/print/plugin.js":"tinymce/plugins/print/plugin.js","tinymce/plugins/save/plugin.js":"tinymce/plugins/save/plugin.js","tinymce/plugins/searchreplace/plugin.js":"tinymce/plugins/searchreplace/plugin.js","tinymce/plugins/spellchecker/plugin.dev.js":"tinymce/plugins/spellchecker/plugin.dev.js","tinymce/plugins/spellchecker/plugin.js":"tinymce/plugins/spellchecker/plugin.js","tinymce/plugins/tabfocus/plugin.js":"tinymce/plugins/tabfocus/plugin.js","tinymce/plugins/table/plugin.dev.js":"tinymce/plugins/table/plugin.dev.js","tinymce/plugins/table/plugin.js":"tinymce/plugins/table/plugin.js","tinymce/plugins/template/plugin.js":"tinymce/plugins/template/plugin.js","tinymce/plugins/textcolor/plugin.js":"tinymce/plugins/textcolor/plugin.js","tinymce/plugins/textpattern/plugin.js":"tinymce/plugins/textpattern/plugin.js","tinymce/plugins/toc/plugin.js":"tinymce/plugins/toc/plugin.js","tinymce/plugins/visualblocks/css/visualblocks.css":"tinymce/plugins/visualblocks/css/visualblocks.css","tinymce/plugins/visualblocks/plugin.js":"tinymce/plugins/visualblocks/plugin.js","tinymce/plugins/visualchars/plugin.js":"tinymce/plugins/visualchars/plugin.js","tinymce/plugins/wordcount/plugin.js":"tinymce/plugins/wordcount/plugin.js","tinymce/skins/lightgray/AbsoluteLayout.less":"tinymce/skins/lightgray/AbsoluteLayout.less","tinymce/skins/lightgray/Animations.less":"tinymce/skins/lightgray/Animations.less","tinymce/skins/lightgray/Arrows.less":"tinymce/skins/lightgray/Arrows.less","tinymce/skins/lightgray/Button.less":"tinymce/skins/lightgray/Button.less","tinymce/skins/lightgray/ButtonGroup.less":"tinymce/skins/lightgray/ButtonGroup.less","tinymce/skins/lightgray/Checkbox.less":"tinymce/skins/lightgray/Checkbox.less","tinymce/skins/lightgray/ColorBox.less":"tinymce/skins/lightgray/ColorBox.less","tinymce/skins/lightgray/ColorButton.less":"tinymce/skins/lightgray/ColorButton.less","tinymce/skins/lightgray/ColorPicker.less":"tinymce/skins/lightgray/ColorPicker.less","tinymce/skins/lightgray/ComboBox.less":"tinymce/skins/lightgray/ComboBox.less","tinymce/skins/lightgray/Container.less":"tinymce/skins/lightgray/Container.less","tinymce/skins/lightgray/Content.Inline.less":"tinymce/skins/lightgray/Content.Inline.less","tinymce/skins/lightgray/Content.Objects.less":"tinymce/skins/lightgray/Content.Objects.less","tinymce/skins/lightgray/Content.less":"tinymce/skins/lightgray/Content.less","tinymce/skins/lightgray/CropRect.less":"tinymce/skins/lightgray/CropRect.less","tinymce/skins/lightgray/FieldSet.less":"tinymce/skins/lightgray/FieldSet.less","tinymce/skins/lightgray/FitLayout.less":"tinymce/skins/lightgray/FitLayout.less","tinymce/skins/lightgray/FloatPanel.less":"tinymce/skins/lightgray/FloatPanel.less","tinymce/skins/lightgray/FlowLayout.less":"tinymce/skins/lightgray/FlowLayout.less","tinymce/skins/lightgray/Icons.Ie7.less":"tinymce/skins/lightgray/Icons.Ie7.less","tinymce/skins/lightgray/Icons.less":"tinymce/skins/lightgray/Icons.less","tinymce/skins/lightgray/Iframe.less":"tinymce/skins/lightgray/Iframe.less","tinymce/skins/lightgray/ImagePanel.less":"tinymce/skins/lightgray/ImagePanel.less","tinymce/skins/lightgray/InfoBox.less":"tinymce/skins/lightgray/InfoBox.less","tinymce/skins/lightgray/Label.less":"tinymce/skins/lightgray/Label.less","tinymce/skins/lightgray/ListBox.less":"tinymce/skins/lightgray/ListBox.less","tinymce/skins/lightgray/Menu.less":"tinymce/skins/lightgray/Menu.less","tinymce/skins/lightgray/MenuBar.less":"tinymce/skins/lightgray/MenuBar.less","tinymce/skins/lightgray/MenuButton.less":"tinymce/skins/lightgray/MenuButton.less","tinymce/skins/lightgray/MenuItem.less":"tinymce/skins/lightgray/MenuItem.less","tinymce/skins/lightgray/Mixins.less":"tinymce/skins/lightgray/Mixins.less","tinymce/skins/lightgray/Notification.less":"tinymce/skins/lightgray/Notification.less","tinymce/skins/lightgray/Panel.less":"tinymce/skins/lightgray/Panel.less","tinymce/skins/lightgray/Path.less":"tinymce/skins/lightgray/Path.less","tinymce/skins/lightgray/Progress.less":"tinymce/skins/lightgray/Progress.less","tinymce/skins/lightgray/Radio.less":"tinymce/skins/lightgray/Radio.less","tinymce/skins/lightgray/Reset.less":"tinymce/skins/lightgray/Reset.less","tinymce/skins/lightgray/ResizeHandle.less":"tinymce/skins/lightgray/ResizeHandle.less","tinymce/skins/lightgray/Scrollable.less":"tinymce/skins/lightgray/Scrollable.less","tinymce/skins/lightgray/SelectBox.less":"tinymce/skins/lightgray/SelectBox.less","tinymce/skins/lightgray/Sidebar.less":"tinymce/skins/lightgray/Sidebar.less","tinymce/skins/lightgray/Slider.less":"tinymce/skins/lightgray/Slider.less","tinymce/skins/lightgray/Spacer.less":"tinymce/skins/lightgray/Spacer.less","tinymce/skins/lightgray/SplitButton.less":"tinymce/skins/lightgray/SplitButton.less","tinymce/skins/lightgray/StackLayout.less":"tinymce/skins/lightgray/StackLayout.less","tinymce/skins/lightgray/TabPanel.less":"tinymce/skins/lightgray/TabPanel.less","tinymce/skins/lightgray/TextBox.less":"tinymce/skins/lightgray/TextBox.less","tinymce/skins/lightgray/Throbber.less":"tinymce/skins/lightgray/Throbber.less","tinymce/skins/lightgray/TinyMCE.less":"tinymce/skins/lightgray/TinyMCE.less","tinymce/skins/lightgray/ToolTip.less":"tinymce/skins/lightgray/ToolTip.less","tinymce/skins/lightgray/Variables.less":"tinymce/skins/lightgray/Variables.less","tinymce/skins/lightgray/Window.less":"tinymce/skins/lightgray/Window.less","tinymce/skins/lightgray/content.inline.min.css":"tinymce/skins/lightgray/content.inline.min.css","tinymce/skins/lightgray/content.min.css":"tinymce/skins/lightgray/content.min.css","tinymce/skins/lightgray/fonts/tinymce-small.eot":"tinymce/skins/lightgray/fonts/tinymce-small.eot","tinymce/skins/lightgray/fonts/tinymce-small.svg":"tinymce/skins/lightgray/fonts/tinymce-small.svg","tinymce/skins/lightgray/fonts/tinymce-small.ttf":"tinymce/skins/lightgray/fonts/tinymce-small.ttf","tinymce/skins/lightgray/fonts/tinymce-small.woff":"tinymce/skins/lightgray/fonts/tinymce-small.woff","tinymce/skins/lightgray/fonts/tinymce.eot":"tinymce/skins/lightgray/fonts/tinymce.eot","tinymce/skins/lightgray/fonts/tinymce.svg":"tinymce/skins/lightgray/fonts/tinymce.svg","tinymce/skins/lightgray/fonts/tinymce.ttf":"tinymce/skins/lightgray/fonts/tinymce.ttf","tinymce/skins/lightgray/fonts/tinymce.woff":"tinymce/skins/lightgray/fonts/tinymce.woff","tinymce/skins/lightgray/skin.dev.less":"tinymce/skins/lightgray/skin.dev.less","tinymce/skins/lightgray/skin.ie7.dev.less":"tinymce/skins/lightgray/skin.ie7.dev.less","tinymce/skins/lightgray/skin.ie7.less":"tinymce/skins/lightgray/skin.ie7.less","tinymce/skins/lightgray/skin.ie7.min.css":"tinymce/skins/lightgray/skin.ie7.min.css","tinymce/skins/lightgray/skin.less":"tinymce/skins/lightgray/skin.less","tinymce/skins/lightgray/skin.min.css":"tinymce/skins/lightgray/skin.min.css","tinymce/themes/inlite/theme.js":"tinymce/themes/inlite/theme.js","tinymce/themes/modern/theme.js":"tinymce/themes/modern/theme.js","tinymce/tinymce.js":"tinymce/tinymce.js","tinymce/langs/ar.js":"tinymce/langs/ar.js","tinymce/langs/ar_SA.js":"tinymce/langs/ar_SA.js","tinymce/langs/az.js":"tinymce/langs/az.js","tinymce/langs/be.js":"tinymce/langs/be.js","tinymce/langs/bg_BG.js":"tinymce/langs/bg_BG.js","tinymce/langs/bn_BD.js":"tinymce/langs/bn_BD.js","tinymce/langs/bs.js":"tinymce/langs/bs.js","tinymce/langs/ca.js":"tinymce/langs/ca.js","tinymce/langs/cs.js":"tinymce/langs/cs.js","tinymce/langs/cs_CZ.js":"tinymce/langs/cs_CZ.js","tinymce/langs/cy.js":"tinymce/langs/cy.js","tinymce/langs/da.js":"tinymce/langs/da.js","tinymce/langs/de.js":"tinymce/langs/de.js","tinymce/langs/de_AT.js":"tinymce/langs/de_AT.js","tinymce/langs/dv.js":"tinymce/langs/dv.js","tinymce/langs/el.js":"tinymce/langs/el.js","tinymce/langs/en_CA.js":"tinymce/langs/en_CA.js","tinymce/langs/en_GB.js":"tinymce/langs/en_GB.js","tinymce/langs/eo.js":"tinymce/langs/eo.js","tinymce/langs/es.js":"tinymce/langs/es.js","tinymce/langs/es_MX.js":"tinymce/langs/es_MX.js","tinymce/langs/et.js":"tinymce/langs/et.js","tinymce/langs/eu.js":"tinymce/langs/eu.js","tinymce/langs/fa.js":"tinymce/langs/fa.js","tinymce/langs/fa_IR.js":"tinymce/langs/fa_IR.js","tinymce/langs/fi.js":"tinymce/langs/fi.js","tinymce/langs/fo.js":"tinymce/langs/fo.js","tinymce/langs/fr_CH.js":"tinymce/langs/fr_CH.js","tinymce/langs/fr_FR.js":"tinymce/langs/fr_FR.js","tinymce/langs/ga.js":"tinymce/langs/ga.js","tinymce/langs/gd.js":"tinymce/langs/gd.js","tinymce/langs/gl.js":"tinymce/langs/gl.js","tinymce/langs/he_IL.js":"tinymce/langs/he_IL.js","tinymce/langs/hi_IN.js":"tinymce/langs/hi_IN.js","tinymce/langs/hr.js":"tinymce/langs/hr.js","tinymce/langs/hu_HU.js":"tinymce/langs/hu_HU.js","tinymce/langs/hy.js":"tinymce/langs/hy.js","tinymce/langs/id.js":"tinymce/langs/id.js","tinymce/langs/is_IS.js":"tinymce/langs/is_IS.js","tinymce/langs/it.js":"tinymce/langs/it.js","tinymce/langs/ja.js":"tinymce/langs/ja.js","tinymce/langs/ka_GE.js":"tinymce/langs/ka_GE.js","tinymce/langs/kab.js":"tinymce/langs/kab.js","tinymce/langs/kk.js":"tinymce/langs/kk.js","tinymce/langs/km_KH.js":"tinymce/langs/km_KH.js","tinymce/langs/ko.js":"tinymce/langs/ko.js","tinymce/langs/ko_KR.js":"tinymce/langs/ko_KR.js","tinymce/langs/ku.js":"tinymce/langs/ku.js","tinymce/langs/ku_IQ.js":"tinymce/langs/ku_IQ.js","tinymce/langs/lb.js":"tinymce/langs/lb.js","tinymce/langs/lt.js":"tinymce/langs/lt.js","tinymce/langs/lv.js":"tinymce/langs/lv.js","tinymce/langs/mk_MK.js":"tinymce/langs/mk_MK.js","tinymce/langs/ml.js":"tinymce/langs/ml.js","tinymce/langs/ml_IN.js":"tinymce/langs/ml_IN.js","tinymce/langs/mn_MN.js":"tinymce/langs/mn_MN.js","tinymce/langs/nb_NO.js":"tinymce/langs/nb_NO.js","tinymce/langs/nl.js":"tinymce/langs/nl.js","tinymce/langs/pl.js":"tinymce/langs/pl.js","tinymce/langs/pt_BR.js":"tinymce/langs/pt_BR.js","tinymce/langs/pt_PT.js":"tinymce/langs/pt_PT.js","tinymce/langs/ro.js":"tinymce/langs/ro.js","tinymce/langs/ru.js":"tinymce/langs/ru.js","tinymce/langs/ru_RU.js":"tinymce/langs/ru_RU.js","tinymce/langs/si_LK.js":"tinymce/langs/si_LK.js","tinymce/langs/sk.js":"tinymce/langs/sk.js","tinymce/langs/sl_SI.js":"tinymce/langs/sl_SI.js","tinymce/langs/sr.js":"tinymce/langs/sr.js","tinymce/langs/sv_SE.js":"tinymce/langs/sv_SE.js","tinymce/langs/ta.js":"tinymce/langs/ta.js","tinymce/langs/ta_IN.js":"tinymce/langs/ta_IN.js","tinymce/langs/tg.js":"tinymce/langs/tg.js","tinymce/langs/th_TH.js":"tinymce/langs/th_TH.js","tinymce/langs/tr.js":"tinymce/langs/tr.js","tinymce/langs/tr_TR.js":"tinymce/langs/tr_TR.js","tinymce/langs/tt.js":"tinymce/langs/tt.js","tinymce/langs/ug.js":"tinymce/langs/ug.js","tinymce/langs/uk.js":"tinymce/langs/uk.js","tinymce/langs/uk_UA.js":"tinymce/langs/uk_UA.js","tinymce/langs/vi.js":"tinymce/langs/vi.js","tinymce/langs/vi_VN.js":"tinymce/langs/vi_VN.js","tinymce/langs/zh_CN.js":"tinymce/langs/zh_CN.js","tinymce/langs/zh_TW.js":"tinymce/langs/zh_TW.js"}} \ No newline at end of file +{"files":{"active_admin-1a6505fe97404d4518ab3540e631c4ba837fd413398b4e75ad4abbcd110398f3.css":{"logical_path":"active_admin.css","mtime":"2016-12-10T11:36:51+01:00","size":78849,"digest":"1a6505fe97404d4518ab3540e631c4ba837fd413398b4e75ad4abbcd110398f3","integrity":"sha256-GmUF/pdATUUYqzVA5jHEuoN/1BM5i051rUq7zREDmPM="},"active_admin/nested_menu_arrow-15084d93c65c1964d7077700ea748bd2d70cfa2d4c19707c58a9c64e232dd442.gif":{"logical_path":"active_admin/nested_menu_arrow.gif","mtime":"2016-12-10T11:36:51+01:00","size":70,"digest":"15084d93c65c1964d7077700ea748bd2d70cfa2d4c19707c58a9c64e232dd442","integrity":"sha256-FQhNk8ZcGWTXB3cA6nSL0tcM+i1MGXB8WKnGTiMt1EI="},"active_admin/nested_menu_arrow_dark-7c43b8e0a5f8823875f49a093c9d7a6b374f885b6f9cc248ae9cd7e6e9b29034.gif":{"logical_path":"active_admin/nested_menu_arrow_dark.gif","mtime":"2016-12-10T11:36:51+01:00","size":70,"digest":"7c43b8e0a5f8823875f49a093c9d7a6b374f885b6f9cc248ae9cd7e6e9b29034","integrity":"sha256-fEO44KX4gjh19JoJPJ16azdPiFtvnMJIrpzX5umykDQ="},"active_admin/datepicker/datepicker-input-icon-d9c2bb73769af777c8a71720d29741f3a499aebd5a043e9a119bd0d9597aed47.png":{"logical_path":"active_admin/datepicker/datepicker-input-icon.png","mtime":"2016-12-10T11:36:51+01:00","size":1535,"digest":"d9c2bb73769af777c8a71720d29741f3a499aebd5a043e9a119bd0d9597aed47","integrity":"sha256-2cK7c3aa93fIpxcg0pdB86SZrr1aBD6aEZvQ2Vl67Uc="},"active_admin/orderable-29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617.png":{"logical_path":"active_admin/orderable.png","mtime":"2016-12-10T11:36:51+01:00","size":220,"digest":"29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617","integrity":"sha256-KTdNu1WwAS14o3xhTVc7s0dPB3mEm0eKFH0PGEXKZhc="},"active_admin/print-87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68.css":{"logical_path":"active_admin/print.css","mtime":"2016-12-10T11:36:51+01:00","size":5494,"digest":"87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68","integrity":"sha256-h8X/wdhpqRkSO8wdxexRsgvHn9muq57td+NDjGrNT2g="},"active_admin-5d8dda85fd2e9296c4f9682a0c4441e4dfc6c2779454e8144be78befe3ff05bb.js":{"logical_path":"active_admin.js","mtime":"2016-12-17T16:47:53+01:00","size":692380,"digest":"5d8dda85fd2e9296c4f9682a0c4441e4dfc6c2779454e8144be78befe3ff05bb","integrity":"sha256-XY3ahf0ukpbE+WgqDERB5N/GwneUVOgUS+eL7+P/Bbs="},"layers-2x-0c02a2388f637d21f86e6d4b314ec9a968e7b05ad4c3a005280a3f76c0fd3cb8.png":{"logical_path":"layers-2x.png","mtime":"2016-10-29T16:45:43+02:00","size":2898,"digest":"0c02a2388f637d21f86e6d4b314ec9a968e7b05ad4c3a005280a3f76c0fd3cb8","integrity":"sha256-DAKiOI9jfSH4bm1LMU7JqWjnsFrUw6AFKAo/dsD9PLg="},"layers-0908aa2a72a082fb2563a2427a5e4fb247571862b448b80fb6f720af1109923e.png":{"logical_path":"layers.png","mtime":"2016-10-29T16:45:43+02:00","size":1502,"digest":"0908aa2a72a082fb2563a2427a5e4fb247571862b448b80fb6f720af1109923e","integrity":"sha256-CQiqKnKggvslY6JCel5PskdXGGK0SLgPtvcgrxEJkj4="},"marker-icon-2x-454dc479e82b487529b6b93d6a9b29ac69ca7b4f5a9d5fdf8e01871f6d216113.png":{"logical_path":"marker-icon-2x.png","mtime":"2016-10-29T16:45:43+02:00","size":4033,"digest":"454dc479e82b487529b6b93d6a9b29ac69ca7b4f5a9d5fdf8e01871f6d216113","integrity":"sha256-RU3EeegrSHUptrk9apsprGnKe09anV/fjgGHH20hYRM="},"marker-icon-915e83a6fc798c599e5c9e3f759d6bc065d65151019acd0410d1f4731bcaaf72.png":{"logical_path":"marker-icon.png","mtime":"2016-10-29T16:45:43+02:00","size":1747,"digest":"915e83a6fc798c599e5c9e3f759d6bc065d65151019acd0410d1f4731bcaaf72","integrity":"sha256-kV6Dpvx5jFmeXJ4/dZ1rwGXWUVEBms0EENH0cxvKr3I="},"marker-shadow-4f340d2d61746333dffe056e074ce1704ae4e47fec5a7de98322fbdbcfcb2b6d.png":{"logical_path":"marker-shadow.png","mtime":"2016-10-29T16:45:43+02:00","size":797,"digest":"4f340d2d61746333dffe056e074ce1704ae4e47fec5a7de98322fbdbcfcb2b6d","integrity":"sha256-TzQNLWF0YzPf/gVuB0zhcErk5H/sWn3pgyL728/LK20="},"tinymce-e760fd8b6f08323d2b85a1a7a149a483d75d4bf23628ffb9340280f59b2d359d.js":{"logical_path":"tinymce.js","mtime":"2016-12-17T16:47:53+01:00","size":971,"digest":"e760fd8b6f08323d2b85a1a7a149a483d75d4bf23628ffb9340280f59b2d359d","integrity":"sha256-52D9i28IMj0rhaGnoUmkg9ddS/I2KP+5NAKA9ZstNZ0="},"application-943a3e70ac370e883a0394cbddf7c60a6ec281cd561d3c6817cfa97974531bc8.css":{"logical_path":"application.css","mtime":"2016-12-17T16:47:53+01:00","size":170905,"digest":"943a3e70ac370e883a0394cbddf7c60a6ec281cd561d3c6817cfa97974531bc8","integrity":"sha256-lDo+cKw3Dog6A5TL3ffGCm7Cgc1WHTxoF8+peXRTG8g="},"jquery-ui/ui-icons_444444_256x240-31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d.png":{"logical_path":"jquery-ui/ui-icons_444444_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d","integrity":"sha256-MdmIdltOb1ZVPClYjFADgdw+bwqimAyCEiAuVkSu/V0="},"jquery-ui/ui-icons_555555_256x240-32175261daee76c82bb0edf0eea16a56421866fbc31e94f3c1d570aa114502f5.png":{"logical_path":"jquery-ui/ui-icons_555555_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"32175261daee76c82bb0edf0eea16a56421866fbc31e94f3c1d570aa114502f5","integrity":"sha256-MhdSYdrudsgrsO3w7qFqVkIYZvvDHpTzwdVwqhFFAvU="},"jquery-ui/ui-icons_ffffff_256x240-350df1b7131037de20e83c5c0f3a41a770d2ac48b5762ea772b3f4a8a7b9d47a.png":{"logical_path":"jquery-ui/ui-icons_ffffff_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"350df1b7131037de20e83c5c0f3a41a770d2ac48b5762ea772b3f4a8a7b9d47a","integrity":"sha256-NQ3xtxMQN94g6DxcDzpBp3DSrEi1di6ncrP0qKe51Ho="},"jquery-ui/ui-icons_777620_256x240-0b020fc6e696d88d296e7bb1f61f1eb2ad827848e2c7382a4c3e0999e702dd9b.png":{"logical_path":"jquery-ui/ui-icons_777620_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"0b020fc6e696d88d296e7bb1f61f1eb2ad827848e2c7382a4c3e0999e702dd9b","integrity":"sha256-CwIPxuaW2I0pbnux9h8esq2CeEjixzgqTD4JmecC3Zs="},"jquery-ui/ui-icons_cc0000_256x240-40985a64b4d5dd213fba27fcd862a1bd1b337a97674f6ff0b9ec20abcee4bc69.png":{"logical_path":"jquery-ui/ui-icons_cc0000_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"40985a64b4d5dd213fba27fcd862a1bd1b337a97674f6ff0b9ec20abcee4bc69","integrity":"sha256-QJhaZLTV3SE/uif82GKhvRszepdnT2/wuewgq87kvGk="},"jquery-ui/ui-icons_777777_256x240-faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f.png":{"logical_path":"jquery-ui/ui-icons_777777_256x240.png","mtime":"2016-12-10T10:15:35+01:00","size":3756,"digest":"faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f","integrity":"sha256-+vMgB64SDDAiE1V2JuZg3RDnEcXdTxET018m3AW3jS8="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2016-10-29T16:46:55+02:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2016-10-29T16:46:55+02:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2016-10-29T16:46:55+02:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2016-10-29T16:46:55+02:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2016-10-29T16:46:55+02:00","size":444379,"digest":"ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4","integrity":"sha256-rWFXkmwWIrpOHQPUePFUE2hSS/xG9R5C/g2UX37zI+Q="},"markers-soft-e78784e4ed70aaffddd73c315fab590233cc4e7b72388d7dd47a14796fc7c739.png":{"logical_path":"markers-soft.png","mtime":"2016-05-21T23:41:15+02:00","size":41226,"digest":"e78784e4ed70aaffddd73c315fab590233cc4e7b72388d7dd47a14796fc7c739","integrity":"sha256-54eE5O1wqv/d1zwxX6tZAjPMTntyOI191HoUeW/Hxzk="},"markers-shadow-8703a2262710f5e3d29e65d2acdf90d6512e159e119d27b8234731d8a6208a20.png":{"logical_path":"markers-shadow.png","mtime":"2016-05-21T23:41:15+02:00","size":535,"digest":"8703a2262710f5e3d29e65d2acdf90d6512e159e119d27b8234731d8a6208a20","integrity":"sha256-hwOiJicQ9ePSnmXSrN+Q1lEuFZ4RnSe4I0cx2KYgiiA="},"markers-soft@2x-c1e77253a8bfbe30cec24885d7046f443b76ebb66f4c961f77083b03f4a5cbaf.png":{"logical_path":"markers-soft@2x.png","mtime":"2016-05-21T23:41:15+02:00","size":66408,"digest":"c1e77253a8bfbe30cec24885d7046f443b76ebb66f4c961f77083b03f4a5cbaf","integrity":"sha256-wedyU6i/vjDOwkiF1wRvRDt267ZvTJYfdwg7A/Sly68="},"markers-shadow@2x-b21a536be27313fb504f69f5899ff0b1245b276571769ac08d6c32c35676e47a.png":{"logical_path":"markers-shadow@2x.png","mtime":"2016-05-21T23:41:15+02:00","size":1469,"digest":"b21a536be27313fb504f69f5899ff0b1245b276571769ac08d6c32c35676e47a","integrity":"sha256-shpTa+JzE/tQT2n1iZ/wsSRbJ2VxdprAjWwyw1Z25Ho="},"france-f4341a7ec8331161a9c8d5298f808014c3fc9c799b5a29ed95eb56a7f3ccd0df.png":{"logical_path":"france.png","mtime":"2014-07-04T14:08:56+02:00","size":276,"digest":"f4341a7ec8331161a9c8d5298f808014c3fc9c799b5a29ed95eb56a7f3ccd0df","integrity":"sha256-9DQafsgzEWGpyNUpj4CAFMP8nHmbWintletWp/PM0N8="},"quebec-776d563b6a4ac4312cae9f0bfe630c20711346e8dbddd41040998eba79f4b588.png":{"logical_path":"quebec.png","mtime":"2015-10-22T22:54:30+02:00","size":567,"digest":"776d563b6a4ac4312cae9f0bfe630c20711346e8dbddd41040998eba79f4b588","integrity":"sha256-d21WO2pKxDEsrp8L/mMMIHETRujb3dQQQJmOunn0tYg="},"belgique-3b8b772a522de2cbae7714b35a956faf2c394419b532a14bba982fed3f341091.png":{"logical_path":"belgique.png","mtime":"2014-07-04T14:08:56+02:00","size":187,"digest":"3b8b772a522de2cbae7714b35a956faf2c394419b532a14bba982fed3f341091","integrity":"sha256-O4t3KlIt4suudxSzWpVvryw5RBm1MqFLupgv7T80EJE="},"suisse-58d067f1c3fcdc4000fa13e95896cd5369a2b91aafd314475aa5e29da0b543d1.png":{"logical_path":"suisse.png","mtime":"2015-10-22T22:54:30+02:00","size":299,"digest":"58d067f1c3fcdc4000fa13e95896cd5369a2b91aafd314475aa5e29da0b543d1","integrity":"sha256-WNBn8cP83EAA+hPpWJbNU2miuRqv0xRHWqXinaC1Q9E="},"modernizr-654222debe8018b12f1993ceddff30dc163a7d5008d79869c399d6d167321f97.js":{"logical_path":"modernizr.js","mtime":"2016-10-29T16:45:47+02:00","size":51365,"digest":"654222debe8018b12f1993ceddff30dc163a7d5008d79869c399d6d167321f97","integrity":"sha256-ZUIi3r6AGLEvGZPO3f8w3BY6fVAI15hpw5nW0WcyH5c="},"agendadescommuns-cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6.png":{"logical_path":"agendadescommuns.png","mtime":"2015-10-22T22:54:30+02:00","size":2760,"digest":"cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6","integrity":"sha256-zUDjQgJL4Fh/jnoOOQLTLPZwCdNJtnwA9ofgSZ/en/Y="},"alert-762ace9479328243a44061346b64c4d6b997e963c68dfc6bddd9e4d241192906.png":{"logical_path":"alert.png","mtime":"2014-07-04T14:08:56+02:00","size":47876,"digest":"762ace9479328243a44061346b64c4d6b997e963c68dfc6bddd9e4d241192906","integrity":"sha256-dirOlHkygkOkQGE0a2TE1rmX6WPGjfxr3dnk0kEZKQY="},"baby_gnu_adl-232caf355c30740d5d9b30491887cd546b8849b33ca9bdb6cc71f8a47ea61815.png":{"logical_path":"baby_gnu_adl.png","mtime":"2016-09-11T17:42:49+02:00","size":10155,"digest":"232caf355c30740d5d9b30491887cd546b8849b33ca9bdb6cc71f8a47ea61815","integrity":"sha256-IyyvNVwwdA1dmzBJGIfNVGuISbM8qb22zHH4pH6mGBU="},"baby_gnu_adl-97251005d3225cf1d58b8c497d6b7905dbc9560cc8acd50118fcce60d0a2679e.svg":{"logical_path":"baby_gnu_adl.svg","mtime":"2016-09-11T17:42:49+02:00","size":109635,"digest":"97251005d3225cf1d58b8c497d6b7905dbc9560cc8acd50118fcce60d0a2679e","integrity":"sha256-lyUQBdMiXPHVi4xJfWt5BdvJVgzIrNUBGPzOYNCiZ54="},"communs-cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6.png":{"logical_path":"communs.png","mtime":"2015-10-22T22:54:30+02:00","size":2760,"digest":"cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6","integrity":"sha256-zUDjQgJL4Fh/jnoOOQLTLPZwCdNJtnwA9ofgSZ/en/Y="},"lef-small-160cf5b883add60c9c0f4361bd8425c75f6fb23b0e551a0b941fa0491c70e0c9.png":{"logical_path":"lef-small.png","mtime":"2015-03-29T11:07:15+02:00","size":1089,"digest":"160cf5b883add60c9c0f4361bd8425c75f6fb23b0e551a0b941fa0491c70e0c9","integrity":"sha256-Fgz1uIOt1gycD0NhvYQlx19vsjsOVRoLlB+gSRxw4Mk="},"lef-bec4081a11fbef165216827cf72c7a292ad772a77af6b8132e5bc0bbf83cb2d9.png":{"logical_path":"lef.png","mtime":"2016-01-24T11:38:05+01:00","size":8415,"digest":"bec4081a11fbef165216827cf72c7a292ad772a77af6b8132e5bc0bbf83cb2d9","integrity":"sha256-vsQIGhH77xZSFoJ89yx6KSrXcqd69rgTLlvAu/g8stk="},"priorite-logiciel-libre-je-soutiens-april_2_m-6442e454e96ed45cc1ebc40673a6c50bd286b9c28ea6a8b58572e94f7d6459fc.png":{"logical_path":"priorite-logiciel-libre-je-soutiens-april_2_m.png","mtime":"2015-10-22T22:54:30+02:00","size":16952,"digest":"6442e454e96ed45cc1ebc40673a6c50bd286b9c28ea6a8b58572e94f7d6459fc","integrity":"sha256-ZELkVOlu1FzB68QGc6bFC9KGucKOpqi1hXLpT31kWfw="},"team-cb04c7a311f7160c4eb6a281eae68be84f26991dde5d415bb4e205e6726ae275.png":{"logical_path":"team.png","mtime":"2014-07-04T14:08:56+02:00","size":3586,"digest":"cb04c7a311f7160c4eb6a281eae68be84f26991dde5d415bb4e205e6726ae275","integrity":"sha256-ywTHoxH3FgxOtqKB6uaL6E8mmR3eXUFbtOIF5nJq4nU="},"application-224c876758d567479b1ebc1da354a712a64d27a100fb0ba5b8f1c0ca5188d55c.js":{"logical_path":"application.js","mtime":"2016-12-17T16:47:53+01:00","size":2158066,"digest":"224c876758d567479b1ebc1da354a712a64d27a100fb0ba5b8f1c0ca5188d55c","integrity":"sha256-IkyHZ1jVZ0ebHrwdo1SnEqZNJ6EA+wuluPHAylGI1Vw="},"markers-matte-497826545a90e09a240504d14530eba45823b19fd44175e09e27c47cd822ddb9.png":{"logical_path":"markers-matte.png","mtime":"2016-05-21T23:41:15+02:00","size":14323,"digest":"497826545a90e09a240504d14530eba45823b19fd44175e09e27c47cd822ddb9","integrity":"sha256-SXgmVFqQ4JokBQTRRTDrpFgjsZ/UQXXgnifEfNgi3bk="},"markers-matte@2x-948fc8c4426f04f60964ed20394247f45b0b60e575d02398b9b6810e7a29a823.png":{"logical_path":"markers-matte@2x.png","mtime":"2016-05-21T23:41:15+02:00","size":31113,"digest":"948fc8c4426f04f60964ed20394247f45b0b60e575d02398b9b6810e7a29a823","integrity":"sha256-lI/IxEJvBPYJZO0gOUJH9FsLYOV10COYubaBDnopqCM="},"markers-plain-cf233423aa44e75ac0031e77b8ba571cd3331010517e1197e63fb7b06856c1ff.png":{"logical_path":"markers-plain.png","mtime":"2016-05-21T23:41:15+02:00","size":7946,"digest":"cf233423aa44e75ac0031e77b8ba571cd3331010517e1197e63fb7b06856c1ff","integrity":"sha256-zyM0I6pE51rAAx53uLpXHNMzEBBRfhGX5j+3sGhWwf8="},"jquery-ui/ui-bg_flat_0_aaaaaa_40x100-ae65a7ae22c4c23115948fdeb5c05c9137dbd13ca2d426b3c4c3c4183451e410.png":{"logical_path":"jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png","mtime":"2016-12-10T10:15:35+01:00","size":86,"digest":"ae65a7ae22c4c23115948fdeb5c05c9137dbd13ca2d426b3c4c3c4183451e410","integrity":"sha256-rmWnriLEwjEVlI/etcBckTfb0Tyi1CazxMPEGDRR5BA="},"tinymce-a6d3b77f66eb3e1971dc7bd18d9cefefd2947a846683d2a189a092f94280acf4.js":{"logical_path":"tinymce.js","mtime":"2016-12-17T22:42:28+01:00","size":971,"digest":"a6d3b77f66eb3e1971dc7bd18d9cefefd2947a846683d2a189a092f94280acf4","integrity":"sha256-ptO3f2brPhlx3HvRjZzv79KUeoRmg9KhiaCS+UKArPQ="},"application-6054b0bc1c370f1c7d66e9d0c3d21aa9fa886547e9c7732570440a4f42618a99.css":{"logical_path":"application.css","mtime":"2016-12-17T22:42:28+01:00","size":170862,"digest":"6054b0bc1c370f1c7d66e9d0c3d21aa9fa886547e9c7732570440a4f42618a99","integrity":"sha256-YFSwvBw3Dxx9ZunQw9IaqfqIZUfpx3MlcEQKT0Jhipk="},"application-97155f8afdbec83bd10d2feeaf475014a2d2efa855a16491aa7b922d8c1cbf58.js":{"logical_path":"application.js","mtime":"2016-12-17T22:42:28+01:00","size":2158066,"digest":"97155f8afdbec83bd10d2feeaf475014a2d2efa855a16491aa7b922d8c1cbf58","integrity":"sha256-lxVfiv2+yDvRDS/ur0dQFKLS76hVoWSRqnuSLYwcv1g="},"tinymce-7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa.js":{"logical_path":"tinymce.js","mtime":"2016-12-17T22:48:39+01:00","size":971,"digest":"7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa","integrity":"sha256-ckNUnFNf5AGntpu1l6J3ylIEkf6C4/bvtrgA9gXvVvo="},"application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css":{"logical_path":"application.css","mtime":"2016-12-17T22:48:39+01:00","size":170862,"digest":"1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937","integrity":"sha256-HqD4gxilh+bKIJlNIcnbRicZVDS8yKUvWedb4htQ+Tc="},"application-ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd.js":{"logical_path":"application.js","mtime":"2016-12-17T22:48:39+01:00","size":2158066,"digest":"ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd","integrity":"sha256-7Jf/1DvMVl3Fqr2Kbhw8dbJDf2snJl9WwvqY63KGa80="},"tinymce/plugins/emoticons/img/smiley-cool.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-cool.gif","mtime":"2016-12-10T10:15:36+01:00","size":354,"digest":null},"tinymce/plugins/emoticons/img/smiley-cry.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-cry.gif","mtime":"2016-12-10T10:15:36+01:00","size":329,"digest":null},"tinymce/plugins/emoticons/img/smiley-embarassed.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-embarassed.gif","mtime":"2016-12-10T10:15:36+01:00","size":331,"digest":null},"tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif","mtime":"2016-12-10T10:15:36+01:00","size":342,"digest":null},"tinymce/plugins/emoticons/img/smiley-frown.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-frown.gif","mtime":"2016-12-10T10:15:36+01:00","size":340,"digest":null},"tinymce/plugins/emoticons/img/smiley-innocent.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-innocent.gif","mtime":"2016-12-10T10:15:36+01:00","size":336,"digest":null},"tinymce/plugins/emoticons/img/smiley-kiss.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-kiss.gif","mtime":"2016-12-10T10:15:36+01:00","size":338,"digest":null},"tinymce/plugins/emoticons/img/smiley-laughing.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-laughing.gif","mtime":"2016-12-10T10:15:36+01:00","size":343,"digest":null},"tinymce/plugins/emoticons/img/smiley-money-mouth.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-money-mouth.gif","mtime":"2016-12-10T10:15:36+01:00","size":321,"digest":null},"tinymce/plugins/emoticons/img/smiley-sealed.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-sealed.gif","mtime":"2016-12-10T10:15:36+01:00","size":323,"digest":null},"tinymce/plugins/emoticons/img/smiley-smile.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-smile.gif","mtime":"2016-12-10T10:15:36+01:00","size":344,"digest":null},"tinymce/plugins/emoticons/img/smiley-surprised.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-surprised.gif","mtime":"2016-12-10T10:15:36+01:00","size":338,"digest":null},"tinymce/plugins/emoticons/img/smiley-tongue-out.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-tongue-out.gif","mtime":"2016-12-10T10:15:36+01:00","size":328,"digest":null},"tinymce/plugins/emoticons/img/smiley-undecided.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-undecided.gif","mtime":"2016-12-10T10:15:36+01:00","size":337,"digest":null},"tinymce/plugins/emoticons/img/smiley-wink.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-wink.gif","mtime":"2016-12-10T10:15:36+01:00","size":350,"digest":null},"tinymce/plugins/emoticons/img/smiley-yell.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-yell.gif","mtime":"2016-12-10T10:15:36+01:00","size":336,"digest":null},"tinymce/skins/lightgray/img/anchor.gif":{"logical_path":"tinymce/skins/lightgray/img/anchor.gif","mtime":"2016-12-10T10:15:36+01:00","size":53,"digest":null},"tinymce/skins/lightgray/img/loader.gif":{"logical_path":"tinymce/skins/lightgray/img/loader.gif","mtime":"2016-12-10T10:15:36+01:00","size":2608,"digest":null},"tinymce/skins/lightgray/img/object.gif":{"logical_path":"tinymce/skins/lightgray/img/object.gif","mtime":"2016-12-10T10:15:36+01:00","size":152,"digest":null},"tinymce/skins/lightgray/img/trans.gif":{"logical_path":"tinymce/skins/lightgray/img/trans.gif","mtime":"2016-12-10T10:15:36+01:00","size":43,"digest":null},"tinymce/jquery.tinymce.js":{"logical_path":"tinymce/jquery.tinymce.js","mtime":"2016-12-10T10:15:36+01:00","size":3591,"digest":null},"tinymce/langs/readme.md":{"logical_path":"tinymce/langs/readme.md","mtime":"2016-12-10T10:15:36+01:00","size":151,"digest":null},"tinymce/license.txt":{"logical_path":"tinymce/license.txt","mtime":"2016-12-10T10:15:36+01:00","size":26427,"digest":null},"tinymce/plugins/advlist/plugin.js":{"logical_path":"tinymce/plugins/advlist/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2048,"digest":null},"tinymce/plugins/anchor/plugin.js":{"logical_path":"tinymce/plugins/anchor/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":957,"digest":null},"tinymce/plugins/autolink/plugin.js":{"logical_path":"tinymce/plugins/autolink/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2060,"digest":null},"tinymce/plugins/autoresize/plugin.js":{"logical_path":"tinymce/plugins/autoresize/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1903,"digest":null},"tinymce/plugins/autosave/plugin.js":{"logical_path":"tinymce/plugins/autosave/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2187,"digest":null},"tinymce/plugins/bbcode/plugin.js":{"logical_path":"tinymce/plugins/bbcode/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":3136,"digest":null},"tinymce/plugins/charmap/plugin.js":{"logical_path":"tinymce/plugins/charmap/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":8199,"digest":null},"tinymce/plugins/code/plugin.js":{"logical_path":"tinymce/plugins/code/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":721,"digest":null},"tinymce/plugins/codesample/css/prism.css":{"logical_path":"tinymce/plugins/codesample/css/prism.css","mtime":"2016-12-10T10:15:36+01:00","size":2289,"digest":null},"tinymce/plugins/codesample/plugin.dev.js":{"logical_path":"tinymce/plugins/codesample/plugin.dev.js","mtime":"2016-12-10T10:15:36+01:00","size":3168,"digest":null},"tinymce/plugins/codesample/plugin.js":{"logical_path":"tinymce/plugins/codesample/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":20264,"digest":null},"tinymce/plugins/colorpicker/plugin.js":{"logical_path":"tinymce/plugins/colorpicker/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1222,"digest":null},"tinymce/plugins/contextmenu/plugin.js":{"logical_path":"tinymce/plugins/contextmenu/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1131,"digest":null},"tinymce/plugins/directionality/plugin.js":{"logical_path":"tinymce/plugins/directionality/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":726,"digest":null},"tinymce/plugins/emoticons/plugin.js":{"logical_path":"tinymce/plugins/emoticons/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":911,"digest":null},"tinymce/plugins/example/dialog.html":{"logical_path":"tinymce/plugins/example/dialog.html","mtime":"2016-12-10T10:15:36+01:00","size":213,"digest":null},"tinymce/plugins/example/plugin.js":{"logical_path":"tinymce/plugins/example/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":658,"digest":null},"tinymce/plugins/example_dependency/plugin.js":{"logical_path":"tinymce/plugins/example_dependency/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":73,"digest":null},"tinymce/plugins/fullpage/plugin.js":{"logical_path":"tinymce/plugins/fullpage/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":6308,"digest":null},"tinymce/plugins/fullscreen/plugin.js":{"logical_path":"tinymce/plugins/fullscreen/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1675,"digest":null},"tinymce/plugins/hr/plugin.js":{"logical_path":"tinymce/plugins/hr/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":322,"digest":null},"tinymce/plugins/image/plugin.js":{"logical_path":"tinymce/plugins/image/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":8195,"digest":null},"tinymce/plugins/imagetools/plugin.js":{"logical_path":"tinymce/plugins/imagetools/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":30900,"digest":null},"tinymce/plugins/importcss/plugin.js":{"logical_path":"tinymce/plugins/importcss/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2746,"digest":null},"tinymce/plugins/insertdatetime/plugin.js":{"logical_path":"tinymce/plugins/insertdatetime/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1971,"digest":null},"tinymce/plugins/layer/plugin.js":{"logical_path":"tinymce/plugins/layer/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2857,"digest":null},"tinymce/plugins/legacyoutput/plugin.js":{"logical_path":"tinymce/plugins/legacyoutput/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":3263,"digest":null},"tinymce/plugins/link/plugin.js":{"logical_path":"tinymce/plugins/link/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":6903,"digest":null},"tinymce/plugins/lists/plugin.js":{"logical_path":"tinymce/plugins/lists/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":10331,"digest":null},"tinymce/plugins/media/plugin.js":{"logical_path":"tinymce/plugins/media/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":14999,"digest":null},"tinymce/plugins/nonbreaking/plugin.js":{"logical_path":"tinymce/plugins/nonbreaking/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":655,"digest":null},"tinymce/plugins/noneditable/plugin.js":{"logical_path":"tinymce/plugins/noneditable/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1270,"digest":null},"tinymce/plugins/pagebreak/plugin.js":{"logical_path":"tinymce/plugins/pagebreak/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1230,"digest":null},"tinymce/plugins/paste/plugin.dev.js":{"logical_path":"tinymce/plugins/paste/plugin.dev.js","mtime":"2016-12-10T10:15:36+01:00","size":3130,"digest":null},"tinymce/plugins/paste/plugin.js":{"logical_path":"tinymce/plugins/paste/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":17676,"digest":null},"tinymce/plugins/preview/plugin.js":{"logical_path":"tinymce/plugins/preview/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1602,"digest":null},"tinymce/plugins/print/plugin.js":{"logical_path":"tinymce/plugins/print/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":293,"digest":null},"tinymce/plugins/save/plugin.js":{"logical_path":"tinymce/plugins/save/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1150,"digest":null},"tinymce/plugins/searchreplace/plugin.js":{"logical_path":"tinymce/plugins/searchreplace/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":6493,"digest":null},"tinymce/plugins/spellchecker/plugin.dev.js":{"logical_path":"tinymce/plugins/spellchecker/plugin.dev.js","mtime":"2016-12-10T10:15:36+01:00","size":3032,"digest":null},"tinymce/plugins/spellchecker/plugin.js":{"logical_path":"tinymce/plugins/spellchecker/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":10044,"digest":null},"tinymce/plugins/tabfocus/plugin.js":{"logical_path":"tinymce/plugins/tabfocus/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1264,"digest":null},"tinymce/plugins/table/plugin.dev.js":{"logical_path":"tinymce/plugins/table/plugin.dev.js","mtime":"2016-12-10T10:15:36+01:00","size":3152,"digest":null},"tinymce/plugins/table/plugin.js":{"logical_path":"tinymce/plugins/table/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":45825,"digest":null},"tinymce/plugins/template/plugin.js":{"logical_path":"tinymce/plugins/template/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":4520,"digest":null},"tinymce/plugins/textcolor/plugin.js":{"logical_path":"tinymce/plugins/textcolor/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":4145,"digest":null},"tinymce/plugins/textpattern/plugin.js":{"logical_path":"tinymce/plugins/textpattern/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2745,"digest":null},"tinymce/plugins/toc/plugin.js":{"logical_path":"tinymce/plugins/toc/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":2771,"digest":null},"tinymce/plugins/visualblocks/css/visualblocks.css":{"logical_path":"tinymce/plugins/visualblocks/css/visualblocks.css","mtime":"2016-12-10T10:15:36+01:00","size":5092,"digest":null},"tinymce/plugins/visualblocks/plugin.js":{"logical_path":"tinymce/plugins/visualblocks/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1153,"digest":null},"tinymce/plugins/visualchars/plugin.js":{"logical_path":"tinymce/plugins/visualchars/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":1178,"digest":null},"tinymce/plugins/wordcount/plugin.js":{"logical_path":"tinymce/plugins/wordcount/plugin.js","mtime":"2016-12-10T10:15:36+01:00","size":11674,"digest":null},"tinymce/skins/lightgray/AbsoluteLayout.less":{"logical_path":"tinymce/skins/lightgray/AbsoluteLayout.less","mtime":"2016-12-10T10:15:36+01:00","size":265,"digest":null},"tinymce/skins/lightgray/Animations.less":{"logical_path":"tinymce/skins/lightgray/Animations.less","mtime":"2016-12-10T10:15:36+01:00","size":119,"digest":null},"tinymce/skins/lightgray/Arrows.less":{"logical_path":"tinymce/skins/lightgray/Arrows.less","mtime":"2016-12-10T10:15:36+01:00","size":2437,"digest":null},"tinymce/skins/lightgray/Button.less":{"logical_path":"tinymce/skins/lightgray/Button.less","mtime":"2016-12-10T10:15:36+01:00","size":3853,"digest":null},"tinymce/skins/lightgray/ButtonGroup.less":{"logical_path":"tinymce/skins/lightgray/ButtonGroup.less","mtime":"2016-12-10T10:15:36+01:00","size":1681,"digest":null},"tinymce/skins/lightgray/Checkbox.less":{"logical_path":"tinymce/skins/lightgray/Checkbox.less","mtime":"2016-12-10T10:15:36+01:00","size":1096,"digest":null},"tinymce/skins/lightgray/ColorBox.less":{"logical_path":"tinymce/skins/lightgray/ColorBox.less","mtime":"2016-12-10T10:15:36+01:00","size":103,"digest":null},"tinymce/skins/lightgray/ColorButton.less":{"logical_path":"tinymce/skins/lightgray/ColorButton.less","mtime":"2016-12-10T10:15:36+01:00","size":1301,"digest":null},"tinymce/skins/lightgray/ColorPicker.less":{"logical_path":"tinymce/skins/lightgray/ColorPicker.less","mtime":"2016-12-10T10:15:36+01:00","size":1864,"digest":null},"tinymce/skins/lightgray/ComboBox.less":{"logical_path":"tinymce/skins/lightgray/ComboBox.less","mtime":"2016-12-10T10:15:36+01:00","size":1729,"digest":null},"tinymce/skins/lightgray/Container.less":{"logical_path":"tinymce/skins/lightgray/Container.less","mtime":"2016-12-10T10:15:36+01:00","size":129,"digest":null},"tinymce/skins/lightgray/Content.Inline.less":{"logical_path":"tinymce/skins/lightgray/Content.Inline.less","mtime":"2016-12-10T10:15:36+01:00","size":82,"digest":null},"tinymce/skins/lightgray/Content.Objects.less":{"logical_path":"tinymce/skins/lightgray/Content.Objects.less","mtime":"2016-12-10T10:15:36+01:00","size":3210,"digest":null},"tinymce/skins/lightgray/Content.less":{"logical_path":"tinymce/skins/lightgray/Content.less","mtime":"2016-12-10T10:15:36+01:00","size":592,"digest":null},"tinymce/skins/lightgray/CropRect.less":{"logical_path":"tinymce/skins/lightgray/CropRect.less","mtime":"2016-12-10T10:15:36+01:00","size":1108,"digest":null},"tinymce/skins/lightgray/FieldSet.less":{"logical_path":"tinymce/skins/lightgray/FieldSet.less","mtime":"2016-12-10T10:15:36+01:00","size":233,"digest":null},"tinymce/skins/lightgray/FitLayout.less":{"logical_path":"tinymce/skins/lightgray/FitLayout.less","mtime":"2016-12-10T10:15:36+01:00","size":111,"digest":null},"tinymce/skins/lightgray/FloatPanel.less":{"logical_path":"tinymce/skins/lightgray/FloatPanel.less","mtime":"2016-12-10T10:15:36+01:00","size":1477,"digest":null},"tinymce/skins/lightgray/FlowLayout.less":{"logical_path":"tinymce/skins/lightgray/FlowLayout.less","mtime":"2016-12-10T10:15:36+01:00","size":694,"digest":null},"tinymce/skins/lightgray/Icons.Ie7.less":{"logical_path":"tinymce/skins/lightgray/Icons.Ie7.less","mtime":"2016-12-10T10:15:36+01:00","size":5621,"digest":null},"tinymce/skins/lightgray/Icons.less":{"logical_path":"tinymce/skins/lightgray/Icons.less","mtime":"2016-12-10T10:15:36+01:00","size":9372,"digest":null},"tinymce/skins/lightgray/Iframe.less":{"logical_path":"tinymce/skins/lightgray/Iframe.less","mtime":"2016-12-10T10:15:36+01:00","size":94,"digest":null},"tinymce/skins/lightgray/ImagePanel.less":{"logical_path":"tinymce/skins/lightgray/ImagePanel.less","mtime":"2016-12-10T10:15:36+01:00","size":476,"digest":null},"tinymce/skins/lightgray/InfoBox.less":{"logical_path":"tinymce/skins/lightgray/InfoBox.less","mtime":"2016-12-10T10:15:36+01:00","size":1087,"digest":null},"tinymce/skins/lightgray/Label.less":{"logical_path":"tinymce/skins/lightgray/Label.less","mtime":"2016-12-10T10:15:36+01:00","size":554,"digest":null},"tinymce/skins/lightgray/ListBox.less":{"logical_path":"tinymce/skins/lightgray/ListBox.less","mtime":"2016-12-10T10:15:36+01:00","size":388,"digest":null},"tinymce/skins/lightgray/Menu.less":{"logical_path":"tinymce/skins/lightgray/Menu.less","mtime":"2016-12-10T10:15:36+01:00","size":821,"digest":null},"tinymce/skins/lightgray/MenuBar.less":{"logical_path":"tinymce/skins/lightgray/MenuBar.less","mtime":"2016-12-10T10:15:36+01:00","size":689,"digest":null},"tinymce/skins/lightgray/MenuButton.less":{"logical_path":"tinymce/skins/lightgray/MenuButton.less","mtime":"2016-12-10T10:15:36+01:00","size":607,"digest":null},"tinymce/skins/lightgray/MenuItem.less":{"logical_path":"tinymce/skins/lightgray/MenuItem.less","mtime":"2016-12-10T10:15:36+01:00","size":4782,"digest":null},"tinymce/skins/lightgray/Mixins.less":{"logical_path":"tinymce/skins/lightgray/Mixins.less","mtime":"2016-12-10T10:15:36+01:00","size":1799,"digest":null},"tinymce/skins/lightgray/Notification.less":{"logical_path":"tinymce/skins/lightgray/Notification.less","mtime":"2016-12-10T10:15:36+01:00","size":3628,"digest":null},"tinymce/skins/lightgray/Panel.less":{"logical_path":"tinymce/skins/lightgray/Panel.less","mtime":"2016-12-10T10:15:36+01:00","size":220,"digest":null},"tinymce/skins/lightgray/Path.less":{"logical_path":"tinymce/skins/lightgray/Path.less","mtime":"2016-12-10T10:15:36+01:00","size":650,"digest":null},"tinymce/skins/lightgray/Progress.less":{"logical_path":"tinymce/skins/lightgray/Progress.less","mtime":"2016-12-10T10:15:36+01:00","size":619,"digest":null},"tinymce/skins/lightgray/Radio.less":{"logical_path":"tinymce/skins/lightgray/Radio.less","mtime":"2016-12-10T10:15:36+01:00","size":31,"digest":null},"tinymce/skins/lightgray/Reset.less":{"logical_path":"tinymce/skins/lightgray/Reset.less","mtime":"2016-12-10T10:15:36+01:00","size":905,"digest":null},"tinymce/skins/lightgray/ResizeHandle.less":{"logical_path":"tinymce/skins/lightgray/ResizeHandle.less","mtime":"2016-12-10T10:15:36+01:00","size":301,"digest":null},"tinymce/skins/lightgray/Scrollable.less":{"logical_path":"tinymce/skins/lightgray/Scrollable.less","mtime":"2016-12-10T10:15:36+01:00","size":687,"digest":null},"tinymce/skins/lightgray/SelectBox.less":{"logical_path":"tinymce/skins/lightgray/SelectBox.less","mtime":"2016-12-10T10:15:36+01:00","size":105,"digest":null},"tinymce/skins/lightgray/Sidebar.less":{"logical_path":"tinymce/skins/lightgray/Sidebar.less","mtime":"2016-12-10T10:15:36+01:00","size":956,"digest":null},"tinymce/skins/lightgray/Slider.less":{"logical_path":"tinymce/skins/lightgray/Slider.less","mtime":"2016-12-10T10:15:36+01:00","size":579,"digest":null},"tinymce/skins/lightgray/Spacer.less":{"logical_path":"tinymce/skins/lightgray/Spacer.less","mtime":"2016-12-10T10:15:36+01:00","size":54,"digest":null},"tinymce/skins/lightgray/SplitButton.less":{"logical_path":"tinymce/skins/lightgray/SplitButton.less","mtime":"2016-12-10T10:15:36+01:00","size":980,"digest":null},"tinymce/skins/lightgray/StackLayout.less":{"logical_path":"tinymce/skins/lightgray/StackLayout.less","mtime":"2016-12-10T10:15:36+01:00","size":67,"digest":null},"tinymce/skins/lightgray/TabPanel.less":{"logical_path":"tinymce/skins/lightgray/TabPanel.less","mtime":"2016-12-10T10:15:36+01:00","size":706,"digest":null},"tinymce/skins/lightgray/TextBox.less":{"logical_path":"tinymce/skins/lightgray/TextBox.less","mtime":"2016-12-10T10:15:36+01:00","size":870,"digest":null},"tinymce/skins/lightgray/Throbber.less":{"logical_path":"tinymce/skins/lightgray/Throbber.less","mtime":"2016-12-10T10:15:36+01:00","size":303,"digest":null},"tinymce/skins/lightgray/TinyMCE.less":{"logical_path":"tinymce/skins/lightgray/TinyMCE.less","mtime":"2016-12-10T10:15:36+01:00","size":2449,"digest":null},"tinymce/skins/lightgray/ToolTip.less":{"logical_path":"tinymce/skins/lightgray/ToolTip.less","mtime":"2016-12-10T10:15:36+01:00","size":2533,"digest":null},"tinymce/skins/lightgray/Variables.less":{"logical_path":"tinymce/skins/lightgray/Variables.less","mtime":"2016-12-10T10:15:36+01:00","size":8371,"digest":null},"tinymce/skins/lightgray/Window.less":{"logical_path":"tinymce/skins/lightgray/Window.less","mtime":"2016-12-10T10:15:36+01:00","size":2217,"digest":null},"tinymce/skins/lightgray/content.inline.min.css":{"logical_path":"tinymce/skins/lightgray/content.inline.min.css","mtime":"2016-12-10T10:15:36+01:00","size":2769,"digest":null},"tinymce/skins/lightgray/content.min.css":{"logical_path":"tinymce/skins/lightgray/content.min.css","mtime":"2016-12-10T10:15:36+01:00","size":3193,"digest":null},"tinymce/skins/lightgray/fonts/tinymce-small.eot":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.eot","mtime":"2016-12-10T10:15:36+01:00","size":9492,"digest":null},"tinymce/skins/lightgray/fonts/tinymce-small.svg":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.svg","mtime":"2016-12-10T10:15:36+01:00","size":24727,"digest":null},"tinymce/skins/lightgray/fonts/tinymce-small.ttf":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.ttf","mtime":"2016-12-10T10:15:36+01:00","size":9304,"digest":null},"tinymce/skins/lightgray/fonts/tinymce-small.woff":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.woff","mtime":"2016-12-10T10:15:36+01:00","size":9380,"digest":null},"tinymce/skins/lightgray/fonts/tinymce.eot":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.eot","mtime":"2016-12-10T10:15:36+01:00","size":17572,"digest":null},"tinymce/skins/lightgray/fonts/tinymce.svg":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.svg","mtime":"2016-12-10T10:15:36+01:00","size":45991,"digest":null},"tinymce/skins/lightgray/fonts/tinymce.ttf":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.ttf","mtime":"2016-12-10T10:15:36+01:00","size":17408,"digest":null},"tinymce/skins/lightgray/fonts/tinymce.woff":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.woff","mtime":"2016-12-10T10:15:36+01:00","size":17484,"digest":null},"tinymce/skins/lightgray/skin.dev.less":{"logical_path":"tinymce/skins/lightgray/skin.dev.less","mtime":"2016-12-10T10:15:36+01:00","size":1201,"digest":null},"tinymce/skins/lightgray/skin.ie7.dev.less":{"logical_path":"tinymce/skins/lightgray/skin.ie7.dev.less","mtime":"2016-12-10T10:15:36+01:00","size":1181,"digest":null},"tinymce/skins/lightgray/skin.ie7.less":{"logical_path":"tinymce/skins/lightgray/skin.ie7.less","mtime":"2016-12-10T10:15:36+01:00","size":61487,"digest":null},"tinymce/skins/lightgray/skin.ie7.min.css":{"logical_path":"tinymce/skins/lightgray/skin.ie7.min.css","mtime":"2016-12-10T10:15:36+01:00","size":34904,"digest":null},"tinymce/skins/lightgray/skin.less":{"logical_path":"tinymce/skins/lightgray/skin.less","mtime":"2016-12-10T10:15:36+01:00","size":66196,"digest":null},"tinymce/skins/lightgray/skin.min.css":{"logical_path":"tinymce/skins/lightgray/skin.min.css","mtime":"2016-12-10T10:15:36+01:00","size":38232,"digest":null},"tinymce/themes/inlite/theme.js":{"logical_path":"tinymce/themes/inlite/theme.js","mtime":"2016-12-10T10:15:36+01:00","size":16364,"digest":null},"tinymce/themes/modern/theme.js":{"logical_path":"tinymce/themes/modern/theme.js","mtime":"2016-12-10T10:15:36+01:00","size":13146,"digest":null},"tinymce/tinymce.js":{"logical_path":"tinymce/tinymce.js","mtime":"2016-12-10T10:15:36+01:00","size":401179,"digest":null},"tinymce/langs/ar.js":{"logical_path":"tinymce/langs/ar.js","mtime":"2016-10-29T16:47:26+02:00","size":17775,"digest":null},"tinymce/langs/ar_SA.js":{"logical_path":"tinymce/langs/ar_SA.js","mtime":"2016-10-29T16:47:26+02:00","size":10002,"digest":null},"tinymce/langs/az.js":{"logical_path":"tinymce/langs/az.js","mtime":"2016-10-29T16:47:26+02:00","size":9679,"digest":null},"tinymce/langs/be.js":{"logical_path":"tinymce/langs/be.js","mtime":"2016-10-29T16:47:26+02:00","size":21063,"digest":null},"tinymce/langs/bg_BG.js":{"logical_path":"tinymce/langs/bg_BG.js","mtime":"2016-10-29T16:47:26+02:00","size":23944,"digest":null},"tinymce/langs/bn_BD.js":{"logical_path":"tinymce/langs/bn_BD.js","mtime":"2016-10-29T16:47:26+02:00","size":6845,"digest":null},"tinymce/langs/bs.js":{"logical_path":"tinymce/langs/bs.js","mtime":"2016-10-29T16:47:26+02:00","size":6969,"digest":null},"tinymce/langs/ca.js":{"logical_path":"tinymce/langs/ca.js","mtime":"2016-10-29T16:47:26+02:00","size":7786,"digest":null},"tinymce/langs/cs.js":{"logical_path":"tinymce/langs/cs.js","mtime":"2016-10-29T16:47:26+02:00","size":8750,"digest":null},"tinymce/langs/cs_CZ.js":{"logical_path":"tinymce/langs/cs_CZ.js","mtime":"2016-10-29T16:47:26+02:00","size":8398,"digest":null},"tinymce/langs/cy.js":{"logical_path":"tinymce/langs/cy.js","mtime":"2016-10-29T16:47:26+02:00","size":6141,"digest":null},"tinymce/langs/da.js":{"logical_path":"tinymce/langs/da.js","mtime":"2016-10-29T16:47:26+02:00","size":7512,"digest":null},"tinymce/langs/de.js":{"logical_path":"tinymce/langs/de.js","mtime":"2016-10-29T16:47:26+02:00","size":8279,"digest":null},"tinymce/langs/de_AT.js":{"logical_path":"tinymce/langs/de_AT.js","mtime":"2016-10-29T16:47:26+02:00","size":8307,"digest":null},"tinymce/langs/dv.js":{"logical_path":"tinymce/langs/dv.js","mtime":"2016-10-29T16:47:26+02:00","size":18099,"digest":null},"tinymce/langs/el.js":{"logical_path":"tinymce/langs/el.js","mtime":"2016-10-29T16:47:26+02:00","size":22972,"digest":null},"tinymce/langs/en_CA.js":{"logical_path":"tinymce/langs/en_CA.js","mtime":"2016-10-29T16:47:26+02:00","size":6915,"digest":null},"tinymce/langs/en_GB.js":{"logical_path":"tinymce/langs/en_GB.js","mtime":"2016-10-29T16:47:26+02:00","size":5906,"digest":null},"tinymce/langs/eo.js":{"logical_path":"tinymce/langs/eo.js","mtime":"2016-10-29T16:47:26+02:00","size":7465,"digest":null},"tinymce/langs/es.js":{"logical_path":"tinymce/langs/es.js","mtime":"2016-10-29T16:47:26+02:00","size":7808,"digest":null},"tinymce/langs/es_MX.js":{"logical_path":"tinymce/langs/es_MX.js","mtime":"2016-10-29T16:47:26+02:00","size":7893,"digest":null},"tinymce/langs/et.js":{"logical_path":"tinymce/langs/et.js","mtime":"2016-10-29T16:47:26+02:00","size":7783,"digest":null},"tinymce/langs/eu.js":{"logical_path":"tinymce/langs/eu.js","mtime":"2016-10-29T16:47:26+02:00","size":7010,"digest":null},"tinymce/langs/fa.js":{"logical_path":"tinymce/langs/fa.js","mtime":"2016-10-29T16:47:26+02:00","size":16461,"digest":null},"tinymce/langs/fa_IR.js":{"logical_path":"tinymce/langs/fa_IR.js","mtime":"2016-10-29T16:47:26+02:00","size":17724,"digest":null},"tinymce/langs/fi.js":{"logical_path":"tinymce/langs/fi.js","mtime":"2016-10-29T16:47:26+02:00","size":8142,"digest":null},"tinymce/langs/fo.js":{"logical_path":"tinymce/langs/fo.js","mtime":"2016-10-29T16:47:26+02:00","size":8043,"digest":null},"tinymce/langs/fr_CH.js":{"logical_path":"tinymce/langs/fr_CH.js","mtime":"2016-10-29T16:47:26+02:00","size":8073,"digest":null},"tinymce/langs/fr_FR.js":{"logical_path":"tinymce/langs/fr_FR.js","mtime":"2016-10-29T16:47:26+02:00","size":8008,"digest":null},"tinymce/langs/ga.js":{"logical_path":"tinymce/langs/ga.js","mtime":"2016-10-29T16:47:26+02:00","size":8590,"digest":null},"tinymce/langs/gd.js":{"logical_path":"tinymce/langs/gd.js","mtime":"2016-10-29T16:47:26+02:00","size":8807,"digest":null},"tinymce/langs/gl.js":{"logical_path":"tinymce/langs/gl.js","mtime":"2016-10-29T16:47:26+02:00","size":7065,"digest":null},"tinymce/langs/he_IL.js":{"logical_path":"tinymce/langs/he_IL.js","mtime":"2016-10-29T16:47:26+02:00","size":16080,"digest":null},"tinymce/langs/hi_IN.js":{"logical_path":"tinymce/langs/hi_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":18457,"digest":null},"tinymce/langs/hr.js":{"logical_path":"tinymce/langs/hr.js","mtime":"2016-10-29T16:47:26+02:00","size":7576,"digest":null},"tinymce/langs/hu_HU.js":{"logical_path":"tinymce/langs/hu_HU.js","mtime":"2016-10-29T16:47:26+02:00","size":9409,"digest":null},"tinymce/langs/hy.js":{"logical_path":"tinymce/langs/hy.js","mtime":"2016-10-29T16:47:26+02:00","size":20374,"digest":null},"tinymce/langs/id.js":{"logical_path":"tinymce/langs/id.js","mtime":"2016-10-29T16:47:26+02:00","size":7140,"digest":null},"tinymce/langs/is_IS.js":{"logical_path":"tinymce/langs/is_IS.js","mtime":"2016-10-29T16:47:26+02:00","size":7927,"digest":null},"tinymce/langs/it.js":{"logical_path":"tinymce/langs/it.js","mtime":"2016-10-29T16:47:26+02:00","size":7597,"digest":null},"tinymce/langs/ja.js":{"logical_path":"tinymce/langs/ja.js","mtime":"2016-10-29T16:47:26+02:00","size":12289,"digest":null},"tinymce/langs/ka_GE.js":{"logical_path":"tinymce/langs/ka_GE.js","mtime":"2016-10-29T16:47:26+02:00","size":21283,"digest":null},"tinymce/langs/kab.js":{"logical_path":"tinymce/langs/kab.js","mtime":"2016-10-29T16:47:26+02:00","size":7390,"digest":null},"tinymce/langs/kk.js":{"logical_path":"tinymce/langs/kk.js","mtime":"2016-10-29T16:47:26+02:00","size":16680,"digest":null},"tinymce/langs/km_KH.js":{"logical_path":"tinymce/langs/km_KH.js","mtime":"2016-10-29T16:47:26+02:00","size":21395,"digest":null},"tinymce/langs/ko.js":{"logical_path":"tinymce/langs/ko.js","mtime":"2016-10-29T16:47:26+02:00","size":10104,"digest":null},"tinymce/langs/ko_KR.js":{"logical_path":"tinymce/langs/ko_KR.js","mtime":"2016-10-29T16:47:26+02:00","size":10004,"digest":null},"tinymce/langs/ku.js":{"logical_path":"tinymce/langs/ku.js","mtime":"2016-10-29T16:47:26+02:00","size":18273,"digest":null},"tinymce/langs/ku_IQ.js":{"logical_path":"tinymce/langs/ku_IQ.js","mtime":"2016-10-29T16:47:26+02:00","size":18363,"digest":null},"tinymce/langs/lb.js":{"logical_path":"tinymce/langs/lb.js","mtime":"2016-10-29T16:47:26+02:00","size":7466,"digest":null},"tinymce/langs/lt.js":{"logical_path":"tinymce/langs/lt.js","mtime":"2016-10-29T16:47:26+02:00","size":8898,"digest":null},"tinymce/langs/lv.js":{"logical_path":"tinymce/langs/lv.js","mtime":"2016-10-29T16:47:26+02:00","size":8281,"digest":null},"tinymce/langs/mk_MK.js":{"logical_path":"tinymce/langs/mk_MK.js","mtime":"2016-10-29T16:47:26+02:00","size":18732,"digest":null},"tinymce/langs/ml.js":{"logical_path":"tinymce/langs/ml.js","mtime":"2016-10-29T16:47:26+02:00","size":7338,"digest":null},"tinymce/langs/ml_IN.js":{"logical_path":"tinymce/langs/ml_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":20018,"digest":null},"tinymce/langs/mn_MN.js":{"logical_path":"tinymce/langs/mn_MN.js","mtime":"2016-10-29T16:47:26+02:00","size":6876,"digest":null},"tinymce/langs/nb_NO.js":{"logical_path":"tinymce/langs/nb_NO.js","mtime":"2016-10-29T16:47:26+02:00","size":7527,"digest":null},"tinymce/langs/nl.js":{"logical_path":"tinymce/langs/nl.js","mtime":"2016-10-29T16:47:26+02:00","size":7234,"digest":null},"tinymce/langs/pl.js":{"logical_path":"tinymce/langs/pl.js","mtime":"2016-10-29T16:47:26+02:00","size":8128,"digest":null},"tinymce/langs/pt_BR.js":{"logical_path":"tinymce/langs/pt_BR.js","mtime":"2016-10-29T16:47:26+02:00","size":8001,"digest":null},"tinymce/langs/pt_PT.js":{"logical_path":"tinymce/langs/pt_PT.js","mtime":"2016-10-29T16:47:26+02:00","size":8144,"digest":null},"tinymce/langs/ro.js":{"logical_path":"tinymce/langs/ro.js","mtime":"2016-10-29T16:47:26+02:00","size":8274,"digest":null},"tinymce/langs/ru.js":{"logical_path":"tinymce/langs/ru.js","mtime":"2016-10-29T16:47:26+02:00","size":22289,"digest":null},"tinymce/langs/ru_RU.js":{"logical_path":"tinymce/langs/ru_RU.js","mtime":"2016-10-29T16:47:26+02:00","size":4954,"digest":null},"tinymce/langs/si_LK.js":{"logical_path":"tinymce/langs/si_LK.js","mtime":"2016-10-29T16:47:26+02:00","size":15855,"digest":null},"tinymce/langs/sk.js":{"logical_path":"tinymce/langs/sk.js","mtime":"2016-10-29T16:47:26+02:00","size":8957,"digest":null},"tinymce/langs/sl_SI.js":{"logical_path":"tinymce/langs/sl_SI.js","mtime":"2016-10-29T16:47:26+02:00","size":7038,"digest":null},"tinymce/langs/sr.js":{"logical_path":"tinymce/langs/sr.js","mtime":"2016-10-29T16:47:26+02:00","size":6335,"digest":null},"tinymce/langs/sv_SE.js":{"logical_path":"tinymce/langs/sv_SE.js","mtime":"2016-10-29T16:47:26+02:00","size":7495,"digest":null},"tinymce/langs/ta.js":{"logical_path":"tinymce/langs/ta.js","mtime":"2016-10-29T16:47:26+02:00","size":22616,"digest":null},"tinymce/langs/ta_IN.js":{"logical_path":"tinymce/langs/ta_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":22619,"digest":null},"tinymce/langs/tg.js":{"logical_path":"tinymce/langs/tg.js","mtime":"2016-10-29T16:47:26+02:00","size":18182,"digest":null},"tinymce/langs/th_TH.js":{"logical_path":"tinymce/langs/th_TH.js","mtime":"2016-10-29T16:47:26+02:00","size":17296,"digest":null},"tinymce/langs/tr.js":{"logical_path":"tinymce/langs/tr.js","mtime":"2016-10-29T16:47:26+02:00","size":8704,"digest":null},"tinymce/langs/tr_TR.js":{"logical_path":"tinymce/langs/tr_TR.js","mtime":"2016-10-29T16:47:26+02:00","size":8140,"digest":null},"tinymce/langs/tt.js":{"logical_path":"tinymce/langs/tt.js","mtime":"2016-10-29T16:47:26+02:00","size":19034,"digest":null},"tinymce/langs/ug.js":{"logical_path":"tinymce/langs/ug.js","mtime":"2016-10-29T16:47:26+02:00","size":16887,"digest":null},"tinymce/langs/uk.js":{"logical_path":"tinymce/langs/uk.js","mtime":"2016-10-29T16:47:26+02:00","size":22263,"digest":null},"tinymce/langs/uk_UA.js":{"logical_path":"tinymce/langs/uk_UA.js","mtime":"2016-10-29T16:47:26+02:00","size":21690,"digest":null},"tinymce/langs/vi.js":{"logical_path":"tinymce/langs/vi.js","mtime":"2016-10-29T16:47:26+02:00","size":9900,"digest":null},"tinymce/langs/vi_VN.js":{"logical_path":"tinymce/langs/vi_VN.js","mtime":"2016-10-29T16:47:26+02:00","size":9898,"digest":null},"tinymce/langs/zh_CN.js":{"logical_path":"tinymce/langs/zh_CN.js","mtime":"2016-10-29T16:47:26+02:00","size":8482,"digest":null},"tinymce/langs/zh_TW.js":{"logical_path":"tinymce/langs/zh_TW.js","mtime":"2016-10-29T16:47:26+02:00","size":9378,"digest":null}},"assets":{"active_admin.css":"active_admin-1a6505fe97404d4518ab3540e631c4ba837fd413398b4e75ad4abbcd110398f3.css","active_admin/nested_menu_arrow.gif":"active_admin/nested_menu_arrow-15084d93c65c1964d7077700ea748bd2d70cfa2d4c19707c58a9c64e232dd442.gif","active_admin/nested_menu_arrow_dark.gif":"active_admin/nested_menu_arrow_dark-7c43b8e0a5f8823875f49a093c9d7a6b374f885b6f9cc248ae9cd7e6e9b29034.gif","active_admin/datepicker/datepicker-input-icon.png":"active_admin/datepicker/datepicker-input-icon-d9c2bb73769af777c8a71720d29741f3a499aebd5a043e9a119bd0d9597aed47.png","active_admin/orderable.png":"active_admin/orderable-29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617.png","active_admin/print.css":"active_admin/print-87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68.css","active_admin.js":"active_admin-5d8dda85fd2e9296c4f9682a0c4441e4dfc6c2779454e8144be78befe3ff05bb.js","layers-2x.png":"layers-2x-0c02a2388f637d21f86e6d4b314ec9a968e7b05ad4c3a005280a3f76c0fd3cb8.png","layers.png":"layers-0908aa2a72a082fb2563a2427a5e4fb247571862b448b80fb6f720af1109923e.png","marker-icon-2x.png":"marker-icon-2x-454dc479e82b487529b6b93d6a9b29ac69ca7b4f5a9d5fdf8e01871f6d216113.png","marker-icon.png":"marker-icon-915e83a6fc798c599e5c9e3f759d6bc065d65151019acd0410d1f4731bcaaf72.png","marker-shadow.png":"marker-shadow-4f340d2d61746333dffe056e074ce1704ae4e47fec5a7de98322fbdbcfcb2b6d.png","tinymce.js":"tinymce-7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa.js","application.css":"application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css","jquery-ui/ui-icons_444444_256x240.png":"jquery-ui/ui-icons_444444_256x240-31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d.png","jquery-ui/ui-icons_555555_256x240.png":"jquery-ui/ui-icons_555555_256x240-32175261daee76c82bb0edf0eea16a56421866fbc31e94f3c1d570aa114502f5.png","jquery-ui/ui-icons_ffffff_256x240.png":"jquery-ui/ui-icons_ffffff_256x240-350df1b7131037de20e83c5c0f3a41a770d2ac48b5762ea772b3f4a8a7b9d47a.png","jquery-ui/ui-icons_777620_256x240.png":"jquery-ui/ui-icons_777620_256x240-0b020fc6e696d88d296e7bb1f61f1eb2ad827848e2c7382a4c3e0999e702dd9b.png","jquery-ui/ui-icons_cc0000_256x240.png":"jquery-ui/ui-icons_cc0000_256x240-40985a64b4d5dd213fba27fcd862a1bd1b337a97674f6ff0b9ec20abcee4bc69.png","jquery-ui/ui-icons_777777_256x240.png":"jquery-ui/ui-icons_777777_256x240-faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f.png","font-awesome/fontawesome-webfont.eot":"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot","font-awesome/fontawesome-webfont.woff2":"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2","font-awesome/fontawesome-webfont.woff":"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff","font-awesome/fontawesome-webfont.ttf":"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf","font-awesome/fontawesome-webfont.svg":"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg","markers-soft.png":"markers-soft-e78784e4ed70aaffddd73c315fab590233cc4e7b72388d7dd47a14796fc7c739.png","markers-shadow.png":"markers-shadow-8703a2262710f5e3d29e65d2acdf90d6512e159e119d27b8234731d8a6208a20.png","markers-soft@2x.png":"markers-soft@2x-c1e77253a8bfbe30cec24885d7046f443b76ebb66f4c961f77083b03f4a5cbaf.png","markers-shadow@2x.png":"markers-shadow@2x-b21a536be27313fb504f69f5899ff0b1245b276571769ac08d6c32c35676e47a.png","france.png":"france-f4341a7ec8331161a9c8d5298f808014c3fc9c799b5a29ed95eb56a7f3ccd0df.png","quebec.png":"quebec-776d563b6a4ac4312cae9f0bfe630c20711346e8dbddd41040998eba79f4b588.png","belgique.png":"belgique-3b8b772a522de2cbae7714b35a956faf2c394419b532a14bba982fed3f341091.png","suisse.png":"suisse-58d067f1c3fcdc4000fa13e95896cd5369a2b91aafd314475aa5e29da0b543d1.png","modernizr.js":"modernizr-654222debe8018b12f1993ceddff30dc163a7d5008d79869c399d6d167321f97.js","agendadescommuns.png":"agendadescommuns-cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6.png","alert.png":"alert-762ace9479328243a44061346b64c4d6b997e963c68dfc6bddd9e4d241192906.png","baby_gnu_adl.png":"baby_gnu_adl-232caf355c30740d5d9b30491887cd546b8849b33ca9bdb6cc71f8a47ea61815.png","baby_gnu_adl.svg":"baby_gnu_adl-97251005d3225cf1d58b8c497d6b7905dbc9560cc8acd50118fcce60d0a2679e.svg","communs.png":"communs-cd40e342024be0587f8e7a0e3902d32cf67009d349b67c00f687e0499fde9ff6.png","lef-small.png":"lef-small-160cf5b883add60c9c0f4361bd8425c75f6fb23b0e551a0b941fa0491c70e0c9.png","lef.png":"lef-bec4081a11fbef165216827cf72c7a292ad772a77af6b8132e5bc0bbf83cb2d9.png","priorite-logiciel-libre-je-soutiens-april_2_m.png":"priorite-logiciel-libre-je-soutiens-april_2_m-6442e454e96ed45cc1ebc40673a6c50bd286b9c28ea6a8b58572e94f7d6459fc.png","team.png":"team-cb04c7a311f7160c4eb6a281eae68be84f26991dde5d415bb4e205e6726ae275.png","application.js":"application-ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd.js","markers-matte.png":"markers-matte-497826545a90e09a240504d14530eba45823b19fd44175e09e27c47cd822ddb9.png","markers-matte@2x.png":"markers-matte@2x-948fc8c4426f04f60964ed20394247f45b0b60e575d02398b9b6810e7a29a823.png","markers-plain.png":"markers-plain-cf233423aa44e75ac0031e77b8ba571cd3331010517e1197e63fb7b06856c1ff.png","tinymce/plugins/emoticons/img/smiley-cool.gif":"tinymce/plugins/emoticons/img/smiley-cool.gif","tinymce/plugins/emoticons/img/smiley-cry.gif":"tinymce/plugins/emoticons/img/smiley-cry.gif","tinymce/plugins/emoticons/img/smiley-embarassed.gif":"tinymce/plugins/emoticons/img/smiley-embarassed.gif","tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif":"tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif","tinymce/plugins/emoticons/img/smiley-frown.gif":"tinymce/plugins/emoticons/img/smiley-frown.gif","tinymce/plugins/emoticons/img/smiley-innocent.gif":"tinymce/plugins/emoticons/img/smiley-innocent.gif","tinymce/plugins/emoticons/img/smiley-kiss.gif":"tinymce/plugins/emoticons/img/smiley-kiss.gif","tinymce/plugins/emoticons/img/smiley-laughing.gif":"tinymce/plugins/emoticons/img/smiley-laughing.gif","tinymce/plugins/emoticons/img/smiley-money-mouth.gif":"tinymce/plugins/emoticons/img/smiley-money-mouth.gif","tinymce/plugins/emoticons/img/smiley-sealed.gif":"tinymce/plugins/emoticons/img/smiley-sealed.gif","tinymce/plugins/emoticons/img/smiley-smile.gif":"tinymce/plugins/emoticons/img/smiley-smile.gif","tinymce/plugins/emoticons/img/smiley-surprised.gif":"tinymce/plugins/emoticons/img/smiley-surprised.gif","tinymce/plugins/emoticons/img/smiley-tongue-out.gif":"tinymce/plugins/emoticons/img/smiley-tongue-out.gif","tinymce/plugins/emoticons/img/smiley-undecided.gif":"tinymce/plugins/emoticons/img/smiley-undecided.gif","tinymce/plugins/emoticons/img/smiley-wink.gif":"tinymce/plugins/emoticons/img/smiley-wink.gif","tinymce/plugins/emoticons/img/smiley-yell.gif":"tinymce/plugins/emoticons/img/smiley-yell.gif","tinymce/skins/lightgray/img/anchor.gif":"tinymce/skins/lightgray/img/anchor.gif","tinymce/skins/lightgray/img/loader.gif":"tinymce/skins/lightgray/img/loader.gif","tinymce/skins/lightgray/img/object.gif":"tinymce/skins/lightgray/img/object.gif","tinymce/skins/lightgray/img/trans.gif":"tinymce/skins/lightgray/img/trans.gif","jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png":"jquery-ui/ui-bg_flat_0_aaaaaa_40x100-ae65a7ae22c4c23115948fdeb5c05c9137dbd13ca2d426b3c4c3c4183451e410.png","tinymce/jquery.tinymce.js":"tinymce/jquery.tinymce.js","tinymce/langs/readme.md":"tinymce/langs/readme.md","tinymce/license.txt":"tinymce/license.txt","tinymce/plugins/advlist/plugin.js":"tinymce/plugins/advlist/plugin.js","tinymce/plugins/anchor/plugin.js":"tinymce/plugins/anchor/plugin.js","tinymce/plugins/autolink/plugin.js":"tinymce/plugins/autolink/plugin.js","tinymce/plugins/autoresize/plugin.js":"tinymce/plugins/autoresize/plugin.js","tinymce/plugins/autosave/plugin.js":"tinymce/plugins/autosave/plugin.js","tinymce/plugins/bbcode/plugin.js":"tinymce/plugins/bbcode/plugin.js","tinymce/plugins/charmap/plugin.js":"tinymce/plugins/charmap/plugin.js","tinymce/plugins/code/plugin.js":"tinymce/plugins/code/plugin.js","tinymce/plugins/codesample/css/prism.css":"tinymce/plugins/codesample/css/prism.css","tinymce/plugins/codesample/plugin.dev.js":"tinymce/plugins/codesample/plugin.dev.js","tinymce/plugins/codesample/plugin.js":"tinymce/plugins/codesample/plugin.js","tinymce/plugins/colorpicker/plugin.js":"tinymce/plugins/colorpicker/plugin.js","tinymce/plugins/contextmenu/plugin.js":"tinymce/plugins/contextmenu/plugin.js","tinymce/plugins/directionality/plugin.js":"tinymce/plugins/directionality/plugin.js","tinymce/plugins/emoticons/plugin.js":"tinymce/plugins/emoticons/plugin.js","tinymce/plugins/example/dialog.html":"tinymce/plugins/example/dialog.html","tinymce/plugins/example/plugin.js":"tinymce/plugins/example/plugin.js","tinymce/plugins/example_dependency/plugin.js":"tinymce/plugins/example_dependency/plugin.js","tinymce/plugins/fullpage/plugin.js":"tinymce/plugins/fullpage/plugin.js","tinymce/plugins/fullscreen/plugin.js":"tinymce/plugins/fullscreen/plugin.js","tinymce/plugins/hr/plugin.js":"tinymce/plugins/hr/plugin.js","tinymce/plugins/image/plugin.js":"tinymce/plugins/image/plugin.js","tinymce/plugins/imagetools/plugin.js":"tinymce/plugins/imagetools/plugin.js","tinymce/plugins/importcss/plugin.js":"tinymce/plugins/importcss/plugin.js","tinymce/plugins/insertdatetime/plugin.js":"tinymce/plugins/insertdatetime/plugin.js","tinymce/plugins/layer/plugin.js":"tinymce/plugins/layer/plugin.js","tinymce/plugins/legacyoutput/plugin.js":"tinymce/plugins/legacyoutput/plugin.js","tinymce/plugins/link/plugin.js":"tinymce/plugins/link/plugin.js","tinymce/plugins/lists/plugin.js":"tinymce/plugins/lists/plugin.js","tinymce/plugins/media/plugin.js":"tinymce/plugins/media/plugin.js","tinymce/plugins/nonbreaking/plugin.js":"tinymce/plugins/nonbreaking/plugin.js","tinymce/plugins/noneditable/plugin.js":"tinymce/plugins/noneditable/plugin.js","tinymce/plugins/pagebreak/plugin.js":"tinymce/plugins/pagebreak/plugin.js","tinymce/plugins/paste/plugin.dev.js":"tinymce/plugins/paste/plugin.dev.js","tinymce/plugins/paste/plugin.js":"tinymce/plugins/paste/plugin.js","tinymce/plugins/preview/plugin.js":"tinymce/plugins/preview/plugin.js","tinymce/plugins/print/plugin.js":"tinymce/plugins/print/plugin.js","tinymce/plugins/save/plugin.js":"tinymce/plugins/save/plugin.js","tinymce/plugins/searchreplace/plugin.js":"tinymce/plugins/searchreplace/plugin.js","tinymce/plugins/spellchecker/plugin.dev.js":"tinymce/plugins/spellchecker/plugin.dev.js","tinymce/plugins/spellchecker/plugin.js":"tinymce/plugins/spellchecker/plugin.js","tinymce/plugins/tabfocus/plugin.js":"tinymce/plugins/tabfocus/plugin.js","tinymce/plugins/table/plugin.dev.js":"tinymce/plugins/table/plugin.dev.js","tinymce/plugins/table/plugin.js":"tinymce/plugins/table/plugin.js","tinymce/plugins/template/plugin.js":"tinymce/plugins/template/plugin.js","tinymce/plugins/textcolor/plugin.js":"tinymce/plugins/textcolor/plugin.js","tinymce/plugins/textpattern/plugin.js":"tinymce/plugins/textpattern/plugin.js","tinymce/plugins/toc/plugin.js":"tinymce/plugins/toc/plugin.js","tinymce/plugins/visualblocks/css/visualblocks.css":"tinymce/plugins/visualblocks/css/visualblocks.css","tinymce/plugins/visualblocks/plugin.js":"tinymce/plugins/visualblocks/plugin.js","tinymce/plugins/visualchars/plugin.js":"tinymce/plugins/visualchars/plugin.js","tinymce/plugins/wordcount/plugin.js":"tinymce/plugins/wordcount/plugin.js","tinymce/skins/lightgray/AbsoluteLayout.less":"tinymce/skins/lightgray/AbsoluteLayout.less","tinymce/skins/lightgray/Animations.less":"tinymce/skins/lightgray/Animations.less","tinymce/skins/lightgray/Arrows.less":"tinymce/skins/lightgray/Arrows.less","tinymce/skins/lightgray/Button.less":"tinymce/skins/lightgray/Button.less","tinymce/skins/lightgray/ButtonGroup.less":"tinymce/skins/lightgray/ButtonGroup.less","tinymce/skins/lightgray/Checkbox.less":"tinymce/skins/lightgray/Checkbox.less","tinymce/skins/lightgray/ColorBox.less":"tinymce/skins/lightgray/ColorBox.less","tinymce/skins/lightgray/ColorButton.less":"tinymce/skins/lightgray/ColorButton.less","tinymce/skins/lightgray/ColorPicker.less":"tinymce/skins/lightgray/ColorPicker.less","tinymce/skins/lightgray/ComboBox.less":"tinymce/skins/lightgray/ComboBox.less","tinymce/skins/lightgray/Container.less":"tinymce/skins/lightgray/Container.less","tinymce/skins/lightgray/Content.Inline.less":"tinymce/skins/lightgray/Content.Inline.less","tinymce/skins/lightgray/Content.Objects.less":"tinymce/skins/lightgray/Content.Objects.less","tinymce/skins/lightgray/Content.less":"tinymce/skins/lightgray/Content.less","tinymce/skins/lightgray/CropRect.less":"tinymce/skins/lightgray/CropRect.less","tinymce/skins/lightgray/FieldSet.less":"tinymce/skins/lightgray/FieldSet.less","tinymce/skins/lightgray/FitLayout.less":"tinymce/skins/lightgray/FitLayout.less","tinymce/skins/lightgray/FloatPanel.less":"tinymce/skins/lightgray/FloatPanel.less","tinymce/skins/lightgray/FlowLayout.less":"tinymce/skins/lightgray/FlowLayout.less","tinymce/skins/lightgray/Icons.Ie7.less":"tinymce/skins/lightgray/Icons.Ie7.less","tinymce/skins/lightgray/Icons.less":"tinymce/skins/lightgray/Icons.less","tinymce/skins/lightgray/Iframe.less":"tinymce/skins/lightgray/Iframe.less","tinymce/skins/lightgray/ImagePanel.less":"tinymce/skins/lightgray/ImagePanel.less","tinymce/skins/lightgray/InfoBox.less":"tinymce/skins/lightgray/InfoBox.less","tinymce/skins/lightgray/Label.less":"tinymce/skins/lightgray/Label.less","tinymce/skins/lightgray/ListBox.less":"tinymce/skins/lightgray/ListBox.less","tinymce/skins/lightgray/Menu.less":"tinymce/skins/lightgray/Menu.less","tinymce/skins/lightgray/MenuBar.less":"tinymce/skins/lightgray/MenuBar.less","tinymce/skins/lightgray/MenuButton.less":"tinymce/skins/lightgray/MenuButton.less","tinymce/skins/lightgray/MenuItem.less":"tinymce/skins/lightgray/MenuItem.less","tinymce/skins/lightgray/Mixins.less":"tinymce/skins/lightgray/Mixins.less","tinymce/skins/lightgray/Notification.less":"tinymce/skins/lightgray/Notification.less","tinymce/skins/lightgray/Panel.less":"tinymce/skins/lightgray/Panel.less","tinymce/skins/lightgray/Path.less":"tinymce/skins/lightgray/Path.less","tinymce/skins/lightgray/Progress.less":"tinymce/skins/lightgray/Progress.less","tinymce/skins/lightgray/Radio.less":"tinymce/skins/lightgray/Radio.less","tinymce/skins/lightgray/Reset.less":"tinymce/skins/lightgray/Reset.less","tinymce/skins/lightgray/ResizeHandle.less":"tinymce/skins/lightgray/ResizeHandle.less","tinymce/skins/lightgray/Scrollable.less":"tinymce/skins/lightgray/Scrollable.less","tinymce/skins/lightgray/SelectBox.less":"tinymce/skins/lightgray/SelectBox.less","tinymce/skins/lightgray/Sidebar.less":"tinymce/skins/lightgray/Sidebar.less","tinymce/skins/lightgray/Slider.less":"tinymce/skins/lightgray/Slider.less","tinymce/skins/lightgray/Spacer.less":"tinymce/skins/lightgray/Spacer.less","tinymce/skins/lightgray/SplitButton.less":"tinymce/skins/lightgray/SplitButton.less","tinymce/skins/lightgray/StackLayout.less":"tinymce/skins/lightgray/StackLayout.less","tinymce/skins/lightgray/TabPanel.less":"tinymce/skins/lightgray/TabPanel.less","tinymce/skins/lightgray/TextBox.less":"tinymce/skins/lightgray/TextBox.less","tinymce/skins/lightgray/Throbber.less":"tinymce/skins/lightgray/Throbber.less","tinymce/skins/lightgray/TinyMCE.less":"tinymce/skins/lightgray/TinyMCE.less","tinymce/skins/lightgray/ToolTip.less":"tinymce/skins/lightgray/ToolTip.less","tinymce/skins/lightgray/Variables.less":"tinymce/skins/lightgray/Variables.less","tinymce/skins/lightgray/Window.less":"tinymce/skins/lightgray/Window.less","tinymce/skins/lightgray/content.inline.min.css":"tinymce/skins/lightgray/content.inline.min.css","tinymce/skins/lightgray/content.min.css":"tinymce/skins/lightgray/content.min.css","tinymce/skins/lightgray/fonts/tinymce-small.eot":"tinymce/skins/lightgray/fonts/tinymce-small.eot","tinymce/skins/lightgray/fonts/tinymce-small.svg":"tinymce/skins/lightgray/fonts/tinymce-small.svg","tinymce/skins/lightgray/fonts/tinymce-small.ttf":"tinymce/skins/lightgray/fonts/tinymce-small.ttf","tinymce/skins/lightgray/fonts/tinymce-small.woff":"tinymce/skins/lightgray/fonts/tinymce-small.woff","tinymce/skins/lightgray/fonts/tinymce.eot":"tinymce/skins/lightgray/fonts/tinymce.eot","tinymce/skins/lightgray/fonts/tinymce.svg":"tinymce/skins/lightgray/fonts/tinymce.svg","tinymce/skins/lightgray/fonts/tinymce.ttf":"tinymce/skins/lightgray/fonts/tinymce.ttf","tinymce/skins/lightgray/fonts/tinymce.woff":"tinymce/skins/lightgray/fonts/tinymce.woff","tinymce/skins/lightgray/skin.dev.less":"tinymce/skins/lightgray/skin.dev.less","tinymce/skins/lightgray/skin.ie7.dev.less":"tinymce/skins/lightgray/skin.ie7.dev.less","tinymce/skins/lightgray/skin.ie7.less":"tinymce/skins/lightgray/skin.ie7.less","tinymce/skins/lightgray/skin.ie7.min.css":"tinymce/skins/lightgray/skin.ie7.min.css","tinymce/skins/lightgray/skin.less":"tinymce/skins/lightgray/skin.less","tinymce/skins/lightgray/skin.min.css":"tinymce/skins/lightgray/skin.min.css","tinymce/themes/inlite/theme.js":"tinymce/themes/inlite/theme.js","tinymce/themes/modern/theme.js":"tinymce/themes/modern/theme.js","tinymce/tinymce.js":"tinymce/tinymce.js","tinymce/langs/ar.js":"tinymce/langs/ar.js","tinymce/langs/ar_SA.js":"tinymce/langs/ar_SA.js","tinymce/langs/az.js":"tinymce/langs/az.js","tinymce/langs/be.js":"tinymce/langs/be.js","tinymce/langs/bg_BG.js":"tinymce/langs/bg_BG.js","tinymce/langs/bn_BD.js":"tinymce/langs/bn_BD.js","tinymce/langs/bs.js":"tinymce/langs/bs.js","tinymce/langs/ca.js":"tinymce/langs/ca.js","tinymce/langs/cs.js":"tinymce/langs/cs.js","tinymce/langs/cs_CZ.js":"tinymce/langs/cs_CZ.js","tinymce/langs/cy.js":"tinymce/langs/cy.js","tinymce/langs/da.js":"tinymce/langs/da.js","tinymce/langs/de.js":"tinymce/langs/de.js","tinymce/langs/de_AT.js":"tinymce/langs/de_AT.js","tinymce/langs/dv.js":"tinymce/langs/dv.js","tinymce/langs/el.js":"tinymce/langs/el.js","tinymce/langs/en_CA.js":"tinymce/langs/en_CA.js","tinymce/langs/en_GB.js":"tinymce/langs/en_GB.js","tinymce/langs/eo.js":"tinymce/langs/eo.js","tinymce/langs/es.js":"tinymce/langs/es.js","tinymce/langs/es_MX.js":"tinymce/langs/es_MX.js","tinymce/langs/et.js":"tinymce/langs/et.js","tinymce/langs/eu.js":"tinymce/langs/eu.js","tinymce/langs/fa.js":"tinymce/langs/fa.js","tinymce/langs/fa_IR.js":"tinymce/langs/fa_IR.js","tinymce/langs/fi.js":"tinymce/langs/fi.js","tinymce/langs/fo.js":"tinymce/langs/fo.js","tinymce/langs/fr_CH.js":"tinymce/langs/fr_CH.js","tinymce/langs/fr_FR.js":"tinymce/langs/fr_FR.js","tinymce/langs/ga.js":"tinymce/langs/ga.js","tinymce/langs/gd.js":"tinymce/langs/gd.js","tinymce/langs/gl.js":"tinymce/langs/gl.js","tinymce/langs/he_IL.js":"tinymce/langs/he_IL.js","tinymce/langs/hi_IN.js":"tinymce/langs/hi_IN.js","tinymce/langs/hr.js":"tinymce/langs/hr.js","tinymce/langs/hu_HU.js":"tinymce/langs/hu_HU.js","tinymce/langs/hy.js":"tinymce/langs/hy.js","tinymce/langs/id.js":"tinymce/langs/id.js","tinymce/langs/is_IS.js":"tinymce/langs/is_IS.js","tinymce/langs/it.js":"tinymce/langs/it.js","tinymce/langs/ja.js":"tinymce/langs/ja.js","tinymce/langs/ka_GE.js":"tinymce/langs/ka_GE.js","tinymce/langs/kab.js":"tinymce/langs/kab.js","tinymce/langs/kk.js":"tinymce/langs/kk.js","tinymce/langs/km_KH.js":"tinymce/langs/km_KH.js","tinymce/langs/ko.js":"tinymce/langs/ko.js","tinymce/langs/ko_KR.js":"tinymce/langs/ko_KR.js","tinymce/langs/ku.js":"tinymce/langs/ku.js","tinymce/langs/ku_IQ.js":"tinymce/langs/ku_IQ.js","tinymce/langs/lb.js":"tinymce/langs/lb.js","tinymce/langs/lt.js":"tinymce/langs/lt.js","tinymce/langs/lv.js":"tinymce/langs/lv.js","tinymce/langs/mk_MK.js":"tinymce/langs/mk_MK.js","tinymce/langs/ml.js":"tinymce/langs/ml.js","tinymce/langs/ml_IN.js":"tinymce/langs/ml_IN.js","tinymce/langs/mn_MN.js":"tinymce/langs/mn_MN.js","tinymce/langs/nb_NO.js":"tinymce/langs/nb_NO.js","tinymce/langs/nl.js":"tinymce/langs/nl.js","tinymce/langs/pl.js":"tinymce/langs/pl.js","tinymce/langs/pt_BR.js":"tinymce/langs/pt_BR.js","tinymce/langs/pt_PT.js":"tinymce/langs/pt_PT.js","tinymce/langs/ro.js":"tinymce/langs/ro.js","tinymce/langs/ru.js":"tinymce/langs/ru.js","tinymce/langs/ru_RU.js":"tinymce/langs/ru_RU.js","tinymce/langs/si_LK.js":"tinymce/langs/si_LK.js","tinymce/langs/sk.js":"tinymce/langs/sk.js","tinymce/langs/sl_SI.js":"tinymce/langs/sl_SI.js","tinymce/langs/sr.js":"tinymce/langs/sr.js","tinymce/langs/sv_SE.js":"tinymce/langs/sv_SE.js","tinymce/langs/ta.js":"tinymce/langs/ta.js","tinymce/langs/ta_IN.js":"tinymce/langs/ta_IN.js","tinymce/langs/tg.js":"tinymce/langs/tg.js","tinymce/langs/th_TH.js":"tinymce/langs/th_TH.js","tinymce/langs/tr.js":"tinymce/langs/tr.js","tinymce/langs/tr_TR.js":"tinymce/langs/tr_TR.js","tinymce/langs/tt.js":"tinymce/langs/tt.js","tinymce/langs/ug.js":"tinymce/langs/ug.js","tinymce/langs/uk.js":"tinymce/langs/uk.js","tinymce/langs/uk_UA.js":"tinymce/langs/uk_UA.js","tinymce/langs/vi.js":"tinymce/langs/vi.js","tinymce/langs/vi_VN.js":"tinymce/langs/vi_VN.js","tinymce/langs/zh_CN.js":"tinymce/langs/zh_CN.js","tinymce/langs/zh_TW.js":"tinymce/langs/zh_TW.js"}} \ No newline at end of file diff --git a/public/assets/application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css b/public/assets/application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css new file mode 100644 index 00000000..c372bd4e --- /dev/null +++ b/public/assets/application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css @@ -0,0 +1,202 @@ +/*! + * jQuery UI CSS Framework 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/ + */.ui-widget{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Arial,Helvetica,sans-serif;font-size:1em}.ui-widget.ui-widget-content{border:1px solid #c5c5c5}.ui-widget-content{border:1px solid #ddd;background:#fff;color:#333}.ui-widget-content a{color:#333}.ui-widget-header{border:1px solid #ddd;background:#e9e9e9;color:#333;font-weight:bold}.ui-widget-header a{color:#333}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default,.ui-button,html .ui-button.ui-state-disabled:hover,html .ui-button.ui-state-disabled:active{border:1px solid #c5c5c5;background:#f6f6f6;font-weight:normal;color:#454545}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited,a.ui-button,a:link.ui-button,a:visited.ui-button,.ui-button{color:#454545;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus,.ui-button:hover,.ui-button:focus{border:1px solid #ccc;background:#ededed;font-weight:normal;color:#2b2b2b}.ui-state-hover a,.ui-state-hover a:hover,.ui-state-hover a:link,.ui-state-hover a:visited,.ui-state-focus a,.ui-state-focus a:hover,.ui-state-focus a:link,.ui-state-focus a:visited,a.ui-button:hover,a.ui-button:focus{color:#2b2b2b;text-decoration:none}.ui-visual-focus{box-shadow:0 0 3px 1px #5e9ed6}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active,a.ui-button:active,.ui-button:active,.ui-button.ui-state-active:hover{border:1px solid #003eff;background:#007fff;font-weight:normal;color:#fff}.ui-icon-background,.ui-state-active .ui-icon-background{border:#003eff;background-color:#fff}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#fff;text-decoration:none}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #dad55e;background:#fffa90;color:#777620}.ui-state-checked{border:1px solid #dad55e;background:#fffa90}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#777620}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #f1a899;background:#fddfdf;color:#5f3f3f}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#5f3f3f}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#5f3f3f}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-state-disabled .ui-icon{filter:Alpha(Opacity=35)}.ui-icon{width:16px;height:16px}.ui-icon,.ui-widget-content .ui-icon{background-image:url(/assets/jquery-ui/ui-icons_444444_256x240-31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d.png)}.ui-widget-header .ui-icon{background-image:url(/assets/jquery-ui/ui-icons_444444_256x240-31d988765b4e6f56553c29588c500381dc3e6f0aa2980c8212202e5644aefd5d.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon,.ui-button:hover .ui-icon,.ui-button:focus .ui-icon{background-image:url(/assets/jquery-ui/ui-icons_555555_256x240-32175261daee76c82bb0edf0eea16a56421866fbc31e94f3c1d570aa114502f5.png)}.ui-state-active .ui-icon,.ui-button:active .ui-icon{background-image:url(/assets/jquery-ui/ui-icons_ffffff_256x240-350df1b7131037de20e83c5c0f3a41a770d2ac48b5762ea772b3f4a8a7b9d47a.png)}.ui-state-highlight .ui-icon,.ui-button .ui-state-highlight.ui-icon{background-image:url(/assets/jquery-ui/ui-icons_777620_256x240-0b020fc6e696d88d296e7bb1f61f1eb2ad827848e2c7382a4c3e0999e702dd9b.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(/assets/jquery-ui/ui-icons_cc0000_256x240-40985a64b4d5dd213fba27fcd862a1bd1b337a97674f6ff0b9ec20abcee4bc69.png)}.ui-button .ui-icon{background-image:url(/assets/jquery-ui/ui-icons_777777_256x240-faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f.png)}.ui-icon-blank{background-position:16px 16px}.ui-icon-caret-1-n{background-position:0 0}.ui-icon-caret-1-ne{background-position:-16px 0}.ui-icon-caret-1-e{background-position:-32px 0}.ui-icon-caret-1-se{background-position:-48px 0}.ui-icon-caret-1-s{background-position:-65px 0}.ui-icon-caret-1-sw{background-position:-80px 0}.ui-icon-caret-1-w{background-position:-96px 0}.ui-icon-caret-1-nw{background-position:-112px 0}.ui-icon-caret-2-n-s{background-position:-128px 0}.ui-icon-caret-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-65px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-65px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:1px -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-on{background-position:-96px -144px}.ui-icon-radio-off{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-all,.ui-corner-top,.ui-corner-left,.ui-corner-tl{border-top-left-radius:3px}.ui-corner-all,.ui-corner-top,.ui-corner-right,.ui-corner-tr{border-top-right-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-left,.ui-corner-bl{border-bottom-left-radius:3px}.ui-corner-all,.ui-corner-bottom,.ui-corner-right,.ui-corner-br{border-bottom-right-radius:3px}.ui-widget-overlay{background:#aaa;opacity:0.3;filter:Alpha(Opacity=30)}.ui-widget-shadow{-webkit-box-shadow:0 0 5px #666;box-shadow:0 0 5px #666}/*! + * jQuery UI CSS Framework 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + */.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{border:0;clip:rect(0 0 0 0);height:1px;margin:-1px;overflow:hidden;padding:0;position:absolute;width:1px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:before,.ui-helper-clearfix:after{content:"";display:table;border-collapse:collapse}.ui-helper-clearfix:after{clear:both}.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-front{z-index:100}.ui-state-disabled{cursor:default !important;pointer-events:none}.ui-icon{display:inline-block;vertical-align:middle;margin-top:-.25em;position:relative;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-icon-block{left:50%;margin-left:-8px;display:block}.ui-widget-overlay{position:fixed;top:0;left:0;width:100%;height:100%}/*! + * jQuery UI Accordion 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/accordion/#theming + */.ui-accordion .ui-accordion-header{display:block;cursor:pointer;position:relative;margin:2px 0 0 0;padding:.5em .5em .5em .7em;font-size:100%}.ui-accordion .ui-accordion-content{padding:1em 2.2em;border-top:0;overflow:auto}/*! + * jQuery UI Autocomplete 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/autocomplete/#theming + */.ui-autocomplete{position:absolute;top:0;left:0;cursor:default}/*! + * jQuery UI Button 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/button/#theming + */.ui-button{padding:.4em 1em;display:inline-block;position:relative;line-height:normal;margin-right:.1em;cursor:pointer;vertical-align:middle;text-align:center;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;overflow:visible}.ui-button,.ui-button:link,.ui-button:visited,.ui-button:hover,.ui-button:active{text-decoration:none}.ui-button-icon-only{width:2em;box-sizing:border-box;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-button-icon-only{text-indent:0}.ui-button-icon-only .ui-icon{position:absolute;top:50%;left:50%;margin-top:-8px;margin-left:-8px}.ui-button.ui-icon-notext .ui-icon{padding:0;width:2.1em;height:2.1em;text-indent:-9999px;white-space:nowrap}input.ui-button.ui-icon-notext .ui-icon{width:auto;height:auto;text-indent:0;white-space:normal;padding:.4em 1em}input.ui-button::-moz-focus-inner,button.ui-button::-moz-focus-inner{border:0;padding:0}/*! + * jQuery UI Checkboxradio 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/checkboxradio/#theming + */.ui-checkboxradio-label .ui-icon-background{box-shadow:inset 1px 1px 1px #ccc;border-radius:.12em;border:none}.ui-checkboxradio-radio-label .ui-icon-background{width:16px;height:16px;border-radius:1em;overflow:visible;border:none}.ui-checkboxradio-radio-label.ui-checkboxradio-checked .ui-icon,.ui-checkboxradio-radio-label.ui-checkboxradio-checked:hover .ui-icon{background-image:none;width:8px;height:8px;border-width:4px;border-style:solid}.ui-checkboxradio-disabled{pointer-events:none}/*! + * jQuery UI Controlgroup 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/controlgroup/#theming + */.ui-controlgroup{vertical-align:middle;display:inline-block}.ui-controlgroup>.ui-controlgroup-item{float:left;margin-left:0;margin-right:0}.ui-controlgroup>.ui-controlgroup-item:focus,.ui-controlgroup>.ui-controlgroup-item.ui-visual-focus{z-index:9999}.ui-controlgroup-vertical>.ui-controlgroup-item{display:block;float:none;width:100%;margin-top:0;margin-bottom:0;text-align:left}.ui-controlgroup-vertical .ui-controlgroup-item{box-sizing:border-box}.ui-controlgroup .ui-controlgroup-label{padding:.4em 1em}.ui-controlgroup .ui-controlgroup-label span{font-size:80%}.ui-controlgroup-horizontal .ui-controlgroup-label+.ui-controlgroup-item{border-left:none}.ui-controlgroup-vertical .ui-controlgroup-label+.ui-controlgroup-item{border-top:none}.ui-controlgroup-horizontal .ui-controlgroup-label.ui-widget-content{border-right:none}.ui-controlgroup-vertical .ui-controlgroup-label.ui-widget-content{border-bottom:none}.ui-controlgroup-vertical .ui-spinner-input{width:75%;width:calc( 100% - 2.4em )}.ui-controlgroup-vertical .ui-spinner .ui-spinner-up{border-top-style:solid}/*! + * jQuery UI Datepicker 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/datepicker/#theming + */.ui-datepicker{width:17em;padding:.2em .2em 0;display:none}.ui-datepicker .ui-datepicker-header{position:relative;padding:.2em 0}.ui-datepicker .ui-datepicker-prev,.ui-datepicker .ui-datepicker-next{position:absolute;top:2px;width:1.8em;height:1.8em}.ui-datepicker .ui-datepicker-prev-hover,.ui-datepicker .ui-datepicker-next-hover{top:1px}.ui-datepicker .ui-datepicker-prev{left:2px}.ui-datepicker .ui-datepicker-next{right:2px}.ui-datepicker .ui-datepicker-prev-hover{left:1px}.ui-datepicker .ui-datepicker-next-hover{right:1px}.ui-datepicker .ui-datepicker-prev span,.ui-datepicker .ui-datepicker-next span{display:block;position:absolute;left:50%;margin-left:-8px;top:50%;margin-top:-8px}.ui-datepicker .ui-datepicker-title{margin:0 2.3em;line-height:1.8em;text-align:center}.ui-datepicker .ui-datepicker-title select{font-size:1em;margin:1px 0}.ui-datepicker select.ui-datepicker-month,.ui-datepicker select.ui-datepicker-year{width:45%}.ui-datepicker table{width:100%;font-size:.9em;border-collapse:collapse;margin:0 0 .4em}.ui-datepicker th{padding:.7em .3em;text-align:center;font-weight:bold;border:0}.ui-datepicker td{border:0;padding:1px}.ui-datepicker td span,.ui-datepicker td a{display:block;padding:.2em;text-align:right;text-decoration:none}.ui-datepicker .ui-datepicker-buttonpane{background-image:none;margin:.7em 0 0 0;padding:0 .2em;border-left:0;border-right:0;border-bottom:0}.ui-datepicker .ui-datepicker-buttonpane button{float:right;margin:.5em .2em .4em;cursor:pointer;padding:.2em .6em .3em .6em;width:auto;overflow:visible}.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current{float:left}.ui-datepicker.ui-datepicker-multi{width:auto}.ui-datepicker-multi .ui-datepicker-group{float:left}.ui-datepicker-multi .ui-datepicker-group table{width:95%;margin:0 auto .4em}.ui-datepicker-multi-2 .ui-datepicker-group{width:50%}.ui-datepicker-multi-3 .ui-datepicker-group{width:33.3%}.ui-datepicker-multi-4 .ui-datepicker-group{width:25%}.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header{border-left-width:0}.ui-datepicker-multi .ui-datepicker-buttonpane{clear:left}.ui-datepicker-row-break{clear:both;width:100%;font-size:0}.ui-datepicker-rtl{direction:rtl}.ui-datepicker-rtl .ui-datepicker-prev{right:2px;left:auto}.ui-datepicker-rtl .ui-datepicker-next{left:2px;right:auto}.ui-datepicker-rtl .ui-datepicker-prev:hover{right:1px;left:auto}.ui-datepicker-rtl .ui-datepicker-next:hover{left:1px;right:auto}.ui-datepicker-rtl .ui-datepicker-buttonpane{clear:right}.ui-datepicker-rtl .ui-datepicker-buttonpane button{float:left}.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current,.ui-datepicker-rtl .ui-datepicker-group{float:right}.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header,.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header{border-right-width:0;border-left-width:1px}.ui-datepicker .ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat;left:.5em;top:.3em}/*! + * jQuery UI Dialog 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/dialog/#theming + */.ui-dialog{position:absolute;top:0;left:0;padding:.2em;outline:0}.ui-dialog .ui-dialog-titlebar{padding:.4em 1em;position:relative}.ui-dialog .ui-dialog-title{float:left;margin:.1em 0;white-space:nowrap;width:90%;overflow:hidden;text-overflow:ellipsis}.ui-dialog .ui-dialog-titlebar-close{position:absolute;right:.3em;top:50%;width:20px;margin:-10px 0 0 0;padding:1px;height:20px}.ui-dialog .ui-dialog-content{position:relative;border:0;padding:.5em 1em;background:none;overflow:auto}.ui-dialog .ui-dialog-buttonpane{text-align:left;border-width:1px 0 0 0;background-image:none;margin-top:.5em;padding:.3em 1em .5em .4em}.ui-dialog .ui-dialog-buttonpane .ui-dialog-buttonset{float:right}.ui-dialog .ui-dialog-buttonpane button{margin:.5em .4em .5em 0;cursor:pointer}.ui-dialog .ui-resizable-n{height:2px;top:0}.ui-dialog .ui-resizable-e{width:2px;right:0}.ui-dialog .ui-resizable-s{height:2px;bottom:0}.ui-dialog .ui-resizable-w{width:2px;left:0}.ui-dialog .ui-resizable-se,.ui-dialog .ui-resizable-sw,.ui-dialog .ui-resizable-ne,.ui-dialog .ui-resizable-nw{width:7px;height:7px}.ui-dialog .ui-resizable-se{right:0;bottom:0}.ui-dialog .ui-resizable-sw{left:0;bottom:0}.ui-dialog .ui-resizable-ne{right:0;top:0}.ui-dialog .ui-resizable-nw{left:0;top:0}.ui-draggable .ui-dialog-titlebar{cursor:move}/*! + * jQuery UI Draggable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */.ui-draggable-handle{-ms-touch-action:none;touch-action:none}/*! + * jQuery UI Menu 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/menu/#theming + */.ui-menu{list-style:none;padding:0;margin:0;display:block;outline:0}.ui-menu .ui-menu{position:absolute}.ui-menu .ui-menu-item{margin:0;cursor:pointer;list-style-image:url("data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7")}.ui-menu .ui-menu-item-wrapper{position:relative;padding:3px 1em 3px .4em}.ui-menu .ui-menu-divider{margin:5px 0;height:0;font-size:0;line-height:0;border-width:1px 0 0 0}.ui-menu .ui-state-focus,.ui-menu .ui-state-active{margin:-1px}.ui-menu-icons{position:relative}.ui-menu-icons .ui-menu-item-wrapper{padding-left:2em}.ui-menu .ui-icon{position:absolute;top:0;bottom:0;left:.2em;margin:auto 0}.ui-menu .ui-menu-icon{left:auto;right:0}/*! + * jQuery UI Progressbar 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/progressbar/#theming + */.ui-progressbar{height:2em;text-align:left;overflow:hidden}.ui-progressbar .ui-progressbar-value{margin:-1px;height:100%}.ui-progressbar .ui-progressbar-overlay{background:url("data:image/gif;base64,R0lGODlhKAAoAIABAAAAAP///yH/C05FVFNDQVBFMi4wAwEAAAAh+QQJAQABACwAAAAAKAAoAAACkYwNqXrdC52DS06a7MFZI+4FHBCKoDeWKXqymPqGqxvJrXZbMx7Ttc+w9XgU2FB3lOyQRWET2IFGiU9m1frDVpxZZc6bfHwv4c1YXP6k1Vdy292Fb6UkuvFtXpvWSzA+HycXJHUXiGYIiMg2R6W459gnWGfHNdjIqDWVqemH2ekpObkpOlppWUqZiqr6edqqWQAAIfkECQEAAQAsAAAAACgAKAAAApSMgZnGfaqcg1E2uuzDmmHUBR8Qil95hiPKqWn3aqtLsS18y7G1SzNeowWBENtQd+T1JktP05nzPTdJZlR6vUxNWWjV+vUWhWNkWFwxl9VpZRedYcflIOLafaa28XdsH/ynlcc1uPVDZxQIR0K25+cICCmoqCe5mGhZOfeYSUh5yJcJyrkZWWpaR8doJ2o4NYq62lAAACH5BAkBAAEALAAAAAAoACgAAAKVDI4Yy22ZnINRNqosw0Bv7i1gyHUkFj7oSaWlu3ovC8GxNso5fluz3qLVhBVeT/Lz7ZTHyxL5dDalQWPVOsQWtRnuwXaFTj9jVVh8pma9JjZ4zYSj5ZOyma7uuolffh+IR5aW97cHuBUXKGKXlKjn+DiHWMcYJah4N0lYCMlJOXipGRr5qdgoSTrqWSq6WFl2ypoaUAAAIfkECQEAAQAsAAAAACgAKAAAApaEb6HLgd/iO7FNWtcFWe+ufODGjRfoiJ2akShbueb0wtI50zm02pbvwfWEMWBQ1zKGlLIhskiEPm9R6vRXxV4ZzWT2yHOGpWMyorblKlNp8HmHEb/lCXjcW7bmtXP8Xt229OVWR1fod2eWqNfHuMjXCPkIGNileOiImVmCOEmoSfn3yXlJWmoHGhqp6ilYuWYpmTqKUgAAIfkECQEAAQAsAAAAACgAKAAAApiEH6kb58biQ3FNWtMFWW3eNVcojuFGfqnZqSebuS06w5V80/X02pKe8zFwP6EFWOT1lDFk8rGERh1TTNOocQ61Hm4Xm2VexUHpzjymViHrFbiELsefVrn6XKfnt2Q9G/+Xdie499XHd2g4h7ioOGhXGJboGAnXSBnoBwKYyfioubZJ2Hn0RuRZaflZOil56Zp6iioKSXpUAAAh+QQJAQABACwAAAAAKAAoAAACkoQRqRvnxuI7kU1a1UU5bd5tnSeOZXhmn5lWK3qNTWvRdQxP8qvaC+/yaYQzXO7BMvaUEmJRd3TsiMAgswmNYrSgZdYrTX6tSHGZO73ezuAw2uxuQ+BbeZfMxsexY35+/Qe4J1inV0g4x3WHuMhIl2jXOKT2Q+VU5fgoSUI52VfZyfkJGkha6jmY+aaYdirq+lQAACH5BAkBAAEALAAAAAAoACgAAAKWBIKpYe0L3YNKToqswUlvznigd4wiR4KhZrKt9Upqip61i9E3vMvxRdHlbEFiEXfk9YARYxOZZD6VQ2pUunBmtRXo1Lf8hMVVcNl8JafV38aM2/Fu5V16Bn63r6xt97j09+MXSFi4BniGFae3hzbH9+hYBzkpuUh5aZmHuanZOZgIuvbGiNeomCnaxxap2upaCZsq+1kAACH5BAkBAAEALAAAAAAoACgAAAKXjI8By5zf4kOxTVrXNVlv1X0d8IGZGKLnNpYtm8Lr9cqVeuOSvfOW79D9aDHizNhDJidFZhNydEahOaDH6nomtJjp1tutKoNWkvA6JqfRVLHU/QUfau9l2x7G54d1fl995xcIGAdXqMfBNadoYrhH+Mg2KBlpVpbluCiXmMnZ2Sh4GBqJ+ckIOqqJ6LmKSllZmsoq6wpQAAAh+QQJAQABACwAAAAAKAAoAAAClYx/oLvoxuJDkU1a1YUZbJ59nSd2ZXhWqbRa2/gF8Gu2DY3iqs7yrq+xBYEkYvFSM8aSSObE+ZgRl1BHFZNr7pRCavZ5BW2142hY3AN/zWtsmf12p9XxxFl2lpLn1rseztfXZjdIWIf2s5dItwjYKBgo9yg5pHgzJXTEeGlZuenpyPmpGQoKOWkYmSpaSnqKileI2FAAACH5BAkBAAEALAAAAAAoACgAAAKVjB+gu+jG4kORTVrVhRlsnn2dJ3ZleFaptFrb+CXmO9OozeL5VfP99HvAWhpiUdcwkpBH3825AwYdU8xTqlLGhtCosArKMpvfa1mMRae9VvWZfeB2XfPkeLmm18lUcBj+p5dnN8jXZ3YIGEhYuOUn45aoCDkp16hl5IjYJvjWKcnoGQpqyPlpOhr3aElaqrq56Bq7VAAAOw==");height:100%;filter:alpha(opacity=25);opacity:0.25}.ui-progressbar-indeterminate .ui-progressbar-value{background-image:none}/*! + * jQuery UI Resizable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */.ui-resizable{position:relative}.ui-resizable-handle{position:absolute;font-size:0.1px;display:block;-ms-touch-action:none;touch-action:none}.ui-resizable-disabled .ui-resizable-handle,.ui-resizable-autohide .ui-resizable-handle{display:none}.ui-resizable-n{cursor:n-resize;height:7px;width:100%;top:-5px;left:0}.ui-resizable-s{cursor:s-resize;height:7px;width:100%;bottom:-5px;left:0}.ui-resizable-e{cursor:e-resize;width:7px;right:-5px;top:0;height:100%}.ui-resizable-w{cursor:w-resize;width:7px;left:-5px;top:0;height:100%}.ui-resizable-se{cursor:se-resize;width:12px;height:12px;right:1px;bottom:1px}.ui-resizable-sw{cursor:sw-resize;width:9px;height:9px;left:-5px;bottom:-5px}.ui-resizable-nw{cursor:nw-resize;width:9px;height:9px;left:-5px;top:-5px}.ui-resizable-ne{cursor:ne-resize;width:9px;height:9px;right:-5px;top:-5px}/*! + * jQuery UI Selectable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */.ui-selectable{-ms-touch-action:none;touch-action:none}.ui-selectable-helper{position:absolute;z-index:100;border:1px dotted black}/*! + * jQuery UI Selectmenu 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/selectmenu/#theming + */.ui-selectmenu-menu{padding:0;margin:0;position:absolute;top:0;left:0;display:none}.ui-selectmenu-menu .ui-menu{overflow:auto;overflow-x:hidden;padding-bottom:1px}.ui-selectmenu-menu .ui-menu .ui-selectmenu-optgroup{font-size:1em;font-weight:bold;line-height:1.5;padding:2px 0.4em;margin:0.5em 0 0 0;height:auto;border:0}.ui-selectmenu-open{display:block}.ui-selectmenu-text{display:block;margin-right:20px;overflow:hidden;text-overflow:ellipsis}.ui-selectmenu-button.ui-button{text-align:left;white-space:nowrap;width:14em}.ui-selectmenu-icon.ui-icon{float:right;margin-top:0}/*! + * jQuery UI Sortable 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + */.ui-sortable-handle{-ms-touch-action:none;touch-action:none}/*! + * jQuery UI Slider 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/slider/#theming + */.ui-slider{position:relative;text-align:left}.ui-slider .ui-slider-handle{position:absolute;z-index:2;width:1.2em;height:1.2em;cursor:default;-ms-touch-action:none;touch-action:none}.ui-slider .ui-slider-range{position:absolute;z-index:1;font-size:.7em;display:block;border:0;background-position:0 0}.ui-slider.ui-state-disabled .ui-slider-handle,.ui-slider.ui-state-disabled .ui-slider-range{filter:inherit}.ui-slider-horizontal{height:.8em}.ui-slider-horizontal .ui-slider-handle{top:-.3em;margin-left:-.6em}.ui-slider-horizontal .ui-slider-range{top:0;height:100%}.ui-slider-horizontal .ui-slider-range-min{left:0}.ui-slider-horizontal .ui-slider-range-max{right:0}.ui-slider-vertical{width:.8em;height:100px}.ui-slider-vertical .ui-slider-handle{left:-.3em;margin-left:0;margin-bottom:-.6em}.ui-slider-vertical .ui-slider-range{left:0;width:100%}.ui-slider-vertical .ui-slider-range-min{bottom:0}.ui-slider-vertical .ui-slider-range-max{top:0}/*! + * jQuery UI Spinner 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/spinner/#theming + */.ui-spinner{position:relative;display:inline-block;overflow:hidden;padding:0;vertical-align:middle}.ui-spinner-input{border:none;background:none;color:inherit;padding:.222em 0;margin:.2em 0;vertical-align:middle;margin-left:.4em;margin-right:2em}.ui-spinner-button{width:1.6em;height:50%;font-size:.5em;padding:0;margin:0;text-align:center;position:absolute;cursor:default;display:block;overflow:hidden;right:0}.ui-spinner a.ui-spinner-button{border-top-style:none;border-bottom-style:none;border-right-style:none}.ui-spinner-up{top:0}.ui-spinner-down{bottom:0}/*! + * jQuery UI Tabs 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/tabs/#theming + */.ui-tabs{position:relative;padding:.2em}.ui-tabs .ui-tabs-nav{margin:0;padding:.2em .2em 0}.ui-tabs .ui-tabs-nav li{list-style:none;float:left;position:relative;top:0;margin:1px .2em 0 0;border-bottom-width:0;padding:0;white-space:nowrap}.ui-tabs .ui-tabs-nav .ui-tabs-anchor{float:left;padding:.5em 1em;text-decoration:none}.ui-tabs .ui-tabs-nav li.ui-tabs-active{margin-bottom:-1px;padding-bottom:1px}.ui-tabs .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-state-disabled .ui-tabs-anchor,.ui-tabs .ui-tabs-nav li.ui-tabs-loading .ui-tabs-anchor{cursor:text}.ui-tabs-collapsible .ui-tabs-nav li.ui-tabs-active .ui-tabs-anchor{cursor:pointer}.ui-tabs .ui-tabs-panel{display:block;border-width:0;padding:1em 1.4em;background:none}/*! + * jQuery UI Tooltip 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/tooltip/#theming + */.ui-tooltip{padding:8px;position:absolute;z-index:9999;max-width:300px}body .ui-tooltip{border-width:2px}/*! + * jQuery UI CSS Framework 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + *//*! + * jQuery UI CSS Framework 1.12.1 + * http://jqueryui.com + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * http://jquery.org/license + * + * http://api.jqueryui.com/category/theming/ + */div.tagsinput{border:1px solid #CCC;background:#FFF;padding:5px;width:300px;height:100px;overflow-y:auto}div.tagsinput span.tag{border:1px solid #a5d24a;-moz-border-radius:2px;-webkit-border-radius:2px;display:block;float:left;padding:5px;text-decoration:none;background:#cde69c;color:#638421;margin-right:5px;margin-bottom:5px;font-family:helvetica;font-size:13px}div.tagsinput span.tag a{font-weight:bold;color:#82ad2b;text-decoration:none;font-size:11px}div.tagsinput input{width:80px;margin:0px;font-family:helvetica;font-size:13px;border:1px solid transparent;padding:5px;background:transparent;color:#000;outline:0px;margin-right:5px;margin-bottom:5px}div.tagsinput div{display:block;float:left}.tags_clear{clear:both;width:100%;height:0px}.not_valid{background:#FBD8DB !important;color:#90111A !important}body.agendadescommuns{font-family:georgia, serif}body.agendadescommuns header.top{padding-left:120px;padding-right:120px;background-image:url(agendadescommuns.png);background-repeat:no-repeat;background-position:20px top}body.agendadescommuns header.top img.logo{display:none}body.agendadescommuns ul.countries,body.agendadescommuns form#orga_search,body.agendadescommuns a#banner{display:none}body.agendadescommuns .city{text-transform:uppercase}body.agendadescommuns.events.index table tr .day_number{color:#727070}body.agendadescommuns.events.index table tr td.current-month.past,body.agendadescommuns.events.index table tr td.current-month.past .day_number{color:#FFFAEB;background-color:#7494B5}body.agendadescommuns.events.index table tr td.current-month.today{border-color:#FFFAEB;background-color:#DBEDFF}body.agendadescommuns.events.index table tr td.current-month.future{color:#444;border-color:#EAE4D3;background-color:#F0E8D1}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("/assets/font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot?v=4.7.0");src:url("/assets/font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot?v=4.7.0#iefix") format("embedded-opentype"),url("/assets/font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2?v=4.7.0") format("woff2"),url("/assets/font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff?v=4.7.0") format("woff"),url("/assets/font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf?v=4.7.0") format("truetype"),url("/assets/font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.awesome-marker{background:url(/assets/markers-soft-e78784e4ed70aaffddd73c315fab590233cc4e7b72388d7dd47a14796fc7c739.png) no-repeat 0 0;width:35px;height:46px;position:absolute;left:0;top:0;display:block;text-align:center}.awesome-marker-shadow{background:url(/assets/markers-shadow-8703a2262710f5e3d29e65d2acdf90d6512e159e119d27b8234731d8a6208a20.png) no-repeat 0 0;width:36px;height:16px}@media (min--moz-device-pixel-ratio: 1.5), (-o-min-device-pixel-ratio: 3 / 2), (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5), (min-resolution: 1.5dppx){.awesome-marker{background-image:url(/assets/markers-soft@2x-c1e77253a8bfbe30cec24885d7046f443b76ebb66f4c961f77083b03f4a5cbaf.png);background-size:720px 46px}.awesome-marker-shadow{background-image:url(/assets/markers-shadow@2x-b21a536be27313fb504f69f5899ff0b1245b276571769ac08d6c32c35676e47a.png);background-size:35px 16px}}.awesome-marker i{color:#333;margin-top:10px;display:inline-block;font-size:14px}.awesome-marker .icon-white{color:#fff}.awesome-marker-icon-red{background-position:0 0}.awesome-marker-icon-darkred{background-position:-180px 0}.awesome-marker-icon-lightred{background-position:-360px 0}.awesome-marker-icon-orange{background-position:-36px 0}.awesome-marker-icon-beige{background-position:-396px 0}.awesome-marker-icon-green{background-position:-72px 0}.awesome-marker-icon-darkgreen{background-position:-252px 0}.awesome-marker-icon-lightgreen{background-position:-432px 0}.awesome-marker-icon-blue{background-position:-108px 0}.awesome-marker-icon-darkblue{background-position:-216px 0}.awesome-marker-icon-lightblue{background-position:-468px 0}.awesome-marker-icon-purple{background-position:-144px 0}.awesome-marker-icon-darkpurple{background-position:-288px 0}.awesome-marker-icon-pink{background-position:-504px 0}.awesome-marker-icon-cadetblue{background-position:-324px 0}.awesome-marker-icon-white{background-position:-574px 0}.awesome-marker-icon-gray{background-position:-648px 0}.awesome-marker-icon-lightgray{background-position:-612px 0}.awesome-marker-icon-black{background-position:-682px 0}*{height:auto;padding:0;max-width:100%;box-sizing:border-box;margin-left:auto;margin-right:auto}html{color:black;height:100%;text-align:center;font-family:sans-serif;background-color:#9CC5EE}html.iframe{background-color:transparent}html.iframe body{border:none;margin:0;padding:0;background-color:transparent}html.iframe body>header,html.iframe body>footer,html.iframe body h2,html.iframe body nav,html.iframe body .region_selector,html.iframe body .orga_search,html.iframe body .formats,html.iframe body #banner{display:none}html.iframe body ul{margin:0}body{border:1px solid #68A6E5;margin:0.3em;padding:10px;padding-top:0;background-color:white}body header *,body main *,body footer *{transition-duration:0.5s}a:link,a:visited{color:#258;text-decoration:none}a:hover,a:active,a:focus{color:#2E97FF}#flash_messages .flash{color:white;border:solid 1px #007ba0;margin:1em auto;padding:0.5em 1.1em;max-width:50em;font-size:x-large;text-align:justify;box-shadow:0 0 0.3em gray;border-radius:3px;background-color:#008cba}#flash_messages .flash.notice{border-color:#3a945b;background-color:#43ac6a}#flash_messages .flash.alert{border-color:#de2b0f;background-color:#f04124}h3.warning{color:white;margin:0;padding:1em;display:inline-block;text-align:center;background-color:orange;box-shadow:0 0 0.3em gray;border-radius:1em}header.top img.logo{float:left;margin-left:20px}header.top ul.countries{float:right;font-size:smaller;margin-top:0.2em;line-height:1em;margin-left:2em;margin-right:1em;list-style-type:none}header.top ul.countries li{text-align:left}header.top ul.countries li a:before{margin-right:0.75em}header.top ul.countries li.map_france a:before{content:url(/assets/france-f4341a7ec8331161a9c8d5298f808014c3fc9c799b5a29ed95eb56a7f3ccd0df.png)}header.top ul.countries li.map_quebec a:before{content:url(/assets/quebec-776d563b6a4ac4312cae9f0bfe630c20711346e8dbddd41040998eba79f4b588.png)}header.top ul.countries li.map_belgique a:before{content:url(/assets/belgique-3b8b772a522de2cbae7714b35a956faf2c394419b532a14bba982fed3f341091.png)}header.top ul.countries li.map_suisse a:before{content:url(/assets/suisse-58d067f1c3fcdc4000fa13e95896cd5369a2b91aafd314475aa5e29da0b543d1.png)}header.top h1{margin:0.5em 0;font-size:2.1em}header.top h2{font-size:1.2em;font-style:italic;font-weight:normal;margin-top:-0.8em}main,body.mce-content-body{position:relative}main>*,body.mce-content-body>*{max-width:80em}main h1,body.mce-content-body h1{margin:10px auto 5px auto;letter-spacing:2px}main h3,body.mce-content-body h3{text-align:left}main ul,main ol,body.mce-content-body ul,body.mce-content-body ol{text-align:left;padding-left:1em;list-style-position:inside}main p,body.mce-content-body p{text-align:left;line-height:1.5}main p.label,body.mce-content-body p.label{text-align:center}main p span.label:after,body.mce-content-body p span.label:after{content:":"}main hr,body.mce-content-body hr{margin:10px 20px}main.mce-content-body,body.mce-content-body.mce-content-body{margin:0;border:solid thin transparent;font-size:initial;min-height:100%;font-family:Palatino, "URW Palladio L", "dejavu serif", georgia, serif}aside{float:right;width:16em;border:1px solid #96CFFF;margin:0 1.2em;z-index:10;padding:5px;position:relative;font-size:0.7em;box-shadow:4px 4px 1em gray;margin-right:-4em;border-radius:10px;background-color:#D6ECFF}aside h1{font-weight:normal;border-bottom:1px black solid;padding-bottom:0.3em}aside h2{margin:0;font-size:larger}aside h2 a{padding:0.5em 0;display:block}iframe{border:none;max-width:100%;margin-bottom:1.6em;border-radius:1em}fieldset{border:none;margin:1em auto;padding:1em;box-shadow:1px 1px 5px gray;border-radius:0.4em}fieldset legend{float:left;color:gray;padding:0 1em;margin-top:-1.6em;background-color:white}fieldset h2{margin-top:0}label{text-align:right;padding-right:0.4em}.mce-tinymce{border:1px solid darkgray;box-shadow:1px 1px 2px lightgray;border-radius:0.4em}input,textarea,select,a.button,button{color:black;margin:3px 0;border:1px solid darkgray;outline:none;padding:0.2em 0.4em;font-size:inherit;box-shadow:1px 1px 2px lightgray;font-family:inherit;border-radius:0.4em;background-color:white}input:focus,textarea:focus,select:focus,a.button:focus,button:focus{background-color:#F0F8FF;box-shadow:1px 1px 4px gray}input[type=radio],textarea[type=radio],select[type=radio],a.button[type=radio],button[type=radio]{box-shadow:none}input[type=radio]+label,textarea[type=radio]+label,select[type=radio]+label,a.button[type=radio]+label,button[type=radio]+label{text-align:left}input[type=submit],input.button,input button,textarea[type=submit],textarea.button,textarea button,select[type=submit],select.button,select button,a.button[type=submit],a.button.button,a.button button,button[type=submit],button.button,button button{color:black;border:none;cursor:pointer;margin:3px 0.4em;padding:0.2em 0.6em;font-size:larger;box-shadow:2px 2px 2px gray;font-weight:bolder;background-color:#9CC5EE}select,option{color:black;padding:2px 4px;background-color:white}button.ws-popover-opener{color:darkgray;background-color:transparent}.field_with_errors{display:inline}.field_with_errors input,.field_with_errors textarea,.field_with_errors select,.field_with_errors a.button{background-color:pink}footer.bottom nav{clear:both}footer.bottom nav a{padding:1.8%;display:inline-block;font-weight:bolder}footer.bottom nav a em.fa{font-size:x-large}footer.bottom nav a:hover{text-shadow:0 0 1em lightgray}#loading{top:45%;left:45%;color:gray;display:none;position:fixed;font-size:10vw;text-shadow:0 0 4px gray;vertical-align:baseline}.pagination span{display:none}.pagination span.next{display:block}body.digests main{padding:1em;max-width:60em;box-shadow:1px 1px 5px gray;border-radius:0.4em}body.digests main h2{margin:0;padding:0;font-size:larger;text-align:left}body.digests main .description p,body.digests main .second_part p{white-space:pre-line}body.digests main textarea{width:40em;height:20em}header.calendar-header{font-size:1.5em;margin-top:0.4em;font-weight:bolder}header.calendar-header a{font-weight:normal}header.calendar-header a:first-child,header.calendar-header a:last-child{margin:0 0.3em}em.locality.fa{color:gray}span.city:after,strong.city:after,em.city:after{content:":"}p.full_address span:after{content:","}p.full_address span:last-child:after{content:""}.calendar-header,.month{text-transform:capitalize}body.events.index table{width:90%;max-width:100%;table-layout:fixed}body.events.index table tr th{padding-bottom:0.35em;text-transform:capitalize}body.events.index table tr td{width:14%;height:4.8em;border:1px solid #89B7E5;padding:1px;position:relative;vertical-align:top;background-color:#A1C3E7}body.events.index table tr td.past .event{overflow:hidden;white-space:nowrap;text-overflow:ellipsis}body.events.index table tr td.today{border:1px solid #FFB578;background-color:#FFE0C7}body.events.index table tr td.today .day_number{color:#531}body.events.index table tr td.today .event a:link,body.events.index table tr td.today .event a:visited{color:#730}body.events.index table tr td.future{border-color:#96cfff;background-color:#C6E5FF}body.events.index table tr td.prev-month,body.events.index table tr td.next-month{border:none;background-color:#ededed}body.events.index table tr td.prev-month li,body.events.index table tr td.next-month li{color:#555}body.events.index table tr td.prev-month .event a:link,body.events.index table tr td.prev-month .event a:visited,body.events.index table tr td.next-month .event a:link,body.events.index table tr td.next-month .event a:visited{color:#444}body.events.index table tr td .day_number{float:left;color:#111;font-size:0.6em;font-weight:bolder;line-height:1.2em;letter-spacing:2px}body.events.index table tr td:hover .day_number{color:black}body.events.index table tr td ul{clear:left;margin:0;padding:0}body.events.index table tr td ul li{color:#444;margin:0;padding:0}body.events.index table tr td ul li em.fa.fa-globe{color:red;font-size:smaller}body.events.index table tr td ul .event{padding-left:0.2em}body.events.index table tr td ul .event img{vertical-align:middle}body.events.index table tr td ul .event a:link,body.events.index table tr td ul .event a:visited{color:#111;font-size:smaller}body.events.index table tr td ul .event a:link:hover,body.events.index table tr td ul .event a:visited:hover{color:black;font-weight:bolder}.links{float:right;margin-right:5%;padding-right:2px}a#banner{float:left;margin-left:5%;padding-left:2px}body.events.show main,body.orgas.show main,body.tags.show main,body.pages main{padding:1em;max-width:60em;box-shadow:1px 1px 5px gray;border-radius:0.4em}body.events.show main h2,body.orgas.show main h2,body.tags.show main h2,body.pages main h2{margin-top:1em}#advises{border:1px solid #FFB578;padding-left:10px;padding-right:10px;margin-bottom:1.2em;background-color:#ffe0c7}#advises h1{font-size:1.9em;font-weight:normal}#advises a{color:#8F4900}.description img,body.mce-content-body img{margin-left:1%;margin-right:1%}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("/assets/font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot?v=4.7.0");src:url("/assets/font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot?v=4.7.0#iefix") format("embedded-opentype"),url("/assets/font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2?v=4.7.0") format("woff2"),url("/assets/font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff?v=4.7.0") format("woff"),url("/assets/font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf?v=4.7.0") format("truetype"),url("/assets/font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}.events form .field,.events form .actions,.moderations form .field,.moderations form .actions,.orgas .field,.orgas .actions,.digests form .field,.digests form .actions,.pages form .field,.pages form .actions{max-width:70em;text-align:left;margin-left:auto;margin-right:auto}.events form .field .helper,.events form .actions .helper,.moderations form .field .helper,.moderations form .actions .helper,.orgas .field .helper,.orgas .actions .helper,.digests form .field .helper,.digests form .actions .helper,.pages form .field .helper,.pages form .actions .helper{color:gray;font-style:italic}.events form .field .helper p,.events form .actions .helper p,.moderations form .field .helper p,.moderations form .actions .helper p,.orgas .field .helper p,.orgas .actions .helper p,.digests form .field .helper p,.digests form .actions .helper p,.pages form .field .helper p,.pages form .actions .helper p{margin:0;margin-left:8em;padding-left:0.4em}.events form .field label,.events form .actions label,.moderations form .field label,.moderations form .actions label,.orgas .field label,.orgas .actions label,.digests form .field label,.digests form .actions label,.pages form .field label,.pages form .actions label{width:8em;display:inline-block}.events form .field input,.events form .field select,.events form .actions input,.events form .actions select,.moderations form .field input,.moderations form .field select,.moderations form .actions input,.moderations form .actions select,.orgas .field input,.orgas .field select,.orgas .actions input,.orgas .actions select,.digests form .field input,.digests form .field select,.digests form .actions input,.digests form .actions select,.pages form .field input,.pages form .field select,.pages form .actions input,.pages form .actions select{border-top-left-radius:0;border-bottom-left-radius:0}.events form .field input[type=radio]+label,.events form .actions input[type=radio]+label,.moderations form .field input[type=radio]+label,.moderations form .actions input[type=radio]+label,.orgas .field input[type=radio]+label,.orgas .actions input[type=radio]+label,.digests form .field input[type=radio]+label,.digests form .actions input[type=radio]+label,.pages form .field input[type=radio]+label,.pages form .actions input[type=radio]+label{width:20em}.events form .field input[type=radio]+label:after,.events form .actions input[type=radio]+label:after,.moderations form .field input[type=radio]+label:after,.moderations form .actions input[type=radio]+label:after,.orgas .field input[type=radio]+label:after,.orgas .actions input[type=radio]+label:after,.digests form .field input[type=radio]+label:after,.digests form .actions input[type=radio]+label:after,.pages form .field input[type=radio]+label:after,.pages form .actions input[type=radio]+label:after{content:""}.events form .field input[type=text],.events form .field input[type=url],.events form .field input[type=email],.events form .field .mce-tinymce,.events form .actions input[type=text],.events form .actions input[type=url],.events form .actions input[type=email],.events form .actions .mce-tinymce,.moderations form .field input[type=text],.moderations form .field input[type=url],.moderations form .field input[type=email],.moderations form .field .mce-tinymce,.moderations form .actions input[type=text],.moderations form .actions input[type=url],.moderations form .actions input[type=email],.moderations form .actions .mce-tinymce,.orgas .field input[type=text],.orgas .field input[type=url],.orgas .field input[type=email],.orgas .field .mce-tinymce,.orgas .actions input[type=text],.orgas .actions input[type=url],.orgas .actions input[type=email],.orgas .actions .mce-tinymce,.digests form .field input[type=text],.digests form .field input[type=url],.digests form .field input[type=email],.digests form .field .mce-tinymce,.digests form .actions input[type=text],.digests form .actions input[type=url],.digests form .actions input[type=email],.digests form .actions .mce-tinymce,.pages form .field input[type=text],.pages form .field input[type=url],.pages form .field input[type=email],.pages form .field .mce-tinymce,.pages form .actions input[type=text],.pages form .actions input[type=url],.pages form .actions input[type=email],.pages form .actions .mce-tinymce{min-width:22em}.events form .field .mce-tinymce,.events form .actions .mce-tinymce,.moderations form .field .mce-tinymce,.moderations form .actions .mce-tinymce,.orgas .field .mce-tinymce,.orgas .actions .mce-tinymce,.digests form .field .mce-tinymce,.digests form .actions .mce-tinymce,.pages form .field .mce-tinymce,.pages form .actions .mce-tinymce{display:inline-block;font-size:inherit;font-family:inherit;border-radius:0.4em;border-top-left-radius:0;border-bottom-left-radius:0}.events form .field .mce-tinymce .mce-container-body .mce-panel.mce-first,.events form .actions .mce-tinymce .mce-container-body .mce-panel.mce-first,.moderations form .field .mce-tinymce .mce-container-body .mce-panel.mce-first,.moderations form .actions .mce-tinymce .mce-container-body .mce-panel.mce-first,.orgas .field .mce-tinymce .mce-container-body .mce-panel.mce-first,.orgas .actions .mce-tinymce .mce-container-body .mce-panel.mce-first,.digests form .field .mce-tinymce .mce-container-body .mce-panel.mce-first,.digests form .actions .mce-tinymce .mce-container-body .mce-panel.mce-first,.pages form .field .mce-tinymce .mce-container-body .mce-panel.mce-first,.pages form .actions .mce-tinymce .mce-container-body .mce-panel.mce-first{border-top-right-radius:0.4em}.events form .field .mce-tinymce .mce-container-body .mce-panel.mce-last,.events form .actions .mce-tinymce .mce-container-body .mce-panel.mce-last,.moderations form .field .mce-tinymce .mce-container-body .mce-panel.mce-last,.moderations form .actions .mce-tinymce .mce-container-body .mce-panel.mce-last,.orgas .field .mce-tinymce .mce-container-body .mce-panel.mce-last,.orgas .actions .mce-tinymce .mce-container-body .mce-panel.mce-last,.digests form .field .mce-tinymce .mce-container-body .mce-panel.mce-last,.digests form .actions .mce-tinymce .mce-container-body .mce-panel.mce-last,.pages form .field .mce-tinymce .mce-container-body .mce-panel.mce-last,.pages form .actions .mce-tinymce .mce-container-body .mce-panel.mce-last{border-bottom-right-radius:0.4em}.events form .field label:before,.events form th:before,.events form th a:before,.moderations form .field label:before,.moderations form th:before,.moderations form th a:before,.orgas .field label:before,.orgas th:before,.orgas th a:before,.digests form .field label:before,.digests form th:before,.digests form th a:before,.pages form .field label:before,.pages form th:before,.pages form th a:before{color:gray;margin-right:0.4em;display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.events form .field.title label:before,.moderations form .field.title label:before,.orgas .field.title label:before,.digests form .field.title label:before,.pages form .field.title label:before{content:""}.events form .field.start_time label:before,.moderations form .field.start_time label:before,.orgas .field.start_time label:before,.digests form .field.start_time label:before,.pages form .field.start_time label:before{content:""}.events form .field.end_time label:before,.moderations form .field.end_time label:before,.orgas .field.end_time label:before,.digests form .field.end_time label:before,.pages form .field.end_time label:before{content:""}.events form .field.repeat label:before,.moderations form .field.repeat label:before,.orgas .field.repeat label:before,.digests form .field.repeat label:before,.pages form .field.repeat label:before{content:""}.events form .field.rule>label:before,.moderations form .field.rule>label:before,.orgas .field.rule>label:before,.digests form .field.rule>label:before,.pages form .field.rule>label:before{content:""}.events form .field.description label:before,.moderations form .field.description label:before,.orgas .field.description label:before,.digests form .field.description label:before,.pages form .field.description label:before{content:""}.events form .field.place_name label:before,.moderations form .field.place_name label:before,.orgas .field.place_name label:before,.digests form .field.place_name label:before,.pages form .field.place_name label:before{content:""}.events form .field.address label:before,.moderations form .field.address label:before,.orgas .field.address label:before,.digests form .field.address label:before,.pages form .field.address label:before{content:""}.events form .field.city label:before,.events form th.city a:before,.moderations form .field.city label:before,.moderations form th.city a:before,.orgas .field.city label:before,.orgas th.city a:before,.digests form .field.city label:before,.digests form th.city a:before,.pages form .field.city label:before,.pages form th.city a:before{content:""}.events form .field.department label:before,.events form th.department a:before,.moderations form .field.department label:before,.moderations form th.department a:before,.orgas .field.department label:before,.orgas th.department a:before,.digests form .field.department label:before,.digests form th.department a:before,.pages form .field.department label:before,.pages form th.department a:before{content:""}.events form .field.region label:before,.events form th.region a:before,.moderations form .field.region label:before,.moderations form th.region a:before,.orgas .field.region label:before,.orgas th.region a:before,.digests form .field.region label:before,.digests form th.region a:before,.pages form .field.region label:before,.pages form th.region a:before{content:""}.events form .field.locality>label:before,.moderations form .field.locality>label:before,.orgas .field.locality>label:before,.digests form .field.locality>label:before,.pages form .field.locality>label:before{content:""}.events form .field.contact label:before,.moderations form .field.contact label:before,.orgas .field.contact label:before,.digests form .field.contact label:before,.pages form .field.contact label:before{content:""}.events form .field.submitter label:before,.moderations form .field.submitter label:before,.orgas .field.submitter label:before,.digests form .field.submitter label:before,.pages form .field.submitter label:before{content:""}.events form .field.url label:before,.events form th.url:before,.moderations form .field.url label:before,.moderations form th.url:before,.orgas .field.url label:before,.orgas th.url:before,.digests form .field.url label:before,.digests form th.url:before,.pages form .field.url label:before,.pages form th.url:before{content:""}.events form .field.diaspora label:before,.events form th.diaspora:before,.moderations form .field.diaspora label:before,.moderations form th.diaspora:before,.orgas .field.diaspora label:before,.orgas th.diaspora:before,.digests form .field.diaspora label:before,.digests form th.diaspora:before,.pages form .field.diaspora label:before,.pages form th.diaspora:before{content:""}.events form .field.tags label:before,.events form th.tags a:before,.moderations form .field.tags label:before,.moderations form th.tags a:before,.orgas .field.tags label:before,.orgas th.tags a:before,.digests form .field.tags label:before,.digests form th.tags a:before,.pages form .field.tags label:before,.pages form th.tags a:before{content:""}.events form .field.active label:before,.moderations form .field.active label:before,.orgas .field.active label:before,.digests form .field.active label:before,.pages form .field.active label:before{content:""}.events form .field.reason label:before,.moderations form .field.reason label:before,.orgas .field.reason label:before,.digests form .field.reason label:before,.pages form .field.reason label:before{content:""}.events form .field.kind>label:before,.events form th.kind:before,.moderations form .field.kind>label:before,.moderations form th.kind:before,.orgas .field.kind>label:before,.orgas th.kind:before,.digests form .field.kind>label:before,.digests form th.kind:before,.pages form .field.kind>label:before,.pages form th.kind:before{content:""}.events form .field.feed label:before,.events form th.feed:before,.moderations form .field.feed label:before,.moderations form th.feed:before,.orgas .field.feed label:before,.orgas th.feed:before,.digests form .field.feed label:before,.digests form th.feed:before,.pages form .field.feed label:before,.pages form th.feed:before{content:""}.events form .field.future label:before,.moderations form .field.future label:before,.orgas .field.future label:before,.digests form .field.future label:before,.pages form .field.future label:before{content:""}.events form .field.period_year label:before,.moderations form .field.period_year label:before,.orgas .field.period_year label:before,.digests form .field.period_year label:before,.pages form .field.period_year label:before{content:""}.events form .field.period_week label:before,.moderations form .field.period_week label:before,.orgas .field.period_week label:before,.digests form .field.period_week label:before,.pages form .field.period_week label:before{content:""}.events form .field.near_location label:before,.moderations form .field.near_location label:before,.orgas .field.near_location label:before,.digests form .field.near_location label:before,.pages form .field.near_location label:before{content:""}.events form .field.near_distance label:before,.moderations form .field.near_distance label:before,.orgas .field.near_distance label:before,.digests form .field.near_distance label:before,.pages form .field.near_distance label:before{content:""}.events form .field.iframe label:before,.moderations form .field.iframe label:before,.orgas .field.iframe label:before,.digests form .field.iframe label:before,.pages form .field.iframe label:before{content:""}.events form .field .radios label,.moderations form .field .radios label,.orgas .field .radios label,.digests form .field .radios label,.pages form .field .radios label{width:auto !important;margin-right:1em}.events form [type=radio],.moderations form [type=radio],.orgas [type=radio],.digests form [type=radio],.pages form [type=radio]{display:none}.events form [type=radio]+label:before,.moderations form [type=radio]+label:before,.orgas [type=radio]+label:before,.digests form [type=radio]+label:before,.pages form [type=radio]+label:before{color:black;content:"";font-size:larger}.events form [type=radio]:checked+label:before,.moderations form [type=radio]:checked+label:before,.orgas [type=radio]:checked+label:before,.digests form [type=radio]:checked+label:before,.pages form [type=radio]:checked+label:before{color:black;content:"";text-shadow:0 0 1px gray}.events form .actions,.moderations form .actions,.orgas .actions,.digests form .actions,.pages form .actions{text-align:center}.events form .actions input,.moderations form .actions input,.orgas .actions input,.digests form .actions input,.pages form .actions input{font-size:x-large}.quantity{text-align:right !important;white-space:nowrap}table{margin:1em auto;border-spacing:2px}table th{padding:0.3em 0.6em}table.list tbody tr:nth-child(odd){background-color:#D1EAFF}table.list tbody tr:nth-child(even){background-color:#C9E2F5}table.list th{font-size:larger}table.list td{padding:0.2em;text-align:left}table.list td.updated_at{text-align:center}dl{margin:1em auto;max-width:40em}dl dt{clear:left;float:left;padding:0.2em 1em;min-width:50%;text-align:right}dl dd{padding:0.2em 1em;text-align:left}dl dd.quantity{padding-right:16em}dl dd h3{display:inline}dl dt+dd+dt+dd{border-top:1px solid #999}ul.counters{text-align:center}ul.counters li{display:inline}ul.counters li a:link,ul.counters li a:visited{color:white;display:inline-block;padding:0.2em 0.8em;border-radius:1em;background-color:darkblue}.edit_link:link,.delete_link:link{display:none}.leaflet-map-pane,.leaflet-tile,.leaflet-marker-icon,.leaflet-marker-shadow,.leaflet-tile-pane,.leaflet-tile-container,.leaflet-overlay-pane,.leaflet-shadow-pane,.leaflet-marker-pane,.leaflet-popup-pane,.leaflet-overlay-pane svg,.leaflet-zoom-box,.leaflet-image-layer,.leaflet-layer{position:absolute;left:0;top:0}.leaflet-container{overflow:hidden;-ms-touch-action:none;touch-action:none}.leaflet-tile,.leaflet-marker-icon,.leaflet-marker-shadow{-webkit-user-select:none;-moz-user-select:none;user-select:none;-webkit-user-drag:none}.leaflet-marker-icon,.leaflet-marker-shadow{display:block}.leaflet-container img{max-width:none !important}.leaflet-container img.leaflet-image-layer{max-width:15000px !important}.leaflet-tile{filter:inherit;visibility:hidden}.leaflet-tile-loaded{visibility:inherit}.leaflet-zoom-box{width:0;height:0}.leaflet-overlay-pane svg{-moz-user-select:none}.leaflet-tile-pane{z-index:2}.leaflet-objects-pane{z-index:3}.leaflet-overlay-pane{z-index:4}.leaflet-shadow-pane{z-index:5}.leaflet-marker-pane{z-index:6}.leaflet-popup-pane{z-index:7}.leaflet-vml-shape{width:1px;height:1px}.lvml{behavior:url(#default#VML);display:inline-block;position:absolute}.leaflet-control{position:relative;z-index:7;pointer-events:auto}.leaflet-top,.leaflet-bottom{position:absolute;z-index:1000;pointer-events:none}.leaflet-top{top:0}.leaflet-right{right:0}.leaflet-bottom{bottom:0}.leaflet-left{left:0}.leaflet-control{float:left;clear:both}.leaflet-right .leaflet-control{float:right}.leaflet-top .leaflet-control{margin-top:10px}.leaflet-bottom .leaflet-control{margin-bottom:10px}.leaflet-left .leaflet-control{margin-left:10px}.leaflet-right .leaflet-control{margin-right:10px}.leaflet-fade-anim .leaflet-tile,.leaflet-fade-anim .leaflet-popup{opacity:0;-webkit-transition:opacity 0.2s linear;-moz-transition:opacity 0.2s linear;-o-transition:opacity 0.2s linear;transition:opacity 0.2s linear}.leaflet-fade-anim .leaflet-tile-loaded,.leaflet-fade-anim .leaflet-map-pane .leaflet-popup{opacity:1}.leaflet-zoom-anim .leaflet-zoom-animated{-webkit-transition:-webkit-transform 0.25s cubic-bezier(0, 0, 0.25, 1);-moz-transition:-moz-transform 0.25s cubic-bezier(0, 0, 0.25, 1);-o-transition:-o-transform 0.25s cubic-bezier(0, 0, 0.25, 1);transition:transform 0.25s cubic-bezier(0, 0, 0.25, 1)}.leaflet-zoom-anim .leaflet-tile,.leaflet-pan-anim .leaflet-tile,.leaflet-touching .leaflet-zoom-animated{-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none}.leaflet-zoom-anim .leaflet-zoom-hide{visibility:hidden}.leaflet-clickable{cursor:pointer}.leaflet-container{cursor:-webkit-grab;cursor:-moz-grab}.leaflet-popup-pane,.leaflet-control{cursor:auto}.leaflet-dragging .leaflet-container,.leaflet-dragging .leaflet-clickable{cursor:move;cursor:-webkit-grabbing;cursor:-moz-grabbing}.leaflet-container{background:#ddd;outline:0}.leaflet-container a{color:#0078A8}.leaflet-container a.leaflet-active{outline:2px solid orange}.leaflet-zoom-box{border:2px dotted #38f;background:rgba(255,255,255,0.5)}.leaflet-container{font:12px/1.5 "Helvetica Neue", Arial, Helvetica, sans-serif}.leaflet-bar{box-shadow:0 1px 5px rgba(0,0,0,0.65);border-radius:4px}.leaflet-bar a,.leaflet-bar a:hover{background-color:#fff;border-bottom:1px solid #ccc;width:26px;height:26px;line-height:26px;display:block;text-align:center;text-decoration:none;color:black}.leaflet-bar a,.leaflet-control-layers-toggle{background-position:50% 50%;background-repeat:no-repeat;display:block}.leaflet-bar a:hover{background-color:#f4f4f4}.leaflet-bar a:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.leaflet-bar a:last-child{border-bottom-left-radius:4px;border-bottom-right-radius:4px;border-bottom:none}.leaflet-bar a.leaflet-disabled{cursor:default;background-color:#f4f4f4;color:#bbb}.leaflet-touch .leaflet-bar a{width:30px;height:30px;line-height:30px}.leaflet-control-zoom-in,.leaflet-control-zoom-out{font:bold 18px 'Lucida Console', Monaco, monospace;text-indent:1px}.leaflet-control-zoom-out{font-size:20px}.leaflet-touch .leaflet-control-zoom-in{font-size:22px}.leaflet-touch .leaflet-control-zoom-out{font-size:24px}.leaflet-control-layers{box-shadow:0 1px 5px rgba(0,0,0,0.4);background:#fff;border-radius:5px}.leaflet-control-layers-toggle{background-image:url(/assets/layers-0908aa2a72a082fb2563a2427a5e4fb247571862b448b80fb6f720af1109923e.png);width:36px;height:36px}.leaflet-retina .leaflet-control-layers-toggle{background-image:url(/assets/layers-2x-0c02a2388f637d21f86e6d4b314ec9a968e7b05ad4c3a005280a3f76c0fd3cb8.png);background-size:26px 26px}.leaflet-touch .leaflet-control-layers-toggle{width:44px;height:44px}.leaflet-control-layers .leaflet-control-layers-list,.leaflet-control-layers-expanded .leaflet-control-layers-toggle{display:none}.leaflet-control-layers-expanded .leaflet-control-layers-list{display:block;position:relative}.leaflet-control-layers-expanded{padding:6px 10px 6px 6px;color:#333;background:#fff}.leaflet-control-layers-selector{margin-top:2px;position:relative;top:1px}.leaflet-control-layers label{display:block}.leaflet-control-layers-separator{height:0;border-top:1px solid #ddd;margin:5px -10px 5px -6px}.leaflet-container .leaflet-control-attribution{background:#fff;background:rgba(255,255,255,0.7);margin:0}.leaflet-control-attribution,.leaflet-control-scale-line{padding:0 5px;color:#333}.leaflet-control-attribution a{text-decoration:none}.leaflet-control-attribution a:hover{text-decoration:underline}.leaflet-container .leaflet-control-attribution,.leaflet-container .leaflet-control-scale{font-size:11px}.leaflet-left .leaflet-control-scale{margin-left:5px}.leaflet-bottom .leaflet-control-scale{margin-bottom:5px}.leaflet-control-scale-line{border:2px solid #777;border-top:none;line-height:1.1;padding:2px 5px 1px;font-size:11px;white-space:nowrap;overflow:hidden;-moz-box-sizing:content-box;box-sizing:content-box;background:#fff;background:rgba(255,255,255,0.5)}.leaflet-control-scale-line:not(:first-child){border-top:2px solid #777;border-bottom:none;margin-top:-2px}.leaflet-control-scale-line:not(:first-child):not(:last-child){border-bottom:2px solid #777}.leaflet-touch .leaflet-control-attribution,.leaflet-touch .leaflet-control-layers,.leaflet-touch .leaflet-bar{box-shadow:none}.leaflet-touch .leaflet-control-layers,.leaflet-touch .leaflet-bar{border:2px solid rgba(0,0,0,0.2);background-clip:padding-box}.leaflet-popup{position:absolute;text-align:center}.leaflet-popup-content-wrapper{padding:1px;text-align:left;border-radius:12px}.leaflet-popup-content{margin:13px 19px;line-height:1.4}.leaflet-popup-content p{margin:18px 0}.leaflet-popup-tip-container{margin:0 auto;width:40px;height:20px;position:relative;overflow:hidden}.leaflet-popup-tip{width:17px;height:17px;padding:1px;margin:-10px auto 0;-webkit-transform:rotate(45deg);-moz-transform:rotate(45deg);-ms-transform:rotate(45deg);-o-transform:rotate(45deg);transform:rotate(45deg)}.leaflet-popup-content-wrapper,.leaflet-popup-tip{background:white;box-shadow:0 3px 14px rgba(0,0,0,0.4)}.leaflet-container a.leaflet-popup-close-button{position:absolute;top:0;right:0;padding:4px 4px 0 0;text-align:center;width:18px;height:14px;font:16px/14px Tahoma, Verdana, sans-serif;color:#c3c3c3;text-decoration:none;font-weight:bold;background:transparent}.leaflet-container a.leaflet-popup-close-button:hover{color:#999}.leaflet-popup-scrolled{overflow:auto;border-bottom:1px solid #ddd;border-top:1px solid #ddd}.leaflet-oldie .leaflet-popup-content-wrapper{zoom:1}.leaflet-oldie .leaflet-popup-tip{width:24px;margin:0 auto;-ms-filter:"progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)";filter:progid:DXImageTransform.Microsoft.Matrix(M11=0.70710678, M12=0.70710678, M21=-0.70710678, M22=0.70710678)}.leaflet-oldie .leaflet-popup-tip-container{margin-top:-1px}.leaflet-oldie .leaflet-control-zoom,.leaflet-oldie .leaflet-control-layers,.leaflet-oldie .leaflet-popup-content-wrapper,.leaflet-oldie .leaflet-popup-tip{border:1px solid #999}.leaflet-div-icon{background:#fff;border:1px solid #666}.leaflet-cluster-anim .leaflet-marker-icon,.leaflet-cluster-anim .leaflet-marker-shadow{-webkit-transition:-webkit-transform 0.3s ease-out, opacity 0.3s ease-in;-moz-transition:-moz-transform 0.3s ease-out, opacity 0.3s ease-in;-o-transition:-o-transform 0.3s ease-out, opacity 0.3s ease-in;transition:transform 0.3s ease-out, opacity 0.3s ease-in}.marker-cluster-small{background-color:rgba(181,226,140,0.6)}.marker-cluster-small div{background-color:rgba(110,204,57,0.6)}.marker-cluster-medium{background-color:rgba(241,211,87,0.6)}.marker-cluster-medium div{background-color:rgba(240,194,12,0.6)}.marker-cluster-large{background-color:rgba(253,156,115,0.6)}.marker-cluster-large div{background-color:rgba(241,128,23,0.6)}.leaflet-oldie .marker-cluster-small{background-color:#b5e28c}.leaflet-oldie .marker-cluster-small div{background-color:#6ecc39}.leaflet-oldie .marker-cluster-medium{background-color:#f1d357}.leaflet-oldie .marker-cluster-medium div{background-color:#f0c20c}.leaflet-oldie .marker-cluster-large{background-color:#fd9c73}.leaflet-oldie .marker-cluster-large div{background-color:#f18017}.marker-cluster{background-clip:padding-box;border-radius:20px}.marker-cluster div{width:30px;height:30px;margin-left:5px;margin-top:5px;text-align:center;border-radius:15px;font:12px "Helvetica Neue", Arial, Helvetica, sans-serif}.marker-cluster span{line-height:30px}#map{height:60em;box-shadow:0 0 1em SteelBlue;transition:none}#map *{max-width:initial}#map.event,#map.orga{height:20em}.tags #map{width:40%;height:60em;margin:1.6em 2%;display:inline-block}form.leaflet-control-layers-list label{text-align:left}form.leaflet-control-layers-list label .awesome-marker{height:auto !important;display:inline-block !important;position:relative !important}html.iframe #map{width:100%;max-width:100%;box-shadow:none}@media all and (min-width: 1600px){body{font-size:larger}aside#orga-list{width:auto}#advises{max-width:60em}}@media all and (max-width: 1024px){body.events.index table{width:100%}form#orga_search{right:0}.links,a#banner{margin:0.6em auto}table,table.list,table.dates,table.list.dates{border-spacing:1px}table tbody th,table.list tbody th,table.dates tbody th,table.list.dates tbody th{overflow:hidden;max-width:3em}}@media all and (max-width: 900px){header.top img.logo{margin-left:0}header.top ul.countries{margin-right:0}body{border:none;margin:0}body header.top h1{margin-top:0}body main ul{margin-left:0}form#orga_search{font-size:smaller}}@media all and (max-width: 34em){body{padding:0}p{padding-left:0.4em;padding-right:0.4em}header.top ul.countries{display:none}header.top h1{letter-spacing:initial}header.top h2{font-size:initial}form#orga_search{display:none}body.index table thead{display:none}body.index.events table tr td{clear:both;width:auto;height:auto;margin:1px;display:block;font-size:larger;min-height:1em;border-width:1px !important}body.index.events table tr td.prev-month,body.index.events table tr td.next-month,body.index.events table tr td.current-month.past{display:none}body.index.events table tr td .day_number{font-size:initial}body.index.events table tr td .event a{padding:0.4em 1em}body.index header:active+table tr td.current-month.past,body.index header:hover+table tr td.current-month.past{display:block}body.index.versions table tbody tr td,body.index.orgas table tbody tr td{width:auto;padding:0 0.2em;display:block}body.index.versions table tbody tr td:first-child,body.index.orgas table tbody tr td:first-child{float:left}body.index.versions table tbody td+td+td,body.index.orgas table tbody td+td+td{display:inline-block}body.index.versions table tbody td:first-child+td,body.index.orgas table tbody td:first-child+td{width:100%;display:block}dl dt{min-width:auto;text-align:left}dl dd,dl dd.quantity{padding-right:1em}aside{display:none}.links,a#banner{float:none;padding:0.4em 0}.events h2+p,.events #advises,.events .helper,.moderations h2+p,.moderations #advises,.moderations .helper{display:none}.events form .field,.events form .actions,.moderations form .field,.moderations form .actions{max-width:100%}.events form .field .helper p,.events form .actions .helper p,.moderations form .field .helper p,.moderations form .actions .helper p{margin-left:0;padding-left:0}.events form .field label,.events form .actions label,.moderations form .field label,.moderations form .actions label{width:auto;display:block;text-align:left;margin-left:0}.events form .field input,.events form .field .mce-tinymce,.events form .actions input,.events form .actions .mce-tinymce,.moderations form .field input,.moderations form .field .mce-tinymce,.moderations form .actions input,.moderations form .actions .mce-tinymce{width:100%;border-bottom-left-radius:0.4em}}h2.warning{color:red}.moderations table.list,.notes table.list{min-width:90%;margin-top:15px}.moderations table.list td,.notes table.list td{padding-top:0.5em;padding-bottom:0.5em}.moderations table.list td.empty,.notes table.list td.empty{background-color:white}.moderations table.list td.note,.notes table.list td.note{text-align:left}.moderations table.list td pre,.notes table.list td pre{width:70%;margin:auto;padding:5px;overflow:auto;text-align:left;background-color:#eee}.moderations table.list td em.author,.notes table.list td em.author{display:block;font-size:smaller;text-align:right;font-style:italic;margin-top:15px}.moderations table.list th.actions,.notes table.list th.actions{padding:0}.moderations table.list th.actions a,.notes table.list th.actions a{padding:0.2em;display:inline-block;font-weight:normal}.fa.fa-check,.fa.fa-thumbs-up{color:green}.fa.fa-thumbs-down{color:red}.notes form label:after{content:":"}img.favicon{max-height:2em;border-radius:3px;vertical-align:middle}form#orga_search{right:5%;width:15em;position:absolute;font-size:larger;text-align:right;margin-top:-1.2em;white-space:nowrap;padding-right:2px}form#orga_search label{display:none}form#orga_search em.fa{color:#258}form#orga_search input{border:solid thin #258;margin-left:-1.5em;margin-right:-1.6em;padding-left:1.5em;padding-right:1.2em;background-color:transparent;width:14em}form#orga_search button.search{color:#258;border:none;padding:0;box-shadow:none;margin-top:-0.1em;margin-left:0;vertical-align:middle;background-color:transparent}form#orga_search button.search:hover em.fa{color:#2E97FF}form#orga_search button.search span.label{display:none}body.orgas.show main fieldset.orga{min-width:60em}body.orgas.show main dl,body.orgas.show main iframe,body.orgas.show main fieldset{display:inline-block;min-width:30em;vertical-align:top}table tr.inactive{color:gray}.region_selector{display:inline-block}.region_selector h3{margin:0}.region_selector h3 .placeholder{color:gray}.region_selector ul{margin:0;z-index:10;display:none;position:absolute;margin-top:-7em;list-style:none;box-shadow:1px 1px 4px gray;background-color:white;border-bottom-left-radius:0.4em;border-bottom-right-radius:0.4em}.region_selector ul li{margin:0}.region_selector ul li a{display:block;padding:0.2em 1em;border-top:solid thin black}.region_selector ul li a:hover{text-shadow:1px 1px 0.2em gray;background-color:#eee}.region_selector ul li a em.fa-globe{color:red}.region_selector:hover ul{display:block}body.stats h3{text-align:center}table.list.dates th,table.list.dates td{padding:0 0.6em}table.list.dates .sparkline{padding:0}div.field,div.actions{margin-bottom:10px}#notice{color:green}#error_explanation{width:450px;border:2px solid red;padding-bottom:0;margin-bottom:20px;background-color:#f0f0f0}#error_explanation h2{text-align:left;font-weight:bold;padding:15px;font-size:12px;background-color:#c00;color:#fff}#error_explanation p,#error_explanation ul{padding:0 1em}#error_explanation p li,#error_explanation ul li{font-size:12px;list-style:square}.sessions form{margin:1em auto;display:inline-block}.sessions form .field,.sessions form .actions{text-align:left;margin:0.5em}.sessions form .field *,.sessions form .actions *{vertical-align:middle}.sessions form .field .helper p,.sessions form .actions .helper p{margin:1px 5px 1px 6.6em}.sessions form .field label,.sessions form .actions label{width:8em;display:inline-block}.sessions form .field label:after,.sessions form .actions label:after{content:":"}.sessions form .actions{margin-left:6.75em;margin-bottom:10px}.logout{margin:2em auto}table tfoot td canvas{width:100% !important;background-color:white}p.tags a:after{color:black;content:","}p.tags a:last-child:after{content:""}.css1{font-size:0.8em}.css2{font-size:1em}.css3{font-size:1.4em}.css4{font-size:1.8em}.tagsinput{border:none !important;padding:0 !important;display:inline-block;min-width:22em;vertical-align:top}.tagsinput input,.tagsinput .ui-autocomplete-input{border:1px solid darkgray;padding:0.2em 0.4em;font-size:inherit;margin-right:0}.tagsinput *:first-child input,.tagsinput *:first-child .ui-autocomplete-input{min-width:22em}.ui-widget-content{text-align:left}body.versions td.changes{white-space:pre-wrap}/*! + * Font Awesome 4.7.0 by @davegandy - http://fontawesome.io - @fontawesome + * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) + */@font-face{font-family:'FontAwesome';src:url("/assets/font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot?v=4.7.0");src:url("/assets/font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot?v=4.7.0#iefix") format("embedded-opentype"),url("/assets/font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2?v=4.7.0") format("woff2"),url("/assets/font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff?v=4.7.0") format("woff"),url("/assets/font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf?v=4.7.0") format("truetype"),url("/assets/font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg?v=4.7.0#fontawesomeregular") format("svg");font-weight:normal;font-style:normal}.fa{display:inline-block;font:normal normal normal 14px/1 FontAwesome;font-size:inherit;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333em;line-height:0.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14286em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14286em;width:2.14286em;top:0.14286em;text-align:center}.fa-li.fa-lg{left:-1.85714em}.fa-border{padding:.2em .25em .15em;border:solid 0.08em #eee;border-radius:.1em}.fa-pull-left{float:left}.fa-pull-right{float:right}.fa.fa-pull-left{margin-right:.3em}.fa.fa-pull-right{margin-left:.3em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:fa-spin 2s infinite linear;animation:fa-spin 2s infinite linear}.fa-pulse{-webkit-animation:fa-spin 1s infinite steps(8);animation:fa-spin 1s infinite steps(8)}@-webkit-keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}@keyframes fa-spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";-webkit-transform:rotate(90deg);-ms-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";-webkit-transform:rotate(180deg);-ms-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";-webkit-transform:rotate(270deg);-ms-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";-webkit-transform:scale(-1, 1);-ms-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{-ms-filter:"progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";-webkit-transform:scale(1, -1);-ms-transform:scale(1, -1);transform:scale(1, -1)}:root .fa-rotate-90,:root .fa-rotate-180,:root .fa-rotate-270,:root .fa-flip-horizontal,:root .fa-flip-vertical{filter:none}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:""}.fa-music:before{content:""}.fa-search:before{content:""}.fa-envelope-o:before{content:""}.fa-heart:before{content:""}.fa-star:before{content:""}.fa-star-o:before{content:""}.fa-user:before{content:""}.fa-film:before{content:""}.fa-th-large:before{content:""}.fa-th:before{content:""}.fa-th-list:before{content:""}.fa-check:before{content:""}.fa-remove:before,.fa-close:before,.fa-times:before{content:""}.fa-search-plus:before{content:""}.fa-search-minus:before{content:""}.fa-power-off:before{content:""}.fa-signal:before{content:""}.fa-gear:before,.fa-cog:before{content:""}.fa-trash-o:before{content:""}.fa-home:before{content:""}.fa-file-o:before{content:""}.fa-clock-o:before{content:""}.fa-road:before{content:""}.fa-download:before{content:""}.fa-arrow-circle-o-down:before{content:""}.fa-arrow-circle-o-up:before{content:""}.fa-inbox:before{content:""}.fa-play-circle-o:before{content:""}.fa-rotate-right:before,.fa-repeat:before{content:""}.fa-refresh:before{content:""}.fa-list-alt:before{content:""}.fa-lock:before{content:""}.fa-flag:before{content:""}.fa-headphones:before{content:""}.fa-volume-off:before{content:""}.fa-volume-down:before{content:""}.fa-volume-up:before{content:""}.fa-qrcode:before{content:""}.fa-barcode:before{content:""}.fa-tag:before{content:""}.fa-tags:before{content:""}.fa-book:before{content:""}.fa-bookmark:before{content:""}.fa-print:before{content:""}.fa-camera:before{content:""}.fa-font:before{content:""}.fa-bold:before{content:""}.fa-italic:before{content:""}.fa-text-height:before{content:""}.fa-text-width:before{content:""}.fa-align-left:before{content:""}.fa-align-center:before{content:""}.fa-align-right:before{content:""}.fa-align-justify:before{content:""}.fa-list:before{content:""}.fa-dedent:before,.fa-outdent:before{content:""}.fa-indent:before{content:""}.fa-video-camera:before{content:""}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:""}.fa-pencil:before{content:""}.fa-map-marker:before{content:""}.fa-adjust:before{content:""}.fa-tint:before{content:""}.fa-edit:before,.fa-pencil-square-o:before{content:""}.fa-share-square-o:before{content:""}.fa-check-square-o:before{content:""}.fa-arrows:before{content:""}.fa-step-backward:before{content:""}.fa-fast-backward:before{content:""}.fa-backward:before{content:""}.fa-play:before{content:""}.fa-pause:before{content:""}.fa-stop:before{content:""}.fa-forward:before{content:""}.fa-fast-forward:before{content:""}.fa-step-forward:before{content:""}.fa-eject:before{content:""}.fa-chevron-left:before{content:""}.fa-chevron-right:before{content:""}.fa-plus-circle:before{content:""}.fa-minus-circle:before{content:""}.fa-times-circle:before{content:""}.fa-check-circle:before{content:""}.fa-question-circle:before{content:""}.fa-info-circle:before{content:""}.fa-crosshairs:before{content:""}.fa-times-circle-o:before{content:""}.fa-check-circle-o:before{content:""}.fa-ban:before{content:""}.fa-arrow-left:before{content:""}.fa-arrow-right:before{content:""}.fa-arrow-up:before{content:""}.fa-arrow-down:before{content:""}.fa-mail-forward:before,.fa-share:before{content:""}.fa-expand:before{content:""}.fa-compress:before{content:""}.fa-plus:before{content:""}.fa-minus:before{content:""}.fa-asterisk:before{content:""}.fa-exclamation-circle:before{content:""}.fa-gift:before{content:""}.fa-leaf:before{content:""}.fa-fire:before{content:""}.fa-eye:before{content:""}.fa-eye-slash:before{content:""}.fa-warning:before,.fa-exclamation-triangle:before{content:""}.fa-plane:before{content:""}.fa-calendar:before{content:""}.fa-random:before{content:""}.fa-comment:before{content:""}.fa-magnet:before{content:""}.fa-chevron-up:before{content:""}.fa-chevron-down:before{content:""}.fa-retweet:before{content:""}.fa-shopping-cart:before{content:""}.fa-folder:before{content:""}.fa-folder-open:before{content:""}.fa-arrows-v:before{content:""}.fa-arrows-h:before{content:""}.fa-bar-chart-o:before,.fa-bar-chart:before{content:""}.fa-twitter-square:before{content:""}.fa-facebook-square:before{content:""}.fa-camera-retro:before{content:""}.fa-key:before{content:""}.fa-gears:before,.fa-cogs:before{content:""}.fa-comments:before{content:""}.fa-thumbs-o-up:before{content:""}.fa-thumbs-o-down:before{content:""}.fa-star-half:before{content:""}.fa-heart-o:before{content:""}.fa-sign-out:before{content:""}.fa-linkedin-square:before{content:""}.fa-thumb-tack:before{content:""}.fa-external-link:before{content:""}.fa-sign-in:before{content:""}.fa-trophy:before{content:""}.fa-github-square:before{content:""}.fa-upload:before{content:""}.fa-lemon-o:before{content:""}.fa-phone:before{content:""}.fa-square-o:before{content:""}.fa-bookmark-o:before{content:""}.fa-phone-square:before{content:""}.fa-twitter:before{content:""}.fa-facebook-f:before,.fa-facebook:before{content:""}.fa-github:before{content:""}.fa-unlock:before{content:""}.fa-credit-card:before{content:""}.fa-feed:before,.fa-rss:before{content:""}.fa-hdd-o:before{content:""}.fa-bullhorn:before{content:""}.fa-bell:before{content:""}.fa-certificate:before{content:""}.fa-hand-o-right:before{content:""}.fa-hand-o-left:before{content:""}.fa-hand-o-up:before{content:""}.fa-hand-o-down:before{content:""}.fa-arrow-circle-left:before{content:""}.fa-arrow-circle-right:before{content:""}.fa-arrow-circle-up:before{content:""}.fa-arrow-circle-down:before{content:""}.fa-globe:before{content:""}.fa-wrench:before{content:""}.fa-tasks:before{content:""}.fa-filter:before{content:""}.fa-briefcase:before{content:""}.fa-arrows-alt:before{content:""}.fa-group:before,.fa-users:before{content:""}.fa-chain:before,.fa-link:before{content:""}.fa-cloud:before{content:""}.fa-flask:before{content:""}.fa-cut:before,.fa-scissors:before{content:""}.fa-copy:before,.fa-files-o:before{content:""}.fa-paperclip:before{content:""}.fa-save:before,.fa-floppy-o:before{content:""}.fa-square:before{content:""}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:""}.fa-list-ul:before{content:""}.fa-list-ol:before{content:""}.fa-strikethrough:before{content:""}.fa-underline:before{content:""}.fa-table:before{content:""}.fa-magic:before{content:""}.fa-truck:before{content:""}.fa-pinterest:before{content:""}.fa-pinterest-square:before{content:""}.fa-google-plus-square:before{content:""}.fa-google-plus:before{content:""}.fa-money:before{content:""}.fa-caret-down:before{content:""}.fa-caret-up:before{content:""}.fa-caret-left:before{content:""}.fa-caret-right:before{content:""}.fa-columns:before{content:""}.fa-unsorted:before,.fa-sort:before{content:""}.fa-sort-down:before,.fa-sort-desc:before{content:""}.fa-sort-up:before,.fa-sort-asc:before{content:""}.fa-envelope:before{content:""}.fa-linkedin:before{content:""}.fa-rotate-left:before,.fa-undo:before{content:""}.fa-legal:before,.fa-gavel:before{content:""}.fa-dashboard:before,.fa-tachometer:before{content:""}.fa-comment-o:before{content:""}.fa-comments-o:before{content:""}.fa-flash:before,.fa-bolt:before{content:""}.fa-sitemap:before{content:""}.fa-umbrella:before{content:""}.fa-paste:before,.fa-clipboard:before{content:""}.fa-lightbulb-o:before{content:""}.fa-exchange:before{content:""}.fa-cloud-download:before{content:""}.fa-cloud-upload:before{content:""}.fa-user-md:before{content:""}.fa-stethoscope:before{content:""}.fa-suitcase:before{content:""}.fa-bell-o:before{content:""}.fa-coffee:before{content:""}.fa-cutlery:before{content:""}.fa-file-text-o:before{content:""}.fa-building-o:before{content:""}.fa-hospital-o:before{content:""}.fa-ambulance:before{content:""}.fa-medkit:before{content:""}.fa-fighter-jet:before{content:""}.fa-beer:before{content:""}.fa-h-square:before{content:""}.fa-plus-square:before{content:""}.fa-angle-double-left:before{content:""}.fa-angle-double-right:before{content:""}.fa-angle-double-up:before{content:""}.fa-angle-double-down:before{content:""}.fa-angle-left:before{content:""}.fa-angle-right:before{content:""}.fa-angle-up:before{content:""}.fa-angle-down:before{content:""}.fa-desktop:before{content:""}.fa-laptop:before{content:""}.fa-tablet:before{content:""}.fa-mobile-phone:before,.fa-mobile:before{content:""}.fa-circle-o:before{content:""}.fa-quote-left:before{content:""}.fa-quote-right:before{content:""}.fa-spinner:before{content:""}.fa-circle:before{content:""}.fa-mail-reply:before,.fa-reply:before{content:""}.fa-github-alt:before{content:""}.fa-folder-o:before{content:""}.fa-folder-open-o:before{content:""}.fa-smile-o:before{content:""}.fa-frown-o:before{content:""}.fa-meh-o:before{content:""}.fa-gamepad:before{content:""}.fa-keyboard-o:before{content:""}.fa-flag-o:before{content:""}.fa-flag-checkered:before{content:""}.fa-terminal:before{content:""}.fa-code:before{content:""}.fa-mail-reply-all:before,.fa-reply-all:before{content:""}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:""}.fa-location-arrow:before{content:""}.fa-crop:before{content:""}.fa-code-fork:before{content:""}.fa-unlink:before,.fa-chain-broken:before{content:""}.fa-question:before{content:""}.fa-info:before{content:""}.fa-exclamation:before{content:""}.fa-superscript:before{content:""}.fa-subscript:before{content:""}.fa-eraser:before{content:""}.fa-puzzle-piece:before{content:""}.fa-microphone:before{content:""}.fa-microphone-slash:before{content:""}.fa-shield:before{content:""}.fa-calendar-o:before{content:""}.fa-fire-extinguisher:before{content:""}.fa-rocket:before{content:""}.fa-maxcdn:before{content:""}.fa-chevron-circle-left:before{content:""}.fa-chevron-circle-right:before{content:""}.fa-chevron-circle-up:before{content:""}.fa-chevron-circle-down:before{content:""}.fa-html5:before{content:""}.fa-css3:before{content:""}.fa-anchor:before{content:""}.fa-unlock-alt:before{content:""}.fa-bullseye:before{content:""}.fa-ellipsis-h:before{content:""}.fa-ellipsis-v:before{content:""}.fa-rss-square:before{content:""}.fa-play-circle:before{content:""}.fa-ticket:before{content:""}.fa-minus-square:before{content:""}.fa-minus-square-o:before{content:""}.fa-level-up:before{content:""}.fa-level-down:before{content:""}.fa-check-square:before{content:""}.fa-pencil-square:before{content:""}.fa-external-link-square:before{content:""}.fa-share-square:before{content:""}.fa-compass:before{content:""}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:""}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:""}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:""}.fa-euro:before,.fa-eur:before{content:""}.fa-gbp:before{content:""}.fa-dollar:before,.fa-usd:before{content:""}.fa-rupee:before,.fa-inr:before{content:""}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:""}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:""}.fa-won:before,.fa-krw:before{content:""}.fa-bitcoin:before,.fa-btc:before{content:""}.fa-file:before{content:""}.fa-file-text:before{content:""}.fa-sort-alpha-asc:before{content:""}.fa-sort-alpha-desc:before{content:""}.fa-sort-amount-asc:before{content:""}.fa-sort-amount-desc:before{content:""}.fa-sort-numeric-asc:before{content:""}.fa-sort-numeric-desc:before{content:""}.fa-thumbs-up:before{content:""}.fa-thumbs-down:before{content:""}.fa-youtube-square:before{content:""}.fa-youtube:before{content:""}.fa-xing:before{content:""}.fa-xing-square:before{content:""}.fa-youtube-play:before{content:""}.fa-dropbox:before{content:""}.fa-stack-overflow:before{content:""}.fa-instagram:before{content:""}.fa-flickr:before{content:""}.fa-adn:before{content:""}.fa-bitbucket:before{content:""}.fa-bitbucket-square:before{content:""}.fa-tumblr:before{content:""}.fa-tumblr-square:before{content:""}.fa-long-arrow-down:before{content:""}.fa-long-arrow-up:before{content:""}.fa-long-arrow-left:before{content:""}.fa-long-arrow-right:before{content:""}.fa-apple:before{content:""}.fa-windows:before{content:""}.fa-android:before{content:""}.fa-linux:before{content:""}.fa-dribbble:before{content:""}.fa-skype:before{content:""}.fa-foursquare:before{content:""}.fa-trello:before{content:""}.fa-female:before{content:""}.fa-male:before{content:""}.fa-gittip:before,.fa-gratipay:before{content:""}.fa-sun-o:before{content:""}.fa-moon-o:before{content:""}.fa-archive:before{content:""}.fa-bug:before{content:""}.fa-vk:before{content:""}.fa-weibo:before{content:""}.fa-renren:before{content:""}.fa-pagelines:before{content:""}.fa-stack-exchange:before{content:""}.fa-arrow-circle-o-right:before{content:""}.fa-arrow-circle-o-left:before{content:""}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:""}.fa-dot-circle-o:before{content:""}.fa-wheelchair:before{content:""}.fa-vimeo-square:before{content:""}.fa-turkish-lira:before,.fa-try:before{content:""}.fa-plus-square-o:before{content:""}.fa-space-shuttle:before{content:""}.fa-slack:before{content:""}.fa-envelope-square:before{content:""}.fa-wordpress:before{content:""}.fa-openid:before{content:""}.fa-institution:before,.fa-bank:before,.fa-university:before{content:""}.fa-mortar-board:before,.fa-graduation-cap:before{content:""}.fa-yahoo:before{content:""}.fa-google:before{content:""}.fa-reddit:before{content:""}.fa-reddit-square:before{content:""}.fa-stumbleupon-circle:before{content:""}.fa-stumbleupon:before{content:""}.fa-delicious:before{content:""}.fa-digg:before{content:""}.fa-pied-piper-pp:before{content:""}.fa-pied-piper-alt:before{content:""}.fa-drupal:before{content:""}.fa-joomla:before{content:""}.fa-language:before{content:""}.fa-fax:before{content:""}.fa-building:before{content:""}.fa-child:before{content:""}.fa-paw:before{content:""}.fa-spoon:before{content:""}.fa-cube:before{content:""}.fa-cubes:before{content:""}.fa-behance:before{content:""}.fa-behance-square:before{content:""}.fa-steam:before{content:""}.fa-steam-square:before{content:""}.fa-recycle:before{content:""}.fa-automobile:before,.fa-car:before{content:""}.fa-cab:before,.fa-taxi:before{content:""}.fa-tree:before{content:""}.fa-spotify:before{content:""}.fa-deviantart:before{content:""}.fa-soundcloud:before{content:""}.fa-database:before{content:""}.fa-file-pdf-o:before{content:""}.fa-file-word-o:before{content:""}.fa-file-excel-o:before{content:""}.fa-file-powerpoint-o:before{content:""}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:""}.fa-file-zip-o:before,.fa-file-archive-o:before{content:""}.fa-file-sound-o:before,.fa-file-audio-o:before{content:""}.fa-file-movie-o:before,.fa-file-video-o:before{content:""}.fa-file-code-o:before{content:""}.fa-vine:before{content:""}.fa-codepen:before{content:""}.fa-jsfiddle:before{content:""}.fa-life-bouy:before,.fa-life-buoy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:""}.fa-circle-o-notch:before{content:""}.fa-ra:before,.fa-resistance:before,.fa-rebel:before{content:""}.fa-ge:before,.fa-empire:before{content:""}.fa-git-square:before{content:""}.fa-git:before{content:""}.fa-y-combinator-square:before,.fa-yc-square:before,.fa-hacker-news:before{content:""}.fa-tencent-weibo:before{content:""}.fa-qq:before{content:""}.fa-wechat:before,.fa-weixin:before{content:""}.fa-send:before,.fa-paper-plane:before{content:""}.fa-send-o:before,.fa-paper-plane-o:before{content:""}.fa-history:before{content:""}.fa-circle-thin:before{content:""}.fa-header:before{content:""}.fa-paragraph:before{content:""}.fa-sliders:before{content:""}.fa-share-alt:before{content:""}.fa-share-alt-square:before{content:""}.fa-bomb:before{content:""}.fa-soccer-ball-o:before,.fa-futbol-o:before{content:""}.fa-tty:before{content:""}.fa-binoculars:before{content:""}.fa-plug:before{content:""}.fa-slideshare:before{content:""}.fa-twitch:before{content:""}.fa-yelp:before{content:""}.fa-newspaper-o:before{content:""}.fa-wifi:before{content:""}.fa-calculator:before{content:""}.fa-paypal:before{content:""}.fa-google-wallet:before{content:""}.fa-cc-visa:before{content:""}.fa-cc-mastercard:before{content:""}.fa-cc-discover:before{content:""}.fa-cc-amex:before{content:""}.fa-cc-paypal:before{content:""}.fa-cc-stripe:before{content:""}.fa-bell-slash:before{content:""}.fa-bell-slash-o:before{content:""}.fa-trash:before{content:""}.fa-copyright:before{content:""}.fa-at:before{content:""}.fa-eyedropper:before{content:""}.fa-paint-brush:before{content:""}.fa-birthday-cake:before{content:""}.fa-area-chart:before{content:""}.fa-pie-chart:before{content:""}.fa-line-chart:before{content:""}.fa-lastfm:before{content:""}.fa-lastfm-square:before{content:""}.fa-toggle-off:before{content:""}.fa-toggle-on:before{content:""}.fa-bicycle:before{content:""}.fa-bus:before{content:""}.fa-ioxhost:before{content:""}.fa-angellist:before{content:""}.fa-cc:before{content:""}.fa-shekel:before,.fa-sheqel:before,.fa-ils:before{content:""}.fa-meanpath:before{content:""}.fa-buysellads:before{content:""}.fa-connectdevelop:before{content:""}.fa-dashcube:before{content:""}.fa-forumbee:before{content:""}.fa-leanpub:before{content:""}.fa-sellsy:before{content:""}.fa-shirtsinbulk:before{content:""}.fa-simplybuilt:before{content:""}.fa-skyatlas:before{content:""}.fa-cart-plus:before{content:""}.fa-cart-arrow-down:before{content:""}.fa-diamond:before{content:""}.fa-ship:before{content:""}.fa-user-secret:before{content:""}.fa-motorcycle:before{content:""}.fa-street-view:before{content:""}.fa-heartbeat:before{content:""}.fa-venus:before{content:""}.fa-mars:before{content:""}.fa-mercury:before{content:""}.fa-intersex:before,.fa-transgender:before{content:""}.fa-transgender-alt:before{content:""}.fa-venus-double:before{content:""}.fa-mars-double:before{content:""}.fa-venus-mars:before{content:""}.fa-mars-stroke:before{content:""}.fa-mars-stroke-v:before{content:""}.fa-mars-stroke-h:before{content:""}.fa-neuter:before{content:""}.fa-genderless:before{content:""}.fa-facebook-official:before{content:""}.fa-pinterest-p:before{content:""}.fa-whatsapp:before{content:""}.fa-server:before{content:""}.fa-user-plus:before{content:""}.fa-user-times:before{content:""}.fa-hotel:before,.fa-bed:before{content:""}.fa-viacoin:before{content:""}.fa-train:before{content:""}.fa-subway:before{content:""}.fa-medium:before{content:""}.fa-yc:before,.fa-y-combinator:before{content:""}.fa-optin-monster:before{content:""}.fa-opencart:before{content:""}.fa-expeditedssl:before{content:""}.fa-battery-4:before,.fa-battery:before,.fa-battery-full:before{content:""}.fa-battery-3:before,.fa-battery-three-quarters:before{content:""}.fa-battery-2:before,.fa-battery-half:before{content:""}.fa-battery-1:before,.fa-battery-quarter:before{content:""}.fa-battery-0:before,.fa-battery-empty:before{content:""}.fa-mouse-pointer:before{content:""}.fa-i-cursor:before{content:""}.fa-object-group:before{content:""}.fa-object-ungroup:before{content:""}.fa-sticky-note:before{content:""}.fa-sticky-note-o:before{content:""}.fa-cc-jcb:before{content:""}.fa-cc-diners-club:before{content:""}.fa-clone:before{content:""}.fa-balance-scale:before{content:""}.fa-hourglass-o:before{content:""}.fa-hourglass-1:before,.fa-hourglass-start:before{content:""}.fa-hourglass-2:before,.fa-hourglass-half:before{content:""}.fa-hourglass-3:before,.fa-hourglass-end:before{content:""}.fa-hourglass:before{content:""}.fa-hand-grab-o:before,.fa-hand-rock-o:before{content:""}.fa-hand-stop-o:before,.fa-hand-paper-o:before{content:""}.fa-hand-scissors-o:before{content:""}.fa-hand-lizard-o:before{content:""}.fa-hand-spock-o:before{content:""}.fa-hand-pointer-o:before{content:""}.fa-hand-peace-o:before{content:""}.fa-trademark:before{content:""}.fa-registered:before{content:""}.fa-creative-commons:before{content:""}.fa-gg:before{content:""}.fa-gg-circle:before{content:""}.fa-tripadvisor:before{content:""}.fa-odnoklassniki:before{content:""}.fa-odnoklassniki-square:before{content:""}.fa-get-pocket:before{content:""}.fa-wikipedia-w:before{content:""}.fa-safari:before{content:""}.fa-chrome:before{content:""}.fa-firefox:before{content:""}.fa-opera:before{content:""}.fa-internet-explorer:before{content:""}.fa-tv:before,.fa-television:before{content:""}.fa-contao:before{content:""}.fa-500px:before{content:""}.fa-amazon:before{content:""}.fa-calendar-plus-o:before{content:""}.fa-calendar-minus-o:before{content:""}.fa-calendar-times-o:before{content:""}.fa-calendar-check-o:before{content:""}.fa-industry:before{content:""}.fa-map-pin:before{content:""}.fa-map-signs:before{content:""}.fa-map-o:before{content:""}.fa-map:before{content:""}.fa-commenting:before{content:""}.fa-commenting-o:before{content:""}.fa-houzz:before{content:""}.fa-vimeo:before{content:""}.fa-black-tie:before{content:""}.fa-fonticons:before{content:""}.fa-reddit-alien:before{content:""}.fa-edge:before{content:""}.fa-credit-card-alt:before{content:""}.fa-codiepie:before{content:""}.fa-modx:before{content:""}.fa-fort-awesome:before{content:""}.fa-usb:before{content:""}.fa-product-hunt:before{content:""}.fa-mixcloud:before{content:""}.fa-scribd:before{content:""}.fa-pause-circle:before{content:""}.fa-pause-circle-o:before{content:""}.fa-stop-circle:before{content:""}.fa-stop-circle-o:before{content:""}.fa-shopping-bag:before{content:""}.fa-shopping-basket:before{content:""}.fa-hashtag:before{content:""}.fa-bluetooth:before{content:""}.fa-bluetooth-b:before{content:""}.fa-percent:before{content:""}.fa-gitlab:before{content:""}.fa-wpbeginner:before{content:""}.fa-wpforms:before{content:""}.fa-envira:before{content:""}.fa-universal-access:before{content:""}.fa-wheelchair-alt:before{content:""}.fa-question-circle-o:before{content:""}.fa-blind:before{content:""}.fa-audio-description:before{content:""}.fa-volume-control-phone:before{content:""}.fa-braille:before{content:""}.fa-assistive-listening-systems:before{content:""}.fa-asl-interpreting:before,.fa-american-sign-language-interpreting:before{content:""}.fa-deafness:before,.fa-hard-of-hearing:before,.fa-deaf:before{content:""}.fa-glide:before{content:""}.fa-glide-g:before{content:""}.fa-signing:before,.fa-sign-language:before{content:""}.fa-low-vision:before{content:""}.fa-viadeo:before{content:""}.fa-viadeo-square:before{content:""}.fa-snapchat:before{content:""}.fa-snapchat-ghost:before{content:""}.fa-snapchat-square:before{content:""}.fa-pied-piper:before{content:""}.fa-first-order:before{content:""}.fa-yoast:before{content:""}.fa-themeisle:before{content:""}.fa-google-plus-circle:before,.fa-google-plus-official:before{content:""}.fa-fa:before,.fa-font-awesome:before{content:""}.fa-handshake-o:before{content:""}.fa-envelope-open:before{content:""}.fa-envelope-open-o:before{content:""}.fa-linode:before{content:""}.fa-address-book:before{content:""}.fa-address-book-o:before{content:""}.fa-vcard:before,.fa-address-card:before{content:""}.fa-vcard-o:before,.fa-address-card-o:before{content:""}.fa-user-circle:before{content:""}.fa-user-circle-o:before{content:""}.fa-user-o:before{content:""}.fa-id-badge:before{content:""}.fa-drivers-license:before,.fa-id-card:before{content:""}.fa-drivers-license-o:before,.fa-id-card-o:before{content:""}.fa-quora:before{content:""}.fa-free-code-camp:before{content:""}.fa-telegram:before{content:""}.fa-thermometer-4:before,.fa-thermometer:before,.fa-thermometer-full:before{content:""}.fa-thermometer-3:before,.fa-thermometer-three-quarters:before{content:""}.fa-thermometer-2:before,.fa-thermometer-half:before{content:""}.fa-thermometer-1:before,.fa-thermometer-quarter:before{content:""}.fa-thermometer-0:before,.fa-thermometer-empty:before{content:""}.fa-shower:before{content:""}.fa-bathtub:before,.fa-s15:before,.fa-bath:before{content:""}.fa-podcast:before{content:""}.fa-window-maximize:before{content:""}.fa-window-minimize:before{content:""}.fa-window-restore:before{content:""}.fa-times-rectangle:before,.fa-window-close:before{content:""}.fa-times-rectangle-o:before,.fa-window-close-o:before{content:""}.fa-bandcamp:before{content:""}.fa-grav:before{content:""}.fa-etsy:before{content:""}.fa-imdb:before{content:""}.fa-ravelry:before{content:""}.fa-eercast:before{content:""}.fa-microchip:before{content:""}.fa-snowflake-o:before{content:""}.fa-superpowers:before{content:""}.fa-wpexplorer:before{content:""}.fa-meetup:before{content:""}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0, 0, 0, 0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}body.pages main h4{text-align:left} diff --git a/public/assets/application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css.gz b/public/assets/application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css.gz new file mode 100644 index 00000000..f999f74b Binary files /dev/null and b/public/assets/application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css.gz differ diff --git a/public/assets/application-ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd.js b/public/assets/application-ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd.js new file mode 100644 index 00000000..b68d0101 --- /dev/null +++ b/public/assets/application-ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd.js @@ -0,0 +1,77575 @@ +/*! + * jQuery JavaScript Library v1.12.4 + * http://jquery.com/ + * + * Includes Sizzle.js + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2016-05-20T17:17Z + */ + + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper `window` + // is present, execute the factory and get jQuery. + // For environments that do not have a `window` with a `document` + // (such as Node.js), expose a factory as module.exports. + // This accentuates the need for the creation of a real `window`. + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info. + module.exports = global.document ? + factory( global, true ) : + function( w ) { + if ( !w.document ) { + throw new Error( "jQuery requires a window with a document" ); + } + return factory( w ); + }; + } else { + factory( global ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { + +// Support: Firefox 18+ +// Can't be in strict mode, several libs including ASP.NET trace +// the stack via arguments.caller.callee and Firefox dies if +// you try to trace through "use strict" call chains. (#13335) +//"use strict"; +var deletedIds = []; + +var document = window.document; + +var slice = deletedIds.slice; + +var concat = deletedIds.concat; + +var push = deletedIds.push; + +var indexOf = deletedIds.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + +var + version = "1.12.4", + + // Define a local copy of jQuery + jQuery = function( selector, context ) { + + // The jQuery object is actually just the init constructor 'enhanced' + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); + }, + + // Support: Android<4.1, IE<9 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, + + // Matches dashed string for camelizing + rmsPrefix = /^-ms-/, + rdashAlpha = /-([\da-z])/gi, + + // Used by jQuery.camelCase as callback to replace() + fcamelCase = function( all, letter ) { + return letter.toUpperCase(); + }; + +jQuery.fn = jQuery.prototype = { + + // The current version of jQuery being used + jquery: version, + + constructor: jQuery, + + // Start with an empty selector + selector: "", + + // The default length of a jQuery object is 0 + length: 0, + + toArray: function() { + return slice.call( this ); + }, + + // Get the Nth element in the matched element set OR + // Get the whole matched element set as a clean array + get: function( num ) { + return num != null ? + + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : + + // Return all the elements in a clean array + slice.call( this ); + }, + + // Take an array of elements and push it onto the stack + // (returning the new matched element set) + pushStack: function( elems ) { + + // Build a new jQuery matched element set + var ret = jQuery.merge( this.constructor(), elems ); + + // Add the old object onto the stack (as a reference) + ret.prevObject = this; + ret.context = this.context; + + // Return the newly-formed element set + return ret; + }, + + // Execute a callback for every element in the matched set. + each: function( callback ) { + return jQuery.each( this, callback ); + }, + + map: function( callback ) { + return this.pushStack( jQuery.map( this, function( elem, i ) { + return callback.call( elem, i, elem ); + } ) ); + }, + + slice: function() { + return this.pushStack( slice.apply( this, arguments ) ); + }, + + first: function() { + return this.eq( 0 ); + }, + + last: function() { + return this.eq( -1 ); + }, + + eq: function( i ) { + var len = this.length, + j = +i + ( i < 0 ? len : 0 ); + return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] ); + }, + + end: function() { + return this.prevObject || this.constructor(); + }, + + // For internal use only. + // Behaves like an Array's method, not like a jQuery method. + push: push, + sort: deletedIds.sort, + splice: deletedIds.splice +}; + +jQuery.extend = jQuery.fn.extend = function() { + var src, copyIsArray, copy, name, options, clone, + target = arguments[ 0 ] || {}, + i = 1, + length = arguments.length, + deep = false; + + // Handle a deep copy situation + if ( typeof target === "boolean" ) { + deep = target; + + // skip the boolean and the target + target = arguments[ i ] || {}; + i++; + } + + // Handle case when target is a string or something (possible in deep copy) + if ( typeof target !== "object" && !jQuery.isFunction( target ) ) { + target = {}; + } + + // extend jQuery itself if only one argument is passed + if ( i === length ) { + target = this; + i--; + } + + for ( ; i < length; i++ ) { + + // Only deal with non-null/undefined values + if ( ( options = arguments[ i ] ) != null ) { + + // Extend the base object + for ( name in options ) { + src = target[ name ]; + copy = options[ name ]; + + // Prevent never-ending loop + if ( target === copy ) { + continue; + } + + // Recurse if we're merging plain objects or arrays + if ( deep && copy && ( jQuery.isPlainObject( copy ) || + ( copyIsArray = jQuery.isArray( copy ) ) ) ) { + + if ( copyIsArray ) { + copyIsArray = false; + clone = src && jQuery.isArray( src ) ? src : []; + + } else { + clone = src && jQuery.isPlainObject( src ) ? src : {}; + } + + // Never move original objects, clone them + target[ name ] = jQuery.extend( deep, clone, copy ); + + // Don't bring in undefined values + } else if ( copy !== undefined ) { + target[ name ] = copy; + } + } + } + } + + // Return the modified object + return target; +}; + +jQuery.extend( { + + // Unique for each copy of jQuery on the page + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), + + // Assume jQuery is ready without the ready module + isReady: true, + + error: function( msg ) { + throw new Error( msg ); + }, + + noop: function() {}, + + // See test/unit/core.js for details concerning isFunction. + // Since version 1.3, DOM methods and functions like alert + // aren't supported. They return false on IE (#2968). + isFunction: function( obj ) { + return jQuery.type( obj ) === "function"; + }, + + isArray: Array.isArray || function( obj ) { + return jQuery.type( obj ) === "array"; + }, + + isWindow: function( obj ) { + /* jshint eqeqeq: false */ + return obj != null && obj == obj.window; + }, + + isNumeric: function( obj ) { + + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + // adding 1 corrects loss of precision from parseFloat (#15100) + var realStringObj = obj && obj.toString(); + return !jQuery.isArray( obj ) && ( realStringObj - parseFloat( realStringObj ) + 1 ) >= 0; + }, + + isEmptyObject: function( obj ) { + var name; + for ( name in obj ) { + return false; + } + return true; + }, + + isPlainObject: function( obj ) { + var key; + + // Must be an Object. + // Because of IE, we also have to check the presence of the constructor property. + // Make sure that DOM nodes and window objects don't pass through, as well + if ( !obj || jQuery.type( obj ) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) { + return false; + } + + try { + + // Not own constructor property must be Object + if ( obj.constructor && + !hasOwn.call( obj, "constructor" ) && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { + return false; + } + } catch ( e ) { + + // IE8,9 Will throw exceptions on certain host objects #9897 + return false; + } + + // Support: IE<9 + // Handle iteration over inherited properties before own properties. + if ( !support.ownFirst ) { + for ( key in obj ) { + return hasOwn.call( obj, key ); + } + } + + // Own properties are enumerated firstly, so to speed up, + // if last one is own, then all properties are own. + for ( key in obj ) {} + + return key === undefined || hasOwn.call( obj, key ); + }, + + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call( obj ) ] || "object" : + typeof obj; + }, + + // Workarounds based on findings by Jim Driscoll + // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context + globalEval: function( data ) { + if ( data && jQuery.trim( data ) ) { + + // We use execScript on Internet Explorer + // We use an anonymous function so that context is window + // rather than jQuery in Firefox + ( window.execScript || function( data ) { + window[ "eval" ].call( window, data ); // jscs:ignore requireDotNotation + } )( data ); + } + }, + + // Convert dashed to camelCase; used by the css and data modules + // Microsoft forgot to hump their vendor prefix (#9572) + camelCase: function( string ) { + return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase ); + }, + + nodeName: function( elem, name ) { + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + }, + + each: function( obj, callback ) { + var length, i = 0; + + if ( isArrayLike( obj ) ) { + length = obj.length; + for ( ; i < length; i++ ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } else { + for ( i in obj ) { + if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) { + break; + } + } + } + + return obj; + }, + + // Support: Android<4.1, IE<9 + trim: function( text ) { + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); + }, + + // results is for internal usage only + makeArray: function( arr, results ) { + var ret = results || []; + + if ( arr != null ) { + if ( isArrayLike( Object( arr ) ) ) { + jQuery.merge( ret, + typeof arr === "string" ? + [ arr ] : arr + ); + } else { + push.call( ret, arr ); + } + } + + return ret; + }, + + inArray: function( elem, arr, i ) { + var len; + + if ( arr ) { + if ( indexOf ) { + return indexOf.call( arr, elem, i ); + } + + len = arr.length; + i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0; + + for ( ; i < len; i++ ) { + + // Skip accessing in sparse arrays + if ( i in arr && arr[ i ] === elem ) { + return i; + } + } + } + + return -1; + }, + + merge: function( first, second ) { + var len = +second.length, + j = 0, + i = first.length; + + while ( j < len ) { + first[ i++ ] = second[ j++ ]; + } + + // Support: IE<9 + // Workaround casting of .length to NaN on otherwise arraylike objects (e.g., NodeLists) + if ( len !== len ) { + while ( second[ j ] !== undefined ) { + first[ i++ ] = second[ j++ ]; + } + } + + first.length = i; + + return first; + }, + + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], + i = 0, + length = elems.length, + callbackExpect = !invert; + + // Go through the array, only saving the items + // that pass the validator function + for ( ; i < length; i++ ) { + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); + } + } + + return matches; + }, + + // arg is for internal usage only + map: function( elems, callback, arg ) { + var length, value, + i = 0, + ret = []; + + // Go through the array, translating each of the items to their new values + if ( isArrayLike( elems ) ) { + length = elems.length; + for ( ; i < length; i++ ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + + // Go through every key on the object, + } else { + for ( i in elems ) { + value = callback( elems[ i ], i, arg ); + + if ( value != null ) { + ret.push( value ); + } + } + } + + // Flatten any nested arrays + return concat.apply( [], ret ); + }, + + // A global GUID counter for objects + guid: 1, + + // Bind a function to a context, optionally partially applying any + // arguments. + proxy: function( fn, context ) { + var args, proxy, tmp; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // Quick check to determine if target is callable, in the spec + // this throws a TypeError, but we will just return undefined. + if ( !jQuery.isFunction( fn ) ) { + return undefined; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; + }, + + now: function() { + return +( new Date() ); + }, + + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support +} ); + +// JSHint would error on this code due to the Symbol not being defined in ES5. +// Defining this global in .jshintrc would create a danger of using the global +// unguarded in another place, it seems safer to just disable JSHint for these +// three lines. +/* jshint ignore: start */ +if ( typeof Symbol === "function" ) { + jQuery.fn[ Symbol.iterator ] = deletedIds[ Symbol.iterator ]; +} +/* jshint ignore: end */ + +// Populate the class2type map +jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ), +function( i, name ) { + class2type[ "[object " + name + "]" ] = name.toLowerCase(); +} ); + +function isArrayLike( obj ) { + + // Support: iOS 8.2 (not reproducible in simulator) + // `in` check used to prevent JIT error (gh-2145) + // hasOwn isn't used here due to false negatives + // regarding Nodelist length in IE + var length = !!obj && "length" in obj && obj.length, + type = jQuery.type( obj ); + + if ( type === "function" || jQuery.isWindow( obj ) ) { + return false; + } + + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; +} +var Sizzle = +/*! + * Sizzle CSS Selector Engine v2.2.1 + * http://sizzlejs.com/ + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license + * http://jquery.org/license + * + * Date: 2015-10-17 + */ +(function( window ) { + +var i, + support, + Expr, + getText, + isXML, + tokenize, + compile, + select, + outermostContext, + sortInput, + hasDuplicate, + + // Local document vars + setDocument, + document, + docElem, + documentIsHTML, + rbuggyQSA, + rbuggyMatches, + matches, + contains, + + // Instance-specific data + expando = "sizzle" + 1 * new Date(), + preferredDoc = window.document, + dirruns = 0, + done = 0, + classCache = createCache(), + tokenCache = createCache(), + compilerCache = createCache(), + sortOrder = function( a, b ) { + if ( a === b ) { + hasDuplicate = true; + } + return 0; + }, + + // General-purpose constants + MAX_NEGATIVE = 1 << 31, + + // Instance methods + hasOwn = ({}).hasOwnProperty, + arr = [], + pop = arr.pop, + push_native = arr.push, + push = arr.push, + slice = arr.slice, + // Use a stripped-down indexOf as it's faster than native + // http://jsperf.com/thor-indexof-vs-for/5 + indexOf = function( list, elem ) { + var i = 0, + len = list.length; + for ( ; i < len; i++ ) { + if ( list[i] === elem ) { + return i; + } + } + return -1; + }, + + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", + + // Regular expressions + + // http://www.w3.org/TR/css3-selectors/#whitespace + whitespace = "[\\x20\\t\\r\\n\\f]", + + // http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier + identifier = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", + + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", + + pseudos = ":(" + identifier + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", + + // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter + rwhitespace = new RegExp( whitespace + "+", "g" ), + rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), + + rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), + rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), + + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), + + rpseudo = new RegExp( pseudos ), + ridentifier = new RegExp( "^" + identifier + "$" ), + + matchExpr = { + "ID": new RegExp( "^#(" + identifier + ")" ), + "CLASS": new RegExp( "^\\.(" + identifier + ")" ), + "TAG": new RegExp( "^(" + identifier + "|[*])" ), + "ATTR": new RegExp( "^" + attributes ), + "PSEUDO": new RegExp( "^" + pseudos ), + "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + // For use in libraries implementing .is() + // We use this for POS matching in `select` + "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) + }, + + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + + rnative = /^[^{]+\{\s*\[native \w/, + + // Easily-parseable/retrievable ID or TAG or CLASS selectors + rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, + + rsibling = /[+~]/, + rescape = /'|\\/g, + + // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), + funescape = function( _, escaped, escapedWhitespace ) { + var high = "0x" + escaped - 0x10000; + // NaN means non-codepoint + // Support: Firefox<24 + // Workaround erroneous numeric interpretation of +"0x" + return high !== high || escapedWhitespace ? + escaped : + high < 0 ? + // BMP codepoint + String.fromCharCode( high + 0x10000 ) : + // Supplemental Plane codepoint (surrogate pair) + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); + }, + + // Used for iframes + // See setDocument() + // Removing the function wrapper causes a "Permission Denied" + // error in IE + unloadHandler = function() { + setDocument(); + }; + +// Optimize for push.apply( _, NodeList ) +try { + push.apply( + (arr = slice.call( preferredDoc.childNodes )), + preferredDoc.childNodes + ); + // Support: Android<4.0 + // Detect silently failing push.apply + arr[ preferredDoc.childNodes.length ].nodeType; +} catch ( e ) { + push = { apply: arr.length ? + + // Leverage slice if possible + function( target, els ) { + push_native.apply( target, slice.call(els) ); + } : + + // Support: IE<9 + // Otherwise append directly + function( target, els ) { + var j = target.length, + i = 0; + // Can't trust NodeList.length + while ( (target[j++] = els[i++]) ) {} + target.length = j - 1; + } + }; +} + +function Sizzle( selector, context, results, seed ) { + var m, i, elem, nid, nidselect, match, groups, newSelector, + newContext = context && context.ownerDocument, + + // nodeType defaults to 9, since context defaults to document + nodeType = context ? context.nodeType : 9; + + results = results || []; + + // Return early from calls with invalid selector or context + if ( typeof selector !== "string" || !selector || + nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) { + + return results; + } + + // Try to shortcut find operations (as opposed to filters) in HTML documents + if ( !seed ) { + + if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { + setDocument( context ); + } + context = context || document; + + if ( documentIsHTML ) { + + // If the selector is sufficiently simple, try using a "get*By*" DOM method + // (excepting DocumentFragment context, where the methods don't exist) + if ( nodeType !== 11 && (match = rquickExpr.exec( selector )) ) { + + // ID selector + if ( (m = match[1]) ) { + + // Document context + if ( nodeType === 9 ) { + if ( (elem = context.getElementById( m )) ) { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( elem.id === m ) { + results.push( elem ); + return results; + } + } else { + return results; + } + + // Element context + } else { + + // Support: IE, Opera, Webkit + // TODO: identify versions + // getElementById can match elements by name instead of ID + if ( newContext && (elem = newContext.getElementById( m )) && + contains( context, elem ) && + elem.id === m ) { + + results.push( elem ); + return results; + } + } + + // Type selector + } else if ( match[2] ) { + push.apply( results, context.getElementsByTagName( selector ) ); + return results; + + // Class selector + } else if ( (m = match[3]) && support.getElementsByClassName && + context.getElementsByClassName ) { + + push.apply( results, context.getElementsByClassName( m ) ); + return results; + } + } + + // Take advantage of querySelectorAll + if ( support.qsa && + !compilerCache[ selector + " " ] && + (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { + + if ( nodeType !== 1 ) { + newContext = context; + newSelector = selector; + + // qSA looks outside Element context, which is not what we want + // Thanks to Andrew Dupont for this workaround technique + // Support: IE <=8 + // Exclude object elements + } else if ( context.nodeName.toLowerCase() !== "object" ) { + + // Capture the context ID, setting it first if necessary + if ( (nid = context.getAttribute( "id" )) ) { + nid = nid.replace( rescape, "\\$&" ); + } else { + context.setAttribute( "id", (nid = expando) ); + } + + // Prefix every selector in the list + groups = tokenize( selector ); + i = groups.length; + nidselect = ridentifier.test( nid ) ? "#" + nid : "[id='" + nid + "']"; + while ( i-- ) { + groups[i] = nidselect + " " + toSelector( groups[i] ); + } + newSelector = groups.join( "," ); + + // Expand context for sibling selectors + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || + context; + } + + if ( newSelector ) { + try { + push.apply( results, + newContext.querySelectorAll( newSelector ) + ); + return results; + } catch ( qsaError ) { + } finally { + if ( nid === expando ) { + context.removeAttribute( "id" ); + } + } + } + } + } + } + + // All others + return select( selector.replace( rtrim, "$1" ), context, results, seed ); +} + +/** + * Create key-value caches of limited size + * @returns {function(string, object)} Returns the Object data after storing it on itself with + * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) + * deleting the oldest entry + */ +function createCache() { + var keys = []; + + function cache( key, value ) { + // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + if ( keys.push( key + " " ) > Expr.cacheLength ) { + // Only keep the most recent entries + delete cache[ keys.shift() ]; + } + return (cache[ key + " " ] = value); + } + return cache; +} + +/** + * Mark a function for special use by Sizzle + * @param {Function} fn The function to mark + */ +function markFunction( fn ) { + fn[ expando ] = true; + return fn; +} + +/** + * Support testing using an element + * @param {Function} fn Passed the created div and expects a boolean result + */ +function assert( fn ) { + var div = document.createElement("div"); + + try { + return !!fn( div ); + } catch (e) { + return false; + } finally { + // Remove from its parent by default + if ( div.parentNode ) { + div.parentNode.removeChild( div ); + } + // release memory in IE + div = null; + } +} + +/** + * Adds the same handler for all of the specified attrs + * @param {String} attrs Pipe-separated list of attributes + * @param {Function} handler The method that will be applied + */ +function addHandle( attrs, handler ) { + var arr = attrs.split("|"), + i = arr.length; + + while ( i-- ) { + Expr.attrHandle[ arr[i] ] = handler; + } +} + +/** + * Checks document order of two siblings + * @param {Element} a + * @param {Element} b + * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b + */ +function siblingCheck( a, b ) { + var cur = b && a, + diff = cur && a.nodeType === 1 && b.nodeType === 1 && + ( ~b.sourceIndex || MAX_NEGATIVE ) - + ( ~a.sourceIndex || MAX_NEGATIVE ); + + // Use IE sourceIndex if available on both nodes + if ( diff ) { + return diff; + } + + // Check if b follows a + if ( cur ) { + while ( (cur = cur.nextSibling) ) { + if ( cur === b ) { + return -1; + } + } + } + + return a ? 1 : -1; +} + +/** + * Returns a function to use in pseudos for input types + * @param {String} type + */ +function createInputPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for buttons + * @param {String} type + */ +function createButtonPseudo( type ) { + return function( elem ) { + var name = elem.nodeName.toLowerCase(); + return (name === "input" || name === "button") && elem.type === type; + }; +} + +/** + * Returns a function to use in pseudos for positionals + * @param {Function} fn + */ +function createPositionalPseudo( fn ) { + return markFunction(function( argument ) { + argument = +argument; + return markFunction(function( seed, matches ) { + var j, + matchIndexes = fn( [], seed.length, argument ), + i = matchIndexes.length; + + // Match elements found at the specified indexes + while ( i-- ) { + if ( seed[ (j = matchIndexes[i]) ] ) { + seed[j] = !(matches[j] = seed[j]); + } + } + }); + }); +} + +/** + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== "undefined" && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes + * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node + */ +isXML = Sizzle.isXML = function( elem ) { + // documentElement is verified for cases where it doesn't yet exist + // (such as loading iframes in IE - #4833) + var documentElement = elem && (elem.ownerDocument || elem).documentElement; + return documentElement ? documentElement.nodeName !== "HTML" : false; +}; + +/** + * Sets document-related variables once based on the current document + * @param {Element|Object} [doc] An element or document object to use to set the document + * @returns {Object} Returns the current document + */ +setDocument = Sizzle.setDocument = function( node ) { + var hasCompare, parent, + doc = node ? node.ownerDocument || node : preferredDoc; + + // Return early if doc is invalid or already selected + if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { + return document; + } + + // Update global variables + document = doc; + docElem = document.documentElement; + documentIsHTML = !isXML( document ); + + // Support: IE 9-11, Edge + // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + if ( (parent = document.defaultView) && parent.top !== parent ) { + // Support: IE 11 + if ( parent.addEventListener ) { + parent.addEventListener( "unload", unloadHandler, false ); + + // Support: IE 9 - 10 only + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", unloadHandler ); + } + } + + /* Attributes + ---------------------------------------------------------------------- */ + + // Support: IE<8 + // Verify that getAttribute really returns attributes and not properties + // (excepting IE8 booleans) + support.attributes = assert(function( div ) { + div.className = "i"; + return !div.getAttribute("className"); + }); + + /* getElement(s)By* + ---------------------------------------------------------------------- */ + + // Check if getElementsByTagName("*") returns only elements + support.getElementsByTagName = assert(function( div ) { + div.appendChild( document.createComment("") ); + return !div.getElementsByTagName("*").length; + }); + + // Support: IE<9 + support.getElementsByClassName = rnative.test( document.getElementsByClassName ); + + // Support: IE<10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert(function( div ) { + docElem.appendChild( div ).id = expando; + return !document.getElementsByName || !document.getElementsByName( expando ).length; + }); + + // ID find and filter + if ( support.getById ) { + Expr.find["ID"] = function( id, context ) { + if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { + var m = context.getElementById( id ); + return m ? [ m ] : []; + } + }; + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + return elem.getAttribute("id") === attrId; + }; + }; + } else { + // Support: IE6/7 + // getElementById is not reliable as a find shortcut + delete Expr.find["ID"]; + + Expr.filter["ID"] = function( id ) { + var attrId = id.replace( runescape, funescape ); + return function( elem ) { + var node = typeof elem.getAttributeNode !== "undefined" && + elem.getAttributeNode("id"); + return node && node.value === attrId; + }; + }; + } + + // Tag + Expr.find["TAG"] = support.getElementsByTagName ? + function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); + + // DocumentFragment nodes don't have gEBTN + } else if ( support.qsa ) { + return context.querySelectorAll( tag ); + } + } : + + function( tag, context ) { + var elem, + tmp = [], + i = 0, + // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too + results = context.getElementsByTagName( tag ); + + // Filter out possible comments + if ( tag === "*" ) { + while ( (elem = results[i++]) ) { + if ( elem.nodeType === 1 ) { + tmp.push( elem ); + } + } + + return tmp; + } + return results; + }; + + // Class + Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { + if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { + return context.getElementsByClassName( className ); + } + }; + + /* QSA/matchesSelector + ---------------------------------------------------------------------- */ + + // QSA and matchesSelector support + + // matchesSelector(:active) reports false when true (IE9/Opera 11.5) + rbuggyMatches = []; + + // qSa(:focus) reports false when true (Chrome 21) + // We allow this because of a bug in IE8/9 that throws an error + // whenever `document.activeElement` is accessed on an iframe + // So, we allow :focus to pass through QSA all the time to avoid the IE error + // See http://bugs.jquery.com/ticket/13378 + rbuggyQSA = []; + + if ( (support.qsa = rnative.test( document.querySelectorAll )) ) { + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert(function( div ) { + // Select is set to empty string on purpose + // This is to test IE's treatment of not explicitly + // setting a boolean content attribute, + // since its presence should be enough + // http://bugs.jquery.com/ticket/12359 + docElem.appendChild( div ).innerHTML = "" + + ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowcapture^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } + + // Support: IE8 + // Boolean attributes and "value" are not treated correctly + if ( !div.querySelectorAll("[selected]").length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } + + // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ + if ( !div.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push("~="); + } + + // Webkit/Opera - :checked should return selected option elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":checked").length ) { + rbuggyQSA.push(":checked"); + } + + // Support: Safari 8+, iOS 8+ + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibing-combinator selector` fails + if ( !div.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push(".#.+[+~]"); + } + }); + + assert(function( div ) { + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + var input = document.createElement("input"); + input.setAttribute( "type", "hidden" ); + div.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); + } + + // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) + // IE8 throws error here and will not see later tests + if ( !div.querySelectorAll(":enabled").length ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Opera 10-11 does not throw on post-comma invalid pseudos + div.querySelectorAll("*,:x"); + rbuggyQSA.push(",.*:"); + }); + } + + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || + docElem.mozMatchesSelector || + docElem.oMatchesSelector || + docElem.msMatchesSelector) )) ) { + + assert(function( div ) { + // Check to see if it's possible to do matchesSelector + // on a disconnected node (IE 9) + support.disconnectedMatch = matches.call( div, "div" ); + + // This should fail with an exception + // Gecko does not error, returns false instead + matches.call( div, "[s!='']:x" ); + rbuggyMatches.push( "!=", pseudos ); + }); + } + + rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); + rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); + + /* Contains + ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); + + // Element contains another + // Purposefully self-exclusive + // As in, an element does not contain itself + contains = hasCompare || rnative.test( docElem.contains ) ? + function( a, b ) { + var adown = a.nodeType === 9 ? a.documentElement : a, + bup = b && b.parentNode; + return a === bup || !!( bup && bup.nodeType === 1 && ( + adown.contains ? + adown.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + )); + } : + function( a, b ) { + if ( b ) { + while ( (b = b.parentNode) ) { + if ( b === a ) { + return true; + } + } + } + return false; + }; + + /* Sorting + ---------------------------------------------------------------------- */ + + // Document order sorting + sortOrder = hasCompare ? + function( a, b ) { + + // Flag for duplicate removal + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; + if ( compare ) { + return compare; + } + + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === document || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === document || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; + } : + function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + + var cur, + i = 0, + aup = a.parentNode, + bup = b.parentNode, + ap = [ a ], + bp = [ b ]; + + // Parentless nodes are either documents or disconnected + if ( !aup || !bup ) { + return a === document ? -1 : + b === document ? 1 : + aup ? -1 : + bup ? 1 : + sortInput ? + ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + 0; + + // If the nodes are siblings, we can do a quick check + } else if ( aup === bup ) { + return siblingCheck( a, b ); + } + + // Otherwise we need full lists of their ancestors for comparison + cur = a; + while ( (cur = cur.parentNode) ) { + ap.unshift( cur ); + } + cur = b; + while ( (cur = cur.parentNode) ) { + bp.unshift( cur ); + } + + // Walk down the tree looking for a discrepancy + while ( ap[i] === bp[i] ) { + i++; + } + + return i ? + // Do a sibling check if the nodes have a common ancestor + siblingCheck( ap[i], bp[i] ) : + + // Otherwise nodes in our document sort first + ap[i] === preferredDoc ? -1 : + bp[i] === preferredDoc ? 1 : + 0; + }; + + return document; +}; + +Sizzle.matches = function( expr, elements ) { + return Sizzle( expr, null, null, elements ); +}; + +Sizzle.matchesSelector = function( elem, expr ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + // Make sure that attribute selectors are quoted + expr = expr.replace( rattributeQuotes, "='$1']" ); + + if ( support.matchesSelector && documentIsHTML && + !compilerCache[ expr + " " ] && + ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + + try { + var ret = matches.call( elem, expr ); + + // IE 9's matchesSelector returns false on disconnected nodes + if ( ret || support.disconnectedMatch || + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { + return ret; + } + } catch (e) {} + } + + return Sizzle( expr, document, null, [ elem ] ).length > 0; +}; + +Sizzle.contains = function( context, elem ) { + // Set document vars if needed + if ( ( context.ownerDocument || context ) !== document ) { + setDocument( context ); + } + return contains( context, elem ); +}; + +Sizzle.attr = function( elem, name ) { + // Set document vars if needed + if ( ( elem.ownerDocument || elem ) !== document ) { + setDocument( elem ); + } + + var fn = Expr.attrHandle[ name.toLowerCase() ], + // Don't get fooled by Object.prototype properties (jQuery #13807) + val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? + fn( elem, name, !documentIsHTML ) : + undefined; + + return val !== undefined ? + val : + support.attributes || !documentIsHTML ? + elem.getAttribute( name ) : + (val = elem.getAttributeNode(name)) && val.specified ? + val.value : + null; +}; + +Sizzle.error = function( msg ) { + throw new Error( "Syntax error, unrecognized expression: " + msg ); +}; + +/** + * Document sorting and removing duplicates + * @param {ArrayLike} results + */ +Sizzle.uniqueSort = function( results ) { + var elem, + duplicates = [], + j = 0, + i = 0; + + // Unless we *know* we can detect duplicates, assume their presence + hasDuplicate = !support.detectDuplicates; + sortInput = !support.sortStable && results.slice( 0 ); + results.sort( sortOrder ); + + if ( hasDuplicate ) { + while ( (elem = results[i++]) ) { + if ( elem === results[ i ] ) { + j = duplicates.push( i ); + } + } + while ( j-- ) { + results.splice( duplicates[ j ], 1 ); + } + } + + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + + return results; +}; + +/** + * Utility function for retrieving the text value of an array of DOM nodes + * @param {Array|Element} elem + */ +getText = Sizzle.getText = function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; +}; + +Expr = Sizzle.selectors = { + + // Can be adjusted by the user + cacheLength: 50, + + createPseudo: markFunction, + + match: matchExpr, + + attrHandle: {}, + + find: {}, + + relative: { + ">": { dir: "parentNode", first: true }, + " ": { dir: "parentNode" }, + "+": { dir: "previousSibling", first: true }, + "~": { dir: "previousSibling" } + }, + + preFilter: { + "ATTR": function( match ) { + match[1] = match[1].replace( runescape, funescape ); + + // Move the given value to match[3] whether quoted or unquoted + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); + + if ( match[2] === "~=" ) { + match[3] = " " + match[3] + " "; + } + + return match.slice( 0, 4 ); + }, + + "CHILD": function( match ) { + /* matches from matchExpr["CHILD"] + 1 type (only|nth|...) + 2 what (child|of-type) + 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) + 4 xn-component of xn+y argument ([+-]?\d*n|) + 5 sign of xn-component + 6 x of xn-component + 7 sign of y-component + 8 y of y-component + */ + match[1] = match[1].toLowerCase(); + + if ( match[1].slice( 0, 3 ) === "nth" ) { + // nth-* requires argument + if ( !match[3] ) { + Sizzle.error( match[0] ); + } + + // numeric x and y parameters for Expr.filter.CHILD + // remember that false/true cast respectively to 0/1 + match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); + match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); + + // other types prohibit arguments + } else if ( match[3] ) { + Sizzle.error( match[0] ); + } + + return match; + }, + + "PSEUDO": function( match ) { + var excess, + unquoted = !match[6] && match[2]; + + if ( matchExpr["CHILD"].test( match[0] ) ) { + return null; + } + + // Accept quoted arguments as-is + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; + + // Strip excess characters from unquoted arguments + } else if ( unquoted && rpseudo.test( unquoted ) && + // Get excess from tokenize (recursively) + (excess = tokenize( unquoted, true )) && + // advance to the next closing parenthesis + (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { + + // excess is a negative index + match[0] = match[0].slice( 0, excess ); + match[2] = unquoted.slice( 0, excess ); + } + + // Return only captures needed by the pseudo filter method (type and argument) + return match.slice( 0, 3 ); + } + }, + + filter: { + + "TAG": function( nodeNameSelector ) { + var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + return nodeNameSelector === "*" ? + function() { return true; } : + function( elem ) { + return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + }; + }, + + "CLASS": function( className ) { + var pattern = classCache[ className + " " ]; + + return pattern || + (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && + classCache( className, function( elem ) { + return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== "undefined" && elem.getAttribute("class") || "" ); + }); + }, + + "ATTR": function( name, operator, check ) { + return function( elem ) { + var result = Sizzle.attr( elem, name ); + + if ( result == null ) { + return operator === "!="; + } + if ( !operator ) { + return true; + } + + result += ""; + + return operator === "=" ? result === check : + operator === "!=" ? result !== check : + operator === "^=" ? check && result.indexOf( check ) === 0 : + operator === "*=" ? check && result.indexOf( check ) > -1 : + operator === "$=" ? check && result.slice( -check.length ) === check : + operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : + operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : + false; + }; + }, + + "CHILD": function( type, what, argument, first, last ) { + var simple = type.slice( 0, 3 ) !== "nth", + forward = type.slice( -4 ) !== "last", + ofType = what === "of-type"; + + return first === 1 && last === 0 ? + + // Shortcut for :nth-*(n) + function( elem ) { + return !!elem.parentNode; + } : + + function( elem, context, xml ) { + var cache, uniqueCache, outerCache, node, nodeIndex, start, + dir = simple !== forward ? "nextSibling" : "previousSibling", + parent = elem.parentNode, + name = ofType && elem.nodeName.toLowerCase(), + useCache = !xml && !ofType, + diff = false; + + if ( parent ) { + + // :(first|last|only)-(child|of-type) + if ( simple ) { + while ( dir ) { + node = elem; + while ( (node = node[ dir ]) ) { + if ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) { + + return false; + } + } + // Reverse direction for :only-* (if we haven't yet done so) + start = dir = type === "only" && !start && "nextSibling"; + } + return true; + } + + start = [ forward ? parent.firstChild : parent.lastChild ]; + + // non-xml :nth-child(...) stores cache data on `parent` + if ( forward && useCache ) { + + // Seek `elem` from a previously-cached index + + // ...in a gzip-friendly way + node = parent; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex && cache[ 2 ]; + node = nodeIndex && parent.childNodes[ nodeIndex ]; + + while ( (node = ++nodeIndex && node && node[ dir ] || + + // Fallback to seeking `elem` from the start + (diff = nodeIndex = 0) || start.pop()) ) { + + // When found, cache indexes on `parent` and break + if ( node.nodeType === 1 && ++diff && node === elem ) { + uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + break; + } + } + + } else { + // Use previously-cached element index if available + if ( useCache ) { + // ...in a gzip-friendly way + node = elem; + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + cache = uniqueCache[ type ] || []; + nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; + diff = nodeIndex; + } + + // xml :nth-child(...) + // or :nth-last-child(...) or :nth(-last)?-of-type(...) + if ( diff === false ) { + // Use the same loop as above to seek `elem` from the start + while ( (node = ++nodeIndex && node && node[ dir ] || + (diff = nodeIndex = 0) || start.pop()) ) { + + if ( ( ofType ? + node.nodeName.toLowerCase() === name : + node.nodeType === 1 ) && + ++diff ) { + + // Cache the index of each encountered element + if ( useCache ) { + outerCache = node[ expando ] || (node[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ node.uniqueID ] || + (outerCache[ node.uniqueID ] = {}); + + uniqueCache[ type ] = [ dirruns, diff ]; + } + + if ( node === elem ) { + break; + } + } + } + } + } + + // Incorporate the offset, then check against cycle size + diff -= last; + return diff === first || ( diff % first === 0 && diff / first >= 0 ); + } + }; + }, + + "PSEUDO": function( pseudo, argument ) { + // pseudo-class names are case-insensitive + // http://www.w3.org/TR/selectors/#pseudo-classes + // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters + // Remember that setFilters inherits from pseudos + var args, + fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || + Sizzle.error( "unsupported pseudo: " + pseudo ); + + // The user may use createPseudo to indicate that + // arguments are needed to create the filter function + // just as Sizzle does + if ( fn[ expando ] ) { + return fn( argument ); + } + + // But maintain support for old signatures + if ( fn.length > 1 ) { + args = [ pseudo, pseudo, "", argument ]; + return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? + markFunction(function( seed, matches ) { + var idx, + matched = fn( seed, argument ), + i = matched.length; + while ( i-- ) { + idx = indexOf( seed, matched[i] ); + seed[ idx ] = !( matches[ idx ] = matched[i] ); + } + }) : + function( elem ) { + return fn( elem, 0, args ); + }; + } + + return fn; + } + }, + + pseudos: { + // Potentially complex pseudos + "not": markFunction(function( selector ) { + // Trim the selector passed to compile + // to avoid treating leading and trailing + // spaces as combinators + var input = [], + results = [], + matcher = compile( selector.replace( rtrim, "$1" ) ); + + return matcher[ expando ] ? + markFunction(function( seed, matches, context, xml ) { + var elem, + unmatched = matcher( seed, null, xml, [] ), + i = seed.length; + + // Match elements unmatched by `matcher` + while ( i-- ) { + if ( (elem = unmatched[i]) ) { + seed[i] = !(matches[i] = elem); + } + } + }) : + function( elem, context, xml ) { + input[0] = elem; + matcher( input, null, xml, results ); + // Don't keep the element (issue #299) + input[0] = null; + return !results.pop(); + }; + }), + + "has": markFunction(function( selector ) { + return function( elem ) { + return Sizzle( selector, elem ).length > 0; + }; + }), + + "contains": markFunction(function( text ) { + text = text.replace( runescape, funescape ); + return function( elem ) { + return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; + }; + }), + + // "Whether an element is represented by a :lang() selector + // is based solely on the element's language value + // being equal to the identifier C, + // or beginning with the identifier C immediately followed by "-". + // The matching of C against the element's language value is performed case-insensitively. + // The identifier C does not have to be a valid language name." + // http://www.w3.org/TR/selectors/#lang-pseudo + "lang": markFunction( function( lang ) { + // lang value must be a valid identifier + if ( !ridentifier.test(lang || "") ) { + Sizzle.error( "unsupported lang: " + lang ); + } + lang = lang.replace( runescape, funescape ).toLowerCase(); + return function( elem ) { + var elemLang; + do { + if ( (elemLang = documentIsHTML ? + elem.lang : + elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { + + elemLang = elemLang.toLowerCase(); + return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; + } + } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); + return false; + }; + }), + + // Miscellaneous + "target": function( elem ) { + var hash = window.location && window.location.hash; + return hash && hash.slice( 1 ) === elem.id; + }, + + "root": function( elem ) { + return elem === docElem; + }, + + "focus": function( elem ) { + return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); + }, + + // Boolean properties + "enabled": function( elem ) { + return elem.disabled === false; + }, + + "disabled": function( elem ) { + return elem.disabled === true; + }, + + "checked": function( elem ) { + // In CSS3, :checked should return both checked and selected elements + // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + var nodeName = elem.nodeName.toLowerCase(); + return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); + }, + + "selected": function( elem ) { + // Accessing this property makes selected-by-default + // options in Safari work properly + if ( elem.parentNode ) { + elem.parentNode.selectedIndex; + } + + return elem.selected === true; + }, + + // Contents + "empty": function( elem ) { + // http://www.w3.org/TR/selectors/#empty-pseudo + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + if ( elem.nodeType < 6 ) { + return false; + } + } + return true; + }, + + "parent": function( elem ) { + return !Expr.pseudos["empty"]( elem ); + }, + + // Element/input types + "header": function( elem ) { + return rheader.test( elem.nodeName ); + }, + + "input": function( elem ) { + return rinputs.test( elem.nodeName ); + }, + + "button": function( elem ) { + var name = elem.nodeName.toLowerCase(); + return name === "input" && elem.type === "button" || name === "button"; + }, + + "text": function( elem ) { + var attr; + return elem.nodeName.toLowerCase() === "input" && + elem.type === "text" && + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); + }, + + // Position-in-collection + "first": createPositionalPseudo(function() { + return [ 0 ]; + }), + + "last": createPositionalPseudo(function( matchIndexes, length ) { + return [ length - 1 ]; + }), + + "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { + return [ argument < 0 ? argument + length : argument ]; + }), + + "even": createPositionalPseudo(function( matchIndexes, length ) { + var i = 0; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "odd": createPositionalPseudo(function( matchIndexes, length ) { + var i = 1; + for ( ; i < length; i += 2 ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; --i >= 0; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }), + + "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { + var i = argument < 0 ? argument + length : argument; + for ( ; ++i < length; ) { + matchIndexes.push( i ); + } + return matchIndexes; + }) + } +}; + +Expr.pseudos["nth"] = Expr.pseudos["eq"]; + +// Add button/input type pseudos +for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { + Expr.pseudos[ i ] = createInputPseudo( i ); +} +for ( i in { submit: true, reset: true } ) { + Expr.pseudos[ i ] = createButtonPseudo( i ); +} + +// Easy API for creating new setFilters +function setFilters() {} +setFilters.prototype = Expr.filters = Expr.pseudos; +Expr.setFilters = new setFilters(); + +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { + var matched, match, tokens, type, + soFar, groups, preFilters, + cached = tokenCache[ selector + " " ]; + + if ( cached ) { + return parseOnly ? 0 : cached.slice( 0 ); + } + + soFar = selector; + groups = []; + preFilters = Expr.preFilter; + + while ( soFar ) { + + // Comma and first run + if ( !matched || (match = rcomma.exec( soFar )) ) { + if ( match ) { + // Don't consume trailing commas as valid + soFar = soFar.slice( match[0].length ) || soFar; + } + groups.push( (tokens = []) ); + } + + matched = false; + + // Combinators + if ( (match = rcombinators.exec( soFar )) ) { + matched = match.shift(); + tokens.push({ + value: matched, + // Cast descendant combinators to space + type: match[0].replace( rtrim, " " ) + }); + soFar = soFar.slice( matched.length ); + } + + // Filters + for ( type in Expr.filter ) { + if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || + (match = preFilters[ type ]( match ))) ) { + matched = match.shift(); + tokens.push({ + value: matched, + type: type, + matches: match + }); + soFar = soFar.slice( matched.length ); + } + } + + if ( !matched ) { + break; + } + } + + // Return the length of the invalid excess + // if we're just parsing + // Otherwise, throw an error or return tokens + return parseOnly ? + soFar.length : + soFar ? + Sizzle.error( selector ) : + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +}; + +function toSelector( tokens ) { + var i = 0, + len = tokens.length, + selector = ""; + for ( ; i < len; i++ ) { + selector += tokens[i].value; + } + return selector; +} + +function addCombinator( matcher, combinator, base ) { + var dir = combinator.dir, + checkNonElements = base && dir === "parentNode", + doneName = done++; + + return combinator.first ? + // Check against closest ancestor/preceding element + function( elem, context, xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + return matcher( elem, context, xml ); + } + } + } : + + // Check against all ancestor/preceding elements + function( elem, context, xml ) { + var oldCache, uniqueCache, outerCache, + newCache = [ dirruns, doneName ]; + + // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching + if ( xml ) { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + if ( matcher( elem, context, xml ) ) { + return true; + } + } + } + } else { + while ( (elem = elem[ dir ]) ) { + if ( elem.nodeType === 1 || checkNonElements ) { + outerCache = elem[ expando ] || (elem[ expando ] = {}); + + // Support: IE <9 only + // Defend against cloned attroperties (jQuery gh-1709) + uniqueCache = outerCache[ elem.uniqueID ] || (outerCache[ elem.uniqueID ] = {}); + + if ( (oldCache = uniqueCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); + } else { + // Reuse newcache so results back-propagate to previous elements + uniqueCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { + return true; + } + } + } + } + } + }; +} + +function elementMatcher( matchers ) { + return matchers.length > 1 ? + function( elem, context, xml ) { + var i = matchers.length; + while ( i-- ) { + if ( !matchers[i]( elem, context, xml ) ) { + return false; + } + } + return true; + } : + matchers[0]; +} + +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + +function condense( unmatched, map, filter, context, xml ) { + var elem, + newUnmatched = [], + i = 0, + len = unmatched.length, + mapped = map != null; + + for ( ; i < len; i++ ) { + if ( (elem = unmatched[i]) ) { + if ( !filter || filter( elem, context, xml ) ) { + newUnmatched.push( elem ); + if ( mapped ) { + map.push( i ); + } + } + } + } + + return newUnmatched; +} + +function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { + if ( postFilter && !postFilter[ expando ] ) { + postFilter = setMatcher( postFilter ); + } + if ( postFinder && !postFinder[ expando ] ) { + postFinder = setMatcher( postFinder, postSelector ); + } + return markFunction(function( seed, results, context, xml ) { + var temp, i, elem, + preMap = [], + postMap = [], + preexisting = results.length, + + // Get initial elements from seed or context + elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), + + // Prefilter to get matcher input, preserving a map for seed-results synchronization + matcherIn = preFilter && ( seed || !selector ) ? + condense( elems, preMap, preFilter, context, xml ) : + elems, + + matcherOut = matcher ? + // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, + postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + + // ...intermediate processing is necessary + [] : + + // ...otherwise use results directly + results : + matcherIn; + + // Find primary matches + if ( matcher ) { + matcher( matcherIn, matcherOut, context, xml ); + } + + // Apply postFilter + if ( postFilter ) { + temp = condense( matcherOut, postMap ); + postFilter( temp, [], context, xml ); + + // Un-match failing elements by moving them back to matcherIn + i = temp.length; + while ( i-- ) { + if ( (elem = temp[i]) ) { + matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); + } + } + } + + if ( seed ) { + if ( postFinder || preFilter ) { + if ( postFinder ) { + // Get the final matcherOut by condensing this intermediate into postFinder contexts + temp = []; + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) ) { + // Restore matcherIn since elem is not yet a final match + temp.push( (matcherIn[i] = elem) ); + } + } + postFinder( null, (matcherOut = []), temp, xml ); + } + + // Move matched elements from seed to results to keep them synchronized + i = matcherOut.length; + while ( i-- ) { + if ( (elem = matcherOut[i]) && + (temp = postFinder ? indexOf( seed, elem ) : preMap[i]) > -1 ) { + + seed[temp] = !(results[temp] = elem); + } + } + } + + // Add elements to results, through postFinder if defined + } else { + matcherOut = condense( + matcherOut === results ? + matcherOut.splice( preexisting, matcherOut.length ) : + matcherOut + ); + if ( postFinder ) { + postFinder( null, results, matcherOut, xml ); + } else { + push.apply( results, matcherOut ); + } + } + }); +} + +function matcherFromTokens( tokens ) { + var checkContext, matcher, j, + len = tokens.length, + leadingRelative = Expr.relative[ tokens[0].type ], + implicitRelative = leadingRelative || Expr.relative[" "], + i = leadingRelative ? 1 : 0, + + // The foundational matcher ensures that elements are reachable from top-level context(s) + matchContext = addCombinator( function( elem ) { + return elem === checkContext; + }, implicitRelative, true ), + matchAnyContext = addCombinator( function( elem ) { + return indexOf( checkContext, elem ) > -1; + }, implicitRelative, true ), + matchers = [ function( elem, context, xml ) { + var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + (checkContext = context).nodeType ? + matchContext( elem, context, xml ) : + matchAnyContext( elem, context, xml ) ); + // Avoid hanging onto element (issue #299) + checkContext = null; + return ret; + } ]; + + for ( ; i < len; i++ ) { + if ( (matcher = Expr.relative[ tokens[i].type ]) ) { + matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; + } else { + matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); + + // Return special upon seeing a positional matcher + if ( matcher[ expando ] ) { + // Find the next relative operator (if any) for proper handling + j = ++i; + for ( ; j < len; j++ ) { + if ( Expr.relative[ tokens[j].type ] ) { + break; + } + } + return setMatcher( + i > 1 && elementMatcher( matchers ), + i > 1 && toSelector( + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) + ).replace( rtrim, "$1" ), + matcher, + i < j && matcherFromTokens( tokens.slice( i, j ) ), + j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), + j < len && toSelector( tokens ) + ); + } + matchers.push( matcher ); + } + } + + return elementMatcher( matchers ); +} + +function matcherFromGroupMatchers( elementMatchers, setMatchers ) { + var bySet = setMatchers.length > 0, + byElement = elementMatchers.length > 0, + superMatcher = function( seed, context, xml, results, outermost ) { + var elem, j, matcher, + matchedCount = 0, + i = "0", + unmatched = seed && [], + setMatched = [], + contextBackup = outermostContext, + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), + // Use integer dirruns iff this is the outermost matcher + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; + + if ( outermost ) { + outermostContext = context === document || context || outermost; + } + + // Add elements passing elementMatchers directly to results + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { + if ( byElement && elem ) { + j = 0; + if ( !context && elem.ownerDocument !== document ) { + setDocument( elem ); + xml = !documentIsHTML; + } + while ( (matcher = elementMatchers[j++]) ) { + if ( matcher( elem, context || document, xml) ) { + results.push( elem ); + break; + } + } + if ( outermost ) { + dirruns = dirrunsUnique; + } + } + + // Track unmatched elements for set filters + if ( bySet ) { + // They will have gone through all possible matchers + if ( (elem = !matcher && elem) ) { + matchedCount--; + } + + // Lengthen the array for every element, matched or not + if ( seed ) { + unmatched.push( elem ); + } + } + } + + // `i` is now the count of elements visited above, and adding it to `matchedCount` + // makes the latter nonnegative. + matchedCount += i; + + // Apply set filters to unmatched elements + // NOTE: This can be skipped if there are no unmatched elements (i.e., `matchedCount` + // equals `i`), unless we didn't visit _any_ elements in the above loop because we have + // no element matchers and no seed. + // Incrementing an initially-string "0" `i` allows `i` to remain a string only in that + // case, which will result in a "00" `matchedCount` that differs from `i` but is also + // numerically zero. + if ( bySet && i !== matchedCount ) { + j = 0; + while ( (matcher = setMatchers[j++]) ) { + matcher( unmatched, setMatched, context, xml ); + } + + if ( seed ) { + // Reintegrate element matches to eliminate the need for sorting + if ( matchedCount > 0 ) { + while ( i-- ) { + if ( !(unmatched[i] || setMatched[i]) ) { + setMatched[i] = pop.call( results ); + } + } + } + + // Discard index placeholder values to get only actual matches + setMatched = condense( setMatched ); + } + + // Add matches to results + push.apply( results, setMatched ); + + // Seedless set matches succeeding multiple successful matchers stipulate sorting + if ( outermost && !seed && setMatched.length > 0 && + ( matchedCount + setMatchers.length ) > 1 ) { + + Sizzle.uniqueSort( results ); + } + } + + // Override manipulation of globals by nested matchers + if ( outermost ) { + dirruns = dirrunsUnique; + outermostContext = contextBackup; + } + + return unmatched; + }; + + return bySet ? + markFunction( superMatcher ) : + superMatcher; +} + +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { + var i, + setMatchers = [], + elementMatchers = [], + cached = compilerCache[ selector + " " ]; + + if ( !cached ) { + // Generate a function of recursive functions that can be used to check each element + if ( !match ) { + match = tokenize( selector ); + } + i = match.length; + while ( i-- ) { + cached = matcherFromTokens( match[i] ); + if ( cached[ expando ] ) { + setMatchers.push( cached ); + } else { + elementMatchers.push( cached ); + } + } + + // Cache the compiled function + cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; + } + return cached; +}; + +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { + var i, tokens, token, type, find, + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); + + results = results || []; + + // Try to minimize operations if there is only one selector in the list and no seed + // (the latter of which guarantees us context) + if ( match.length === 1 ) { + + // Reduce context if the leading compound selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && context.nodeType === 9 && documentIsHTML && + Expr.relative[ tokens[1].type ] ) { + + context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; + if ( !context ) { + return results; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; + } + + selector = selector.slice( tokens.shift().value.length ); + } + + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; + + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { + + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; + } + + break; + } + } + } + } + + // Compile and execute a filtering function if one is not provided + // Provide `match` to avoid retokenization if we modified the selector above + ( compiled || compile( selector, match ) )( + seed, + context, + !documentIsHTML, + results, + !context || rsibling.test( selector ) && testContext( context.parentNode ) || context + ); + return results; +}; + +// One-time assignments + +// Sort stability +support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; + +// Support: Chrome 14-35+ +// Always assume duplicates if they aren't passed to the comparison function +support.detectDuplicates = !!hasDuplicate; + +// Initialize against the default document +setDocument(); + +// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Detached nodes confoundingly follow *each other* +support.sortDetached = assert(function( div1 ) { + // Should return 1, but returns 4 (following) + return div1.compareDocumentPosition( document.createElement("div") ) & 1; +}); + +// Support: IE<8 +// Prevent attribute/property "interpolation" +// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx +if ( !assert(function( div ) { + div.innerHTML = ""; + return div.firstChild.getAttribute("href") === "#" ; +}) ) { + addHandle( "type|href|height|width", function( elem, name, isXML ) { + if ( !isXML ) { + return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); + } + }); +} + +// Support: IE<9 +// Use defaultValue in place of getAttribute("value") +if ( !support.attributes || !assert(function( div ) { + div.innerHTML = ""; + div.firstChild.setAttribute( "value", "" ); + return div.firstChild.getAttribute( "value" ) === ""; +}) ) { + addHandle( "value", function( elem, name, isXML ) { + if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { + return elem.defaultValue; + } + }); +} + +// Support: IE<9 +// Use getAttributeNode to fetch booleans when getAttribute lies +if ( !assert(function( div ) { + return div.getAttribute("disabled") == null; +}) ) { + addHandle( booleans, function( elem, name, isXML ) { + var val; + if ( !isXML ) { + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; + } + }); +} + +return Sizzle; + +})( window ); + + + +jQuery.find = Sizzle; +jQuery.expr = Sizzle.selectors; +jQuery.expr[ ":" ] = jQuery.expr.pseudos; +jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; +jQuery.text = Sizzle.getText; +jQuery.isXMLDoc = Sizzle.isXML; +jQuery.contains = Sizzle.contains; + + + +var dir = function( elem, dir, until ) { + var matched = [], + truncate = until !== undefined; + + while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) { + if ( elem.nodeType === 1 ) { + if ( truncate && jQuery( elem ).is( until ) ) { + break; + } + matched.push( elem ); + } + } + return matched; +}; + + +var siblings = function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; +}; + + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = ( /^<([\w-]+)\s*\/?>(?:<\/\1>|)$/ ); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + } ); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + } ); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( jQuery.inArray( elem, qualifier ) > -1 ) !== not; + } ); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + } ) ); +}; + +jQuery.fn.extend( { + find: function( selector ) { + var i, + ret = [], + self = this, + len = self.length; + + if ( typeof selector !== "string" ) { + return this.pushStack( jQuery( selector ).filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( self[ i ], this ) ) { + return true; + } + } + } ) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return ret; + }, + filter: function( selector ) { + return this.pushStack( winnow( this, selector || [], false ) ); + }, + not: function( selector ) { + return this.pushStack( winnow( this, selector || [], true ) ); + }, + is: function( selector ) { + return !!winnow( + this, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +} ); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context, root ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // init accepts an alternate rootjQuery + // so migrate can support jQuery.sub (gh-2101) + root = root || rootjQuery; + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector.charAt( 0 ) === "<" && + selector.charAt( selector.length - 1 ) === ">" && + selector.length >= 3 ) { + + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && ( match[ 1 ] || !context ) ) { + + // HANDLE: $(html) -> $(array) + if ( match[ 1 ] ) { + context = context instanceof jQuery ? context[ 0 ] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[ 1 ], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[ 2 ] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + + // Handle the case where IE and Opera return items + // by name instead of ID + if ( elem.id !== match[ 2 ] ) { + return rootjQuery.find( selector ); + } + + // Otherwise, we inject the element directly into the jQuery object + this.length = 1; + this[ 0 ] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || root ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[ 0 ] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof root.ready !== "undefined" ? + root.ready( selector ) : + + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.fn.extend( { + has: function( target ) { + var i, + targets = jQuery( target, this ), + len = targets.length; + + return this.filter( function() { + for ( i = 0; i < len; i++ ) { + if ( jQuery.contains( this, targets[ i ] ) ) { + return true; + } + } + } ); + }, + + closest: function( selectors, context ) { + var cur, + i = 0, + l = this.length, + matched = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) { + + // Always skip document fragments + if ( cur.nodeType < 11 && ( pos ? + pos.index( cur ) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector( cur, selectors ) ) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return jQuery.inArray( this[ 0 ], jQuery( elem ) ); + } + + // Locate the position of the desired element + return jQuery.inArray( + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem, this ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.uniqueSort( + jQuery.merge( this.get(), jQuery( selector, context ) ) + ) + ); + }, + + addBack: function( selector ) { + return this.add( selector == null ? + this.prevObject : this.prevObject.filter( selector ) + ); + } +} ); + +function sibling( cur, dir ) { + do { + cur = cur[ dir ]; + } while ( cur && cur.nodeType !== 1 ); + + return cur; +} + +jQuery.each( { + parent: function( elem ) { + var parent = elem.parentNode; + return parent && parent.nodeType !== 11 ? parent : null; + }, + parents: function( elem ) { + return dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return siblings( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return siblings( elem.firstChild ); + }, + contents: function( elem ) { + return jQuery.nodeName( elem, "iframe" ) ? + elem.contentDocument || elem.contentWindow.document : + jQuery.merge( [], elem.childNodes ); + } +}, function( name, fn ) { + jQuery.fn[ name ] = function( until, selector ) { + var ret = jQuery.map( this, fn, until ); + + if ( name.slice( -5 ) !== "Until" ) { + selector = until; + } + + if ( selector && typeof selector === "string" ) { + ret = jQuery.filter( selector, ret ); + } + + if ( this.length > 1 ) { + + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + ret = jQuery.uniqueSort( ret ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + ret = ret.reverse(); + } + } + + return this.pushStack( ret ); + }; +} ); +var rnotwhite = ( /\S+/g ); + + + +// Convert String-formatted options into Object-formatted ones +function createOptions( options ) { + var object = {}; + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { + object[ flag ] = true; + } ); + return object; +} + +/* + * Create a callback list using the following parameters: + * + * options: an optional list of space-separated options that will change how + * the callback list behaves or a more traditional option object + * + * By default a callback list will act like an event callback list and can be + * "fired" multiple times. + * + * Possible options: + * + * once: will ensure the callback list can only be fired once (like a Deferred) + * + * memory: will keep track of previous values and will call any callback added + * after the list has been fired right away with the latest "memorized" + * values (like a Deferred) + * + * unique: will ensure a callback can only be added once (no duplicate in the list) + * + * stopOnFalse: interrupt callings when a callback returns false + * + */ +jQuery.Callbacks = function( options ) { + + // Convert options from String-formatted to Object-formatted if needed + // (we check in cache first) + options = typeof options === "string" ? + createOptions( options ) : + jQuery.extend( {}, options ); + + var // Flag to know if list is currently firing + firing, + + // Last fire value for non-forgettable lists + memory, + + // Flag to know if list was already fired + fired, + + // Flag to prevent firing + locked, + + // Actual callback list + list = [], + + // Queue of execution data for repeatable lists + queue = [], + + // Index of currently firing callback (modified by add/remove as needed) + firingIndex = -1, + + // Fire callbacks + fire = function() { + + // Enforce single-firing + locked = options.once; + + // Execute callbacks for all pending executions, + // respecting firingIndex overrides and runtime changes + fired = firing = true; + for ( ; queue.length; firingIndex = -1 ) { + memory = queue.shift(); + while ( ++firingIndex < list.length ) { + + // Run callback and check for early termination + if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false && + options.stopOnFalse ) { + + // Jump to end and forget the data so .add doesn't re-fire + firingIndex = list.length; + memory = false; + } + } + } + + // Forget the data if we're done with it + if ( !options.memory ) { + memory = false; + } + + firing = false; + + // Clean up if we're done firing for good + if ( locked ) { + + // Keep an empty list if we have data for future add calls + if ( memory ) { + list = []; + + // Otherwise, this object is spent + } else { + list = ""; + } + } + }, + + // Actual Callbacks object + self = { + + // Add a callback or a collection of callbacks to the list + add: function() { + if ( list ) { + + // If we have memory from a past run, we should fire after adding + if ( memory && !firing ) { + firingIndex = list.length - 1; + queue.push( memory ); + } + + ( function add( args ) { + jQuery.each( args, function( _, arg ) { + if ( jQuery.isFunction( arg ) ) { + if ( !options.unique || !self.has( arg ) ) { + list.push( arg ); + } + } else if ( arg && arg.length && jQuery.type( arg ) !== "string" ) { + + // Inspect recursively + add( arg ); + } + } ); + } )( arguments ); + + if ( memory && !firing ) { + fire(); + } + } + return this; + }, + + // Remove a callback from the list + remove: function() { + jQuery.each( arguments, function( _, arg ) { + var index; + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + list.splice( index, 1 ); + + // Handle firing indexes + if ( index <= firingIndex ) { + firingIndex--; + } + } + } ); + return this; + }, + + // Check if a given callback is in the list. + // If no argument is given, return whether or not list has callbacks attached. + has: function( fn ) { + return fn ? + jQuery.inArray( fn, list ) > -1 : + list.length > 0; + }, + + // Remove all callbacks from the list + empty: function() { + if ( list ) { + list = []; + } + return this; + }, + + // Disable .fire and .add + // Abort any current/pending executions + // Clear all callbacks and values + disable: function() { + locked = queue = []; + list = memory = ""; + return this; + }, + disabled: function() { + return !list; + }, + + // Disable .fire + // Also disable .add unless we have memory (since it would have no effect) + // Abort any pending executions + lock: function() { + locked = true; + if ( !memory ) { + self.disable(); + } + return this; + }, + locked: function() { + return !!locked; + }, + + // Call all callbacks with the given context and arguments + fireWith: function( context, args ) { + if ( !locked ) { + args = args || []; + args = [ context, args.slice ? args.slice() : args ]; + queue.push( args ); + if ( !firing ) { + fire(); + } + } + return this; + }, + + // Call all the callbacks with the given arguments + fire: function() { + self.fireWith( this, arguments ); + return this; + }, + + // To know if the callbacks have already been called at least once + fired: function() { + return !!fired; + } + }; + + return self; +}; + + +jQuery.extend( { + + Deferred: function( func ) { + var tuples = [ + + // action, add listener, listener list, final state + [ "resolve", "done", jQuery.Callbacks( "once memory" ), "resolved" ], + [ "reject", "fail", jQuery.Callbacks( "once memory" ), "rejected" ], + [ "notify", "progress", jQuery.Callbacks( "memory" ) ] + ], + state = "pending", + promise = { + state: function() { + return state; + }, + always: function() { + deferred.done( arguments ).fail( arguments ); + return this; + }, + then: function( /* fnDone, fnFail, fnProgress */ ) { + var fns = arguments; + return jQuery.Deferred( function( newDefer ) { + jQuery.each( tuples, function( i, tuple ) { + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + + // deferred[ done | fail | progress ] for forwarding actions to newDefer + deferred[ tuple[ 1 ] ]( function() { + var returned = fn && fn.apply( this, arguments ); + if ( returned && jQuery.isFunction( returned.promise ) ) { + returned.promise() + .progress( newDefer.notify ) + .done( newDefer.resolve ) + .fail( newDefer.reject ); + } else { + newDefer[ tuple[ 0 ] + "With" ]( + this === promise ? newDefer.promise() : this, + fn ? [ returned ] : arguments + ); + } + } ); + } ); + fns = null; + } ).promise(); + }, + + // Get a promise for this deferred + // If obj is provided, the promise aspect is added to the object + promise: function( obj ) { + return obj != null ? jQuery.extend( obj, promise ) : promise; + } + }, + deferred = {}; + + // Keep pipe for back-compat + promise.pipe = promise.then; + + // Add list-specific methods + jQuery.each( tuples, function( i, tuple ) { + var list = tuple[ 2 ], + stateString = tuple[ 3 ]; + + // promise[ done | fail | progress ] = list.add + promise[ tuple[ 1 ] ] = list.add; + + // Handle state + if ( stateString ) { + list.add( function() { + + // state = [ resolved | rejected ] + state = stateString; + + // [ reject_list | resolve_list ].disable; progress_list.lock + }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock ); + } + + // deferred[ resolve | reject | notify ] + deferred[ tuple[ 0 ] ] = function() { + deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments ); + return this; + }; + deferred[ tuple[ 0 ] + "With" ] = list.fireWith; + } ); + + // Make the deferred a promise + promise.promise( deferred ); + + // Call given func if any + if ( func ) { + func.call( deferred, deferred ); + } + + // All done! + return deferred; + }, + + // Deferred helper + when: function( subordinate /* , ..., subordinateN */ ) { + var i = 0, + resolveValues = slice.call( arguments ), + length = resolveValues.length, + + // the count of uncompleted subordinates + remaining = length !== 1 || + ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0, + + // the master Deferred. + // If resolveValues consist of only a single Deferred, just use that. + deferred = remaining === 1 ? subordinate : jQuery.Deferred(), + + // Update function for both resolve and progress values + updateFunc = function( i, contexts, values ) { + return function( value ) { + contexts[ i ] = this; + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { + deferred.notifyWith( contexts, values ); + + } else if ( !( --remaining ) ) { + deferred.resolveWith( contexts, values ); + } + }; + }, + + progressValues, progressContexts, resolveContexts; + + // add listeners to Deferred subordinates; treat others as resolved + if ( length > 1 ) { + progressValues = new Array( length ); + progressContexts = new Array( length ); + resolveContexts = new Array( length ); + for ( ; i < length; i++ ) { + if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) { + resolveValues[ i ].promise() + .progress( updateFunc( i, progressContexts, progressValues ) ) + .done( updateFunc( i, resolveContexts, resolveValues ) ) + .fail( deferred.reject ); + } else { + --remaining; + } + } + } + + // if we're not waiting on anything, resolve the master + if ( !remaining ) { + deferred.resolveWith( resolveContexts, resolveValues ); + } + + return deferred.promise(); + } +} ); + + +// The deferred used on DOM ready +var readyList; + +jQuery.fn.ready = function( fn ) { + + // Add the callback + jQuery.ready.promise().done( fn ); + + return this; +}; + +jQuery.extend( { + + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, + + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, + + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, + + // Handle when the DOM is ready + ready: function( wait ) { + + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { + return; + } + + // Remember that the DOM is ready + jQuery.isReady = true; + + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; + } + + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); + + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +} ); + +/** + * Clean-up method for dom ready events + */ +function detach() { + if ( document.addEventListener ) { + document.removeEventListener( "DOMContentLoaded", completed ); + window.removeEventListener( "load", completed ); + + } else { + document.detachEvent( "onreadystatechange", completed ); + window.detachEvent( "onload", completed ); + } +} + +/** + * The ready event handler and self cleanup method + */ +function completed() { + + // readyState === "complete" is good enough for us to call the dom ready in oldIE + if ( document.addEventListener || + window.event.type === "load" || + document.readyState === "complete" ) { + + detach(); + jQuery.ready(); + } +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called + // after the browser event has already occurred. + // Support: IE6-10 + // Older IE sometimes signals "interactive" too soon + if ( document.readyState === "complete" || + ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) { + + // Handle it asynchronously to allow scripts the opportunity to delay ready + window.setTimeout( jQuery.ready ); + + // Standards-based browsers support DOMContentLoaded + } else if ( document.addEventListener ) { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed ); + + // If IE event model is used + } else { + + // Ensure firing before onload, maybe late but safe also for iframes + document.attachEvent( "onreadystatechange", completed ); + + // A fallback to window.onload, that will always work + window.attachEvent( "onload", completed ); + + // If IE and not a frame + // continually check to see if the document is ready + var top = false; + + try { + top = window.frameElement == null && document.documentElement; + } catch ( e ) {} + + if ( top && top.doScroll ) { + ( function doScrollCheck() { + if ( !jQuery.isReady ) { + + try { + + // Use the trick by Diego Perini + // http://javascript.nwbox.com/IEContentLoaded/ + top.doScroll( "left" ); + } catch ( e ) { + return window.setTimeout( doScrollCheck, 50 ); + } + + // detach all dom ready events + detach(); + + // and execute any waiting functions + jQuery.ready(); + } + } )(); + } + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Support: IE<9 +// Iteration over object's inherited properties before its own +var i; +for ( i in jQuery( support ) ) { + break; +} +support.ownFirst = i === "0"; + +// Note: most support tests are defined in their respective modules. +// false until the test is run +support.inlineBlockNeedsLayout = false; + +// Execute ASAP in case we need to set body.style.zoom +jQuery( function() { + + // Minified: var a,b,c,d + var val, div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + + // Return for frameset docs that don't have a body + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + if ( typeof div.style.zoom !== "undefined" ) { + + // Support: IE<8 + // Check if natively block-level elements act like inline-block + // elements when setting their display to 'inline' and giving + // them layout + div.style.cssText = "display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1"; + + support.inlineBlockNeedsLayout = val = div.offsetWidth === 3; + if ( val ) { + + // Prevent IE 6 from affecting layout for positioned elements #11048 + // Prevent IE from shrinking the body in IE 7 mode #12869 + // Support: IE<8 + body.style.zoom = 1; + } + } + + body.removeChild( container ); +} ); + + +( function() { + var div = document.createElement( "div" ); + + // Support: IE<9 + support.deleteExpando = true; + try { + delete div.test; + } catch ( e ) { + support.deleteExpando = false; + } + + // Null elements to avoid leaks in IE. + div = null; +} )(); +var acceptData = function( elem ) { + var noData = jQuery.noData[ ( elem.nodeName + " " ).toLowerCase() ], + nodeType = +elem.nodeType || 1; + + // Do not set data on non-element DOM nodes because it will not be cleared (#8335). + return nodeType !== 1 && nodeType !== 9 ? + false : + + // Nodes accept data unless otherwise specified; rejection can be conditional + !noData || noData !== true && elem.getAttribute( "classid" ) === noData; +}; + + + + +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + + var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch ( e ) {} + + // Make sure we set the data so it isn't changed later + jQuery.data( elem, key, data ); + + } else { + data = undefined; + } + } + + return data; +} + +// checks a cache object for emptiness +function isEmptyDataObject( obj ) { + var name; + for ( name in obj ) { + + // if the public data object is empty, the private is still empty + if ( name === "data" && jQuery.isEmptyObject( obj[ name ] ) ) { + continue; + } + if ( name !== "toJSON" ) { + return false; + } + } + + return true; +} + +function internalData( elem, name, data, pvt /* Internal Use Only */ ) { + if ( !acceptData( elem ) ) { + return; + } + + var ret, thisCache, + internalKey = jQuery.expando, + + // We have to handle DOM nodes and JS objects differently because IE6-7 + // can't GC object references properly across the DOM-JS boundary + isNode = elem.nodeType, + + // Only DOM nodes need the global jQuery cache; JS object data is + // attached directly to the object so GC can occur automatically + cache = isNode ? jQuery.cache : elem, + + // Only defining an ID for JS objects if its cache already exists allows + // the code to shortcut on the same path as a DOM node with no cache + id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey; + + // Avoid doing any more work than we need to when trying to get data on an + // object that has no data at all + if ( ( !id || !cache[ id ] || ( !pvt && !cache[ id ].data ) ) && + data === undefined && typeof name === "string" ) { + return; + } + + if ( !id ) { + + // Only DOM nodes need a new unique ID for each element since their data + // ends up in the global cache + if ( isNode ) { + id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; + } else { + id = internalKey; + } + } + + if ( !cache[ id ] ) { + + // Avoid exposing jQuery metadata on plain JS objects when the object + // is serialized using JSON.stringify + cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; + } + + // An object can be passed to jQuery.data instead of a key/value pair; this gets + // shallow copied over onto the existing cache + if ( typeof name === "object" || typeof name === "function" ) { + if ( pvt ) { + cache[ id ] = jQuery.extend( cache[ id ], name ); + } else { + cache[ id ].data = jQuery.extend( cache[ id ].data, name ); + } + } + + thisCache = cache[ id ]; + + // jQuery data() is stored in a separate object inside the object's internal data + // cache in order to avoid key collisions between internal data and user-defined + // data. + if ( !pvt ) { + if ( !thisCache.data ) { + thisCache.data = {}; + } + + thisCache = thisCache.data; + } + + if ( data !== undefined ) { + thisCache[ jQuery.camelCase( name ) ] = data; + } + + // Check for both converted-to-camel and non-converted data property names + // If a data property was specified + if ( typeof name === "string" ) { + + // First Try to find as-is property data + ret = thisCache[ name ]; + + // Test for null|undefined property data + if ( ret == null ) { + + // Try to find the camelCased property + ret = thisCache[ jQuery.camelCase( name ) ]; + } + } else { + ret = thisCache; + } + + return ret; +} + +function internalRemoveData( elem, name, pvt ) { + if ( !acceptData( elem ) ) { + return; + } + + var thisCache, i, + isNode = elem.nodeType, + + // See jQuery.data for more information + cache = isNode ? jQuery.cache : elem, + id = isNode ? elem[ jQuery.expando ] : jQuery.expando; + + // If there is already no cache entry for this object, there is no + // purpose in continuing + if ( !cache[ id ] ) { + return; + } + + if ( name ) { + + thisCache = pvt ? cache[ id ] : cache[ id ].data; + + if ( thisCache ) { + + // Support array or space separated string names for data keys + if ( !jQuery.isArray( name ) ) { + + // try the string as a key before any manipulation + if ( name in thisCache ) { + name = [ name ]; + } else { + + // split the camel cased version by spaces unless a key with the spaces exists + name = jQuery.camelCase( name ); + if ( name in thisCache ) { + name = [ name ]; + } else { + name = name.split( " " ); + } + } + } else { + + // If "name" is an array of keys... + // When data is initially created, via ("key", "val") signature, + // keys will be converted to camelCase. + // Since there is no way to tell _how_ a key was added, remove + // both plain key and camelCase key. #12786 + // This will only penalize the array argument path. + name = name.concat( jQuery.map( name, jQuery.camelCase ) ); + } + + i = name.length; + while ( i-- ) { + delete thisCache[ name[ i ] ]; + } + + // If there is no data left in the cache, we want to continue + // and let the cache object itself get destroyed + if ( pvt ? !isEmptyDataObject( thisCache ) : !jQuery.isEmptyObject( thisCache ) ) { + return; + } + } + } + + // See jQuery.data for more information + if ( !pvt ) { + delete cache[ id ].data; + + // Don't destroy the parent cache unless the internal data object + // had been the only thing left in it + if ( !isEmptyDataObject( cache[ id ] ) ) { + return; + } + } + + // Destroy the cache + if ( isNode ) { + jQuery.cleanData( [ elem ], true ); + + // Use delete when supported for expandos or `cache` is not a window per isWindow (#10080) + /* jshint eqeqeq: false */ + } else if ( support.deleteExpando || cache != cache.window ) { + /* jshint eqeqeq: true */ + delete cache[ id ]; + + // When all else fails, undefined + } else { + cache[ id ] = undefined; + } +} + +jQuery.extend( { + cache: {}, + + // The following elements (space-suffixed to avoid Object.prototype collisions) + // throw uncatchable exceptions if you attempt to set expando properties + noData: { + "applet ": true, + "embed ": true, + + // ...but Flash objects (which have this classid) *can* handle expandos + "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" + }, + + hasData: function( elem ) { + elem = elem.nodeType ? jQuery.cache[ elem[ jQuery.expando ] ] : elem[ jQuery.expando ]; + return !!elem && !isEmptyDataObject( elem ); + }, + + data: function( elem, name, data ) { + return internalData( elem, name, data ); + }, + + removeData: function( elem, name ) { + return internalRemoveData( elem, name ); + }, + + // For internal use only. + _data: function( elem, name, data ) { + return internalData( elem, name, data, true ); + }, + + _removeData: function( elem, name ) { + return internalRemoveData( elem, name, true ); + } +} ); + +jQuery.fn.extend( { + data: function( key, value ) { + var i, name, data, + elem = this[ 0 ], + attrs = elem && elem.attributes; + + // Special expections of .data basically thwart jQuery.access, + // so implement the relevant behavior ourselves + + // Gets all values + if ( key === undefined ) { + if ( this.length ) { + data = jQuery.data( elem ); + + if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { + i = attrs.length; + while ( i-- ) { + + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice( 5 ) ); + dataAttr( elem, name, data[ name ] ); + } + } + } + jQuery._data( elem, "parsedAttrs", true ); + } + } + + return data; + } + + // Sets multiple values + if ( typeof key === "object" ) { + return this.each( function() { + jQuery.data( this, key ); + } ); + } + + return arguments.length > 1 ? + + // Sets one value + this.each( function() { + jQuery.data( this, key, value ); + } ) : + + // Gets one value + // Try to fetch any internally stored data first + elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; + }, + + removeData: function( key ) { + return this.each( function() { + jQuery.removeData( this, key ); + } ); + } +} ); + + +jQuery.extend( { + queue: function( elem, type, data ) { + var queue; + + if ( elem ) { + type = ( type || "fx" ) + "queue"; + queue = jQuery._data( elem, type ); + + // Speed up dequeue by getting out quickly if this is just a lookup + if ( data ) { + if ( !queue || jQuery.isArray( data ) ) { + queue = jQuery._data( elem, type, jQuery.makeArray( data ) ); + } else { + queue.push( data ); + } + } + return queue || []; + } + }, + + dequeue: function( elem, type ) { + type = type || "fx"; + + var queue = jQuery.queue( elem, type ), + startLength = queue.length, + fn = queue.shift(), + hooks = jQuery._queueHooks( elem, type ), + next = function() { + jQuery.dequeue( elem, type ); + }; + + // If the fx queue is dequeued, always remove the progress sentinel + if ( fn === "inprogress" ) { + fn = queue.shift(); + startLength--; + } + + if ( fn ) { + + // Add a progress sentinel to prevent the fx queue from being + // automatically dequeued + if ( type === "fx" ) { + queue.unshift( "inprogress" ); + } + + // clear up the last queue stop function + delete hooks.stop; + fn.call( elem, next, hooks ); + } + + if ( !startLength && hooks ) { + hooks.empty.fire(); + } + }, + + // not intended for public consumption - generates a queueHooks object, + // or returns the current one + _queueHooks: function( elem, type ) { + var key = type + "queueHooks"; + return jQuery._data( elem, key ) || jQuery._data( elem, key, { + empty: jQuery.Callbacks( "once memory" ).add( function() { + jQuery._removeData( elem, type + "queue" ); + jQuery._removeData( elem, key ); + } ) + } ); + } +} ); + +jQuery.fn.extend( { + queue: function( type, data ) { + var setter = 2; + + if ( typeof type !== "string" ) { + data = type; + type = "fx"; + setter--; + } + + if ( arguments.length < setter ) { + return jQuery.queue( this[ 0 ], type ); + } + + return data === undefined ? + this : + this.each( function() { + var queue = jQuery.queue( this, type, data ); + + // ensure a hooks for this queue + jQuery._queueHooks( this, type ); + + if ( type === "fx" && queue[ 0 ] !== "inprogress" ) { + jQuery.dequeue( this, type ); + } + } ); + }, + dequeue: function( type ) { + return this.each( function() { + jQuery.dequeue( this, type ); + } ); + }, + clearQueue: function( type ) { + return this.queue( type || "fx", [] ); + }, + + // Get a promise resolved when queues of a certain type + // are emptied (fx is the type by default) + promise: function( type, obj ) { + var tmp, + count = 1, + defer = jQuery.Deferred(), + elements = this, + i = this.length, + resolve = function() { + if ( !( --count ) ) { + defer.resolveWith( elements, [ elements ] ); + } + }; + + if ( typeof type !== "string" ) { + obj = type; + type = undefined; + } + type = type || "fx"; + + while ( i-- ) { + tmp = jQuery._data( elements[ i ], type + "queueHooks" ); + if ( tmp && tmp.empty ) { + count++; + tmp.empty.add( resolve ); + } + } + resolve(); + return defer.promise( obj ); + } +} ); + + +( function() { + var shrinkWrapBlocksVal; + + support.shrinkWrapBlocks = function() { + if ( shrinkWrapBlocksVal != null ) { + return shrinkWrapBlocksVal; + } + + // Will be changed later if needed. + shrinkWrapBlocksVal = false; + + // Minified: var b,c,d + var div, body, container; + + body = document.getElementsByTagName( "body" )[ 0 ]; + if ( !body || !body.style ) { + + // Test fired too early or in an unsupported environment, exit. + return; + } + + // Setup + div = document.createElement( "div" ); + container = document.createElement( "div" ); + container.style.cssText = "position:absolute;border:0;width:0;height:0;top:0;left:-9999px"; + body.appendChild( container ).appendChild( div ); + + // Support: IE6 + // Check if elements with layout shrink-wrap their children + if ( typeof div.style.zoom !== "undefined" ) { + + // Reset CSS: box-sizing; display; margin; border + div.style.cssText = + + // Support: Firefox<29, Android 2.3 + // Vendor-prefix box-sizing + "-webkit-box-sizing:content-box;-moz-box-sizing:content-box;" + + "box-sizing:content-box;display:block;margin:0;border:0;" + + "padding:1px;width:1px;zoom:1"; + div.appendChild( document.createElement( "div" ) ).style.width = "5px"; + shrinkWrapBlocksVal = div.offsetWidth !== 3; + } + + body.removeChild( container ); + + return shrinkWrapBlocksVal; + }; + +} )(); +var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source; + +var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" ); + + +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; + +var isHidden = function( elem, el ) { + + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || + !jQuery.contains( elem.ownerDocument, elem ); + }; + + + +function adjustCSS( elem, prop, valueParts, tween ) { + var adjusted, + scale = 1, + maxIterations = 20, + currentValue = tween ? + function() { return tween.cur(); } : + function() { return jQuery.css( elem, prop, "" ); }, + initial = currentValue(), + unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ), + + // Starting value computation is required for potential unit mismatches + initialInUnit = ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) && + rcssNum.exec( jQuery.css( elem, prop ) ); + + if ( initialInUnit && initialInUnit[ 3 ] !== unit ) { + + // Trust units reported by jQuery.css + unit = unit || initialInUnit[ 3 ]; + + // Make sure we update the tween properties later on + valueParts = valueParts || []; + + // Iteratively approximate from a nonzero starting point + initialInUnit = +initial || 1; + + do { + + // If previous iteration zeroed out, double until we get *something*. + // Use string for doubling so we don't accidentally see scale as unchanged below + scale = scale || ".5"; + + // Adjust and apply + initialInUnit = initialInUnit / scale; + jQuery.style( elem, prop, initialInUnit + unit ); + + // Update scale, tolerating zero or NaN from tween.cur() + // Break the loop if scale is unchanged or perfect, or if we've just had enough. + } while ( + scale !== ( scale = currentValue() / initial ) && scale !== 1 && --maxIterations + ); + } + + if ( valueParts ) { + initialInUnit = +initialInUnit || +initial || 0; + + // Apply relative offset (+=/-=) if specified + adjusted = valueParts[ 1 ] ? + initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] : + +valueParts[ 2 ]; + if ( tween ) { + tween.unit = unit; + tween.start = initialInUnit; + tween.end = adjusted; + } + } + return adjusted; +} + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + length = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + access( elems, fn, i, key[ i ], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } else { + bulk = fn; + fn = function( elem, key, value ) { + return bulk.call( jQuery( elem ), value ); + }; + } + } + + if ( fn ) { + for ( ; i < length; i++ ) { + fn( + elems[ i ], + key, + raw ? value : value.call( elems[ i ], i, fn( elems[ i ], key ) ) + ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + length ? fn( elems[ 0 ], key ) : emptyGet; +}; +var rcheckableType = ( /^(?:checkbox|radio)$/i ); + +var rtagName = ( /<([\w:-]+)/ ); + +var rscriptType = ( /^$|\/(?:java|ecma)script/i ); + +var rleadingWhitespace = ( /^\s+/ ); + +var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|" + + "details|dialog|figcaption|figure|footer|header|hgroup|main|" + + "mark|meter|nav|output|picture|progress|section|summary|template|time|video"; + + + +function createSafeFragment( document ) { + var list = nodeNames.split( "|" ), + safeFrag = document.createDocumentFragment(); + + if ( safeFrag.createElement ) { + while ( list.length ) { + safeFrag.createElement( + list.pop() + ); + } + } + return safeFrag; +} + + +( function() { + var div = document.createElement( "div" ), + fragment = document.createDocumentFragment(), + input = document.createElement( "input" ); + + // Setup + div.innerHTML = "
a"; + + // IE strips leading whitespace when .innerHTML is used + support.leadingWhitespace = div.firstChild.nodeType === 3; + + // Make sure that tbody elements aren't automatically inserted + // IE will insert them into empty tables + support.tbody = !div.getElementsByTagName( "tbody" ).length; + + // Make sure that link elements get serialized correctly by innerHTML + // This requires a wrapper element in IE + support.htmlSerialize = !!div.getElementsByTagName( "link" ).length; + + // Makes sure cloning an html5 element does not cause problems + // Where outerHTML is undefined, this still works + support.html5Clone = + document.createElement( "nav" ).cloneNode( true ).outerHTML !== "<:nav>"; + + // Check if a disconnected checkbox will retain its checked + // value of true after appended to the DOM (IE6/7) + input.type = "checkbox"; + input.checked = true; + fragment.appendChild( input ); + support.appendChecked = input.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE6-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; + + // #11217 - WebKit loses check when the name is after the checked attribute + fragment.appendChild( div ); + + // Support: Windows Web Apps (WWA) + // `name` and `type` must use .setAttribute for WWA (#14901) + input = document.createElement( "input" ); + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Support: IE<9 + // Cloned elements keep attachEvent handlers, we use addEventListener on IE9+ + support.noCloneEvent = !!div.addEventListener; + + // Support: IE<9 + // Since attributes and properties are the same in IE, + // cleanData must set properties to undefined rather than use removeAttribute + div[ jQuery.expando ] = 1; + support.attributes = !div.getAttribute( jQuery.expando ); +} )(); + + +// We have to close these tags to support XHTML (#13200) +var wrapMap = { + option: [ 1, "" ], + legend: [ 1, "
", "
" ], + area: [ 1, "", "" ], + + // Support: IE8 + param: [ 1, "", "" ], + thead: [ 1, "", "
" ], + tr: [ 2, "", "
" ], + col: [ 2, "", "
" ], + td: [ 3, "", "
" ], + + // IE6-8 can't serialize link, script, style, or any html5 (NoScope) tags, + // unless wrapped in a div with non-breaking characters in front of it. + _default: support.htmlSerialize ? [ 0, "", "" ] : [ 1, "X
", "
" ] +}; + +// Support: IE8-IE9 +wrapMap.optgroup = wrapMap.option; + +wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; +wrapMap.th = wrapMap.td; + + +function getAll( context, tag ) { + var elems, elem, + i = 0, + found = typeof context.getElementsByTagName !== "undefined" ? + context.getElementsByTagName( tag || "*" ) : + typeof context.querySelectorAll !== "undefined" ? + context.querySelectorAll( tag || "*" ) : + undefined; + + if ( !found ) { + for ( found = [], elems = context.childNodes || context; + ( elem = elems[ i ] ) != null; + i++ + ) { + if ( !tag || jQuery.nodeName( elem, tag ) ) { + found.push( elem ); + } else { + jQuery.merge( found, getAll( elem, tag ) ); + } + } + } + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], found ) : + found; +} + + +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var elem, + i = 0; + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + jQuery._data( + elem, + "globalEval", + !refElements || jQuery._data( refElements[ i ], "globalEval" ) + ); + } +} + + +var rhtml = /<|&#?\w+;/, + rtbody = / from table fragments + if ( !support.tbody ) { + + // String was a , *may* have spurious + elem = tag === "table" && !rtbody.test( elem ) ? + tmp.firstChild : + + // String was a bare or + wrap[ 1 ] === "
" && !rtbody.test( elem ) ? + tmp : + 0; + + j = elem && elem.childNodes.length; + while ( j-- ) { + if ( jQuery.nodeName( ( tbody = elem.childNodes[ j ] ), "tbody" ) && + !tbody.childNodes.length ) { + + elem.removeChild( tbody ); + } + } + } + + jQuery.merge( nodes, tmp.childNodes ); + + // Fix #12392 for WebKit and IE > 9 + tmp.textContent = ""; + + // Fix #12392 for oldIE + while ( tmp.firstChild ) { + tmp.removeChild( tmp.firstChild ); + } + + // Remember the top-level container for proper cleanup + tmp = safe.lastChild; + } + } + } + + // Fix #11356: Clear elements from fragment + if ( tmp ) { + safe.removeChild( tmp ); + } + + // Reset defaultChecked for any radios and checkboxes + // about to be appended to the DOM in IE 6/7 (#8060) + if ( !support.appendChecked ) { + jQuery.grep( getAll( nodes, "input" ), fixDefaultChecked ); + } + + i = 0; + while ( ( elem = nodes[ i++ ] ) ) { + + // Skip elements already in the context collection (trac-4087) + if ( selection && jQuery.inArray( elem, selection ) > -1 ) { + if ( ignored ) { + ignored.push( elem ); + } + + continue; + } + + contains = jQuery.contains( elem.ownerDocument, elem ); + + // Append to fragment + tmp = getAll( safe.appendChild( elem ), "script" ); + + // Preserve script evaluation history + if ( contains ) { + setGlobalEval( tmp ); + } + + // Capture executables + if ( scripts ) { + j = 0; + while ( ( elem = tmp[ j++ ] ) ) { + if ( rscriptType.test( elem.type || "" ) ) { + scripts.push( elem ); + } + } + } + } + + tmp = null; + + return safe; +} + + +( function() { + var i, eventName, + div = document.createElement( "div" ); + + // Support: IE<9 (lack submit/change bubble), Firefox (lack focus(in | out) events) + for ( i in { submit: true, change: true, focusin: true } ) { + eventName = "on" + i; + + if ( !( support[ i ] = eventName in window ) ) { + + // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) + div.setAttribute( eventName, "t" ); + support[ i ] = div.attributes[ eventName ].expando === false; + } + } + + // Null elements to avoid leaks in IE. + div = null; +} )(); + + +var rformElems = /^(?:input|select|textarea)$/i, + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu|drag|drop)|click/, + rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, + rtypenamespace = /^([^.]*)(?:\.(.+)|)/; + +function returnTrue() { + return true; +} + +function returnFalse() { + return false; +} + +// Support: IE9 +// See #13393 for more info +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + +function on( elem, types, selector, data, fn, one ) { + var origFn, type; + + // Types can be a map of types/handlers + if ( typeof types === "object" ) { + + // ( types-Object, selector, data ) + if ( typeof selector !== "string" ) { + + // ( types-Object, data ) + data = data || selector; + selector = undefined; + } + for ( type in types ) { + on( elem, type, selector, data, types[ type ], one ); + } + return elem; + } + + if ( data == null && fn == null ) { + + // ( types, fn ) + fn = selector; + data = selector = undefined; + } else if ( fn == null ) { + if ( typeof selector === "string" ) { + + // ( types, selector, fn ) + fn = data; + data = undefined; + } else { + + // ( types, data, fn ) + fn = data; + data = selector; + selector = undefined; + } + } + if ( fn === false ) { + fn = returnFalse; + } else if ( !fn ) { + return elem; + } + + if ( one === 1 ) { + origFn = fn; + fn = function( event ) { + + // Can use an empty set, since event contains the info + jQuery().off( event ); + return origFn.apply( this, arguments ); + }; + + // Use same guid so caller can remove using origFn + fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ ); + } + return elem.each( function() { + jQuery.event.add( this, types, fn, data, selector ); + } ); +} + +/* + * Helper functions for managing events -- not part of the public interface. + * Props to Dean Edwards' addEvent library for many of the ideas. + */ +jQuery.event = { + + global: {}, + + add: function( elem, types, handler, data, selector ) { + var tmp, events, t, handleObjIn, + special, eventHandle, handleObj, + handlers, type, namespaces, origType, + elemData = jQuery._data( elem ); + + // Don't attach events to noData or text/comment nodes (but allow plain objects) + if ( !elemData ) { + return; + } + + // Caller can pass in an object of custom data in lieu of the handler + if ( handler.handler ) { + handleObjIn = handler; + handler = handleObjIn.handler; + selector = handleObjIn.selector; + } + + // Make sure that the handler has a unique ID, used to find/remove it later + if ( !handler.guid ) { + handler.guid = jQuery.guid++; + } + + // Init the element's event structure and main handler, if this is the first + if ( !( events = elemData.events ) ) { + events = elemData.events = {}; + } + if ( !( eventHandle = elemData.handle ) ) { + eventHandle = elemData.handle = function( e ) { + + // Discard the second event of a jQuery.event.trigger() and + // when an event is called after a page has unloaded + return typeof jQuery !== "undefined" && + ( !e || jQuery.event.triggered !== e.type ) ? + jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : + undefined; + }; + + // Add elem as a property of the handle fn to prevent a memory leak + // with IE non-native events + eventHandle.elem = elem; + } + + // Handle multiple events separated by a space + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // There *must* be a type, no attaching namespace-only handlers + if ( !type ) { + continue; + } + + // If event changes its type, use the special event handlers for the changed type + special = jQuery.event.special[ type ] || {}; + + // If selector defined, determine special event api type, otherwise given type + type = ( selector ? special.delegateType : special.bindType ) || type; + + // Update special based on newly reset type + special = jQuery.event.special[ type ] || {}; + + // handleObj is passed to all event handlers + handleObj = jQuery.extend( { + type: type, + origType: origType, + data: data, + handler: handler, + guid: handler.guid, + selector: selector, + needsContext: selector && jQuery.expr.match.needsContext.test( selector ), + namespace: namespaces.join( "." ) + }, handleObjIn ); + + // Init the event handler queue if we're the first + if ( !( handlers = events[ type ] ) ) { + handlers = events[ type ] = []; + handlers.delegateCount = 0; + + // Only use addEventListener/attachEvent if the special events handler returns false + if ( !special.setup || + special.setup.call( elem, data, namespaces, eventHandle ) === false ) { + + // Bind the global event handler to the element + if ( elem.addEventListener ) { + elem.addEventListener( type, eventHandle, false ); + + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, eventHandle ); + } + } + } + + if ( special.add ) { + special.add.call( elem, handleObj ); + + if ( !handleObj.handler.guid ) { + handleObj.handler.guid = handler.guid; + } + } + + // Add to the element's handler list, delegates in front + if ( selector ) { + handlers.splice( handlers.delegateCount++, 0, handleObj ); + } else { + handlers.push( handleObj ); + } + + // Keep track of which events have ever been used, for event optimization + jQuery.event.global[ type ] = true; + } + + // Nullify elem to prevent memory leaks in IE + elem = null; + }, + + // Detach an event or set of events from an element + remove: function( elem, types, handler, selector, mappedTypes ) { + var j, handleObj, tmp, + origCount, t, events, + special, handlers, type, + namespaces, origType, + elemData = jQuery.hasData( elem ) && jQuery._data( elem ); + + if ( !elemData || !( events = elemData.events ) ) { + return; + } + + // Once for each type.namespace in types; type may be omitted + types = ( types || "" ).match( rnotwhite ) || [ "" ]; + t = types.length; + while ( t-- ) { + tmp = rtypenamespace.exec( types[ t ] ) || []; + type = origType = tmp[ 1 ]; + namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort(); + + // Unbind all events (on this namespace, if provided) for the element + if ( !type ) { + for ( type in events ) { + jQuery.event.remove( elem, type + types[ t ], handler, selector, true ); + } + continue; + } + + special = jQuery.event.special[ type ] || {}; + type = ( selector ? special.delegateType : special.bindType ) || type; + handlers = events[ type ] || []; + tmp = tmp[ 2 ] && + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ); + + // Remove matching events + origCount = j = handlers.length; + while ( j-- ) { + handleObj = handlers[ j ]; + + if ( ( mappedTypes || origType === handleObj.origType ) && + ( !handler || handler.guid === handleObj.guid ) && + ( !tmp || tmp.test( handleObj.namespace ) ) && + ( !selector || selector === handleObj.selector || + selector === "**" && handleObj.selector ) ) { + handlers.splice( j, 1 ); + + if ( handleObj.selector ) { + handlers.delegateCount--; + } + if ( special.remove ) { + special.remove.call( elem, handleObj ); + } + } + } + + // Remove generic event handler if we removed something and no more handlers exist + // (avoids potential for endless recursion during removal of special event handlers) + if ( origCount && !handlers.length ) { + if ( !special.teardown || + special.teardown.call( elem, namespaces, elemData.handle ) === false ) { + + jQuery.removeEvent( elem, type, elemData.handle ); + } + + delete events[ type ]; + } + } + + // Remove the expando if it's no longer used + if ( jQuery.isEmptyObject( events ) ) { + delete elemData.handle; + + // removeData also checks for emptiness and clears the expando if empty + // so use it instead of delete + jQuery._removeData( elem, "events" ); + } + }, + + trigger: function( event, data, elem, onlyHandlers ) { + var handle, ontype, cur, + bubbleType, special, tmp, i, + eventPath = [ elem || document ], + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split( "." ) : []; + + cur = tmp = elem = elem || document; + + // Don't do events on text and comment nodes + if ( elem.nodeType === 3 || elem.nodeType === 8 ) { + return; + } + + // focus/blur morphs to focusin/out; ensure we're not firing them right now + if ( rfocusMorph.test( type + jQuery.event.triggered ) ) { + return; + } + + if ( type.indexOf( "." ) > -1 ) { + + // Namespaced trigger; create a regexp to match event type in handle() + namespaces = type.split( "." ); + type = namespaces.shift(); + namespaces.sort(); + } + ontype = type.indexOf( ":" ) < 0 && "on" + type; + + // Caller can pass in a jQuery.Event object, Object, or just an event type string + event = event[ jQuery.expando ] ? + event : + new jQuery.Event( type, typeof event === "object" && event ); + + // Trigger bitmask: & 1 for native handlers; & 2 for jQuery (always true) + event.isTrigger = onlyHandlers ? 2 : 3; + event.namespace = namespaces.join( "." ); + event.rnamespace = event.namespace ? + new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" ) : + null; + + // Clean up the event in case it is being reused + event.result = undefined; + if ( !event.target ) { + event.target = elem; + } + + // Clone any incoming data and prepend the event, creating the handler arg list + data = data == null ? + [ event ] : + jQuery.makeArray( data, [ event ] ); + + // Allow special events to draw outside the lines + special = jQuery.event.special[ type ] || {}; + if ( !onlyHandlers && special.trigger && special.trigger.apply( elem, data ) === false ) { + return; + } + + // Determine event propagation path in advance, per W3C events spec (#9951) + // Bubble up to document, then to window; watch for a global ownerDocument var (#9724) + if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) { + + bubbleType = special.delegateType || type; + if ( !rfocusMorph.test( bubbleType + type ) ) { + cur = cur.parentNode; + } + for ( ; cur; cur = cur.parentNode ) { + eventPath.push( cur ); + tmp = cur; + } + + // Only add window if we got to document (e.g., not plain obj or detached DOM) + if ( tmp === ( elem.ownerDocument || document ) ) { + eventPath.push( tmp.defaultView || tmp.parentWindow || window ); + } + } + + // Fire handlers on the event path + i = 0; + while ( ( cur = eventPath[ i++ ] ) && !event.isPropagationStopped() ) { + + event.type = i > 1 ? + bubbleType : + special.bindType || type; + + // jQuery handler + handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && + jQuery._data( cur, "handle" ); + + if ( handle ) { + handle.apply( cur, data ); + } + + // Native handler + handle = ontype && cur[ ontype ]; + if ( handle && handle.apply && acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } + } + } + event.type = type; + + // If nobody prevented the default action, do it now + if ( !onlyHandlers && !event.isDefaultPrevented() ) { + + if ( + ( !special._default || + special._default.apply( eventPath.pop(), data ) === false + ) && acceptData( elem ) + ) { + + // Call a native DOM method on the target with the same name name as the event. + // Can't use an .isFunction() check here because IE6/7 fails that test. + // Don't do default actions on window, that's where global variables be (#6170) + if ( ontype && elem[ type ] && !jQuery.isWindow( elem ) ) { + + // Don't re-trigger an onFOO event when we call its FOO() method + tmp = elem[ ontype ]; + + if ( tmp ) { + elem[ ontype ] = null; + } + + // Prevent re-triggering of the same event, since we already bubbled it above + jQuery.event.triggered = type; + try { + elem[ type ](); + } catch ( e ) { + + // IE<9 dies on focus/blur to hidden element (#1486,#12518) + // only reproducible on winXP IE8 native, not IE9 in IE8 mode + } + jQuery.event.triggered = undefined; + + if ( tmp ) { + elem[ ontype ] = tmp; + } + } + } + } + + return event.result; + }, + + dispatch: function( event ) { + + // Make a writable jQuery.Event from the native event object + event = jQuery.event.fix( event ); + + var i, j, ret, matched, handleObj, + handlerQueue = [], + args = slice.call( arguments ), + handlers = ( jQuery._data( this, "events" ) || {} )[ event.type ] || [], + special = jQuery.event.special[ event.type ] || {}; + + // Use the fix-ed jQuery.Event rather than the (read-only) native event + args[ 0 ] = event; + event.delegateTarget = this; + + // Call the preDispatch hook for the mapped type, and let it bail if desired + if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) { + return; + } + + // Determine handlers + handlerQueue = jQuery.event.handlers.call( this, event, handlers ); + + // Run delegates first; they may want to stop propagation beneath us + i = 0; + while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) { + event.currentTarget = matched.elem; + + j = 0; + while ( ( handleObj = matched.handlers[ j++ ] ) && + !event.isImmediatePropagationStopped() ) { + + // Triggered event must either 1) have no namespace, or 2) have namespace(s) + // a subset or equal to those in the bound event (both can have no namespace). + if ( !event.rnamespace || event.rnamespace.test( handleObj.namespace ) ) { + + event.handleObj = handleObj; + event.data = handleObj.data; + + ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle || + handleObj.handler ).apply( matched.elem, args ); + + if ( ret !== undefined ) { + if ( ( event.result = ret ) === false ) { + event.preventDefault(); + event.stopPropagation(); + } + } + } + } + } + + // Call the postDispatch hook for the mapped type + if ( special.postDispatch ) { + special.postDispatch.call( this, event ); + } + + return event.result; + }, + + handlers: function( event, handlers ) { + var i, matches, sel, handleObj, + handlerQueue = [], + delegateCount = handlers.delegateCount, + cur = event.target; + + // Support (at least): Chrome, IE9 + // Find delegate handlers + // Black-hole SVG instance trees (#13180) + // + // Support: Firefox<=42+ + // Avoid non-left-click in FF but don't block IE radio events (#3861, gh-2343) + if ( delegateCount && cur.nodeType && + ( event.type !== "click" || isNaN( event.button ) || event.button < 1 ) ) { + + /* jshint eqeqeq: false */ + for ( ; cur != this; cur = cur.parentNode || this ) { + /* jshint eqeqeq: true */ + + // Don't check non-elements (#13208) + // Don't process clicks on disabled elements (#6911, #8165, #11382, #11764) + if ( cur.nodeType === 1 && ( cur.disabled !== true || event.type !== "click" ) ) { + matches = []; + for ( i = 0; i < delegateCount; i++ ) { + handleObj = handlers[ i ]; + + // Don't conflict with Object.prototype properties (#13203) + sel = handleObj.selector + " "; + + if ( matches[ sel ] === undefined ) { + matches[ sel ] = handleObj.needsContext ? + jQuery( sel, this ).index( cur ) > -1 : + jQuery.find( sel, this, null, [ cur ] ).length; + } + if ( matches[ sel ] ) { + matches.push( handleObj ); + } + } + if ( matches.length ) { + handlerQueue.push( { elem: cur, handlers: matches } ); + } + } + } + } + + // Add the remaining (directly-bound) handlers + if ( delegateCount < handlers.length ) { + handlerQueue.push( { elem: this, handlers: handlers.slice( delegateCount ) } ); + } + + return handlerQueue; + }, + + fix: function( event ) { + if ( event[ jQuery.expando ] ) { + return event; + } + + // Create a writable copy of the event object and normalize some properties + var i, prop, copy, + type = event.type, + originalEvent = event, + fixHook = this.fixHooks[ type ]; + + if ( !fixHook ) { + this.fixHooks[ type ] = fixHook = + rmouseEvent.test( type ) ? this.mouseHooks : + rkeyEvent.test( type ) ? this.keyHooks : + {}; + } + copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props; + + event = new jQuery.Event( originalEvent ); + + i = copy.length; + while ( i-- ) { + prop = copy[ i ]; + event[ prop ] = originalEvent[ prop ]; + } + + // Support: IE<9 + // Fix target property (#1925) + if ( !event.target ) { + event.target = originalEvent.srcElement || document; + } + + // Support: Safari 6-8+ + // Target should not be a text node (#504, #13143) + if ( event.target.nodeType === 3 ) { + event.target = event.target.parentNode; + } + + // Support: IE<9 + // For mouse/key events, metaKey==false if it's undefined (#3368, #11328) + event.metaKey = !!event.metaKey; + + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; + }, + + // Includes some event props shared by KeyEvent and MouseEvent + props: ( "altKey bubbles cancelable ctrlKey currentTarget detail eventPhase " + + "metaKey relatedTarget shiftKey target timeStamp view which" ).split( " " ), + + fixHooks: {}, + + keyHooks: { + props: "char charCode key keyCode".split( " " ), + filter: function( event, original ) { + + // Add which for key events + if ( event.which == null ) { + event.which = original.charCode != null ? original.charCode : original.keyCode; + } + + return event; + } + }, + + mouseHooks: { + props: ( "button buttons clientX clientY fromElement offsetX offsetY " + + "pageX pageY screenX screenY toElement" ).split( " " ), + filter: function( event, original ) { + var body, eventDoc, doc, + button = original.button, + fromElement = original.fromElement; + + // Calculate pageX/Y if missing and clientX/Y available + if ( event.pageX == null && original.clientX != null ) { + eventDoc = event.target.ownerDocument || document; + doc = eventDoc.documentElement; + body = eventDoc.body; + + event.pageX = original.clientX + + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - + ( doc && doc.clientLeft || body && body.clientLeft || 0 ); + event.pageY = original.clientY + + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - + ( doc && doc.clientTop || body && body.clientTop || 0 ); + } + + // Add relatedTarget, if necessary + if ( !event.relatedTarget && fromElement ) { + event.relatedTarget = fromElement === event.target ? + original.toElement : + fromElement; + } + + // Add which for click: 1 === left; 2 === middle; 3 === right + // Note: button is not normalized, so don't use it + if ( !event.which && button !== undefined ) { + event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) ); + } + + return event; + } + }, + + special: { + load: { + + // Prevent triggered image.load events from bubbling to window.load + noBubble: true + }, + focus: { + + // Fire native event if possible so blur/focus sequence is correct + trigger: function() { + if ( this !== safeActiveElement() && this.focus ) { + try { + this.focus(); + return false; + } catch ( e ) { + + // Support: IE<9 + // If we error on focus to hidden element (#1486, #12518), + // let .trigger() run the handlers + } + } + }, + delegateType: "focusin" + }, + blur: { + trigger: function() { + if ( this === safeActiveElement() && this.blur ) { + this.blur(); + return false; + } + }, + delegateType: "focusout" + }, + click: { + + // For checkbox, fire native event so checked state will be right + trigger: function() { + if ( jQuery.nodeName( this, "input" ) && this.type === "checkbox" && this.click ) { + this.click(); + return false; + } + }, + + // For cross-browser consistency, don't fire native .click() on links + _default: function( event ) { + return jQuery.nodeName( event.target, "a" ); + } + }, + + beforeunload: { + postDispatch: function( event ) { + + // Support: Firefox 20+ + // Firefox doesn't alert if the returnValue field is not set. + if ( event.result !== undefined && event.originalEvent ) { + event.originalEvent.returnValue = event.result; + } + } + } + }, + + // Piggyback on a donor event to simulate a different one + simulate: function( type, elem, event ) { + var e = jQuery.extend( + new jQuery.Event(), + event, + { + type: type, + isSimulated: true + + // Previously, `originalEvent: {}` was set here, so stopPropagation call + // would not be triggered on donor event, since in our own + // jQuery.event.stopPropagation function we had a check for existence of + // originalEvent.stopPropagation method, so, consequently it would be a noop. + // + // Guard for simulated events was moved to jQuery.event.stopPropagation function + // since `originalEvent` should point to the original event for the + // constancy with other events and for more focused logic + } + ); + + jQuery.event.trigger( e, null, elem ); + + if ( e.isDefaultPrevented() ) { + event.preventDefault(); + } + } +}; + +jQuery.removeEvent = document.removeEventListener ? + function( elem, type, handle ) { + + // This "if" is needed for plain objects + if ( elem.removeEventListener ) { + elem.removeEventListener( type, handle ); + } + } : + function( elem, type, handle ) { + var name = "on" + type; + + if ( elem.detachEvent ) { + + // #8545, #7054, preventing memory leaks for custom events in IE6-8 + // detachEvent needed property on element, by name of that event, + // to properly expose it to GC + if ( typeof elem[ name ] === "undefined" ) { + elem[ name ] = null; + } + + elem.detachEvent( name, handle ); + } + }; + +jQuery.Event = function( src, props ) { + + // Allow instantiation without the 'new' keyword + if ( !( this instanceof jQuery.Event ) ) { + return new jQuery.Event( src, props ); + } + + // Event object + if ( src && src.type ) { + this.originalEvent = src; + this.type = src.type; + + // Events bubbling up the document may have been marked as prevented + // by a handler lower down the tree; reflect the correct value. + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + + // Support: IE < 9, Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; + + // Event type + } else { + this.type = src; + } + + // Put explicitly provided properties onto the event object + if ( props ) { + jQuery.extend( this, props ); + } + + // Create a timestamp if incoming event doesn't have one + this.timeStamp = src && src.timeStamp || jQuery.now(); + + // Mark it as fixed + this[ jQuery.expando ] = true; +}; + +// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding +// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html +jQuery.Event.prototype = { + constructor: jQuery.Event, + isDefaultPrevented: returnFalse, + isPropagationStopped: returnFalse, + isImmediatePropagationStopped: returnFalse, + + preventDefault: function() { + var e = this.originalEvent; + + this.isDefaultPrevented = returnTrue; + if ( !e ) { + return; + } + + // If preventDefault exists, run it on the original event + if ( e.preventDefault ) { + e.preventDefault(); + + // Support: IE + // Otherwise set the returnValue property of the original event to false + } else { + e.returnValue = false; + } + }, + stopPropagation: function() { + var e = this.originalEvent; + + this.isPropagationStopped = returnTrue; + + if ( !e || this.isSimulated ) { + return; + } + + // If stopPropagation exists, run it on the original event + if ( e.stopPropagation ) { + e.stopPropagation(); + } + + // Support: IE + // Set the cancelBubble property of the original event to true + e.cancelBubble = true; + }, + stopImmediatePropagation: function() { + var e = this.originalEvent; + + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + + this.stopPropagation(); + } +}; + +// Create mouseenter/leave events using mouseover/out and event-time checks +// so that event delegation works in jQuery. +// Do the same for pointerenter/pointerleave and pointerover/pointerout +// +// Support: Safari 7 only +// Safari sends mouseenter too often; see: +// https://code.google.com/p/chromium/issues/detail?id=470258 +// for the description of the bug (it existed in older Chrome versions as well). +jQuery.each( { + mouseenter: "mouseover", + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" +}, function( orig, fix ) { + jQuery.event.special[ orig ] = { + delegateType: fix, + bindType: fix, + + handle: function( event ) { + var ret, + target = this, + related = event.relatedTarget, + handleObj = event.handleObj; + + // For mouseenter/leave call the handler if related is outside the target. + // NB: No relatedTarget if the mouse left/entered the browser window + if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) { + event.type = handleObj.origType; + ret = handleObj.handler.apply( this, arguments ); + event.type = fix; + } + return ret; + } + }; +} ); + +// IE submit delegation +if ( !support.submit ) { + + jQuery.event.special.submit = { + setup: function() { + + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Lazy-add a submit handler when a descendant form may potentially be submitted + jQuery.event.add( this, "click._submit keypress._submit", function( e ) { + + // Node name check avoids a VML-related crash in IE (#9807) + var elem = e.target, + form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? + + // Support: IE <=8 + // We use jQuery.prop instead of elem.form + // to allow fixing the IE8 delegated submit issue (gh-2332) + // by 3rd party polyfills/workarounds. + jQuery.prop( elem, "form" ) : + undefined; + + if ( form && !jQuery._data( form, "submit" ) ) { + jQuery.event.add( form, "submit._submit", function( event ) { + event._submitBubble = true; + } ); + jQuery._data( form, "submit", true ); + } + } ); + + // return undefined since we don't need an event listener + }, + + postDispatch: function( event ) { + + // If form was submitted by the user, bubble the event up the tree + if ( event._submitBubble ) { + delete event._submitBubble; + if ( this.parentNode && !event.isTrigger ) { + jQuery.event.simulate( "submit", this.parentNode, event ); + } + } + }, + + teardown: function() { + + // Only need this for delegated form submit events + if ( jQuery.nodeName( this, "form" ) ) { + return false; + } + + // Remove delegated handlers; cleanData eventually reaps submit handlers attached above + jQuery.event.remove( this, "._submit" ); + } + }; +} + +// IE change delegation and checkbox/radio fix +if ( !support.change ) { + + jQuery.event.special.change = { + + setup: function() { + + if ( rformElems.test( this.nodeName ) ) { + + // IE doesn't fire change on a check/radio until blur; trigger it on click + // after a propertychange. Eat the blur-change in special.change.handle. + // This still fires onchange a second time for check/radio after blur. + if ( this.type === "checkbox" || this.type === "radio" ) { + jQuery.event.add( this, "propertychange._change", function( event ) { + if ( event.originalEvent.propertyName === "checked" ) { + this._justChanged = true; + } + } ); + jQuery.event.add( this, "click._change", function( event ) { + if ( this._justChanged && !event.isTrigger ) { + this._justChanged = false; + } + + // Allow triggered, simulated change events (#11500) + jQuery.event.simulate( "change", this, event ); + } ); + } + return false; + } + + // Delegated event; lazy-add a change handler on descendant inputs + jQuery.event.add( this, "beforeactivate._change", function( e ) { + var elem = e.target; + + if ( rformElems.test( elem.nodeName ) && !jQuery._data( elem, "change" ) ) { + jQuery.event.add( elem, "change._change", function( event ) { + if ( this.parentNode && !event.isSimulated && !event.isTrigger ) { + jQuery.event.simulate( "change", this.parentNode, event ); + } + } ); + jQuery._data( elem, "change", true ); + } + } ); + }, + + handle: function( event ) { + var elem = event.target; + + // Swallow native change events from checkbox/radio, we already triggered them above + if ( this !== elem || event.isSimulated || event.isTrigger || + ( elem.type !== "radio" && elem.type !== "checkbox" ) ) { + + return event.handleObj.handler.apply( this, arguments ); + } + }, + + teardown: function() { + jQuery.event.remove( this, "._change" ); + + return !rformElems.test( this.nodeName ); + } + }; +} + +// Support: Firefox +// Firefox doesn't have focus(in | out) events +// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 +// +// Support: Chrome, Safari +// focus(in | out) events fire after focus & blur events, +// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order +// Related ticket - https://code.google.com/p/chromium/issues/detail?id=449857 +if ( !support.focusin ) { + jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { + + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { + jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); + }; + + jQuery.event.special[ fix ] = { + setup: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); + } + jQuery._data( doc, fix, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this, + attaches = jQuery._data( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + jQuery._removeData( doc, fix ); + } else { + jQuery._data( doc, fix, attaches ); + } + } + }; + } ); +} + +jQuery.fn.extend( { + + on: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn ); + }, + one: function( types, selector, data, fn ) { + return on( this, types, selector, data, fn, 1 ); + }, + off: function( types, selector, fn ) { + var handleObj, type; + if ( types && types.preventDefault && types.handleObj ) { + + // ( event ) dispatched jQuery.Event + handleObj = types.handleObj; + jQuery( types.delegateTarget ).off( + handleObj.namespace ? + handleObj.origType + "." + handleObj.namespace : + handleObj.origType, + handleObj.selector, + handleObj.handler + ); + return this; + } + if ( typeof types === "object" ) { + + // ( types-object [, selector] ) + for ( type in types ) { + this.off( type, selector, types[ type ] ); + } + return this; + } + if ( selector === false || typeof selector === "function" ) { + + // ( types [, fn] ) + fn = selector; + selector = undefined; + } + if ( fn === false ) { + fn = returnFalse; + } + return this.each( function() { + jQuery.event.remove( this, types, fn, selector ); + } ); + }, + + trigger: function( type, data ) { + return this.each( function() { + jQuery.event.trigger( type, data, this ); + } ); + }, + triggerHandler: function( type, data ) { + var elem = this[ 0 ]; + if ( elem ) { + return jQuery.event.trigger( type, data, elem, true ); + } + } +} ); + + +var rinlinejQuery = / jQuery\d+="(?:null|\d+)"/g, + rnoshimcache = new RegExp( "<(?:" + nodeNames + ")[\\s/>]", "i" ), + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:-]+)[^>]*)\/>/gi, + + // Support: IE 10-11, Edge 10240+ + // In IE/Edge using regex groups here causes severe slowdowns. + // See https://connect.microsoft.com/IE/feedback/details/1736512/ + rnoInnerhtml = /\s*$/g, + safeFragment = createSafeFragment( document ), + fragmentDiv = safeFragment.appendChild( document.createElement( "div" ) ); + +// Support: IE<8 +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? + + elem.getElementsByTagName( "tbody" )[ 0 ] || + elem.appendChild( elem.ownerDocument.createElement( "tbody" ) ) : + elem; +} + +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = ( jQuery.find.attr( elem, "type" ) !== null ) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute( "type" ); + } + return elem; +} + +function cloneCopyEvent( src, dest ) { + if ( dest.nodeType !== 1 || !jQuery.hasData( src ) ) { + return; + } + + var type, i, l, + oldData = jQuery._data( src ), + curData = jQuery._data( dest, oldData ), + events = oldData.events; + + if ( events ) { + delete curData.handle; + curData.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } + } + + // make the cloned public data object a copy from the original + if ( curData.data ) { + curData.data = jQuery.extend( {}, curData.data ); + } +} + +function fixCloneNodeIssues( src, dest ) { + var nodeName, e, data; + + // We do not need to do anything for non-Elements + if ( dest.nodeType !== 1 ) { + return; + } + + nodeName = dest.nodeName.toLowerCase(); + + // IE6-8 copies events bound via attachEvent when using cloneNode. + if ( !support.noCloneEvent && dest[ jQuery.expando ] ) { + data = jQuery._data( dest ); + + for ( e in data.events ) { + jQuery.removeEvent( dest, e, data.handle ); + } + + // Event data gets referenced instead of copied if the expando gets copied too + dest.removeAttribute( jQuery.expando ); + } + + // IE blanks contents when cloning scripts, and tries to evaluate newly-set text + if ( nodeName === "script" && dest.text !== src.text ) { + disableScript( dest ).text = src.text; + restoreScript( dest ); + + // IE6-10 improperly clones children of object elements using classid. + // IE10 throws NoModificationAllowedError if parent is null, #12132. + } else if ( nodeName === "object" ) { + if ( dest.parentNode ) { + dest.outerHTML = src.outerHTML; + } + + // This path appears unavoidable for IE9. When cloning an object + // element in IE9, the outerHTML strategy above is not sufficient. + // If the src has innerHTML and the destination does not, + // copy the src.innerHTML into the dest.innerHTML. #10324 + if ( support.html5Clone && ( src.innerHTML && !jQuery.trim( dest.innerHTML ) ) ) { + dest.innerHTML = src.innerHTML; + } + + } else if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + + // IE6-8 fails to persist the checked state of a cloned checkbox + // or radio button. Worse, IE6-7 fail to give the cloned element + // a checked appearance if the defaultChecked value isn't also set + + dest.defaultChecked = dest.checked = src.checked; + + // IE6-7 get confused and end up setting the value of a cloned + // checkbox/radio button to an empty string instead of "on" + if ( dest.value !== src.value ) { + dest.value = src.value; + } + + // IE6-8 fails to return the selected option to the default selected + // state when cloning options + } else if ( nodeName === "option" ) { + dest.defaultSelected = dest.selected = src.defaultSelected; + + // IE6-8 fails to set the defaultValue to the correct value when + // cloning other types of input fields + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} + +function domManip( collection, args, callback, ignored ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var first, node, hasScripts, + scripts, doc, fragment, + i = 0, + l = collection.length, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return collection.each( function( index ) { + var self = collection.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + domManip( self, args, callback, ignored ); + } ); + } + + if ( l ) { + fragment = buildFragment( args, collection[ 0 ].ownerDocument, false, collection, ignored ); + first = fragment.firstChild; + + if ( fragment.childNodes.length === 1 ) { + fragment = first; + } + + // Require either new content or an interest in ignored elements to invoke the callback + if ( first || ignored ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; + + // Use the original fragment for the last item + // instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; + + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + + // Support: Android<4.1, PhantomJS<2 + // push.apply(_, arraylike) throws on ancient WebKit + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( collection[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !jQuery._data( node, "globalEval" ) && + jQuery.contains( doc, node ) ) { + + if ( node.src ) { + + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( + ( node.text || node.textContent || node.innerHTML || "" ) + .replace( rcleanScript, "" ) + ); + } + } + } + } + + // Fix #11809: Avoid leaking memory + fragment = first = null; + } + } + + return collection; +} + +function remove( elem, selector, keepData ) { + var node, + elems = selector ? jQuery.filter( selector, elem ) : elem, + i = 0; + + for ( ; ( node = elems[ i ] ) != null; i++ ) { + + if ( !keepData && node.nodeType === 1 ) { + jQuery.cleanData( getAll( node ) ); + } + + if ( node.parentNode ) { + if ( keepData && jQuery.contains( node.ownerDocument, node ) ) { + setGlobalEval( getAll( node, "script" ) ); + } + node.parentNode.removeChild( node ); + } + } + + return elem; +} + +jQuery.extend( { + htmlPrefilter: function( html ) { + return html.replace( rxhtmlTag, "<$1>" ); + }, + + clone: function( elem, dataAndEvents, deepDataAndEvents ) { + var destElements, node, clone, i, srcElements, + inPage = jQuery.contains( elem.ownerDocument, elem ); + + if ( support.html5Clone || jQuery.isXMLDoc( elem ) || + !rnoshimcache.test( "<" + elem.nodeName + ">" ) ) { + + clone = elem.cloneNode( true ); + + // IE<=8 does not properly clone detached, unknown element nodes + } else { + fragmentDiv.innerHTML = elem.outerHTML; + fragmentDiv.removeChild( clone = fragmentDiv.firstChild ); + } + + if ( ( !support.noCloneEvent || !support.noCloneChecked ) && + ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { + + // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 + destElements = getAll( clone ); + srcElements = getAll( elem ); + + // Fix all IE cloning issues + for ( i = 0; ( node = srcElements[ i ] ) != null; ++i ) { + + // Ensure that the destination node is not null; Fixes #9587 + if ( destElements[ i ] ) { + fixCloneNodeIssues( node, destElements[ i ] ); + } + } + } + + // Copy the events from the original to the clone + if ( dataAndEvents ) { + if ( deepDataAndEvents ) { + srcElements = srcElements || getAll( elem ); + destElements = destElements || getAll( clone ); + + for ( i = 0; ( node = srcElements[ i ] ) != null; i++ ) { + cloneCopyEvent( node, destElements[ i ] ); + } + } else { + cloneCopyEvent( elem, clone ); + } + } + + // Preserve script evaluation history + destElements = getAll( clone, "script" ); + if ( destElements.length > 0 ) { + setGlobalEval( destElements, !inPage && getAll( elem, "script" ) ); + } + + destElements = srcElements = node = null; + + // Return the cloned set + return clone; + }, + + cleanData: function( elems, /* internal */ forceAcceptData ) { + var elem, type, id, data, + i = 0, + internalKey = jQuery.expando, + cache = jQuery.cache, + attributes = support.attributes, + special = jQuery.event.special; + + for ( ; ( elem = elems[ i ] ) != null; i++ ) { + if ( forceAcceptData || acceptData( elem ) ) { + + id = elem[ internalKey ]; + data = id && cache[ id ]; + + if ( data ) { + if ( data.events ) { + for ( type in data.events ) { + if ( special[ type ] ) { + jQuery.event.remove( elem, type ); + + // This is a shortcut to avoid jQuery.event.remove's overhead + } else { + jQuery.removeEvent( elem, type, data.handle ); + } + } + } + + // Remove cache only if it was not already removed by jQuery.event.remove + if ( cache[ id ] ) { + + delete cache[ id ]; + + // Support: IE<9 + // IE does not allow us to delete expando properties from nodes + // IE creates expando attributes along with the property + // IE does not have a removeAttribute function on Document nodes + if ( !attributes && typeof elem.removeAttribute !== "undefined" ) { + elem.removeAttribute( internalKey ); + + // Webkit & Blink performance suffers when deleting properties + // from DOM nodes, so set to undefined instead + // https://code.google.com/p/chromium/issues/detail?id=378607 + } else { + elem[ internalKey ] = undefined; + } + + deletedIds.push( id ); + } + } + } + } + } +} ); + +jQuery.fn.extend( { + + // Keep domManip exposed until 3.0 (gh-2225) + domManip: domManip, + + detach: function( selector ) { + return remove( this, selector, true ); + }, + + remove: function( selector ) { + return remove( this, selector ); + }, + + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().append( + ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) + ); + }, null, value, arguments.length ); + }, + + append: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + } ); + }, + + prepend: function() { + return domManip( this, arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + } ); + }, + + before: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + } ); + }, + + after: function() { + return domManip( this, arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + } ); + }, + + empty: function() { + var elem, + i = 0; + + for ( ; ( elem = this[ i ] ) != null; i++ ) { + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + } + + // Remove any remaining nodes + while ( elem.firstChild ) { + elem.removeChild( elem.firstChild ); + } + + // If this is a select, ensure that it displays empty (#12336) + // Support: IE<9 + if ( elem.options && jQuery.nodeName( elem, "select" ) ) { + elem.options.length = 0; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map( function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + } ); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined ) { + return elem.nodeType === 1 ? + elem.innerHTML.replace( rinlinejQuery, "" ) : + undefined; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + ( support.htmlSerialize || !rnoshimcache.test( value ) ) && + ( support.leadingWhitespace || !rleadingWhitespace.test( value ) ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = jQuery.htmlPrefilter( value ); + + try { + for ( ; i < l; i++ ) { + + // Remove element nodes and prevent memory leaks + elem = this[ i ] || {}; + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch ( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var ignored = []; + + // Make the changes, replacing each non-ignored context element with the new content + return domManip( this, arguments, function( elem ) { + var parent = this.parentNode; + + if ( jQuery.inArray( this, ignored ) < 0 ) { + jQuery.cleanData( getAll( this ) ); + if ( parent ) { + parent.replaceChild( elem, this ); + } + } + + // Force callback invocation + }, ignored ); + } +} ); + +jQuery.each( { + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + i = 0, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Modern browsers can apply jQuery collections as arrays, but oldIE needs a .get() + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +} ); + + +var iframe, + elemdisplay = { + + // Support: Firefox + // We have to pre-define these values for FF (#10227) + HTML: "block", + BODY: "block" + }; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ + +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + display = jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; +} + +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; + + if ( !display ) { + display = actualDisplay( nodeName, doc ); + + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { + + // Use the already-created iframe if possible + iframe = ( iframe || jQuery( "') !== -1; + }; + + var createFragmentedLevel = function (fragments) { + return { + type: 'fragmented', + fragments: fragments, + content: '', + bookmark: null, + beforeBookmark: null + }; + }; + + var createCompleteLevel = function (content) { + return { + type: 'complete', + fragments: null, + content: content, + bookmark: null, + beforeBookmark: null + }; + }; + + var createFromEditor = function (editor) { + var fragments, content; + + fragments = Fragments.read(editor.getBody()); + content = Arr.map(fragments, function (html) { + return editor.serializer.trimContent(html); + }).join(''); + + return hasIframes(content) ? createFragmentedLevel(fragments) : createCompleteLevel(content); + }; + + var applyToEditor = function (editor, level, before) { + if (level.type === 'fragmented') { + Fragments.write(level.fragments, editor.getBody()); + } else { + editor.setContent(level.content, {format: 'raw'}); + } + + editor.selection.moveToBookmark(before ? level.beforeBookmark : level.bookmark); + }; + + var getLevelContent = function (level) { + return level.type === 'fragmented' ? level.fragments.join('') : level.content; + }; + + var isEq = function (level1, level2) { + return getLevelContent(level1) === getLevelContent(level2); + }; + + return { + createFragmentedLevel: createFragmentedLevel, + createCompleteLevel: createCompleteLevel, + createFromEditor: createFromEditor, + applyToEditor: applyToEditor, + isEq: isEq + }; +}); + +// Included from: js/tinymce/classes/UndoManager.js + +/** + * UndoManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class handles the undo/redo history levels for the editor. Since the built-in undo/redo has major drawbacks a custom one was needed. + * + * @class tinymce.UndoManager + */ +define("tinymce/UndoManager", [ + "tinymce/util/VK", + "tinymce/util/Tools", + "tinymce/undo/Levels", + "tinymce/Env" +], function(VK, Tools, Levels, Env) { + return function(editor) { + var self = this, index = 0, data = [], beforeBookmark, isFirstTypedCharacter, locks = 0; + + function setDirty(state) { + editor.setDirty(state); + } + + function addNonTypingUndoLevel(e) { + self.typing = false; + self.add({}, e); + } + + function endTyping() { + if (self.typing) { + self.typing = false; + self.add(); + } + } + + // Add initial undo level when the editor is initialized + editor.on('init', function() { + self.add(); + }); + + // Get position before an execCommand is processed + editor.on('BeforeExecCommand', function(e) { + var cmd = e.command; + + if (cmd !== 'Undo' && cmd !== 'Redo' && cmd !== 'mceRepaint') { + endTyping(); + self.beforeChange(); + } + }); + + // Add undo level after an execCommand call was made + editor.on('ExecCommand', function(e) { + var cmd = e.command; + + if (cmd !== 'Undo' && cmd !== 'Redo' && cmd !== 'mceRepaint') { + addNonTypingUndoLevel(e); + } + }); + + editor.on('ObjectResizeStart Cut', function() { + self.beforeChange(); + }); + + editor.on('SaveContent ObjectResized blur', addNonTypingUndoLevel); + editor.on('DragEnd', addNonTypingUndoLevel); + + editor.on('KeyUp', function(e) { + var keyCode = e.keyCode; + + // If key is prevented then don't add undo level + // This would happen on keyboard shortcuts for example + if (e.isDefaultPrevented()) { + return; + } + + if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode === 45 || e.ctrlKey) { + addNonTypingUndoLevel(); + editor.nodeChanged(); + } + + if (keyCode === 46 || keyCode === 8 || (Env.mac && (keyCode === 91 || keyCode === 93))) { + editor.nodeChanged(); + } + + // Fire a TypingUndo event on the first character entered + if (isFirstTypedCharacter && self.typing) { + // Make it dirty if the content was changed after typing the first character + if (!editor.isDirty()) { + setDirty(data[0] && !Levels.isEq(Levels.createFromEditor(editor), data[0])); + + // Fire initial change event + if (editor.isDirty()) { + editor.fire('change', {level: data[0], lastLevel: null}); + } + } + + editor.fire('TypingUndo'); + isFirstTypedCharacter = false; + editor.nodeChanged(); + } + }); + + editor.on('KeyDown', function(e) { + var keyCode = e.keyCode; + + // If key is prevented then don't add undo level + // This would happen on keyboard shortcuts for example + if (e.isDefaultPrevented()) { + return; + } + + // Is character position keys left,right,up,down,home,end,pgdown,pgup,enter + if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode === 45) { + if (self.typing) { + addNonTypingUndoLevel(e); + } + + return; + } + + // If key isn't Ctrl+Alt/AltGr + var modKey = (e.ctrlKey && !e.altKey) || e.metaKey; + if ((keyCode < 16 || keyCode > 20) && keyCode !== 224 && keyCode !== 91 && !self.typing && !modKey) { + self.beforeChange(); + self.typing = true; + self.add({}, e); + isFirstTypedCharacter = true; + } + }); + + editor.on('MouseDown', function(e) { + if (self.typing) { + addNonTypingUndoLevel(e); + } + }); + + // Add keyboard shortcuts for undo/redo keys + editor.addShortcut('meta+z', '', 'Undo'); + editor.addShortcut('meta+y,meta+shift+z', '', 'Redo'); + + editor.on('AddUndo Undo Redo ClearUndos', function(e) { + if (!e.isDefaultPrevented()) { + editor.nodeChanged(); + } + }); + + /*eslint consistent-this:0 */ + self = { + // Explode for debugging reasons + data: data, + + /** + * State if the user is currently typing or not. This will add a typing operation into one undo + * level instead of one new level for each keystroke. + * + * @field {Boolean} typing + */ + typing: false, + + /** + * Stores away a bookmark to be used when performing an undo action so that the selection is before + * the change has been made. + * + * @method beforeChange + */ + beforeChange: function() { + if (!locks) { + beforeBookmark = editor.selection.getBookmark(2, true); + } + }, + + /** + * Adds a new undo level/snapshot to the undo list. + * + * @method add + * @param {Object} level Optional undo level object to add. + * @param {DOMEvent} event Optional event responsible for the creation of the undo level. + * @return {Object} Undo level that got added or null it a level wasn't needed. + */ + add: function(level, event) { + var i, settings = editor.settings, lastLevel, currentLevel; + + currentLevel = Levels.createFromEditor(editor); + level = level || {}; + level = Tools.extend(level, currentLevel); + + if (locks || editor.removed) { + return null; + } + + lastLevel = data[index]; + if (editor.fire('BeforeAddUndo', {level: level, lastLevel: lastLevel, originalEvent: event}).isDefaultPrevented()) { + return null; + } + + // Add undo level if needed + if (lastLevel && Levels.isEq(lastLevel, level)) { + return null; + } + + // Set before bookmark on previous level + if (data[index]) { + data[index].beforeBookmark = beforeBookmark; + } + + // Time to compress + if (settings.custom_undo_redo_levels) { + if (data.length > settings.custom_undo_redo_levels) { + for (i = 0; i < data.length - 1; i++) { + data[i] = data[i + 1]; + } + + data.length--; + index = data.length; + } + } + + // Get a non intrusive normalized bookmark + level.bookmark = editor.selection.getBookmark(2, true); + + // Crop array if needed + if (index < data.length - 1) { + data.length = index + 1; + } + + data.push(level); + index = data.length - 1; + + var args = {level: level, lastLevel: lastLevel, originalEvent: event}; + + editor.fire('AddUndo', args); + + if (index > 0) { + setDirty(true); + editor.fire('change', args); + } + + return level; + }, + + /** + * Undoes the last action. + * + * @method undo + * @return {Object} Undo level or null if no undo was performed. + */ + undo: function() { + var level; + + if (self.typing) { + self.add(); + self.typing = false; + } + + if (index > 0) { + level = data[--index]; + Levels.applyToEditor(editor, level, true); + setDirty(true); + editor.fire('undo', {level: level}); + } + + return level; + }, + + /** + * Redoes the last action. + * + * @method redo + * @return {Object} Redo level or null if no redo was performed. + */ + redo: function() { + var level; + + if (index < data.length - 1) { + level = data[++index]; + Levels.applyToEditor(editor, level, false); + setDirty(true); + editor.fire('redo', {level: level}); + } + + return level; + }, + + /** + * Removes all undo levels. + * + * @method clear + */ + clear: function() { + data = []; + index = 0; + self.typing = false; + self.data = data; + editor.fire('ClearUndos'); + }, + + /** + * Returns true/false if the undo manager has any undo levels. + * + * @method hasUndo + * @return {Boolean} true/false if the undo manager has any undo levels. + */ + hasUndo: function() { + // Has undo levels or typing and content isn't the same as the initial level + return index > 0 || (self.typing && data[0] && !Levels.isEq(Levels.createFromEditor(editor), data[0])); + }, + + /** + * Returns true/false if the undo manager has any redo levels. + * + * @method hasRedo + * @return {Boolean} true/false if the undo manager has any redo levels. + */ + hasRedo: function() { + return index < data.length - 1 && !self.typing; + }, + + /** + * Executes the specified mutator function as an undo transaction. The selection + * before the modification will be stored to the undo stack and if the DOM changes + * it will add a new undo level. Any methods within the translation that adds undo levels will + * be ignored. So a translation can include calls to execCommand or editor.insertContent. + * + * @method transact + * @param {function} callback Function that gets executed and has dom manipulation logic in it. + * @return {Object} Undo level that got added or null it a level wasn't needed. + */ + transact: function(callback) { + endTyping(); + self.beforeChange(); + + try { + locks++; + callback(); + } finally { + locks--; + } + + return self.add(); + }, + + /** + * Adds an extra "hidden" undo level by first applying the first mutation and store that to the undo stack + * then roll back that change and do the second mutation on top of the stack. This will produce an extra + * undo level that the user doesn't see until they undo. + * + * @method extra + * @param {function} callback1 Function that does mutation but gets stored as a "hidden" extra undo level. + * @param {function} callback2 Function that does mutation but gets displayed to the user. + */ + extra: function (callback1, callback2) { + var lastLevel, bookmark; + + if (self.transact(callback1)) { + bookmark = data[index].bookmark; + lastLevel = data[index - 1]; + Levels.applyToEditor(editor, lastLevel, true); + + if (self.transact(callback2)) { + data[index - 1].beforeBookmark = bookmark; + } + } + } + }; + + return self; + }; +}); + +// Included from: js/tinymce/classes/EnterKey.js + +/** + * EnterKey.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Contains logic for handling the enter key to split/generate block elements. + * + * @private + * @class tinymce.EnterKey + */ +define("tinymce/EnterKey", [ + "tinymce/dom/TreeWalker", + "tinymce/dom/RangeUtils", + "tinymce/caret/CaretContainer", + "tinymce/Env" +], function(TreeWalker, RangeUtils, CaretContainer, Env) { + var isIE = Env.ie && Env.ie < 11; + + return function(editor) { + var dom = editor.dom, selection = editor.selection, settings = editor.settings; + var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements(), + moveCaretBeforeOnEnterElementsMap = schema.getMoveCaretBeforeOnEnterElements(); + + function handleEnterKey(evt) { + var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey, + newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer; + + // Returns true if the block can be split into two blocks or not + function canSplitBlock(node) { + return node && + dom.isBlock(node) && + !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) && + !/^(fixed|absolute)/i.test(node.style.position) && + dom.getContentEditable(node) !== "true"; + } + + function isTableCell(node) { + return node && /^(TD|TH|CAPTION)$/.test(node.nodeName); + } + + // Renders empty block on IE + function renderBlockOnIE(block) { + var oldRng; + + if (dom.isBlock(block)) { + oldRng = selection.getRng(); + block.appendChild(dom.create('span', null, '\u00a0')); + selection.select(block); + block.lastChild.outerHTML = ''; + selection.setRng(oldRng); + } + } + + // Remove the first empty inline element of the block so this:

x

becomes this:

x

+ function trimInlineElementsOnLeftSideOfBlock(block) { + var node = block, firstChilds = [], i; + + if (!node) { + return; + } + + // Find inner most first child ex:

*

+ while ((node = node.firstChild)) { + if (dom.isBlock(node)) { + return; + } + + if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) { + firstChilds.push(node); + } + } + + i = firstChilds.length; + while (i--) { + node = firstChilds[i]; + if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) { + dom.remove(node); + } else { + // Remove see #5381 + if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') { + dom.remove(node); + } + } + } + } + + // Moves the caret to a suitable position within the root for example in the first non + // pure whitespace text node or before an image + function moveToCaretPosition(root) { + var walker, node, rng, lastNode = root, tempElm; + function firstNonWhiteSpaceNodeSibling(node) { + while (node) { + if (node.nodeType == 1 || (node.nodeType == 3 && node.data && /[\r\n\s]/.test(node.data))) { + return node; + } + + node = node.nextSibling; + } + } + + if (!root) { + return; + } + + // Old IE versions doesn't properly render blocks with br elements in them + // For example


wont be rendered correctly in a contentEditable area + // until you remove the br producing

+ if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) { + if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') { + dom.remove(parentBlock.firstChild); + } + } + + if (/^(LI|DT|DD)$/.test(root.nodeName)) { + var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild); + + if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) { + root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild); + } + } + + rng = dom.createRng(); + + // Normalize whitespace to remove empty text nodes. Fix for: #6904 + // Gecko will be able to place the caret in empty text nodes but it won't render propery + // Older IE versions will sometimes crash so for now ignore all IE versions + if (!Env.ie) { + root.normalize(); + } + + if (root.hasChildNodes()) { + walker = new TreeWalker(root, root); + + while ((node = walker.current())) { + if (node.nodeType == 3) { + rng.setStart(node, 0); + rng.setEnd(node, 0); + break; + } + + if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) { + rng.setStartBefore(node); + rng.setEndBefore(node); + break; + } + + lastNode = node; + node = walker.next(); + } + + if (!node) { + rng.setStart(lastNode, 0); + rng.setEnd(lastNode, 0); + } + } else { + if (root.nodeName == 'BR') { + if (root.nextSibling && dom.isBlock(root.nextSibling)) { + // Trick on older IE versions to render the caret before the BR between two lists + if (!documentMode || documentMode < 9) { + tempElm = dom.create('br'); + root.parentNode.insertBefore(tempElm, root); + } + + rng.setStartBefore(root); + rng.setEndBefore(root); + } else { + rng.setStartAfter(root); + rng.setEndAfter(root); + } + } else { + rng.setStart(root, 0); + rng.setEnd(root, 0); + } + } + + selection.setRng(rng); + + // Remove tempElm created for old IE:s + dom.remove(tempElm); + selection.scrollIntoView(root); + } + + function setForcedBlockAttrs(node) { + var forcedRootBlockName = settings.forced_root_block; + + if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) { + dom.setAttribs(node, settings.forced_root_block_attrs); + } + } + + function emptyBlock(elm) { + // BR is needed in empty blocks on non IE browsers + elm.innerHTML = !isIE ? '
' : ''; + } + + // Creates a new block element by cloning the current one or creating a new one if the name is specified + // This function will also copy any text formatting from the parent block and add it to the new one + function createNewBlock(name) { + var node = container, block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements(); + + if (name || parentBlockName == "TABLE") { + block = dom.create(name || newBlockName); + setForcedBlockAttrs(block); + } else { + block = parentBlock.cloneNode(false); + } + + caretNode = block; + + // Clone any parent styles + if (settings.keep_styles !== false) { + do { + if (textInlineElements[node.nodeName]) { + // Never clone a caret containers + if (node.id == '_mce_caret') { + continue; + } + + clonedNode = node.cloneNode(false); + dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique + + if (block.hasChildNodes()) { + clonedNode.appendChild(block.firstChild); + block.appendChild(clonedNode); + } else { + caretNode = clonedNode; + block.appendChild(clonedNode); + } + } + } while ((node = node.parentNode) && node != editableRoot); + } + + // BR is needed in empty blocks on non IE browsers + if (!isIE) { + caretNode.innerHTML = '
'; + } + + return block; + } + + // Returns true/false if the caret is at the start/end of the parent block element + function isCaretAtStartOrEndOfBlock(start) { + var walker, node, name; + + // Caret is in the middle of a text node like "a|b" + if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) { + return false; + } + + // If after the last element in block node edge case for #5091 + if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) { + return true; + } + + // If the caret if before the first element in parentBlock + if (start && container.nodeType == 1 && container == parentBlock.firstChild) { + return true; + } + + // Caret can be before/after a table + if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) { + return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start); + } + + // Walk the DOM and look for text nodes or non empty elements + walker = new TreeWalker(container, parentBlock); + + // If caret is in beginning or end of a text block then jump to the next/previous node + if (container.nodeType == 3) { + if (start && offset === 0) { + walker.prev(); + } else if (!start && offset == container.nodeValue.length) { + walker.next(); + } + } + + while ((node = walker.current())) { + if (node.nodeType === 1) { + // Ignore bogus elements + if (!node.getAttribute('data-mce-bogus')) { + // Keep empty elements like but not trailing br:s like

text|

+ name = node.nodeName.toLowerCase(); + if (nonEmptyElementsMap[name] && name !== 'br') { + return false; + } + } + } else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) { + return false; + } + + if (start) { + walker.prev(); + } else { + walker.next(); + } + } + + return true; + } + + // Wraps any text nodes or inline elements in the specified forced root block name + function wrapSelfAndSiblingsInDefaultBlock(container, offset) { + var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P'; + + // Not in a block element or in a table cell or caption + parentBlock = dom.getParent(container, dom.isBlock); + if (!parentBlock || !canSplitBlock(parentBlock)) { + parentBlock = parentBlock || editableRoot; + + if (parentBlock == editor.getBody() || isTableCell(parentBlock)) { + rootBlockName = parentBlock.nodeName.toLowerCase(); + } else { + rootBlockName = parentBlock.parentNode.nodeName.toLowerCase(); + } + + if (!parentBlock.hasChildNodes()) { + newBlock = dom.create(blockName); + setForcedBlockAttrs(newBlock); + parentBlock.appendChild(newBlock); + rng.setStart(newBlock, 0); + rng.setEnd(newBlock, 0); + return newBlock; + } + + // Find parent that is the first child of parentBlock + node = container; + while (node.parentNode != parentBlock) { + node = node.parentNode; + } + + // Loop left to find start node start wrapping at + while (node && !dom.isBlock(node)) { + startNode = node; + node = node.previousSibling; + } + + if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) { + newBlock = dom.create(blockName); + setForcedBlockAttrs(newBlock); + startNode.parentNode.insertBefore(newBlock, startNode); + + // Start wrapping until we hit a block + node = startNode; + while (node && !dom.isBlock(node)) { + next = node.nextSibling; + newBlock.appendChild(node); + node = next; + } + + // Restore range to it's past location + rng.setStart(container, offset); + rng.setEnd(container, offset); + } + } + + return container; + } + + // Inserts a block or br before/after or in the middle of a split list of the LI is empty + function handleEmptyListItem() { + function isFirstOrLastLi(first) { + var node = containerBlock[first ? 'firstChild' : 'lastChild']; + + // Find first/last element since there might be whitespace there + while (node) { + if (node.nodeType == 1) { + break; + } + + node = node[first ? 'nextSibling' : 'previousSibling']; + } + + return node === parentBlock; + } + + function getContainerBlock() { + var containerBlockParent = containerBlock.parentNode; + + if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) { + return containerBlockParent; + } + + return containerBlock; + } + + if (containerBlock == editor.getBody()) { + return; + } + + // Check if we are in an nested list + var containerBlockParentName = containerBlock.parentNode.nodeName; + if (/^(OL|UL|LI)$/.test(containerBlockParentName)) { + newBlockName = 'LI'; + } + + newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR'); + + if (isFirstOrLastLi(true) && isFirstOrLastLi()) { + if (containerBlockParentName == 'LI') { + // Nested list is inside a LI + dom.insertAfter(newBlock, getContainerBlock()); + } else { + // Is first and last list item then replace the OL/UL with a text block + dom.replace(newBlock, containerBlock); + } + } else if (isFirstOrLastLi(true)) { + if (containerBlockParentName == 'LI') { + // List nested in an LI then move the list to a new sibling LI + dom.insertAfter(newBlock, getContainerBlock()); + newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed + newBlock.appendChild(containerBlock); + } else { + // First LI in list then remove LI and add text block before list + containerBlock.parentNode.insertBefore(newBlock, containerBlock); + } + } else if (isFirstOrLastLi()) { + // Last LI in list then remove LI and add text block after list + dom.insertAfter(newBlock, getContainerBlock()); + renderBlockOnIE(newBlock); + } else { + // Middle LI in list the split the list and insert a text block in the middle + // Extract after fragment and insert it after the current block + containerBlock = getContainerBlock(); + tmpRng = rng.cloneRange(); + tmpRng.setStartAfter(parentBlock); + tmpRng.setEndAfter(containerBlock); + fragment = tmpRng.extractContents(); + + if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') { + newBlock = fragment.firstChild; + dom.insertAfter(fragment, containerBlock); + } else { + dom.insertAfter(fragment, containerBlock); + dom.insertAfter(newBlock, containerBlock); + } + } + + dom.remove(parentBlock); + moveToCaretPosition(newBlock); + undoManager.add(); + } + + // Inserts a BR element if the forced_root_block option is set to false or empty string + function insertBr() { + editor.execCommand("InsertLineBreak", false, evt); + } + + // Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element + function trimLeadingLineBreaks(node) { + do { + if (node.nodeType === 3) { + node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, ''); + } + + node = node.firstChild; + } while (node); + } + + function getEditableRoot(node) { + var root = dom.getRoot(), parent, editableRoot; + + // Get all parents until we hit a non editable parent or the root + parent = node; + while (parent !== root && dom.getContentEditable(parent) !== "false") { + if (dom.getContentEditable(parent) === "true") { + editableRoot = parent; + } + + parent = parent.parentNode; + } + + return parent !== root ? editableRoot : root; + } + + // Adds a BR at the end of blocks that only contains an IMG or INPUT since + // these might be floated and then they won't expand the block + function addBrToBlockIfNeeded(block) { + var lastChild; + + // IE will render the blocks correctly other browsers needs a BR + if (!isIE) { + block.normalize(); // Remove empty text nodes that got left behind by the extract + + // Check if the block is empty or contains a floated last child + lastChild = block.lastChild; + if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) { + dom.add(block, 'br'); + } + } + } + + function insertNewBlockAfter() { + // If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup + if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') { + newBlock = createNewBlock(newBlockName); + } else { + newBlock = createNewBlock(); + } + + // Split the current container block element if enter is pressed inside an empty inner block element + if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) { + // Split container block for example a BLOCKQUOTE at the current blockParent location for example a P + newBlock = dom.split(containerBlock, parentBlock); + } else { + dom.insertAfter(newBlock, parentBlock); + } + + moveToCaretPosition(newBlock); + } + + rng = selection.getRng(true); + + // Event is blocked by some other handler for example the lists plugin + if (evt.isDefaultPrevented()) { + return; + } + + // Delete any selected contents + if (!rng.collapsed) { + editor.execCommand('Delete'); + return; + } + + // Setup range items and newBlockName + new RangeUtils(dom).normalize(rng); + container = rng.startContainer; + offset = rng.startOffset; + newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block; + newBlockName = newBlockName ? newBlockName.toUpperCase() : ''; + documentMode = dom.doc.documentMode; + shiftKey = evt.shiftKey; + + // Resolve node index + if (container.nodeType == 1 && container.hasChildNodes()) { + isAfterLastNodeInContainer = offset > container.childNodes.length - 1; + + container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; + if (isAfterLastNodeInContainer && container.nodeType == 3) { + offset = container.nodeValue.length; + } else { + offset = 0; + } + } + + // Get editable root node, normally the body element but sometimes a div or span + editableRoot = getEditableRoot(container); + + // If there is no editable root then enter is done inside a contentEditable false element + if (!editableRoot) { + return; + } + + undoManager.beforeChange(); + + // If editable root isn't block nor the root of the editor + if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) { + if (!newBlockName || shiftKey) { + insertBr(); + } + + return; + } + + // Wrap the current node and it's sibling in a default block if it's needed. + // for example this
will become this + // This won't happen if root blocks are disabled or the shiftKey is pressed + if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) { + container = wrapSelfAndSiblingsInDefaultBlock(container, offset); + } + + // Find parent block and setup empty block paddings + parentBlock = dom.getParent(container, dom.isBlock); + containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null; + + // Setup block names + parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 + containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 + + // Enter inside block contained within a LI then split or insert before/after LI + if (containerBlockName == 'LI' && !evt.ctrlKey) { + parentBlock = containerBlock; + parentBlockName = containerBlockName; + } + + if (editor.undoManager.typing) { + editor.undoManager.typing = false; + editor.undoManager.add(); + } + + // Handle enter in list item + if (/^(LI|DT|DD)$/.test(parentBlockName)) { + if (!newBlockName && shiftKey) { + insertBr(); + return; + } + + // Handle enter inside an empty list item + if (dom.isEmpty(parentBlock)) { + handleEmptyListItem(); + return; + } + } + + // Don't split PRE tags but insert a BR instead easier when writing code samples etc + if (parentBlockName == 'PRE' && settings.br_in_pre !== false) { + if (!shiftKey) { + insertBr(); + return; + } + } else { + // If no root block is configured then insert a BR by default or if the shiftKey is pressed + if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) { + insertBr(); + return; + } + } + + // If parent block is root then never insert new blocks + if (newBlockName && parentBlock === editor.getBody()) { + return; + } + + // Default block name if it's not configured + newBlockName = newBlockName || 'P'; + + // Insert new block before/after the parent block depending on caret location + if (CaretContainer.isCaretContainerBlock(parentBlock)) { + newBlock = CaretContainer.showCaretContainerBlock(parentBlock); + } else if (isCaretAtStartOrEndOfBlock()) { + insertNewBlockAfter(); + } else if (isCaretAtStartOrEndOfBlock(true)) { + // Insert new block before + newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock); + renderBlockOnIE(newBlock); + moveToCaretPosition(parentBlock); + } else { + // Extract after fragment and insert it after the current block + tmpRng = rng.cloneRange(); + tmpRng.setEndAfter(parentBlock); + fragment = tmpRng.extractContents(); + trimLeadingLineBreaks(fragment); + newBlock = fragment.firstChild; + dom.insertAfter(fragment, parentBlock); + trimInlineElementsOnLeftSideOfBlock(newBlock); + addBrToBlockIfNeeded(parentBlock); + + if (dom.isEmpty(parentBlock)) { + emptyBlock(parentBlock); + } + + newBlock.normalize(); + + // New block might become empty if it's

a |

+ if (dom.isEmpty(newBlock)) { + dom.remove(newBlock); + insertNewBlockAfter(); + } else { + moveToCaretPosition(newBlock); + } + } + + dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique + + // Allow custom handling of new blocks + editor.fire('NewBlock', {newBlock: newBlock}); + + undoManager.typing = false; + undoManager.add(); + } + + editor.on('keydown', function(evt) { + if (evt.keyCode == 13) { + if (handleEnterKey(evt) !== false) { + evt.preventDefault(); + } + } + }); + }; +}); + +// Included from: js/tinymce/classes/ForceBlocks.js + +/** + * ForceBlocks.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Makes sure that everything gets wrapped in paragraphs. + * + * @private + * @class tinymce.ForceBlocks + */ +define("tinymce/ForceBlocks", [], function() { + return function(editor) { + var settings = editor.settings, dom = editor.dom, selection = editor.selection; + var schema = editor.schema, blockElements = schema.getBlockElements(); + + function addRootBlocks() { + var node = selection.getStart(), rootNode = editor.getBody(), rng; + var startContainer, startOffset, endContainer, endOffset, rootBlockNode; + var tempNode, offset = -0xFFFFFF, wrapped, restoreSelection; + var tmpRng, rootNodeName, forcedRootBlock; + + forcedRootBlock = settings.forced_root_block; + + if (!node || node.nodeType !== 1 || !forcedRootBlock) { + return; + } + + // Check if node is wrapped in block + while (node && node != rootNode) { + if (blockElements[node.nodeName]) { + return; + } + + node = node.parentNode; + } + + // Get current selection + rng = selection.getRng(); + if (rng.setStart) { + startContainer = rng.startContainer; + startOffset = rng.startOffset; + endContainer = rng.endContainer; + endOffset = rng.endOffset; + + try { + restoreSelection = editor.getDoc().activeElement === rootNode; + } catch (ex) { + // IE throws unspecified error here sometimes + } + } else { + // Force control range into text range + if (rng.item) { + node = rng.item(0); + rng = editor.getDoc().body.createTextRange(); + rng.moveToElementText(node); + } + + restoreSelection = rng.parentElement().ownerDocument === editor.getDoc(); + tmpRng = rng.duplicate(); + tmpRng.collapse(true); + startOffset = tmpRng.move('character', offset) * -1; + + if (!tmpRng.collapsed) { + tmpRng = rng.duplicate(); + tmpRng.collapse(false); + endOffset = (tmpRng.move('character', offset) * -1) - startOffset; + } + } + + // Wrap non block elements and text nodes + node = rootNode.firstChild; + rootNodeName = rootNode.nodeName.toLowerCase(); + while (node) { + // TODO: Break this up, too complex + if (((node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName]))) && + schema.isValidChild(rootNodeName, forcedRootBlock.toLowerCase())) { + // Remove empty text nodes + if (node.nodeType === 3 && node.nodeValue.length === 0) { + tempNode = node; + node = node.nextSibling; + dom.remove(tempNode); + continue; + } + + if (!rootBlockNode) { + rootBlockNode = dom.create(forcedRootBlock, editor.settings.forced_root_block_attrs); + node.parentNode.insertBefore(rootBlockNode, node); + wrapped = true; + } + + tempNode = node; + node = node.nextSibling; + rootBlockNode.appendChild(tempNode); + } else { + rootBlockNode = null; + node = node.nextSibling; + } + } + + if (wrapped && restoreSelection) { + if (rng.setStart) { + rng.setStart(startContainer, startOffset); + rng.setEnd(endContainer, endOffset); + selection.setRng(rng); + } else { + // Only select if the previous selection was inside the document to prevent auto focus in quirks mode + try { + rng = editor.getDoc().body.createTextRange(); + rng.moveToElementText(rootNode); + rng.collapse(true); + rng.moveStart('character', startOffset); + + if (endOffset > 0) { + rng.moveEnd('character', endOffset); + } + + rng.select(); + } catch (ex) { + // Ignore + } + } + + editor.nodeChanged(); + } + } + + // Force root blocks + if (settings.forced_root_block) { + editor.on('NodeChange', addRootBlocks); + } + }; +}); + +// Included from: js/tinymce/classes/caret/CaretUtils.js + +/** + * CaretUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Utility functions shared by the caret logic. + * + * @private + * @class tinymce.caret.CaretUtils + */ +define("tinymce/caret/CaretUtils", [ + "tinymce/util/Fun", + "tinymce/dom/TreeWalker", + "tinymce/dom/NodeType", + "tinymce/caret/CaretPosition", + "tinymce/caret/CaretContainer", + "tinymce/caret/CaretCandidate" +], function(Fun, TreeWalker, NodeType, CaretPosition, CaretContainer, CaretCandidate) { + var isContentEditableTrue = NodeType.isContentEditableTrue, + isContentEditableFalse = NodeType.isContentEditableFalse, + isBlockLike = NodeType.matchStyleValues('display', 'block table table-cell table-caption'), + isCaretContainer = CaretContainer.isCaretContainer, + isCaretContainerBlock = CaretContainer.isCaretContainerBlock, + curry = Fun.curry, + isElement = NodeType.isElement, + isCaretCandidate = CaretCandidate.isCaretCandidate; + + function isForwards(direction) { + return direction > 0; + } + + function isBackwards(direction) { + return direction < 0; + } + + function skipCaretContainers(walk, shallow) { + var node; + + while ((node = walk(shallow))) { + if (!isCaretContainerBlock(node)) { + return node; + } + } + + return null; + } + + function findNode(node, direction, predicateFn, rootNode, shallow) { + var walker = new TreeWalker(node, rootNode); + + if (isBackwards(direction)) { + if (isContentEditableFalse(node) || isCaretContainerBlock(node)) { + node = skipCaretContainers(walker.prev, true); + if (predicateFn(node)) { + return node; + } + } + + while ((node = skipCaretContainers(walker.prev, shallow))) { + if (predicateFn(node)) { + return node; + } + } + } + + if (isForwards(direction)) { + if (isContentEditableFalse(node) || isCaretContainerBlock(node)) { + node = skipCaretContainers(walker.next, true); + if (predicateFn(node)) { + return node; + } + } + + while ((node = skipCaretContainers(walker.next, shallow))) { + if (predicateFn(node)) { + return node; + } + } + } + + return null; + } + + function getEditingHost(node, rootNode) { + for (node = node.parentNode; node && node != rootNode; node = node.parentNode) { + if (isContentEditableTrue(node)) { + return node; + } + } + + return rootNode; + } + + function getParentBlock(node, rootNode) { + while (node && node != rootNode) { + if (isBlockLike(node)) { + return node; + } + + node = node.parentNode; + } + + return null; + } + + function isInSameBlock(caretPosition1, caretPosition2, rootNode) { + return getParentBlock(caretPosition1.container(), rootNode) == getParentBlock(caretPosition2.container(), rootNode); + } + + function isInSameEditingHost(caretPosition1, caretPosition2, rootNode) { + return getEditingHost(caretPosition1.container(), rootNode) == getEditingHost(caretPosition2.container(), rootNode); + } + + function getChildNodeAtRelativeOffset(relativeOffset, caretPosition) { + var container, offset; + + if (!caretPosition) { + return null; + } + + container = caretPosition.container(); + offset = caretPosition.offset(); + + if (!isElement(container)) { + return null; + } + + return container.childNodes[offset + relativeOffset]; + } + + function beforeAfter(before, node) { + var range = node.ownerDocument.createRange(); + + if (before) { + range.setStartBefore(node); + range.setEndBefore(node); + } else { + range.setStartAfter(node); + range.setEndAfter(node); + } + + return range; + } + + function isNodesInSameBlock(rootNode, node1, node2) { + return getParentBlock(node1, rootNode) == getParentBlock(node2, rootNode); + } + + function lean(left, rootNode, node) { + var sibling, siblingName; + + if (left) { + siblingName = 'previousSibling'; + } else { + siblingName = 'nextSibling'; + } + + while (node && node != rootNode) { + sibling = node[siblingName]; + + if (isCaretContainer(sibling)) { + sibling = sibling[siblingName]; + } + + if (isContentEditableFalse(sibling)) { + if (isNodesInSameBlock(rootNode, sibling, node)) { + return sibling; + } + + break; + } + + if (isCaretCandidate(sibling)) { + break; + } + + node = node.parentNode; + } + + return null; + } + + var before = curry(beforeAfter, true); + var after = curry(beforeAfter, false); + + function normalizeRange(direction, rootNode, range) { + var node, container, offset, location; + var leanLeft = curry(lean, true, rootNode); + var leanRight = curry(lean, false, rootNode); + + container = range.startContainer; + offset = range.startOffset; + + if (CaretContainer.isCaretContainerBlock(container)) { + if (!isElement(container)) { + container = container.parentNode; + } + + location = container.getAttribute('data-mce-caret'); + + if (location == 'before') { + node = container.nextSibling; + if (isContentEditableFalse(node)) { + return before(node); + } + } + + if (location == 'after') { + node = container.previousSibling; + if (isContentEditableFalse(node)) { + return after(node); + } + } + } + + if (!range.collapsed) { + return range; + } + + if (NodeType.isText(container)) { + if (isCaretContainer(container)) { + if (direction === 1) { + node = leanRight(container); + if (node) { + return before(node); + } + + node = leanLeft(container); + if (node) { + return after(node); + } + } + + if (direction === -1) { + node = leanLeft(container); + if (node) { + return after(node); + } + + node = leanRight(container); + if (node) { + return before(node); + } + } + + return range; + } + + if (CaretContainer.endsWithCaretContainer(container) && offset >= container.data.length - 1) { + if (direction === 1) { + node = leanRight(container); + if (node) { + return before(node); + } + } + + return range; + } + + if (CaretContainer.startsWithCaretContainer(container) && offset <= 1) { + if (direction === -1) { + node = leanLeft(container); + if (node) { + return after(node); + } + } + + return range; + } + + if (offset === container.data.length) { + node = leanRight(container); + if (node) { + return before(node); + } + + return range; + } + + if (offset === 0) { + node = leanLeft(container); + if (node) { + return after(node); + } + + return range; + } + } + + return range; + } + + function isNextToContentEditableFalse(relativeOffset, caretPosition) { + return isContentEditableFalse(getChildNodeAtRelativeOffset(relativeOffset, caretPosition)); + } + + return { + isForwards: isForwards, + isBackwards: isBackwards, + findNode: findNode, + getEditingHost: getEditingHost, + getParentBlock: getParentBlock, + isInSameBlock: isInSameBlock, + isInSameEditingHost: isInSameEditingHost, + isBeforeContentEditableFalse: curry(isNextToContentEditableFalse, 0), + isAfterContentEditableFalse: curry(isNextToContentEditableFalse, -1), + normalizeRange: normalizeRange + }; +}); + +// Included from: js/tinymce/classes/caret/CaretWalker.js + +/** + * CaretWalker.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This module contains logic for moving around a virtual caret in logical order within a DOM element. + * + * It ignores the most obvious invalid caret locations such as within a script element or within a + * contentEditable=false element but it will return locations that isn't possible to render visually. + * + * @private + * @class tinymce.caret.CaretWalker + * @example + * var caretWalker = new CaretWalker(rootElm); + * + * var prevLogicalCaretPosition = caretWalker.prev(CaretPosition.fromRangeStart(range)); + * var nextLogicalCaretPosition = caretWalker.next(CaretPosition.fromRangeEnd(range)); + */ +define("tinymce/caret/CaretWalker", [ + "tinymce/dom/NodeType", + "tinymce/caret/CaretCandidate", + "tinymce/caret/CaretPosition", + "tinymce/caret/CaretUtils", + "tinymce/util/Arr", + "tinymce/util/Fun" +], function(NodeType, CaretCandidate, CaretPosition, CaretUtils, Arr, Fun) { + var isContentEditableFalse = NodeType.isContentEditableFalse, + isText = NodeType.isText, + isElement = NodeType.isElement, + isBr = NodeType.isBr, + isForwards = CaretUtils.isForwards, + isBackwards = CaretUtils.isBackwards, + isCaretCandidate = CaretCandidate.isCaretCandidate, + isAtomic = CaretCandidate.isAtomic, + isEditableCaretCandidate = CaretCandidate.isEditableCaretCandidate; + + function getParents(node, rootNode) { + var parents = []; + + while (node && node != rootNode) { + parents.push(node); + node = node.parentNode; + } + + return parents; + } + + function nodeAtIndex(container, offset) { + if (container.hasChildNodes() && offset < container.childNodes.length) { + return container.childNodes[offset]; + } + + return null; + } + + function getCaretCandidatePosition(direction, node) { + if (isForwards(direction)) { + if (isCaretCandidate(node.previousSibling) && !isText(node.previousSibling)) { + return CaretPosition.before(node); + } + + if (isText(node)) { + return CaretPosition(node, 0); + } + } + + if (isBackwards(direction)) { + if (isCaretCandidate(node.nextSibling) && !isText(node.nextSibling)) { + return CaretPosition.after(node); + } + + if (isText(node)) { + return CaretPosition(node, node.data.length); + } + } + + if (isBackwards(direction)) { + if (isBr(node)) { + return CaretPosition.before(node); + } + + return CaretPosition.after(node); + } + + return CaretPosition.before(node); + } + + // Jumps over BR elements

|

a

->


|a

+ function isBrBeforeBlock(node, rootNode) { + var next; + + if (!NodeType.isBr(node)) { + return false; + } + + next = findCaretPosition(1, CaretPosition.after(node), rootNode); + if (!next) { + return false; + } + + return !CaretUtils.isInSameBlock(CaretPosition.before(node), CaretPosition.before(next), rootNode); + } + + function findCaretPosition(direction, startCaretPosition, rootNode) { + var container, offset, node, nextNode, innerNode, + rootContentEditableFalseElm, caretPosition; + + if (!isElement(rootNode) || !startCaretPosition) { + return null; + } + + caretPosition = startCaretPosition; + container = caretPosition.container(); + offset = caretPosition.offset(); + + if (isText(container)) { + if (isBackwards(direction) && offset > 0) { + return CaretPosition(container, --offset); + } + + if (isForwards(direction) && offset < container.length) { + return CaretPosition(container, ++offset); + } + + node = container; + } else { + if (isBackwards(direction) && offset > 0) { + nextNode = nodeAtIndex(container, offset - 1); + if (isCaretCandidate(nextNode)) { + if (!isAtomic(nextNode)) { + innerNode = CaretUtils.findNode(nextNode, direction, isEditableCaretCandidate, nextNode); + if (innerNode) { + if (isText(innerNode)) { + return CaretPosition(innerNode, innerNode.data.length); + } + + return CaretPosition.after(innerNode); + } + } + + if (isText(nextNode)) { + return CaretPosition(nextNode, nextNode.data.length); + } + + return CaretPosition.before(nextNode); + } + } + + if (isForwards(direction) && offset < container.childNodes.length) { + nextNode = nodeAtIndex(container, offset); + if (isCaretCandidate(nextNode)) { + if (isBrBeforeBlock(nextNode, rootNode)) { + return findCaretPosition(direction, CaretPosition.after(nextNode), rootNode); + } + + if (!isAtomic(nextNode)) { + innerNode = CaretUtils.findNode(nextNode, direction, isEditableCaretCandidate, nextNode); + if (innerNode) { + if (isText(innerNode)) { + return CaretPosition(innerNode, 0); + } + + return CaretPosition.before(innerNode); + } + } + + if (isText(nextNode)) { + return CaretPosition(nextNode, 0); + } + + return CaretPosition.after(nextNode); + } + } + + node = caretPosition.getNode(); + } + + if ((isForwards(direction) && caretPosition.isAtEnd()) || (isBackwards(direction) && caretPosition.isAtStart())) { + node = CaretUtils.findNode(node, direction, Fun.constant(true), rootNode, true); + if (isEditableCaretCandidate(node)) { + return getCaretCandidatePosition(direction, node); + } + } + + nextNode = CaretUtils.findNode(node, direction, isEditableCaretCandidate, rootNode); + + rootContentEditableFalseElm = Arr.last(Arr.filter(getParents(container, rootNode), isContentEditableFalse)); + if (rootContentEditableFalseElm && (!nextNode || !rootContentEditableFalseElm.contains(nextNode))) { + if (isForwards(direction)) { + caretPosition = CaretPosition.after(rootContentEditableFalseElm); + } else { + caretPosition = CaretPosition.before(rootContentEditableFalseElm); + } + + return caretPosition; + } + + if (nextNode) { + return getCaretCandidatePosition(direction, nextNode); + } + + return null; + } + + return function(rootNode) { + return { + /** + * Returns the next logical caret position from the specificed input + * caretPoisiton or null if there isn't any more positions left for example + * at the end specified root element. + * + * @method next + * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from. + * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found. + */ + next: function(caretPosition) { + return findCaretPosition(1, caretPosition, rootNode); + }, + + /** + * Returns the previous logical caret position from the specificed input + * caretPoisiton or null if there isn't any more positions left for example + * at the end specified root element. + * + * @method prev + * @param {tinymce.caret.CaretPosition} caretPosition Caret position to start from. + * @return {tinymce.caret.CaretPosition} CaretPosition or null if no position was found. + */ + prev: function(caretPosition) { + return findCaretPosition(-1, caretPosition, rootNode); + } + }; + }; +}); + +// Included from: js/tinymce/classes/InsertList.js + +/** + * InsertList.js + * + * Released under LGPL License. + * Copyright (c) 1999-2016 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Handles inserts of lists into the editor instance. + * + * @class tinymce.InsertList + * @private + */ +define("tinymce/InsertList", [ + "tinymce/util/Tools", + "tinymce/caret/CaretWalker", + "tinymce/caret/CaretPosition" +], function(Tools, CaretWalker, CaretPosition) { + var isListFragment = function(fragment) { + var firstChild = fragment.firstChild; + var lastChild = fragment.lastChild; + + // Skip meta since it's likely + if (firstChild && firstChild.name === 'meta') { + firstChild = firstChild.next; + } + + // Skip mce_marker since it's likely + if (lastChild && lastChild.attr('id') === 'mce_marker') { + lastChild = lastChild.prev; + } + + if (!firstChild || firstChild !== lastChild) { + return false; + } + + return firstChild.name === 'ul' || firstChild.name === 'ol'; + }; + + var cleanupDomFragment = function (domFragment) { + var firstChild = domFragment.firstChild; + var lastChild = domFragment.lastChild; + + // TODO: remove the meta tag from paste logic + if (firstChild && firstChild.nodeName === 'META') { + firstChild.parentNode.removeChild(firstChild); + } + + if (lastChild && lastChild.id === 'mce_marker') { + lastChild.parentNode.removeChild(lastChild); + } + + return domFragment; + }; + + var toDomFragment = function(dom, serializer, fragment) { + var html = serializer.serialize(fragment); + var domFragment = dom.createFragment(html); + + return cleanupDomFragment(domFragment); + }; + + var listItems = function(elm) { + return Tools.grep(elm.childNodes, function(child) { + return child.nodeName === 'LI'; + }); + }; + + var isEmpty = function (elm) { + return !elm.firstChild; + }; + + var trimListItems = function(elms) { + return elms.length > 0 && isEmpty(elms[elms.length - 1]) ? elms.slice(0, -1) : elms; + }; + + var getParentLi = function(dom, node) { + var parentBlock = dom.getParent(node, dom.isBlock); + return parentBlock && parentBlock.nodeName === 'LI' ? parentBlock : null; + }; + + var isParentBlockLi = function(dom, node) { + return !!getParentLi(dom, node); + }; + + var getSplit = function(parentNode, rng) { + var beforeRng = rng.cloneRange(); + var afterRng = rng.cloneRange(); + + beforeRng.setStartBefore(parentNode); + afterRng.setEndAfter(parentNode); + + return [ + beforeRng.cloneContents(), + afterRng.cloneContents() + ]; + }; + + var findFirstIn = function(node, rootNode) { + var caretPos = CaretPosition.before(node); + var caretWalker = new CaretWalker(rootNode); + var newCaretPos = caretWalker.next(caretPos); + + return newCaretPos ? newCaretPos.toRange() : null; + }; + + var findLastOf = function(node, rootNode) { + var caretPos = CaretPosition.after(node); + var caretWalker = new CaretWalker(rootNode); + var newCaretPos = caretWalker.prev(caretPos); + + return newCaretPos ? newCaretPos.toRange() : null; + }; + + var insertMiddle = function(target, elms, rootNode, rng) { + var parts = getSplit(target, rng); + var parentElm = target.parentNode; + + parentElm.insertBefore(parts[0], target); + Tools.each(elms, function(li) { + parentElm.insertBefore(li, target); + }); + parentElm.insertBefore(parts[1], target); + parentElm.removeChild(target); + + return findLastOf(elms[elms.length - 1], rootNode); + }; + + var insertBefore = function(target, elms, rootNode) { + var parentElm = target.parentNode; + + Tools.each(elms, function(elm) { + parentElm.insertBefore(elm, target); + }); + + return findFirstIn(target, rootNode); + }; + + var insertAfter = function(target, elms, rootNode, dom) { + dom.insertAfter(elms.reverse(), target); + return findLastOf(elms[0], rootNode); + }; + + var insertAtCaret = function(serializer, dom, rng, fragment) { + var domFragment = toDomFragment(dom, serializer, fragment); + var liTarget = getParentLi(dom, rng.startContainer); + var liElms = trimListItems(listItems(domFragment.firstChild)); + var BEGINNING = 1, END = 2; + var rootNode = dom.getRoot(); + + var isAt = function(location) { + var caretPos = CaretPosition.fromRangeStart(rng); + var caretWalker = new CaretWalker(dom.getRoot()); + var newPos = location === BEGINNING ? caretWalker.prev(caretPos) : caretWalker.next(caretPos); + + return newPos ? getParentLi(dom, newPos.getNode()) !== liTarget : true; + }; + + if (isAt(BEGINNING)) { + return insertBefore(liTarget, liElms, rootNode); + } else if (isAt(END)) { + return insertAfter(liTarget, liElms, rootNode, dom); + } + + return insertMiddle(liTarget, liElms, rootNode, rng); + }; + + return { + isListFragment: isListFragment, + insertAtCaret: insertAtCaret, + isParentBlockLi: isParentBlockLi, + trimListItems: trimListItems, + listItems: listItems + }; +}); + +// Included from: js/tinymce/classes/InsertContent.js + +/** + * InsertContent.js + * + * Released under LGPL License. + * Copyright (c) 1999-2016 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Handles inserts of contents into the editor instance. + * + * @class tinymce.InsertContent + * @private + */ +define("tinymce/InsertContent", [ + "tinymce/Env", + "tinymce/util/Tools", + "tinymce/html/Serializer", + "tinymce/caret/CaretWalker", + "tinymce/caret/CaretPosition", + "tinymce/dom/ElementUtils", + "tinymce/dom/NodeType", + "tinymce/InsertList" +], function(Env, Tools, Serializer, CaretWalker, CaretPosition, ElementUtils, NodeType, InsertList) { + var isTableCell = NodeType.matchNodeNames('td th'); + + var insertHtmlAtCaret = function(editor, value, details) { + var parser, serializer, parentNode, rootNode, fragment, args; + var marker, rng, node, node2, bookmarkHtml, merge; + var textInlineElements = editor.schema.getTextInlineElements(); + var selection = editor.selection, dom = editor.dom; + + function trimOrPaddLeftRight(html) { + var rng, container, offset; + + rng = selection.getRng(true); + container = rng.startContainer; + offset = rng.startOffset; + + function hasSiblingText(siblingName) { + return container[siblingName] && container[siblingName].nodeType == 3; + } + + if (container.nodeType == 3) { + if (offset > 0) { + html = html.replace(/^ /, ' '); + } else if (!hasSiblingText('previousSibling')) { + html = html.replace(/^ /, ' '); + } + + if (offset < container.length) { + html = html.replace(/ (
|)$/, ' '); + } else if (!hasSiblingText('nextSibling')) { + html = html.replace(/( | )(
|)$/, ' '); + } + } + + return html; + } + + // Removes   from a [b] c -> a  c -> a c + function trimNbspAfterDeleteAndPaddValue() { + var rng, container, offset; + + rng = selection.getRng(true); + container = rng.startContainer; + offset = rng.startOffset; + + if (container.nodeType == 3 && rng.collapsed) { + if (container.data[offset] === '\u00a0') { + container.deleteData(offset, 1); + + if (!/[\u00a0| ]$/.test(value)) { + value += ' '; + } + } else if (container.data[offset - 1] === '\u00a0') { + container.deleteData(offset - 1, 1); + + if (!/[\u00a0| ]$/.test(value)) { + value = ' ' + value; + } + } + } + } + + function reduceInlineTextElements() { + if (merge) { + var root = editor.getBody(), elementUtils = new ElementUtils(dom); + + Tools.each(dom.select('*[data-mce-fragment]'), function(node) { + for (var testNode = node.parentNode; testNode && testNode != root; testNode = testNode.parentNode) { + if (textInlineElements[node.nodeName.toLowerCase()] && elementUtils.compare(testNode, node)) { + dom.remove(node, true); + } + } + }); + } + } + + function markFragmentElements(fragment) { + var node = fragment; + + while ((node = node.walk())) { + if (node.type === 1) { + node.attr('data-mce-fragment', '1'); + } + } + } + + function umarkFragmentElements(elm) { + Tools.each(elm.getElementsByTagName('*'), function(elm) { + elm.removeAttribute('data-mce-fragment'); + }); + } + + function isPartOfFragment(node) { + return !!node.getAttribute('data-mce-fragment'); + } + + function canHaveChildren(node) { + return node && !editor.schema.getShortEndedElements()[node.nodeName]; + } + + function moveSelectionToMarker(marker) { + var parentEditableFalseElm, parentBlock, nextRng; + + function getContentEditableFalseParent(node) { + var root = editor.getBody(); + + for (; node && node !== root; node = node.parentNode) { + if (editor.dom.getContentEditable(node) === 'false') { + return node; + } + } + + return null; + } + + if (!marker) { + return; + } + + selection.scrollIntoView(marker); + + // If marker is in cE=false then move selection to that element instead + parentEditableFalseElm = getContentEditableFalseParent(marker); + if (parentEditableFalseElm) { + dom.remove(marker); + selection.select(parentEditableFalseElm); + return; + } + + // Move selection before marker and remove it + rng = dom.createRng(); + + // If previous sibling is a text node set the selection to the end of that node + node = marker.previousSibling; + if (node && node.nodeType == 3) { + rng.setStart(node, node.nodeValue.length); + + // TODO: Why can't we normalize on IE + if (!Env.ie) { + node2 = marker.nextSibling; + if (node2 && node2.nodeType == 3) { + node.appendData(node2.data); + node2.parentNode.removeChild(node2); + } + } + } else { + // If the previous sibling isn't a text node or doesn't exist set the selection before the marker node + rng.setStartBefore(marker); + rng.setEndBefore(marker); + } + + function findNextCaretRng(rng) { + var caretPos = CaretPosition.fromRangeStart(rng); + var caretWalker = new CaretWalker(editor.getBody()); + + caretPos = caretWalker.next(caretPos); + if (caretPos) { + return caretPos.toRange(); + } + } + + // Remove the marker node and set the new range + parentBlock = dom.getParent(marker, dom.isBlock); + dom.remove(marker); + + if (parentBlock && dom.isEmpty(parentBlock)) { + editor.$(parentBlock).empty(); + + rng.setStart(parentBlock, 0); + rng.setEnd(parentBlock, 0); + + if (!isTableCell(parentBlock) && !isPartOfFragment(parentBlock) && (nextRng = findNextCaretRng(rng))) { + rng = nextRng; + dom.remove(parentBlock); + } else { + dom.add(parentBlock, dom.create('br', {'data-mce-bogus': '1'})); + } + } + + selection.setRng(rng); + } + + // Check for whitespace before/after value + if (/^ | $/.test(value)) { + value = trimOrPaddLeftRight(value); + } + + // Setup parser and serializer + parser = editor.parser; + merge = details.merge; + + serializer = new Serializer({ + validate: editor.settings.validate + }, editor.schema); + bookmarkHtml = '​'; + + // Run beforeSetContent handlers on the HTML to be inserted + args = {content: value, format: 'html', selection: true}; + editor.fire('BeforeSetContent', args); + value = args.content; + + // Add caret at end of contents if it's missing + if (value.indexOf('{$caret}') == -1) { + value += '{$caret}'; + } + + // Replace the caret marker with a span bookmark element + value = value.replace(/\{\$caret\}/, bookmarkHtml); + + // If selection is at |

then move it into

|

+ rng = selection.getRng(); + var caretElement = rng.startContainer || (rng.parentElement ? rng.parentElement() : null); + var body = editor.getBody(); + if (caretElement === body && selection.isCollapsed()) { + if (dom.isBlock(body.firstChild) && canHaveChildren(body.firstChild) && dom.isEmpty(body.firstChild)) { + rng = dom.createRng(); + rng.setStart(body.firstChild, 0); + rng.setEnd(body.firstChild, 0); + selection.setRng(rng); + } + } + + // Insert node maker where we will insert the new HTML and get it's parent + if (!selection.isCollapsed()) { + // Fix for #2595 seems that delete removes one extra character on + // WebKit for some odd reason if you double click select a word + editor.selection.setRng(editor.selection.getRng()); + editor.getDoc().execCommand('Delete', false, null); + trimNbspAfterDeleteAndPaddValue(); + } + + parentNode = selection.getNode(); + + // Parse the fragment within the context of the parent node + var parserArgs = {context: parentNode.nodeName.toLowerCase(), data: details.data}; + fragment = parser.parse(value, parserArgs); + + // Custom handling of lists + if (details.paste === true && InsertList.isListFragment(fragment) && InsertList.isParentBlockLi(dom, parentNode)) { + rng = InsertList.insertAtCaret(serializer, dom, editor.selection.getRng(true), fragment); + editor.selection.setRng(rng); + editor.fire('SetContent', args); + return; + } + + markFragmentElements(fragment); + + // Move the caret to a more suitable location + node = fragment.lastChild; + if (node.attr('id') == 'mce_marker') { + marker = node; + + for (node = node.prev; node; node = node.walk(true)) { + if (node.type == 3 || !dom.isBlock(node.name)) { + if (editor.schema.isValidChild(node.parent.name, 'span')) { + node.parent.insert(marker, node, node.name === 'br'); + } + break; + } + } + } + + editor._selectionOverrides.showBlockCaretContainer(parentNode); + + // If parser says valid we can insert the contents into that parent + if (!parserArgs.invalid) { + value = serializer.serialize(fragment); + + // Check if parent is empty or only has one BR element then set the innerHTML of that parent + node = parentNode.firstChild; + node2 = parentNode.lastChild; + if (!node || (node === node2 && node.nodeName === 'BR')) { + dom.setHTML(parentNode, value); + } else { + selection.setContent(value); + } + } else { + // If the fragment was invalid within that context then we need + // to parse and process the parent it's inserted into + + // Insert bookmark node and get the parent + selection.setContent(bookmarkHtml); + parentNode = selection.getNode(); + rootNode = editor.getBody(); + + // Opera will return the document node when selection is in root + if (parentNode.nodeType == 9) { + parentNode = node = rootNode; + } else { + node = parentNode; + } + + // Find the ancestor just before the root element + while (node !== rootNode) { + parentNode = node; + node = node.parentNode; + } + + // Get the outer/inner HTML depending on if we are in the root and parser and serialize that + value = parentNode == rootNode ? rootNode.innerHTML : dom.getOuterHTML(parentNode); + value = serializer.serialize( + parser.parse( + // Need to replace by using a function since $ in the contents would otherwise be a problem + value.replace(//i, function() { + return serializer.serialize(fragment); + }) + ) + ); + + // Set the inner/outer HTML depending on if we are in the root or not + if (parentNode == rootNode) { + dom.setHTML(rootNode, value); + } else { + dom.setOuterHTML(parentNode, value); + } + } + + reduceInlineTextElements(); + moveSelectionToMarker(dom.get('mce_marker')); + umarkFragmentElements(editor.getBody()); + editor.fire('SetContent', args); + editor.addVisual(); + }; + + var processValue = function (value) { + var details; + + if (typeof value !== 'string') { + details = Tools.extend({ + paste: value.paste, + data: { + paste: value.paste + } + }, value); + + return { + content: value.content, + details: details + }; + } + + return { + content: value, + details: {} + }; + }; + + var insertAtCaret = function (editor, value) { + var result = processValue(value); + insertHtmlAtCaret(editor, result.content, result.details); + }; + + return { + insertAtCaret: insertAtCaret + }; +}); + +// Included from: js/tinymce/classes/EditorCommands.js + +/** + * EditorCommands.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class enables you to add custom editor commands and it contains + * overrides for native browser commands to address various bugs and issues. + * + * @class tinymce.EditorCommands + */ +define("tinymce/EditorCommands", [ + "tinymce/Env", + "tinymce/util/Tools", + "tinymce/dom/RangeUtils", + "tinymce/dom/TreeWalker", + "tinymce/InsertContent" +], function(Env, Tools, RangeUtils, TreeWalker, InsertContent) { + // Added for compression purposes + var each = Tools.each, extend = Tools.extend; + var map = Tools.map, inArray = Tools.inArray, explode = Tools.explode; + var isOldIE = Env.ie && Env.ie < 11; + var TRUE = true, FALSE = false; + + return function(editor) { + var dom, selection, formatter, + commands = {state: {}, exec: {}, value: {}}, + settings = editor.settings, + bookmark; + + editor.on('PreInit', function() { + dom = editor.dom; + selection = editor.selection; + settings = editor.settings; + formatter = editor.formatter; + }); + + /** + * Executes the specified command. + * + * @method execCommand + * @param {String} command Command to execute. + * @param {Boolean} ui Optional user interface state. + * @param {Object} value Optional value for command. + * @param {Object} args Optional extra arguments to the execCommand. + * @return {Boolean} true/false if the command was found or not. + */ + function execCommand(command, ui, value, args) { + var func, customCommand, state = 0; + + if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint)$/.test(command) && (!args || !args.skip_focus)) { + editor.focus(); + } + + args = editor.fire('BeforeExecCommand', {command: command, ui: ui, value: value}); + if (args.isDefaultPrevented()) { + return false; + } + + customCommand = command.toLowerCase(); + if ((func = commands.exec[customCommand])) { + func(customCommand, ui, value); + editor.fire('ExecCommand', {command: command, ui: ui, value: value}); + return true; + } + + // Plugin commands + each(editor.plugins, function(p) { + if (p.execCommand && p.execCommand(command, ui, value)) { + editor.fire('ExecCommand', {command: command, ui: ui, value: value}); + state = true; + return false; + } + }); + + if (state) { + return state; + } + + // Theme commands + if (editor.theme && editor.theme.execCommand && editor.theme.execCommand(command, ui, value)) { + editor.fire('ExecCommand', {command: command, ui: ui, value: value}); + return true; + } + + // Browser commands + try { + state = editor.getDoc().execCommand(command, ui, value); + } catch (ex) { + // Ignore old IE errors + } + + if (state) { + editor.fire('ExecCommand', {command: command, ui: ui, value: value}); + return true; + } + + return false; + } + + /** + * Queries the current state for a command for example if the current selection is "bold". + * + * @method queryCommandState + * @param {String} command Command to check the state of. + * @return {Boolean/Number} true/false if the selected contents is bold or not, -1 if it's not found. + */ + function queryCommandState(command) { + var func; + + // Is hidden then return undefined + if (editor.quirks.isHidden()) { + return; + } + + command = command.toLowerCase(); + if ((func = commands.state[command])) { + return func(command); + } + + // Browser commands + try { + return editor.getDoc().queryCommandState(command); + } catch (ex) { + // Fails sometimes see bug: 1896577 + } + + return false; + } + + /** + * Queries the command value for example the current fontsize. + * + * @method queryCommandValue + * @param {String} command Command to check the value of. + * @return {Object} Command value of false if it's not found. + */ + function queryCommandValue(command) { + var func; + + // Is hidden then return undefined + if (editor.quirks.isHidden()) { + return; + } + + command = command.toLowerCase(); + if ((func = commands.value[command])) { + return func(command); + } + + // Browser commands + try { + return editor.getDoc().queryCommandValue(command); + } catch (ex) { + // Fails sometimes see bug: 1896577 + } + } + + /** + * Adds commands to the command collection. + * + * @method addCommands + * @param {Object} command_list Name/value collection with commands to add, the names can also be comma separated. + * @param {String} type Optional type to add, defaults to exec. Can be value or state as well. + */ + function addCommands(command_list, type) { + type = type || 'exec'; + + each(command_list, function(callback, command) { + each(command.toLowerCase().split(','), function(command) { + commands[type][command] = callback; + }); + }); + } + + function addCommand(command, callback, scope) { + command = command.toLowerCase(); + commands.exec[command] = function(command, ui, value, args) { + return callback.call(scope || editor, ui, value, args); + }; + } + + /** + * Returns true/false if the command is supported or not. + * + * @method queryCommandSupported + * @param {String} command Command that we check support for. + * @return {Boolean} true/false if the command is supported or not. + */ + function queryCommandSupported(command) { + command = command.toLowerCase(); + + if (commands.exec[command]) { + return true; + } + + // Browser commands + try { + return editor.getDoc().queryCommandSupported(command); + } catch (ex) { + // Fails sometimes see bug: 1896577 + } + + return false; + } + + function addQueryStateHandler(command, callback, scope) { + command = command.toLowerCase(); + commands.state[command] = function() { + return callback.call(scope || editor); + }; + } + + function addQueryValueHandler(command, callback, scope) { + command = command.toLowerCase(); + commands.value[command] = function() { + return callback.call(scope || editor); + }; + } + + function hasCustomCommand(command) { + command = command.toLowerCase(); + return !!commands.exec[command]; + } + + // Expose public methods + extend(this, { + execCommand: execCommand, + queryCommandState: queryCommandState, + queryCommandValue: queryCommandValue, + queryCommandSupported: queryCommandSupported, + addCommands: addCommands, + addCommand: addCommand, + addQueryStateHandler: addQueryStateHandler, + addQueryValueHandler: addQueryValueHandler, + hasCustomCommand: hasCustomCommand + }); + + // Private methods + + function execNativeCommand(command, ui, value) { + if (ui === undefined) { + ui = FALSE; + } + + if (value === undefined) { + value = null; + } + + return editor.getDoc().execCommand(command, ui, value); + } + + function isFormatMatch(name) { + return formatter.match(name); + } + + function toggleFormat(name, value) { + formatter.toggle(name, value ? {value: value} : undefined); + editor.nodeChanged(); + } + + function storeSelection(type) { + bookmark = selection.getBookmark(type); + } + + function restoreSelection() { + selection.moveToBookmark(bookmark); + } + + // Add execCommand overrides + addCommands({ + // Ignore these, added for compatibility + 'mceResetDesignMode,mceBeginUndoLevel': function() {}, + + // Add undo manager logic + 'mceEndUndoLevel,mceAddUndoLevel': function() { + editor.undoManager.add(); + }, + + 'Cut,Copy,Paste': function(command) { + var doc = editor.getDoc(), failed; + + // Try executing the native command + try { + execNativeCommand(command); + } catch (ex) { + // Command failed + failed = TRUE; + } + + // Chrome reports the paste command as supported however older IE:s will return false for cut/paste + if (command === 'paste' && !doc.queryCommandEnabled(command)) { + failed = true; + } + + // Present alert message about clipboard access not being available + if (failed || !doc.queryCommandSupported(command)) { + var msg = editor.translate( + "Your browser doesn't support direct access to the clipboard. " + + "Please use the Ctrl+X/C/V keyboard shortcuts instead." + ); + + if (Env.mac) { + msg = msg.replace(/Ctrl\+/g, '\u2318+'); + } + + editor.notificationManager.open({text: msg, type: 'error'}); + } + }, + + // Override unlink command + unlink: function() { + if (selection.isCollapsed()) { + var elm = editor.dom.getParent(editor.selection.getStart(), 'a'); + if (elm) { + editor.dom.remove(elm, true); + } + + return; + } + + formatter.remove("link"); + }, + + // Override justify commands to use the text formatter engine + 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull,JustifyNone': function(command) { + var align = command.substring(7); + + if (align == 'full') { + align = 'justify'; + } + + // Remove all other alignments first + each('left,center,right,justify'.split(','), function(name) { + if (align != name) { + formatter.remove('align' + name); + } + }); + + if (align != 'none') { + toggleFormat('align' + align); + } + }, + + // Override list commands to fix WebKit bug + 'InsertUnorderedList,InsertOrderedList': function(command) { + var listElm, listParent; + + execNativeCommand(command); + + // WebKit produces lists within block elements so we need to split them + // we will replace the native list creation logic to custom logic later on + // TODO: Remove this when the list creation logic is removed + listElm = dom.getParent(selection.getNode(), 'ol,ul'); + if (listElm) { + listParent = listElm.parentNode; + + // If list is within a text block then split that block + if (/^(H[1-6]|P|ADDRESS|PRE)$/.test(listParent.nodeName)) { + storeSelection(); + dom.split(listParent, listElm); + restoreSelection(); + } + } + }, + + // Override commands to use the text formatter engine + 'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) { + toggleFormat(command); + }, + + // Override commands to use the text formatter engine + 'ForeColor,HiliteColor,FontName': function(command, ui, value) { + toggleFormat(command, value); + }, + + FontSize: function(command, ui, value) { + var fontClasses, fontSizes; + + // Convert font size 1-7 to styles + if (value >= 1 && value <= 7) { + fontSizes = explode(settings.font_size_style_values); + fontClasses = explode(settings.font_size_classes); + + if (fontClasses) { + value = fontClasses[value - 1] || value; + } else { + value = fontSizes[value - 1] || value; + } + } + + toggleFormat(command, value); + }, + + RemoveFormat: function(command) { + formatter.remove(command); + }, + + mceBlockQuote: function() { + toggleFormat('blockquote'); + }, + + FormatBlock: function(command, ui, value) { + return toggleFormat(value || 'p'); + }, + + mceCleanup: function() { + var bookmark = selection.getBookmark(); + + editor.setContent(editor.getContent({cleanup: TRUE}), {cleanup: TRUE}); + + selection.moveToBookmark(bookmark); + }, + + mceRemoveNode: function(command, ui, value) { + var node = value || selection.getNode(); + + // Make sure that the body node isn't removed + if (node != editor.getBody()) { + storeSelection(); + editor.dom.remove(node, TRUE); + restoreSelection(); + } + }, + + mceSelectNodeDepth: function(command, ui, value) { + var counter = 0; + + dom.getParent(selection.getNode(), function(node) { + if (node.nodeType == 1 && counter++ == value) { + selection.select(node); + return FALSE; + } + }, editor.getBody()); + }, + + mceSelectNode: function(command, ui, value) { + selection.select(value); + }, + + mceInsertContent: function(command, ui, value) { + InsertContent.insertAtCaret(editor, value); + }, + + mceInsertRawHTML: function(command, ui, value) { + selection.setContent('tiny_mce_marker'); + editor.setContent( + editor.getContent().replace(/tiny_mce_marker/g, function() { + return value; + }) + ); + }, + + mceToggleFormat: function(command, ui, value) { + toggleFormat(value); + }, + + mceSetContent: function(command, ui, value) { + editor.setContent(value); + }, + + 'Indent,Outdent': function(command) { + var intentValue, indentUnit, value; + + // Setup indent level + intentValue = settings.indentation; + indentUnit = /[a-z%]+$/i.exec(intentValue); + intentValue = parseInt(intentValue, 10); + + if (!queryCommandState('InsertUnorderedList') && !queryCommandState('InsertOrderedList')) { + // If forced_root_blocks is set to false we don't have a block to indent so lets create a div + if (!settings.forced_root_block && !dom.getParent(selection.getNode(), dom.isBlock)) { + formatter.apply('div'); + } + + each(selection.getSelectedBlocks(), function(element) { + if (dom.getContentEditable(element) === "false") { + return; + } + + if (element.nodeName !== "LI") { + var indentStyleName = editor.getParam('indent_use_margin', false) ? 'margin' : 'padding'; + indentStyleName = element.nodeName === 'TABLE' ? 'margin' : indentStyleName; + indentStyleName += dom.getStyle(element, 'direction', true) == 'rtl' ? 'Right' : 'Left'; + + if (command == 'outdent') { + value = Math.max(0, parseInt(element.style[indentStyleName] || 0, 10) - intentValue); + dom.setStyle(element, indentStyleName, value ? value + indentUnit : ''); + } else { + value = (parseInt(element.style[indentStyleName] || 0, 10) + intentValue) + indentUnit; + dom.setStyle(element, indentStyleName, value); + } + } + }); + } else { + execNativeCommand(command); + } + }, + + mceRepaint: function() { + }, + + InsertHorizontalRule: function() { + editor.execCommand('mceInsertContent', false, '
'); + }, + + mceToggleVisualAid: function() { + editor.hasVisual = !editor.hasVisual; + editor.addVisual(); + }, + + mceReplaceContent: function(command, ui, value) { + editor.execCommand('mceInsertContent', false, value.replace(/\{\$selection\}/g, selection.getContent({format: 'text'}))); + }, + + mceInsertLink: function(command, ui, value) { + var anchor; + + if (typeof value == 'string') { + value = {href: value}; + } + + anchor = dom.getParent(selection.getNode(), 'a'); + + // Spaces are never valid in URLs and it's a very common mistake for people to make so we fix it here. + value.href = value.href.replace(' ', '%20'); + + // Remove existing links if there could be child links or that the href isn't specified + if (!anchor || !value.href) { + formatter.remove('link'); + } + + // Apply new link to selection + if (value.href) { + formatter.apply('link', value, anchor); + } + }, + + selectAll: function() { + var root = dom.getRoot(), rng; + + if (selection.getRng().setStart) { + rng = dom.createRng(); + rng.setStart(root, 0); + rng.setEnd(root, root.childNodes.length); + selection.setRng(rng); + } else { + // IE will render it's own root level block elements and sometimes + // even put font elements in them when the user starts typing. So we need to + // move the selection to a more suitable element from this: + // |

to this:

|

+ rng = selection.getRng(); + if (!rng.item) { + rng.moveToElementText(root); + rng.select(); + } + } + }, + + "delete": function() { + execNativeCommand("Delete"); + + // Check if body is empty after the delete call if so then set the contents + // to an empty string and move the caret to any block produced by that operation + // this fixes the issue with root blocks not being properly produced after a delete call on IE + var body = editor.getBody(); + + if (dom.isEmpty(body)) { + editor.setContent(''); + + if (body.firstChild && dom.isBlock(body.firstChild)) { + editor.selection.setCursorLocation(body.firstChild, 0); + } else { + editor.selection.setCursorLocation(body, 0); + } + } + }, + + mceNewDocument: function() { + editor.setContent(''); + }, + + InsertLineBreak: function(command, ui, value) { + // We load the current event in from EnterKey.js when appropriate to heed + // certain event-specific variations such as ctrl-enter in a list + var evt = value; + var brElm, extraBr, marker; + var rng = selection.getRng(true); + new RangeUtils(dom).normalize(rng); + + var offset = rng.startOffset; + var container = rng.startContainer; + + // Resolve node index + if (container.nodeType == 1 && container.hasChildNodes()) { + var isAfterLastNodeInContainer = offset > container.childNodes.length - 1; + + container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container; + if (isAfterLastNodeInContainer && container.nodeType == 3) { + offset = container.nodeValue.length; + } else { + offset = 0; + } + } + + var parentBlock = dom.getParent(container, dom.isBlock); + var parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 + var containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null; + var containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5 + + // Enter inside block contained within a LI then split or insert before/after LI + var isControlKey = evt && evt.ctrlKey; + if (containerBlockName == 'LI' && !isControlKey) { + parentBlock = containerBlock; + parentBlockName = containerBlockName; + } + + // Walks the parent block to the right and look for BR elements + function hasRightSideContent() { + var walker = new TreeWalker(container, parentBlock), node; + var nonEmptyElementsMap = editor.schema.getNonEmptyElements(); + + while ((node = walker.next())) { + if (nonEmptyElementsMap[node.nodeName.toLowerCase()] || node.length > 0) { + return true; + } + } + } + + if (container && container.nodeType == 3 && offset >= container.nodeValue.length) { + // Insert extra BR element at the end block elements + if (!isOldIE && !hasRightSideContent()) { + brElm = dom.create('br'); + rng.insertNode(brElm); + rng.setStartAfter(brElm); + rng.setEndAfter(brElm); + extraBr = true; + } + } + + brElm = dom.create('br'); + rng.insertNode(brElm); + + // Rendering modes below IE8 doesn't display BR elements in PRE unless we have a \n before it + var documentMode = dom.doc.documentMode; + if (isOldIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) { + brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm); + } + + // Insert temp marker and scroll to that + marker = dom.create('span', {}, ' '); + brElm.parentNode.insertBefore(marker, brElm); + selection.scrollIntoView(marker); + dom.remove(marker); + + if (!extraBr) { + rng.setStartAfter(brElm); + rng.setEndAfter(brElm); + } else { + rng.setStartBefore(brElm); + rng.setEndBefore(brElm); + } + + selection.setRng(rng); + editor.undoManager.add(); + + return TRUE; + } + }); + + // Add queryCommandState overrides + addCommands({ + // Override justify commands + 'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull': function(command) { + var name = 'align' + command.substring(7); + var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks(); + var matches = map(nodes, function(node) { + return !!formatter.matchNode(node, name); + }); + return inArray(matches, TRUE) !== -1; + }, + + 'Bold,Italic,Underline,Strikethrough,Superscript,Subscript': function(command) { + return isFormatMatch(command); + }, + + mceBlockQuote: function() { + return isFormatMatch('blockquote'); + }, + + Outdent: function() { + var node; + + if (settings.inline_styles) { + if ((node = dom.getParent(selection.getStart(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) { + return TRUE; + } + + if ((node = dom.getParent(selection.getEnd(), dom.isBlock)) && parseInt(node.style.paddingLeft, 10) > 0) { + return TRUE; + } + } + + return ( + queryCommandState('InsertUnorderedList') || + queryCommandState('InsertOrderedList') || + (!settings.inline_styles && !!dom.getParent(selection.getNode(), 'BLOCKQUOTE')) + ); + }, + + 'InsertUnorderedList,InsertOrderedList': function(command) { + var list = dom.getParent(selection.getNode(), 'ul,ol'); + + return list && + ( + command === 'insertunorderedlist' && list.tagName === 'UL' || + command === 'insertorderedlist' && list.tagName === 'OL' + ); + } + }, 'state'); + + // Add queryCommandValue overrides + addCommands({ + 'FontSize,FontName': function(command) { + var value = 0, parent; + + if ((parent = dom.getParent(selection.getNode(), 'span'))) { + if (command == 'fontsize') { + value = parent.style.fontSize; + } else { + value = parent.style.fontFamily.replace(/, /g, ',').replace(/[\'\"]/g, '').toLowerCase(); + } + } + + return value; + } + }, 'value'); + + // Add undo manager logic + addCommands({ + Undo: function() { + editor.undoManager.undo(); + }, + + Redo: function() { + editor.undoManager.redo(); + } + }); + }; +}); + +// Included from: js/tinymce/classes/util/URI.js + +/** + * URI.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class handles parsing, modification and serialization of URI/URL strings. + * @class tinymce.util.URI + */ +define("tinymce/util/URI", [ + "tinymce/util/Tools" +], function(Tools) { + var each = Tools.each, trim = Tools.trim; + var queryParts = "source protocol authority userInfo user password host port relative path directory file query anchor".split(' '); + var DEFAULT_PORTS = { + 'ftp': 21, + 'http': 80, + 'https': 443, + 'mailto': 25 + }; + + /** + * Constructs a new URI instance. + * + * @constructor + * @method URI + * @param {String} url URI string to parse. + * @param {Object} settings Optional settings object. + */ + function URI(url, settings) { + var self = this, baseUri, base_url; + + url = trim(url); + settings = self.settings = settings || {}; + baseUri = settings.base_uri; + + // Strange app protocol that isn't http/https or local anchor + // For example: mailto,skype,tel etc. + if (/^([\w\-]+):([^\/]{2})/i.test(url) || /^\s*#/.test(url)) { + self.source = url; + return; + } + + var isProtocolRelative = url.indexOf('//') === 0; + + // Absolute path with no host, fake host and protocol + if (url.indexOf('/') === 0 && !isProtocolRelative) { + url = (baseUri ? baseUri.protocol || 'http' : 'http') + '://mce_host' + url; + } + + // Relative path http:// or protocol relative //path + if (!/^[\w\-]*:?\/\//.test(url)) { + base_url = settings.base_uri ? settings.base_uri.path : new URI(location.href).directory; + if (settings.base_uri.protocol === "") { + url = '//mce_host' + self.toAbsPath(base_url, url); + } else { + url = /([^#?]*)([#?]?.*)/.exec(url); + url = ((baseUri && baseUri.protocol) || 'http') + '://mce_host' + self.toAbsPath(base_url, url[1]) + url[2]; + } + } + + // Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri) + url = url.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something + + /*jshint maxlen: 255 */ + /*eslint max-len: 0 */ + url = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(url); + + each(queryParts, function(v, i) { + var part = url[i]; + + // Zope 3 workaround, they use @@something + if (part) { + part = part.replace(/\(mce_at\)/g, '@@'); + } + + self[v] = part; + }); + + if (baseUri) { + if (!self.protocol) { + self.protocol = baseUri.protocol; + } + + if (!self.userInfo) { + self.userInfo = baseUri.userInfo; + } + + if (!self.port && self.host === 'mce_host') { + self.port = baseUri.port; + } + + if (!self.host || self.host === 'mce_host') { + self.host = baseUri.host; + } + + self.source = ''; + } + + if (isProtocolRelative) { + self.protocol = ''; + } + + //t.path = t.path || '/'; + } + + URI.prototype = { + /** + * Sets the internal path part of the URI. + * + * @method setPath + * @param {string} path Path string to set. + */ + setPath: function(path) { + var self = this; + + path = /^(.*?)\/?(\w+)?$/.exec(path); + + // Update path parts + self.path = path[0]; + self.directory = path[1]; + self.file = path[2]; + + // Rebuild source + self.source = ''; + self.getURI(); + }, + + /** + * Converts the specified URI into a relative URI based on the current URI instance location. + * + * @method toRelative + * @param {String} uri URI to convert into a relative path/URI. + * @return {String} Relative URI from the point specified in the current URI instance. + * @example + * // Converts an absolute URL to an relative URL url will be somedir/somefile.htm + * var url = new tinymce.util.URI('http://www.site.com/dir/').toRelative('http://www.site.com/dir/somedir/somefile.htm'); + */ + toRelative: function(uri) { + var self = this, output; + + if (uri === "./") { + return uri; + } + + uri = new URI(uri, {base_uri: self}); + + // Not on same domain/port or protocol + if ((uri.host != 'mce_host' && self.host != uri.host && uri.host) || self.port != uri.port || + (self.protocol != uri.protocol && uri.protocol !== "")) { + return uri.getURI(); + } + + var tu = self.getURI(), uu = uri.getURI(); + + // Allow usage of the base_uri when relative_urls = true + if (tu == uu || (tu.charAt(tu.length - 1) == "/" && tu.substr(0, tu.length - 1) == uu)) { + return tu; + } + + output = self.toRelPath(self.path, uri.path); + + // Add query + if (uri.query) { + output += '?' + uri.query; + } + + // Add anchor + if (uri.anchor) { + output += '#' + uri.anchor; + } + + return output; + }, + + /** + * Converts the specified URI into a absolute URI based on the current URI instance location. + * + * @method toAbsolute + * @param {String} uri URI to convert into a relative path/URI. + * @param {Boolean} noHost No host and protocol prefix. + * @return {String} Absolute URI from the point specified in the current URI instance. + * @example + * // Converts an relative URL to an absolute URL url will be http://www.site.com/dir/somedir/somefile.htm + * var url = new tinymce.util.URI('http://www.site.com/dir/').toAbsolute('somedir/somefile.htm'); + */ + toAbsolute: function(uri, noHost) { + uri = new URI(uri, {base_uri: this}); + + return uri.getURI(noHost && this.isSameOrigin(uri)); + }, + + /** + * Determine whether the given URI has the same origin as this URI. Based on RFC-6454. + * Supports default ports for protocols listed in DEFAULT_PORTS. Unsupported protocols will fail safe: they + * won't match, if the port specifications differ. + * + * @method isSameOrigin + * @param {tinymce.util.URI} uri Uri instance to compare. + * @returns {Boolean} True if the origins are the same. + */ + isSameOrigin: function(uri) { + if (this.host == uri.host && this.protocol == uri.protocol) { + if (this.port == uri.port) { + return true; + } + + var defaultPort = DEFAULT_PORTS[this.protocol]; + if (defaultPort && ((this.port || defaultPort) == (uri.port || defaultPort))) { + return true; + } + } + + return false; + }, + + /** + * Converts a absolute path into a relative path. + * + * @method toRelPath + * @param {String} base Base point to convert the path from. + * @param {String} path Absolute path to convert into a relative path. + */ + toRelPath: function(base, path) { + var items, breakPoint = 0, out = '', i, l; + + // Split the paths + base = base.substring(0, base.lastIndexOf('/')); + base = base.split('/'); + items = path.split('/'); + + if (base.length >= items.length) { + for (i = 0, l = base.length; i < l; i++) { + if (i >= items.length || base[i] != items[i]) { + breakPoint = i + 1; + break; + } + } + } + + if (base.length < items.length) { + for (i = 0, l = items.length; i < l; i++) { + if (i >= base.length || base[i] != items[i]) { + breakPoint = i + 1; + break; + } + } + } + + if (breakPoint === 1) { + return path; + } + + for (i = 0, l = base.length - (breakPoint - 1); i < l; i++) { + out += "../"; + } + + for (i = breakPoint - 1, l = items.length; i < l; i++) { + if (i != breakPoint - 1) { + out += "/" + items[i]; + } else { + out += items[i]; + } + } + + return out; + }, + + /** + * Converts a relative path into a absolute path. + * + * @method toAbsPath + * @param {String} base Base point to convert the path from. + * @param {String} path Relative path to convert into an absolute path. + */ + toAbsPath: function(base, path) { + var i, nb = 0, o = [], tr, outPath; + + // Split paths + tr = /\/$/.test(path) ? '/' : ''; + base = base.split('/'); + path = path.split('/'); + + // Remove empty chunks + each(base, function(k) { + if (k) { + o.push(k); + } + }); + + base = o; + + // Merge relURLParts chunks + for (i = path.length - 1, o = []; i >= 0; i--) { + // Ignore empty or . + if (path[i].length === 0 || path[i] === ".") { + continue; + } + + // Is parent + if (path[i] === '..') { + nb++; + continue; + } + + // Move up + if (nb > 0) { + nb--; + continue; + } + + o.push(path[i]); + } + + i = base.length - nb; + + // If /a/b/c or / + if (i <= 0) { + outPath = o.reverse().join('/'); + } else { + outPath = base.slice(0, i).join('/') + '/' + o.reverse().join('/'); + } + + // Add front / if it's needed + if (outPath.indexOf('/') !== 0) { + outPath = '/' + outPath; + } + + // Add traling / if it's needed + if (tr && outPath.lastIndexOf('/') !== outPath.length - 1) { + outPath += tr; + } + + return outPath; + }, + + /** + * Returns the full URI of the internal structure. + * + * @method getURI + * @param {Boolean} noProtoHost Optional no host and protocol part. Defaults to false. + */ + getURI: function(noProtoHost) { + var s, self = this; + + // Rebuild source + if (!self.source || noProtoHost) { + s = ''; + + if (!noProtoHost) { + if (self.protocol) { + s += self.protocol + '://'; + } else { + s += '//'; + } + + if (self.userInfo) { + s += self.userInfo + '@'; + } + + if (self.host) { + s += self.host; + } + + if (self.port) { + s += ':' + self.port; + } + } + + if (self.path) { + s += self.path; + } + + if (self.query) { + s += '?' + self.query; + } + + if (self.anchor) { + s += '#' + self.anchor; + } + + self.source = s; + } + + return self.source; + } + }; + + URI.parseDataUri = function(uri) { + var type, matches; + + uri = decodeURIComponent(uri).split(','); + + matches = /data:([^;]+)/.exec(uri[0]); + if (matches) { + type = matches[1]; + } + + return { + type: type, + data: uri[1] + }; + }; + + URI.getDocumentBaseUrl = function(loc) { + var baseUrl; + + // Pass applewebdata:// and other non web protocols though + if (loc.protocol.indexOf('http') !== 0 && loc.protocol !== 'file:') { + baseUrl = loc.href; + } else { + baseUrl = loc.protocol + '//' + loc.host + loc.pathname; + } + + if (/^[^:]+:\/\/\/?[^\/]+\//.test(baseUrl)) { + baseUrl = baseUrl.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); + + if (!/[\/\\]$/.test(baseUrl)) { + baseUrl += '/'; + } + } + + return baseUrl; + }; + + return URI; +}); + +// Included from: js/tinymce/classes/util/Class.js + +/** + * Class.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This utilitiy class is used for easier inheritance. + * + * Features: + * * Exposed super functions: this._super(); + * * Mixins + * * Dummy functions + * * Property functions: var value = object.value(); and object.value(newValue); + * * Static functions + * * Defaults settings + */ +define("tinymce/util/Class", [ + "tinymce/util/Tools" +], function(Tools) { + var each = Tools.each, extend = Tools.extend; + + var extendClass, initializing; + + function Class() { + } + + // Provides classical inheritance, based on code made by John Resig + Class.extend = extendClass = function(prop) { + var self = this, _super = self.prototype, prototype, name, member; + + // The dummy class constructor + function Class() { + var i, mixins, mixin, self = this; + + // All construction is actually done in the init method + if (!initializing) { + // Run class constuctor + if (self.init) { + self.init.apply(self, arguments); + } + + // Run mixin constructors + mixins = self.Mixins; + if (mixins) { + i = mixins.length; + while (i--) { + mixin = mixins[i]; + if (mixin.init) { + mixin.init.apply(self, arguments); + } + } + } + } + } + + // Dummy function, needs to be extended in order to provide functionality + function dummy() { + return this; + } + + // Creates a overloaded method for the class + // this enables you to use this._super(); to call the super function + function createMethod(name, fn) { + return function() { + var self = this, tmp = self._super, ret; + + self._super = _super[name]; + ret = fn.apply(self, arguments); + self._super = tmp; + + return ret; + }; + } + + // Instantiate a base class (but only create the instance, + // don't run the init constructor) + initializing = true; + + /*eslint new-cap:0 */ + prototype = new self(); + initializing = false; + + // Add mixins + if (prop.Mixins) { + each(prop.Mixins, function(mixin) { + for (var name in mixin) { + if (name !== "init") { + prop[name] = mixin[name]; + } + } + }); + + if (_super.Mixins) { + prop.Mixins = _super.Mixins.concat(prop.Mixins); + } + } + + // Generate dummy methods + if (prop.Methods) { + each(prop.Methods.split(','), function(name) { + prop[name] = dummy; + }); + } + + // Generate property methods + if (prop.Properties) { + each(prop.Properties.split(','), function(name) { + var fieldName = '_' + name; + + prop[name] = function(value) { + var self = this, undef; + + // Set value + if (value !== undef) { + self[fieldName] = value; + + return self; + } + + // Get value + return self[fieldName]; + }; + }); + } + + // Static functions + if (prop.Statics) { + each(prop.Statics, function(func, name) { + Class[name] = func; + }); + } + + // Default settings + if (prop.Defaults && _super.Defaults) { + prop.Defaults = extend({}, _super.Defaults, prop.Defaults); + } + + // Copy the properties over onto the new prototype + for (name in prop) { + member = prop[name]; + + if (typeof member == "function" && _super[name]) { + prototype[name] = createMethod(name, member); + } else { + prototype[name] = member; + } + } + + // Populate our constructed prototype object + Class.prototype = prototype; + + // Enforce the constructor to be what we expect + Class.constructor = Class; + + // And make this class extendible + Class.extend = extendClass; + + return Class; + }; + + return Class; +}); + +// Included from: js/tinymce/classes/util/EventDispatcher.js + +/** + * EventDispatcher.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class lets you add/remove and fire events by name on the specified scope. This makes + * it easy to add event listener logic to any class. + * + * @class tinymce.util.EventDispatcher + * @example + * var eventDispatcher = new EventDispatcher(); + * + * eventDispatcher.on('click', function() {console.log('data');}); + * eventDispatcher.fire('click', {data: 123}); + */ +define("tinymce/util/EventDispatcher", [ + "tinymce/util/Tools" +], function(Tools) { + var nativeEvents = Tools.makeMap( + "focus blur focusin focusout click dblclick mousedown mouseup mousemove mouseover beforepaste paste cut copy selectionchange " + + "mouseout mouseenter mouseleave wheel keydown keypress keyup input contextmenu dragstart dragend dragover " + + "draggesture dragdrop drop drag submit " + + "compositionstart compositionend compositionupdate touchstart touchmove touchend", + ' ' + ); + + function Dispatcher(settings) { + var self = this, scope, bindings = {}, toggleEvent; + + function returnFalse() { + return false; + } + + function returnTrue() { + return true; + } + + settings = settings || {}; + scope = settings.scope || self; + toggleEvent = settings.toggleEvent || returnFalse; + + /** + * Fires the specified event by name. + * + * @method fire + * @param {String} name Name of the event to fire. + * @param {Object?} args Event arguments. + * @return {Object} Event args instance passed in. + * @example + * instance.fire('event', {...}); + */ + function fire(name, args) { + var handlers, i, l, callback; + + name = name.toLowerCase(); + args = args || {}; + args.type = name; + + // Setup target is there isn't one + if (!args.target) { + args.target = scope; + } + + // Add event delegation methods if they are missing + if (!args.preventDefault) { + // Add preventDefault method + args.preventDefault = function() { + args.isDefaultPrevented = returnTrue; + }; + + // Add stopPropagation + args.stopPropagation = function() { + args.isPropagationStopped = returnTrue; + }; + + // Add stopImmediatePropagation + args.stopImmediatePropagation = function() { + args.isImmediatePropagationStopped = returnTrue; + }; + + // Add event delegation states + args.isDefaultPrevented = returnFalse; + args.isPropagationStopped = returnFalse; + args.isImmediatePropagationStopped = returnFalse; + } + + if (settings.beforeFire) { + settings.beforeFire(args); + } + + handlers = bindings[name]; + if (handlers) { + for (i = 0, l = handlers.length; i < l; i++) { + callback = handlers[i]; + + // Unbind handlers marked with "once" + if (callback.once) { + off(name, callback.func); + } + + // Stop immediate propagation if needed + if (args.isImmediatePropagationStopped()) { + args.stopPropagation(); + return args; + } + + // If callback returns false then prevent default and stop all propagation + if (callback.func.call(scope, args) === false) { + args.preventDefault(); + return args; + } + } + } + + return args; + } + + /** + * Binds an event listener to a specific event by name. + * + * @method on + * @param {String} name Event name or space separated list of events to bind. + * @param {callback} callback Callback to be executed when the event occurs. + * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. + * @return {Object} Current class instance. + * @example + * instance.on('event', function(e) { + * // Callback logic + * }); + */ + function on(name, callback, prepend, extra) { + var handlers, names, i; + + if (callback === false) { + callback = returnFalse; + } + + if (callback) { + callback = { + func: callback + }; + + if (extra) { + Tools.extend(callback, extra); + } + + names = name.toLowerCase().split(' '); + i = names.length; + while (i--) { + name = names[i]; + handlers = bindings[name]; + if (!handlers) { + handlers = bindings[name] = []; + toggleEvent(name, true); + } + + if (prepend) { + handlers.unshift(callback); + } else { + handlers.push(callback); + } + } + } + + return self; + } + + /** + * Unbinds an event listener to a specific event by name. + * + * @method off + * @param {String?} name Name of the event to unbind. + * @param {callback?} callback Callback to unbind. + * @return {Object} Current class instance. + * @example + * // Unbind specific callback + * instance.off('event', handler); + * + * // Unbind all listeners by name + * instance.off('event'); + * + * // Unbind all events + * instance.off(); + */ + function off(name, callback) { + var i, handlers, bindingName, names, hi; + + if (name) { + names = name.toLowerCase().split(' '); + i = names.length; + while (i--) { + name = names[i]; + handlers = bindings[name]; + + // Unbind all handlers + if (!name) { + for (bindingName in bindings) { + toggleEvent(bindingName, false); + delete bindings[bindingName]; + } + + return self; + } + + if (handlers) { + // Unbind all by name + if (!callback) { + handlers.length = 0; + } else { + // Unbind specific ones + hi = handlers.length; + while (hi--) { + if (handlers[hi].func === callback) { + handlers = handlers.slice(0, hi).concat(handlers.slice(hi + 1)); + bindings[name] = handlers; + } + } + } + + if (!handlers.length) { + toggleEvent(name, false); + delete bindings[name]; + } + } + } + } else { + for (name in bindings) { + toggleEvent(name, false); + } + + bindings = {}; + } + + return self; + } + + /** + * Binds an event listener to a specific event by name + * and automatically unbind the event once the callback fires. + * + * @method once + * @param {String} name Event name or space separated list of events to bind. + * @param {callback} callback Callback to be executed when the event occurs. + * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. + * @return {Object} Current class instance. + * @example + * instance.once('event', function(e) { + * // Callback logic + * }); + */ + function once(name, callback, prepend) { + return on(name, callback, prepend, {once: true}); + } + + /** + * Returns true/false if the dispatcher has a event of the specified name. + * + * @method has + * @param {String} name Name of the event to check for. + * @return {Boolean} true/false if the event exists or not. + */ + function has(name) { + name = name.toLowerCase(); + return !(!bindings[name] || bindings[name].length === 0); + } + + // Expose + self.fire = fire; + self.on = on; + self.off = off; + self.once = once; + self.has = has; + } + + /** + * Returns true/false if the specified event name is a native browser event or not. + * + * @method isNative + * @param {String} name Name to check if it's native. + * @return {Boolean} true/false if the event is native or not. + * @static + */ + Dispatcher.isNative = function(name) { + return !!nativeEvents[name.toLowerCase()]; + }; + + return Dispatcher; +}); + +// Included from: js/tinymce/classes/data/Binding.js + +/** + * Binding.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class gets dynamically extended to provide a binding between two models. This makes it possible to + * sync the state of two properties in two models by a layer of abstraction. + * + * @private + * @class tinymce.data.Binding + */ +define("tinymce/data/Binding", [], function() { + /** + * Constructs a new bidning. + * + * @constructor + * @method Binding + * @param {Object} settings Settings to the binding. + */ + function Binding(settings) { + this.create = settings.create; + } + + /** + * Creates a binding for a property on a model. + * + * @method create + * @param {tinymce.data.ObservableObject} model Model to create binding to. + * @param {String} name Name of property to bind. + * @return {tinymce.data.Binding} Binding instance. + */ + Binding.create = function(model, name) { + return new Binding({ + create: function(otherModel, otherName) { + var bindings; + + function fromSelfToOther(e) { + otherModel.set(otherName, e.value); + } + + function fromOtherToSelf(e) { + model.set(name, e.value); + } + + otherModel.on('change:' + otherName, fromOtherToSelf); + model.on('change:' + name, fromSelfToOther); + + // Keep track of the bindings + bindings = otherModel._bindings; + + if (!bindings) { + bindings = otherModel._bindings = []; + + otherModel.on('destroy', function() { + var i = bindings.length; + + while (i--) { + bindings[i](); + } + }); + } + + bindings.push(function() { + model.off('change:' + name, fromSelfToOther); + }); + + return model.get(name); + } + }); + }; + + return Binding; +}); + +// Included from: js/tinymce/classes/util/Observable.js + +/** + * Observable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This mixin will add event binding logic to classes. + * + * @mixin tinymce.util.Observable + */ +define("tinymce/util/Observable", [ + "tinymce/util/EventDispatcher" +], function(EventDispatcher) { + function getEventDispatcher(obj) { + if (!obj._eventDispatcher) { + obj._eventDispatcher = new EventDispatcher({ + scope: obj, + toggleEvent: function(name, state) { + if (EventDispatcher.isNative(name) && obj.toggleNativeEvent) { + obj.toggleNativeEvent(name, state); + } + } + }); + } + + return obj._eventDispatcher; + } + + return { + /** + * Fires the specified event by name. Consult the + * event reference for more details on each event. + * + * @method fire + * @param {String} name Name of the event to fire. + * @param {Object?} args Event arguments. + * @param {Boolean?} bubble True/false if the event is to be bubbled. + * @return {Object} Event args instance passed in. + * @example + * instance.fire('event', {...}); + */ + fire: function(name, args, bubble) { + var self = this; + + // Prevent all events except the remove event after the instance has been removed + if (self.removed && name !== "remove") { + return args; + } + + args = getEventDispatcher(self).fire(name, args, bubble); + + // Bubble event up to parents + if (bubble !== false && self.parent) { + var parent = self.parent(); + while (parent && !args.isPropagationStopped()) { + parent.fire(name, args, false); + parent = parent.parent(); + } + } + + return args; + }, + + /** + * Binds an event listener to a specific event by name. Consult the + * event reference for more details on each event. + * + * @method on + * @param {String} name Event name or space separated list of events to bind. + * @param {callback} callback Callback to be executed when the event occurs. + * @param {Boolean} first Optional flag if the event should be prepended. Use this with care. + * @return {Object} Current class instance. + * @example + * instance.on('event', function(e) { + * // Callback logic + * }); + */ + on: function(name, callback, prepend) { + return getEventDispatcher(this).on(name, callback, prepend); + }, + + /** + * Unbinds an event listener to a specific event by name. Consult the + * event reference for more details on each event. + * + * @method off + * @param {String?} name Name of the event to unbind. + * @param {callback?} callback Callback to unbind. + * @return {Object} Current class instance. + * @example + * // Unbind specific callback + * instance.off('event', handler); + * + * // Unbind all listeners by name + * instance.off('event'); + * + * // Unbind all events + * instance.off(); + */ + off: function(name, callback) { + return getEventDispatcher(this).off(name, callback); + }, + + /** + * Bind the event callback and once it fires the callback is removed. Consult the + * event reference for more details on each event. + * + * @method once + * @param {String} name Name of the event to bind. + * @param {callback} callback Callback to bind only once. + * @return {Object} Current class instance. + */ + once: function(name, callback) { + return getEventDispatcher(this).once(name, callback); + }, + + /** + * Returns true/false if the object has a event of the specified name. + * + * @method hasEventListeners + * @param {String} name Name of the event to check for. + * @return {Boolean} true/false if the event exists or not. + */ + hasEventListeners: function(name) { + return getEventDispatcher(this).has(name); + } + }; +}); + +// Included from: js/tinymce/classes/data/ObservableObject.js + +/** + * ObservableObject.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class is a object that is observable when properties changes a change event gets emitted. + * + * @private + * @class tinymce.data.ObservableObject + */ +define("tinymce/data/ObservableObject", [ + "tinymce/data/Binding", + "tinymce/util/Observable", + "tinymce/util/Class", + "tinymce/util/Tools" +], function(Binding, Observable, Class, Tools) { + function isNode(node) { + return node.nodeType > 0; + } + + // Todo: Maybe this should be shallow compare since it might be huge object references + function isEqual(a, b) { + var k, checked; + + // Strict equals + if (a === b) { + return true; + } + + // Compare null + if (a === null || b === null) { + return a === b; + } + + // Compare number, boolean, string, undefined + if (typeof a !== "object" || typeof b !== "object") { + return a === b; + } + + // Compare arrays + if (Tools.isArray(b)) { + if (a.length !== b.length) { + return false; + } + + k = a.length; + while (k--) { + if (!isEqual(a[k], b[k])) { + return false; + } + } + } + + // Shallow compare nodes + if (isNode(a) || isNode(b)) { + return a === b; + } + + // Compare objects + checked = {}; + for (k in b) { + if (!isEqual(a[k], b[k])) { + return false; + } + + checked[k] = true; + } + + for (k in a) { + if (!checked[k] && !isEqual(a[k], b[k])) { + return false; + } + } + + return true; + } + + return Class.extend({ + Mixins: [Observable], + + /** + * Constructs a new observable object instance. + * + * @constructor + * @param {Object} data Initial data for the object. + */ + init: function(data) { + var name, value; + + data = data || {}; + + for (name in data) { + value = data[name]; + + if (value instanceof Binding) { + data[name] = value.create(this, name); + } + } + + this.data = data; + }, + + /** + * Sets a property on the value this will call + * observers if the value is a change from the current value. + * + * @method set + * @param {String/object} name Name of the property to set or a object of items to set. + * @param {Object} value Value to set for the property. + * @return {tinymce.data.ObservableObject} Observable object instance. + */ + set: function(name, value) { + var key, args, oldValue = this.data[name]; + + if (value instanceof Binding) { + value = value.create(this, name); + } + + if (typeof name === "object") { + for (key in name) { + this.set(key, name[key]); + } + + return this; + } + + if (!isEqual(oldValue, value)) { + this.data[name] = value; + + args = { + target: this, + name: name, + value: value, + oldValue: oldValue + }; + + this.fire('change:' + name, args); + this.fire('change', args); + } + + return this; + }, + + /** + * Gets a property by name. + * + * @method get + * @param {String} name Name of the property to get. + * @return {Object} Object value of propery. + */ + get: function(name) { + return this.data[name]; + }, + + /** + * Returns true/false if the specified property exists. + * + * @method has + * @param {String} name Name of the property to check for. + * @return {Boolean} true/false if the item exists. + */ + has: function(name) { + return name in this.data; + }, + + /** + * Returns a dynamic property binding for the specified property name. This makes + * it possible to sync the state of two properties in two ObservableObject instances. + * + * @method bind + * @param {String} name Name of the property to sync with the property it's inserted to. + * @return {tinymce.data.Binding} Data binding instance. + */ + bind: function(name) { + return Binding.create(this, name); + }, + + /** + * Destroys the observable object and fires the "destroy" + * event and clean up any internal resources. + * + * @method destroy + */ + destroy: function() { + this.fire('destroy'); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Selector.js + +/** + * Selector.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*eslint no-nested-ternary:0 */ + +/** + * Selector engine, enables you to select controls by using CSS like expressions. + * We currently only support basic CSS expressions to reduce the size of the core + * and the ones we support should be enough for most cases. + * + * @example + * Supported expressions: + * element + * element#name + * element.class + * element[attr] + * element[attr*=value] + * element[attr~=value] + * element[attr!=value] + * element[attr^=value] + * element[attr$=value] + * element: + * element:not() + * element:first + * element:last + * element:odd + * element:even + * element element + * element > element + * + * @class tinymce.ui.Selector + */ +define("tinymce/ui/Selector", [ + "tinymce/util/Class" +], function(Class) { + "use strict"; + + /** + * Produces an array with a unique set of objects. It will not compare the values + * but the references of the objects. + * + * @private + * @method unqiue + * @param {Array} array Array to make into an array with unique items. + * @return {Array} Array with unique items. + */ + function unique(array) { + var uniqueItems = [], i = array.length, item; + + while (i--) { + item = array[i]; + + if (!item.__checked) { + uniqueItems.push(item); + item.__checked = 1; + } + } + + i = uniqueItems.length; + while (i--) { + delete uniqueItems[i].__checked; + } + + return uniqueItems; + } + + var expression = /^([\w\\*]+)?(?:#([\w\-\\]+))?(?:\.([\w\\\.]+))?(?:\[\@?([\w\\]+)([\^\$\*!~]?=)([\w\\]+)\])?(?:\:(.+))?/i; + + /*jshint maxlen:255 */ + /*eslint max-len:0 */ + var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + whiteSpace = /^\s*|\s*$/g, + Collection; + + var Selector = Class.extend({ + /** + * Constructs a new Selector instance. + * + * @constructor + * @method init + * @param {String} selector CSS like selector expression. + */ + init: function(selector) { + var match = this.match; + + function compileNameFilter(name) { + if (name) { + name = name.toLowerCase(); + + return function(item) { + return name === '*' || item.type === name; + }; + } + } + + function compileIdFilter(id) { + if (id) { + return function(item) { + return item._name === id; + }; + } + } + + function compileClassesFilter(classes) { + if (classes) { + classes = classes.split('.'); + + return function(item) { + var i = classes.length; + + while (i--) { + if (!item.classes.contains(classes[i])) { + return false; + } + } + + return true; + }; + } + } + + function compileAttrFilter(name, cmp, check) { + if (name) { + return function(item) { + var value = item[name] ? item[name]() : ''; + + return !cmp ? !!check : + cmp === "=" ? value === check : + cmp === "*=" ? value.indexOf(check) >= 0 : + cmp === "~=" ? (" " + value + " ").indexOf(" " + check + " ") >= 0 : + cmp === "!=" ? value != check : + cmp === "^=" ? value.indexOf(check) === 0 : + cmp === "$=" ? value.substr(value.length - check.length) === check : + false; + }; + } + } + + function compilePsuedoFilter(name) { + var notSelectors; + + if (name) { + name = /(?:not\((.+)\))|(.+)/i.exec(name); + + if (!name[1]) { + name = name[2]; + + return function(item, index, length) { + return name === 'first' ? index === 0 : + name === 'last' ? index === length - 1 : + name === 'even' ? index % 2 === 0 : + name === 'odd' ? index % 2 === 1 : + item[name] ? item[name]() : + false; + }; + } + + // Compile not expression + notSelectors = parseChunks(name[1], []); + + return function(item) { + return !match(item, notSelectors); + }; + } + } + + function compile(selector, filters, direct) { + var parts; + + function add(filter) { + if (filter) { + filters.push(filter); + } + } + + // Parse expression into parts + parts = expression.exec(selector.replace(whiteSpace, '')); + + add(compileNameFilter(parts[1])); + add(compileIdFilter(parts[2])); + add(compileClassesFilter(parts[3])); + add(compileAttrFilter(parts[4], parts[5], parts[6])); + add(compilePsuedoFilter(parts[7])); + + // Mark the filter with pseudo for performance + filters.pseudo = !!parts[7]; + filters.direct = direct; + + return filters; + } + + // Parser logic based on Sizzle by John Resig + function parseChunks(selector, selectors) { + var parts = [], extra, matches, i; + + do { + chunker.exec(""); + matches = chunker.exec(selector); + + if (matches) { + selector = matches[3]; + parts.push(matches[1]); + + if (matches[2]) { + extra = matches[3]; + break; + } + } + } while (matches); + + if (extra) { + parseChunks(extra, selectors); + } + + selector = []; + for (i = 0; i < parts.length; i++) { + if (parts[i] != '>') { + selector.push(compile(parts[i], [], parts[i - 1] === '>')); + } + } + + selectors.push(selector); + + return selectors; + } + + this._selectors = parseChunks(selector, []); + }, + + /** + * Returns true/false if the selector matches the specified control. + * + * @method match + * @param {tinymce.ui.Control} control Control to match against the selector. + * @param {Array} selectors Optional array of selectors, mostly used internally. + * @return {Boolean} true/false state if the control matches or not. + */ + match: function(control, selectors) { + var i, l, si, sl, selector, fi, fl, filters, index, length, siblings, count, item; + + selectors = selectors || this._selectors; + for (i = 0, l = selectors.length; i < l; i++) { + selector = selectors[i]; + sl = selector.length; + item = control; + count = 0; + + for (si = sl - 1; si >= 0; si--) { + filters = selector[si]; + + while (item) { + // Find the index and length since a pseudo filter like :first needs it + if (filters.pseudo) { + siblings = item.parent().items(); + index = length = siblings.length; + while (index--) { + if (siblings[index] === item) { + break; + } + } + } + + for (fi = 0, fl = filters.length; fi < fl; fi++) { + if (!filters[fi](item, index, length)) { + fi = fl + 1; + break; + } + } + + if (fi === fl) { + count++; + break; + } else { + // If it didn't match the right most expression then + // break since it's no point looking at the parents + if (si === sl - 1) { + break; + } + } + + item = item.parent(); + } + } + + // If we found all selectors then return true otherwise continue looking + if (count === sl) { + return true; + } + } + + return false; + }, + + /** + * Returns a tinymce.ui.Collection with matches of the specified selector inside the specified container. + * + * @method find + * @param {tinymce.ui.Control} container Container to look for items in. + * @return {tinymce.ui.Collection} Collection with matched elements. + */ + find: function(container) { + var matches = [], i, l, selectors = this._selectors; + + function collect(items, selector, index) { + var i, l, fi, fl, item, filters = selector[index]; + + for (i = 0, l = items.length; i < l; i++) { + item = items[i]; + + // Run each filter against the item + for (fi = 0, fl = filters.length; fi < fl; fi++) { + if (!filters[fi](item, i, l)) { + fi = fl + 1; + break; + } + } + + // All filters matched the item + if (fi === fl) { + // Matched item is on the last expression like: panel toolbar [button] + if (index == selector.length - 1) { + matches.push(item); + } else { + // Collect next expression type + if (item.items) { + collect(item.items(), selector, index + 1); + } + } + } else if (filters.direct) { + return; + } + + // Collect child items + if (item.items) { + collect(item.items(), selector, index); + } + } + } + + if (container.items) { + for (i = 0, l = selectors.length; i < l; i++) { + collect(container.items(), selectors[i], 0); + } + + // Unique the matches if needed + if (l > 1) { + matches = unique(matches); + } + } + + // Fix for circular reference + if (!Collection) { + // TODO: Fix me! + Collection = Selector.Collection; + } + + return new Collection(matches); + } + }); + + return Selector; +}); + +// Included from: js/tinymce/classes/ui/Collection.js + +/** + * Collection.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Control collection, this class contains control instances and it enables you to + * perform actions on all the contained items. This is very similar to how jQuery works. + * + * @example + * someCollection.show().disabled(true); + * + * @class tinymce.ui.Collection + */ +define("tinymce/ui/Collection", [ + "tinymce/util/Tools", + "tinymce/ui/Selector", + "tinymce/util/Class" +], function(Tools, Selector, Class) { + "use strict"; + + var Collection, proto, push = Array.prototype.push, slice = Array.prototype.slice; + + proto = { + /** + * Current number of contained control instances. + * + * @field length + * @type Number + */ + length: 0, + + /** + * Constructor for the collection. + * + * @constructor + * @method init + * @param {Array} items Optional array with items to add. + */ + init: function(items) { + if (items) { + this.add(items); + } + }, + + /** + * Adds new items to the control collection. + * + * @method add + * @param {Array} items Array if items to add to collection. + * @return {tinymce.ui.Collection} Current collection instance. + */ + add: function(items) { + var self = this; + + // Force single item into array + if (!Tools.isArray(items)) { + if (items instanceof Collection) { + self.add(items.toArray()); + } else { + push.call(self, items); + } + } else { + push.apply(self, items); + } + + return self; + }, + + /** + * Sets the contents of the collection. This will remove any existing items + * and replace them with the ones specified in the input array. + * + * @method set + * @param {Array} items Array with items to set into the Collection. + * @return {tinymce.ui.Collection} Collection instance. + */ + set: function(items) { + var self = this, len = self.length, i; + + self.length = 0; + self.add(items); + + // Remove old entries + for (i = self.length; i < len; i++) { + delete self[i]; + } + + return self; + }, + + /** + * Filters the collection item based on the specified selector expression or selector function. + * + * @method filter + * @param {String} selector Selector expression to filter items by. + * @return {tinymce.ui.Collection} Collection containing the filtered items. + */ + filter: function(selector) { + var self = this, i, l, matches = [], item, match; + + // Compile string into selector expression + if (typeof selector === "string") { + selector = new Selector(selector); + + match = function(item) { + return selector.match(item); + }; + } else { + // Use selector as matching function + match = selector; + } + + for (i = 0, l = self.length; i < l; i++) { + item = self[i]; + + if (match(item)) { + matches.push(item); + } + } + + return new Collection(matches); + }, + + /** + * Slices the items within the collection. + * + * @method slice + * @param {Number} index Index to slice at. + * @param {Number} len Optional length to slice. + * @return {tinymce.ui.Collection} Current collection. + */ + slice: function() { + return new Collection(slice.apply(this, arguments)); + }, + + /** + * Makes the current collection equal to the specified index. + * + * @method eq + * @param {Number} index Index of the item to set the collection to. + * @return {tinymce.ui.Collection} Current collection. + */ + eq: function(index) { + return index === -1 ? this.slice(index) : this.slice(index, +index + 1); + }, + + /** + * Executes the specified callback on each item in collection. + * + * @method each + * @param {function} callback Callback to execute for each item in collection. + * @return {tinymce.ui.Collection} Current collection instance. + */ + each: function(callback) { + Tools.each(this, callback); + + return this; + }, + + /** + * Returns an JavaScript array object of the contents inside the collection. + * + * @method toArray + * @return {Array} Array with all items from collection. + */ + toArray: function() { + return Tools.toArray(this); + }, + + /** + * Finds the index of the specified control or return -1 if it isn't in the collection. + * + * @method indexOf + * @param {Control} ctrl Control instance to look for. + * @return {Number} Index of the specified control or -1. + */ + indexOf: function(ctrl) { + var self = this, i = self.length; + + while (i--) { + if (self[i] === ctrl) { + break; + } + } + + return i; + }, + + /** + * Returns a new collection of the contents in reverse order. + * + * @method reverse + * @return {tinymce.ui.Collection} Collection instance with reversed items. + */ + reverse: function() { + return new Collection(Tools.toArray(this).reverse()); + }, + + /** + * Returns true/false if the class exists or not. + * + * @method hasClass + * @param {String} cls Class to check for. + * @return {Boolean} true/false state if the class exists or not. + */ + hasClass: function(cls) { + return this[0] ? this[0].classes.contains(cls) : false; + }, + + /** + * Sets/gets the specific property on the items in the collection. The same as executing control.(); + * + * @method prop + * @param {String} name Property name to get/set. + * @param {Object} value Optional object value to set. + * @return {tinymce.ui.Collection} Current collection instance or value of the first item on a get operation. + */ + prop: function(name, value) { + var self = this, undef, item; + + if (value !== undef) { + self.each(function(item) { + if (item[name]) { + item[name](value); + } + }); + + return self; + } + + item = self[0]; + + if (item && item[name]) { + return item[name](); + } + }, + + /** + * Executes the specific function name with optional arguments an all items in collection if it exists. + * + * @example collection.exec("myMethod", arg1, arg2, arg3); + * @method exec + * @param {String} name Name of the function to execute. + * @param {Object} ... Multiple arguments to pass to each function. + * @return {tinymce.ui.Collection} Current collection. + */ + exec: function(name) { + var self = this, args = Tools.toArray(arguments).slice(1); + + self.each(function(item) { + if (item[name]) { + item[name].apply(item, args); + } + }); + + return self; + }, + + /** + * Remove all items from collection and DOM. + * + * @method remove + * @return {tinymce.ui.Collection} Current collection. + */ + remove: function() { + var i = this.length; + + while (i--) { + this[i].remove(); + } + + return this; + }, + + /** + * Adds a class to all items in the collection. + * + * @method addClass + * @param {String} cls Class to add to each item. + * @return {tinymce.ui.Collection} Current collection instance. + */ + addClass: function(cls) { + return this.each(function(item) { + item.classes.add(cls); + }); + }, + + /** + * Removes the specified class from all items in collection. + * + * @method removeClass + * @param {String} cls Class to remove from each item. + * @return {tinymce.ui.Collection} Current collection instance. + */ + removeClass: function(cls) { + return this.each(function(item) { + item.classes.remove(cls); + }); + } + + /** + * Fires the specified event by name and arguments on the control. This will execute all + * bound event handlers. + * + * @method fire + * @param {String} name Name of the event to fire. + * @param {Object} args Optional arguments to pass to the event. + * @return {tinymce.ui.Collection} Current collection instance. + */ + // fire: function(event, args) {}, -- Generated by code below + + /** + * Binds a callback to the specified event. This event can both be + * native browser events like "click" or custom ones like PostRender. + * + * The callback function will have two parameters the first one being the control that received the event + * the second one will be the event object either the browsers native event object or a custom JS object. + * + * @method on + * @param {String} name Name of the event to bind. For example "click". + * @param {String/function} callback Callback function to execute ones the event occurs. + * @return {tinymce.ui.Collection} Current collection instance. + */ + // on: function(name, callback) {}, -- Generated by code below + + /** + * Unbinds the specified event and optionally a specific callback. If you omit the name + * parameter all event handlers will be removed. If you omit the callback all event handles + * by the specified name will be removed. + * + * @method off + * @param {String} name Optional name for the event to unbind. + * @param {function} callback Optional callback function to unbind. + * @return {tinymce.ui.Collection} Current collection instance. + */ + // off: function(name, callback) {}, -- Generated by code below + + /** + * Shows the items in the current collection. + * + * @method show + * @return {tinymce.ui.Collection} Current collection instance. + */ + // show: function() {}, -- Generated by code below + + /** + * Hides the items in the current collection. + * + * @method hide + * @return {tinymce.ui.Collection} Current collection instance. + */ + // hide: function() {}, -- Generated by code below + + /** + * Sets/gets the text contents of the items in the current collection. + * + * @method text + * @return {tinymce.ui.Collection} Current collection instance or text value of the first item on a get operation. + */ + // text: function(value) {}, -- Generated by code below + + /** + * Sets/gets the name contents of the items in the current collection. + * + * @method name + * @return {tinymce.ui.Collection} Current collection instance or name value of the first item on a get operation. + */ + // name: function(value) {}, -- Generated by code below + + /** + * Sets/gets the disabled state on the items in the current collection. + * + * @method disabled + * @return {tinymce.ui.Collection} Current collection instance or disabled state of the first item on a get operation. + */ + // disabled: function(state) {}, -- Generated by code below + + /** + * Sets/gets the active state on the items in the current collection. + * + * @method active + * @return {tinymce.ui.Collection} Current collection instance or active state of the first item on a get operation. + */ + // active: function(state) {}, -- Generated by code below + + /** + * Sets/gets the selected state on the items in the current collection. + * + * @method selected + * @return {tinymce.ui.Collection} Current collection instance or selected state of the first item on a get operation. + */ + // selected: function(state) {}, -- Generated by code below + + /** + * Sets/gets the selected state on the items in the current collection. + * + * @method visible + * @return {tinymce.ui.Collection} Current collection instance or visible state of the first item on a get operation. + */ + // visible: function(state) {}, -- Generated by code below + }; + + // Extend tinymce.ui.Collection prototype with some generated control specific methods + Tools.each('fire on off show hide append prepend before after reflow'.split(' '), function(name) { + proto[name] = function() { + var args = Tools.toArray(arguments); + + this.each(function(ctrl) { + if (name in ctrl) { + ctrl[name].apply(ctrl, args); + } + }); + + return this; + }; + }); + + // Extend tinymce.ui.Collection prototype with some property methods + Tools.each('text name disabled active selected checked visible parent value data'.split(' '), function(name) { + proto[name] = function(value) { + return this.prop(name, value); + }; + }); + + // Create class based on the new prototype + Collection = Class.extend(proto); + + // Stick Collection into Selector to prevent circual references + Selector.Collection = Collection; + + return Collection; +}); + +// Included from: js/tinymce/classes/ui/DomUtils.js + +/** + * DomUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Private UI DomUtils proxy. + * + * @private + * @class tinymce.ui.DomUtils + */ +define("tinymce/ui/DomUtils", [ + "tinymce/Env", + "tinymce/util/Tools", + "tinymce/dom/DOMUtils" +], function(Env, Tools, DOMUtils) { + "use strict"; + + var count = 0; + + var funcs = { + id: function() { + return 'mceu_' + (count++); + }, + + create: function(name, attrs, children) { + var elm = document.createElement(name); + + DOMUtils.DOM.setAttribs(elm, attrs); + + if (typeof children === 'string') { + elm.innerHTML = children; + } else { + Tools.each(children, function(child) { + if (child.nodeType) { + elm.appendChild(child); + } + }); + } + + return elm; + }, + + createFragment: function(html) { + return DOMUtils.DOM.createFragment(html); + }, + + getWindowSize: function() { + return DOMUtils.DOM.getViewPort(); + }, + + getSize: function(elm) { + var width, height; + + if (elm.getBoundingClientRect) { + var rect = elm.getBoundingClientRect(); + + width = Math.max(rect.width || (rect.right - rect.left), elm.offsetWidth); + height = Math.max(rect.height || (rect.bottom - rect.bottom), elm.offsetHeight); + } else { + width = elm.offsetWidth; + height = elm.offsetHeight; + } + + return {width: width, height: height}; + }, + + getPos: function(elm, root) { + return DOMUtils.DOM.getPos(elm, root || funcs.getContainer()); + }, + + getContainer: function () { + return Env.container ? Env.container : document.body; + }, + + getViewPort: function(win) { + return DOMUtils.DOM.getViewPort(win); + }, + + get: function(id) { + return document.getElementById(id); + }, + + addClass: function(elm, cls) { + return DOMUtils.DOM.addClass(elm, cls); + }, + + removeClass: function(elm, cls) { + return DOMUtils.DOM.removeClass(elm, cls); + }, + + hasClass: function(elm, cls) { + return DOMUtils.DOM.hasClass(elm, cls); + }, + + toggleClass: function(elm, cls, state) { + return DOMUtils.DOM.toggleClass(elm, cls, state); + }, + + css: function(elm, name, value) { + return DOMUtils.DOM.setStyle(elm, name, value); + }, + + getRuntimeStyle: function(elm, name) { + return DOMUtils.DOM.getStyle(elm, name, true); + }, + + on: function(target, name, callback, scope) { + return DOMUtils.DOM.bind(target, name, callback, scope); + }, + + off: function(target, name, callback) { + return DOMUtils.DOM.unbind(target, name, callback); + }, + + fire: function(target, name, args) { + return DOMUtils.DOM.fire(target, name, args); + }, + + innerHtml: function(elm, html) { + // Workaround for
in

bug on IE 8 #6178 + DOMUtils.DOM.setHTML(elm, html); + } + }; + + return funcs; +}); + +// Included from: js/tinymce/classes/ui/BoxUtils.js + +/** + * BoxUtils.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Utility class for box parsing and measuring. + * + * @private + * @class tinymce.ui.BoxUtils + */ +define("tinymce/ui/BoxUtils", [ +], function() { + "use strict"; + + return { + /** + * Parses the specified box value. A box value contains 1-4 properties in clockwise order. + * + * @method parseBox + * @param {String/Number} value Box value "0 1 2 3" or "0" etc. + * @return {Object} Object with top/right/bottom/left properties. + * @private + */ + parseBox: function(value) { + var len, radix = 10; + + if (!value) { + return; + } + + if (typeof value === "number") { + value = value || 0; + + return { + top: value, + left: value, + bottom: value, + right: value + }; + } + + value = value.split(' '); + len = value.length; + + if (len === 1) { + value[1] = value[2] = value[3] = value[0]; + } else if (len === 2) { + value[2] = value[0]; + value[3] = value[1]; + } else if (len === 3) { + value[3] = value[1]; + } + + return { + top: parseInt(value[0], radix) || 0, + right: parseInt(value[1], radix) || 0, + bottom: parseInt(value[2], radix) || 0, + left: parseInt(value[3], radix) || 0 + }; + }, + + measureBox: function(elm, prefix) { + function getStyle(name) { + var defaultView = document.defaultView; + + if (defaultView) { + // Remove camelcase + name = name.replace(/[A-Z]/g, function(a) { + return '-' + a; + }); + + return defaultView.getComputedStyle(elm, null).getPropertyValue(name); + } + + return elm.currentStyle[name]; + } + + function getSide(name) { + var val = parseFloat(getStyle(name), 10); + + return isNaN(val) ? 0 : val; + } + + return { + top: getSide(prefix + "TopWidth"), + right: getSide(prefix + "RightWidth"), + bottom: getSide(prefix + "BottomWidth"), + left: getSide(prefix + "LeftWidth") + }; + } + }; +}); + +// Included from: js/tinymce/classes/ui/ClassList.js + +/** + * ClassList.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Handles adding and removal of classes. + * + * @private + * @class tinymce.ui.ClassList + */ +define("tinymce/ui/ClassList", [ + "tinymce/util/Tools" +], function(Tools) { + "use strict"; + + function noop() { + } + + /** + * Constructs a new class list the specified onchange + * callback will be executed when the class list gets modifed. + * + * @constructor ClassList + * @param {function} onchange Onchange callback to be executed. + */ + function ClassList(onchange) { + this.cls = []; + this.cls._map = {}; + this.onchange = onchange || noop; + this.prefix = ''; + } + + Tools.extend(ClassList.prototype, { + /** + * Adds a new class to the class list. + * + * @method add + * @param {String} cls Class to be added. + * @return {tinymce.ui.ClassList} Current class list instance. + */ + add: function(cls) { + if (cls && !this.contains(cls)) { + this.cls._map[cls] = true; + this.cls.push(cls); + this._change(); + } + + return this; + }, + + /** + * Removes the specified class from the class list. + * + * @method remove + * @param {String} cls Class to be removed. + * @return {tinymce.ui.ClassList} Current class list instance. + */ + remove: function(cls) { + if (this.contains(cls)) { + for (var i = 0; i < this.cls.length; i++) { + if (this.cls[i] === cls) { + break; + } + } + + this.cls.splice(i, 1); + delete this.cls._map[cls]; + this._change(); + } + + return this; + }, + + /** + * Toggles a class in the class list. + * + * @method toggle + * @param {String} cls Class to be added/removed. + * @param {Boolean} state Optional state if it should be added/removed. + * @return {tinymce.ui.ClassList} Current class list instance. + */ + toggle: function(cls, state) { + var curState = this.contains(cls); + + if (curState !== state) { + if (curState) { + this.remove(cls); + } else { + this.add(cls); + } + + this._change(); + } + + return this; + }, + + /** + * Returns true if the class list has the specified class. + * + * @method contains + * @param {String} cls Class to look for. + * @return {Boolean} true/false if the class exists or not. + */ + contains: function(cls) { + return !!this.cls._map[cls]; + }, + + /** + * Returns a space separated list of classes. + * + * @method toString + * @return {String} Space separated list of classes. + */ + + _change: function() { + delete this.clsValue; + this.onchange.call(this); + } + }); + + // IE 8 compatibility + ClassList.prototype.toString = function() { + var value; + + if (this.clsValue) { + return this.clsValue; + } + + value = ''; + for (var i = 0; i < this.cls.length; i++) { + if (i > 0) { + value += ' '; + } + + value += this.prefix + this.cls[i]; + } + + return value; + }; + + return ClassList; +}); + +// Included from: js/tinymce/classes/ui/ReflowQueue.js + +/** + * ReflowQueue.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class will automatically reflow controls on the next animation frame within a few milliseconds on older browsers. + * If the user manually reflows then the automatic reflow will be cancelled. This class is used internally when various control states + * changes that triggers a reflow. + * + * @class tinymce.ui.ReflowQueue + * @static + */ +define("tinymce/ui/ReflowQueue", [ + "tinymce/util/Delay" +], function(Delay) { + var dirtyCtrls = {}, animationFrameRequested; + + return { + /** + * Adds a control to the next automatic reflow call. This is the control that had a state + * change for example if the control was hidden/shown. + * + * @method add + * @param {tinymce.ui.Control} ctrl Control to add to queue. + */ + add: function(ctrl) { + var parent = ctrl.parent(); + + if (parent) { + if (!parent._layout || parent._layout.isNative()) { + return; + } + + if (!dirtyCtrls[parent._id]) { + dirtyCtrls[parent._id] = parent; + } + + if (!animationFrameRequested) { + animationFrameRequested = true; + + Delay.requestAnimationFrame(function() { + var id, ctrl; + + animationFrameRequested = false; + + for (id in dirtyCtrls) { + ctrl = dirtyCtrls[id]; + + if (ctrl.state.get('rendered')) { + ctrl.reflow(); + } + } + + dirtyCtrls = {}; + }, document.body); + } + } + }, + + /** + * Removes the specified control from the automatic reflow. This will happen when for example the user + * manually triggers a reflow. + * + * @method remove + * @param {tinymce.ui.Control} ctrl Control to remove from queue. + */ + remove: function(ctrl) { + if (dirtyCtrls[ctrl._id]) { + delete dirtyCtrls[ctrl._id]; + } + } + }; +}); + +// Included from: js/tinymce/classes/ui/Control.js + +/** + * Control.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*eslint consistent-this:0 */ + +/** + * This is the base class for all controls and containers. All UI control instances inherit + * from this one as it has the base logic needed by all of them. + * + * @class tinymce.ui.Control + */ +define("tinymce/ui/Control", [ + "tinymce/util/Class", + "tinymce/util/Tools", + "tinymce/util/EventDispatcher", + "tinymce/data/ObservableObject", + "tinymce/ui/Collection", + "tinymce/ui/DomUtils", + "tinymce/dom/DomQuery", + "tinymce/ui/BoxUtils", + "tinymce/ui/ClassList", + "tinymce/ui/ReflowQueue" +], function(Class, Tools, EventDispatcher, ObservableObject, Collection, DomUtils, $, BoxUtils, ClassList, ReflowQueue) { + "use strict"; + + var hasMouseWheelEventSupport = "onmousewheel" in document; + var hasWheelEventSupport = false; + var classPrefix = "mce-"; + var Control, idCounter = 0; + + var proto = { + Statics: { + classPrefix: classPrefix + }, + + isRtl: function() { + return Control.rtl; + }, + + /** + * Class/id prefix to use for all controls. + * + * @final + * @field {String} classPrefix + */ + classPrefix: classPrefix, + + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} style Style CSS properties to add. + * @setting {String} border Border box values example: 1 1 1 1 + * @setting {String} padding Padding box values example: 1 1 1 1 + * @setting {String} margin Margin box values example: 1 1 1 1 + * @setting {Number} minWidth Minimal width for the control. + * @setting {Number} minHeight Minimal height for the control. + * @setting {String} classes Space separated list of classes to add. + * @setting {String} role WAI-ARIA role to use for control. + * @setting {Boolean} hidden Is the control hidden by default. + * @setting {Boolean} disabled Is the control disabled by default. + * @setting {String} name Name of the control instance. + */ + init: function(settings) { + var self = this, classes, defaultClasses; + + function applyClasses(classes) { + var i; + + classes = classes.split(' '); + for (i = 0; i < classes.length; i++) { + self.classes.add(classes[i]); + } + } + + self.settings = settings = Tools.extend({}, self.Defaults, settings); + + // Initial states + self._id = settings.id || ('mceu_' + (idCounter++)); + self._aria = {role: settings.role}; + self._elmCache = {}; + self.$ = $; + + self.state = new ObservableObject({ + visible: true, + active: false, + disabled: false, + value: '' + }); + + self.data = new ObservableObject(settings.data); + + self.classes = new ClassList(function() { + if (self.state.get('rendered')) { + self.getEl().className = this.toString(); + } + }); + self.classes.prefix = self.classPrefix; + + // Setup classes + classes = settings.classes; + if (classes) { + if (self.Defaults) { + defaultClasses = self.Defaults.classes; + + if (defaultClasses && classes != defaultClasses) { + applyClasses(defaultClasses); + } + } + + applyClasses(classes); + } + + Tools.each('title text name visible disabled active value'.split(' '), function(name) { + if (name in settings) { + self[name](settings[name]); + } + }); + + self.on('click', function() { + if (self.disabled()) { + return false; + } + }); + + /** + * Name/value object with settings for the current control. + * + * @field {Object} settings + */ + self.settings = settings; + + self.borderBox = BoxUtils.parseBox(settings.border); + self.paddingBox = BoxUtils.parseBox(settings.padding); + self.marginBox = BoxUtils.parseBox(settings.margin); + + if (settings.hidden) { + self.hide(); + } + }, + + // Will generate getter/setter methods for these properties + Properties: 'parent,name', + + /** + * Returns the root element to render controls into. + * + * @method getContainerElm + * @return {Element} HTML DOM element to render into. + */ + getContainerElm: function() { + return DomUtils.getContainer(); + }, + + /** + * Returns a control instance for the current DOM element. + * + * @method getParentCtrl + * @param {Element} elm HTML dom element to get parent control from. + * @return {tinymce.ui.Control} Control instance or undefined. + */ + getParentCtrl: function(elm) { + var ctrl, lookup = this.getRoot().controlIdLookup; + + while (elm && lookup) { + ctrl = lookup[elm.id]; + if (ctrl) { + break; + } + + elm = elm.parentNode; + } + + return ctrl; + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, settings = self.settings, borderBox, layoutRect; + var elm = self.getEl(), width, height, minWidth, minHeight, autoResize; + var startMinWidth, startMinHeight, initialSize; + + // Measure the current element + borderBox = self.borderBox = self.borderBox || BoxUtils.measureBox(elm, 'border'); + self.paddingBox = self.paddingBox || BoxUtils.measureBox(elm, 'padding'); + self.marginBox = self.marginBox || BoxUtils.measureBox(elm, 'margin'); + initialSize = DomUtils.getSize(elm); + + // Setup minWidth/minHeight and width/height + startMinWidth = settings.minWidth; + startMinHeight = settings.minHeight; + minWidth = startMinWidth || initialSize.width; + minHeight = startMinHeight || initialSize.height; + width = settings.width; + height = settings.height; + autoResize = settings.autoResize; + autoResize = typeof autoResize != "undefined" ? autoResize : !width && !height; + + width = width || minWidth; + height = height || minHeight; + + var deltaW = borderBox.left + borderBox.right; + var deltaH = borderBox.top + borderBox.bottom; + + var maxW = settings.maxWidth || 0xFFFF; + var maxH = settings.maxHeight || 0xFFFF; + + // Setup initial layout rect + self._layoutRect = layoutRect = { + x: settings.x || 0, + y: settings.y || 0, + w: width, + h: height, + deltaW: deltaW, + deltaH: deltaH, + contentW: width - deltaW, + contentH: height - deltaH, + innerW: width - deltaW, + innerH: height - deltaH, + startMinWidth: startMinWidth || 0, + startMinHeight: startMinHeight || 0, + minW: Math.min(minWidth, maxW), + minH: Math.min(minHeight, maxH), + maxW: maxW, + maxH: maxH, + autoResize: autoResize, + scrollW: 0 + }; + + self._lastLayoutRect = {}; + + return layoutRect; + }, + + /** + * Getter/setter for the current layout rect. + * + * @method layoutRect + * @param {Object} [newRect] Optional new layout rect. + * @return {tinymce.ui.Control/Object} Current control or rect object. + */ + layoutRect: function(newRect) { + var self = this, curRect = self._layoutRect, lastLayoutRect, size, deltaWidth, deltaHeight, undef, repaintControls; + + // Initialize default layout rect + if (!curRect) { + curRect = self.initLayoutRect(); + } + + // Set new rect values + if (newRect) { + // Calc deltas between inner and outer sizes + deltaWidth = curRect.deltaW; + deltaHeight = curRect.deltaH; + + // Set x position + if (newRect.x !== undef) { + curRect.x = newRect.x; + } + + // Set y position + if (newRect.y !== undef) { + curRect.y = newRect.y; + } + + // Set minW + if (newRect.minW !== undef) { + curRect.minW = newRect.minW; + } + + // Set minH + if (newRect.minH !== undef) { + curRect.minH = newRect.minH; + } + + // Set new width and calculate inner width + size = newRect.w; + if (size !== undef) { + size = size < curRect.minW ? curRect.minW : size; + size = size > curRect.maxW ? curRect.maxW : size; + curRect.w = size; + curRect.innerW = size - deltaWidth; + } + + // Set new height and calculate inner height + size = newRect.h; + if (size !== undef) { + size = size < curRect.minH ? curRect.minH : size; + size = size > curRect.maxH ? curRect.maxH : size; + curRect.h = size; + curRect.innerH = size - deltaHeight; + } + + // Set new inner width and calculate width + size = newRect.innerW; + if (size !== undef) { + size = size < curRect.minW - deltaWidth ? curRect.minW - deltaWidth : size; + size = size > curRect.maxW - deltaWidth ? curRect.maxW - deltaWidth : size; + curRect.innerW = size; + curRect.w = size + deltaWidth; + } + + // Set new height and calculate inner height + size = newRect.innerH; + if (size !== undef) { + size = size < curRect.minH - deltaHeight ? curRect.minH - deltaHeight : size; + size = size > curRect.maxH - deltaHeight ? curRect.maxH - deltaHeight : size; + curRect.innerH = size; + curRect.h = size + deltaHeight; + } + + // Set new contentW + if (newRect.contentW !== undef) { + curRect.contentW = newRect.contentW; + } + + // Set new contentH + if (newRect.contentH !== undef) { + curRect.contentH = newRect.contentH; + } + + // Compare last layout rect with the current one to see if we need to repaint or not + lastLayoutRect = self._lastLayoutRect; + if (lastLayoutRect.x !== curRect.x || lastLayoutRect.y !== curRect.y || + lastLayoutRect.w !== curRect.w || lastLayoutRect.h !== curRect.h) { + repaintControls = Control.repaintControls; + + if (repaintControls) { + if (repaintControls.map && !repaintControls.map[self._id]) { + repaintControls.push(self); + repaintControls.map[self._id] = true; + } + } + + lastLayoutRect.x = curRect.x; + lastLayoutRect.y = curRect.y; + lastLayoutRect.w = curRect.w; + lastLayoutRect.h = curRect.h; + } + + return self; + } + + return curRect; + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, style, bodyStyle, bodyElm, rect, borderBox; + var borderW, borderH, lastRepaintRect, round, value; + + // Use Math.round on all values on IE < 9 + round = !document.createRange ? Math.round : function(value) { + return value; + }; + + style = self.getEl().style; + rect = self._layoutRect; + lastRepaintRect = self._lastRepaintRect || {}; + + borderBox = self.borderBox; + borderW = borderBox.left + borderBox.right; + borderH = borderBox.top + borderBox.bottom; + + if (rect.x !== lastRepaintRect.x) { + style.left = round(rect.x) + 'px'; + lastRepaintRect.x = rect.x; + } + + if (rect.y !== lastRepaintRect.y) { + style.top = round(rect.y) + 'px'; + lastRepaintRect.y = rect.y; + } + + if (rect.w !== lastRepaintRect.w) { + value = round(rect.w - borderW); + style.width = (value >= 0 ? value : 0) + 'px'; + lastRepaintRect.w = rect.w; + } + + if (rect.h !== lastRepaintRect.h) { + value = round(rect.h - borderH); + style.height = (value >= 0 ? value : 0) + 'px'; + lastRepaintRect.h = rect.h; + } + + // Update body if needed + if (self._hasBody && rect.innerW !== lastRepaintRect.innerW) { + value = round(rect.innerW); + + bodyElm = self.getEl('body'); + if (bodyElm) { + bodyStyle = bodyElm.style; + bodyStyle.width = (value >= 0 ? value : 0) + 'px'; + } + + lastRepaintRect.innerW = rect.innerW; + } + + if (self._hasBody && rect.innerH !== lastRepaintRect.innerH) { + value = round(rect.innerH); + + bodyElm = bodyElm || self.getEl('body'); + if (bodyElm) { + bodyStyle = bodyStyle || bodyElm.style; + bodyStyle.height = (value >= 0 ? value : 0) + 'px'; + } + + lastRepaintRect.innerH = rect.innerH; + } + + self._lastRepaintRect = lastRepaintRect; + self.fire('repaint', {}, false); + }, + + /** + * Updates the controls layout rect by re-measuing it. + */ + updateLayoutRect: function() { + var self = this; + + self.parent()._lastRect = null; + + DomUtils.css(self.getEl(), {width: '', height: ''}); + + self._layoutRect = self._lastRepaintRect = self._lastLayoutRect = null; + self.initLayoutRect(); + }, + + /** + * Binds a callback to the specified event. This event can both be + * native browser events like "click" or custom ones like PostRender. + * + * The callback function will be passed a DOM event like object that enables yout do stop propagation. + * + * @method on + * @param {String} name Name of the event to bind. For example "click". + * @param {String/function} callback Callback function to execute ones the event occurs. + * @return {tinymce.ui.Control} Current control object. + */ + on: function(name, callback) { + var self = this; + + function resolveCallbackName(name) { + var callback, scope; + + if (typeof name != 'string') { + return name; + } + + return function(e) { + if (!callback) { + self.parentsAndSelf().each(function(ctrl) { + var callbacks = ctrl.settings.callbacks; + + if (callbacks && (callback = callbacks[name])) { + scope = ctrl; + return false; + } + }); + } + + if (!callback) { + e.action = name; + this.fire('execute', e); + return; + } + + return callback.call(scope, e); + }; + } + + getEventDispatcher(self).on(name, resolveCallbackName(callback)); + + return self; + }, + + /** + * Unbinds the specified event and optionally a specific callback. If you omit the name + * parameter all event handlers will be removed. If you omit the callback all event handles + * by the specified name will be removed. + * + * @method off + * @param {String} [name] Name for the event to unbind. + * @param {function} [callback] Callback function to unbind. + * @return {tinymce.ui.Control} Current control object. + */ + off: function(name, callback) { + getEventDispatcher(this).off(name, callback); + return this; + }, + + /** + * Fires the specified event by name and arguments on the control. This will execute all + * bound event handlers. + * + * @method fire + * @param {String} name Name of the event to fire. + * @param {Object} [args] Arguments to pass to the event. + * @param {Boolean} [bubble] Value to control bubbling. Defaults to true. + * @return {Object} Current arguments object. + */ + fire: function(name, args, bubble) { + var self = this; + + args = args || {}; + + if (!args.control) { + args.control = self; + } + + args = getEventDispatcher(self).fire(name, args); + + // Bubble event up to parents + if (bubble !== false && self.parent) { + var parent = self.parent(); + while (parent && !args.isPropagationStopped()) { + parent.fire(name, args, false); + parent = parent.parent(); + } + } + + return args; + }, + + /** + * Returns true/false if the specified event has any listeners. + * + * @method hasEventListeners + * @param {String} name Name of the event to check for. + * @return {Boolean} True/false state if the event has listeners. + */ + hasEventListeners: function(name) { + return getEventDispatcher(this).has(name); + }, + + /** + * Returns a control collection with all parent controls. + * + * @method parents + * @param {String} selector Optional selector expression to find parents. + * @return {tinymce.ui.Collection} Collection with all parent controls. + */ + parents: function(selector) { + var self = this, ctrl, parents = new Collection(); + + // Add each parent to collection + for (ctrl = self.parent(); ctrl; ctrl = ctrl.parent()) { + parents.add(ctrl); + } + + // Filter away everything that doesn't match the selector + if (selector) { + parents = parents.filter(selector); + } + + return parents; + }, + + /** + * Returns the current control and it's parents. + * + * @method parentsAndSelf + * @param {String} selector Optional selector expression to find parents. + * @return {tinymce.ui.Collection} Collection with all parent controls. + */ + parentsAndSelf: function(selector) { + return new Collection(this).add(this.parents(selector)); + }, + + /** + * Returns the control next to the current control. + * + * @method next + * @return {tinymce.ui.Control} Next control instance. + */ + next: function() { + var parentControls = this.parent().items(); + + return parentControls[parentControls.indexOf(this) + 1]; + }, + + /** + * Returns the control previous to the current control. + * + * @method prev + * @return {tinymce.ui.Control} Previous control instance. + */ + prev: function() { + var parentControls = this.parent().items(); + + return parentControls[parentControls.indexOf(this) - 1]; + }, + + /** + * Sets the inner HTML of the control element. + * + * @method innerHtml + * @param {String} html Html string to set as inner html. + * @return {tinymce.ui.Control} Current control object. + */ + innerHtml: function(html) { + this.$el.html(html); + return this; + }, + + /** + * Returns the control DOM element or sub element. + * + * @method getEl + * @param {String} [suffix] Suffix to get element by. + * @return {Element} HTML DOM element for the current control or it's children. + */ + getEl: function(suffix) { + var id = suffix ? this._id + '-' + suffix : this._id; + + if (!this._elmCache[id]) { + this._elmCache[id] = $('#' + id)[0]; + } + + return this._elmCache[id]; + }, + + /** + * Sets the visible state to true. + * + * @method show + * @return {tinymce.ui.Control} Current control instance. + */ + show: function() { + return this.visible(true); + }, + + /** + * Sets the visible state to false. + * + * @method hide + * @return {tinymce.ui.Control} Current control instance. + */ + hide: function() { + return this.visible(false); + }, + + /** + * Focuses the current control. + * + * @method focus + * @return {tinymce.ui.Control} Current control instance. + */ + focus: function() { + try { + this.getEl().focus(); + } catch (ex) { + // Ignore IE error + } + + return this; + }, + + /** + * Blurs the current control. + * + * @method blur + * @return {tinymce.ui.Control} Current control instance. + */ + blur: function() { + this.getEl().blur(); + + return this; + }, + + /** + * Sets the specified aria property. + * + * @method aria + * @param {String} name Name of the aria property to set. + * @param {String} value Value of the aria property. + * @return {tinymce.ui.Control} Current control instance. + */ + aria: function(name, value) { + var self = this, elm = self.getEl(self.ariaTarget); + + if (typeof value === "undefined") { + return self._aria[name]; + } + + self._aria[name] = value; + + if (self.state.get('rendered')) { + elm.setAttribute(name == 'role' ? name : 'aria-' + name, value); + } + + return self; + }, + + /** + * Encodes the specified string with HTML entities. It will also + * translate the string to different languages. + * + * @method encode + * @param {String/Object/Array} text Text to entity encode. + * @param {Boolean} [translate=true] False if the contents shouldn't be translated. + * @return {String} Encoded and possible traslated string. + */ + encode: function(text, translate) { + if (translate !== false) { + text = this.translate(text); + } + + return (text || '').replace(/[&<>"]/g, function(match) { + return '&#' + match.charCodeAt(0) + ';'; + }); + }, + + /** + * Returns the translated string. + * + * @method translate + * @param {String} text Text to translate. + * @return {String} Translated string or the same as the input. + */ + translate: function(text) { + return Control.translate ? Control.translate(text) : text; + }, + + /** + * Adds items before the current control. + * + * @method before + * @param {Array/tinymce.ui.Collection} items Array of items to prepend before this control. + * @return {tinymce.ui.Control} Current control instance. + */ + before: function(items) { + var self = this, parent = self.parent(); + + if (parent) { + parent.insert(items, parent.items().indexOf(self), true); + } + + return self; + }, + + /** + * Adds items after the current control. + * + * @method after + * @param {Array/tinymce.ui.Collection} items Array of items to append after this control. + * @return {tinymce.ui.Control} Current control instance. + */ + after: function(items) { + var self = this, parent = self.parent(); + + if (parent) { + parent.insert(items, parent.items().indexOf(self)); + } + + return self; + }, + + /** + * Removes the current control from DOM and from UI collections. + * + * @method remove + * @return {tinymce.ui.Control} Current control instance. + */ + remove: function() { + var self = this, elm = self.getEl(), parent = self.parent(), newItems, i; + + if (self.items) { + var controls = self.items().toArray(); + i = controls.length; + while (i--) { + controls[i].remove(); + } + } + + if (parent && parent.items) { + newItems = []; + + parent.items().each(function(item) { + if (item !== self) { + newItems.push(item); + } + }); + + parent.items().set(newItems); + parent._lastRect = null; + } + + if (self._eventsRoot && self._eventsRoot == self) { + $(elm).off(); + } + + var lookup = self.getRoot().controlIdLookup; + if (lookup) { + delete lookup[self._id]; + } + + if (elm && elm.parentNode) { + elm.parentNode.removeChild(elm); + } + + self.state.set('rendered', false); + self.state.destroy(); + + self.fire('remove'); + + return self; + }, + + /** + * Renders the control before the specified element. + * + * @method renderBefore + * @param {Element} elm Element to render before. + * @return {tinymce.ui.Control} Current control instance. + */ + renderBefore: function(elm) { + $(elm).before(this.renderHtml()); + this.postRender(); + return this; + }, + + /** + * Renders the control to the specified element. + * + * @method renderBefore + * @param {Element} elm Element to render to. + * @return {tinymce.ui.Control} Current control instance. + */ + renderTo: function(elm) { + $(elm || this.getContainerElm()).append(this.renderHtml()); + this.postRender(); + return this; + }, + + preRender: function() { + }, + + render: function() { + }, + + renderHtml: function() { + return '

'; + }, + + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.Control} Current control instance. + */ + postRender: function() { + var self = this, settings = self.settings, elm, box, parent, name, parentEventsRoot; + + self.$el = $(self.getEl()); + self.state.set('rendered', true); + + // Bind on settings + for (name in settings) { + if (name.indexOf("on") === 0) { + self.on(name.substr(2), settings[name]); + } + } + + if (self._eventsRoot) { + for (parent = self.parent(); !parentEventsRoot && parent; parent = parent.parent()) { + parentEventsRoot = parent._eventsRoot; + } + + if (parentEventsRoot) { + for (name in parentEventsRoot._nativeEvents) { + self._nativeEvents[name] = true; + } + } + } + + bindPendingEvents(self); + + if (settings.style) { + elm = self.getEl(); + if (elm) { + elm.setAttribute('style', settings.style); + elm.style.cssText = settings.style; + } + } + + if (self.settings.border) { + box = self.borderBox; + self.$el.css({ + 'border-top-width': box.top, + 'border-right-width': box.right, + 'border-bottom-width': box.bottom, + 'border-left-width': box.left + }); + } + + // Add instance to lookup + var root = self.getRoot(); + if (!root.controlIdLookup) { + root.controlIdLookup = {}; + } + + root.controlIdLookup[self._id] = self; + + for (var key in self._aria) { + self.aria(key, self._aria[key]); + } + + if (self.state.get('visible') === false) { + self.getEl().style.display = 'none'; + } + + self.bindStates(); + + self.state.on('change:visible', function(e) { + var state = e.value, parentCtrl; + + if (self.state.get('rendered')) { + self.getEl().style.display = state === false ? 'none' : ''; + + // Need to force a reflow here on IE 8 + self.getEl().getBoundingClientRect(); + } + + // Parent container needs to reflow + parentCtrl = self.parent(); + if (parentCtrl) { + parentCtrl._lastRect = null; + } + + self.fire(state ? 'show' : 'hide'); + + ReflowQueue.add(self); + }); + + self.fire('postrender', {}, false); + }, + + bindStates: function() { + }, + + /** + * Scrolls the current control into view. + * + * @method scrollIntoView + * @param {String} align Alignment in view top|center|bottom. + * @return {tinymce.ui.Control} Current control instance. + */ + scrollIntoView: function(align) { + function getOffset(elm, rootElm) { + var x, y, parent = elm; + + x = y = 0; + while (parent && parent != rootElm && parent.nodeType) { + x += parent.offsetLeft || 0; + y += parent.offsetTop || 0; + parent = parent.offsetParent; + } + + return {x: x, y: y}; + } + + var elm = this.getEl(), parentElm = elm.parentNode; + var x, y, width, height, parentWidth, parentHeight; + var pos = getOffset(elm, parentElm); + + x = pos.x; + y = pos.y; + width = elm.offsetWidth; + height = elm.offsetHeight; + parentWidth = parentElm.clientWidth; + parentHeight = parentElm.clientHeight; + + if (align == "end") { + x -= parentWidth - width; + y -= parentHeight - height; + } else if (align == "center") { + x -= (parentWidth / 2) - (width / 2); + y -= (parentHeight / 2) - (height / 2); + } + + parentElm.scrollLeft = x; + parentElm.scrollTop = y; + + return this; + }, + + getRoot: function() { + var ctrl = this, rootControl, parents = []; + + while (ctrl) { + if (ctrl.rootControl) { + rootControl = ctrl.rootControl; + break; + } + + parents.push(ctrl); + rootControl = ctrl; + ctrl = ctrl.parent(); + } + + if (!rootControl) { + rootControl = this; + } + + var i = parents.length; + while (i--) { + parents[i].rootControl = rootControl; + } + + return rootControl; + }, + + /** + * Reflows the current control and it's parents. + * This should be used after you for example append children to the current control so + * that the layout managers know that they need to reposition everything. + * + * @example + * container.append({type: 'button', text: 'My button'}).reflow(); + * + * @method reflow + * @return {tinymce.ui.Control} Current control instance. + */ + reflow: function() { + ReflowQueue.remove(this); + + var parent = this.parent(); + if (parent._layout && !parent._layout.isNative()) { + parent.reflow(); + } + + return this; + } + + /** + * Sets/gets the parent container for the control. + * + * @method parent + * @param {tinymce.ui.Container} parent Optional parent to set. + * @return {tinymce.ui.Control} Parent control or the current control on a set action. + */ + // parent: function(parent) {} -- Generated + + /** + * Sets/gets the text for the control. + * + * @method text + * @param {String} value Value to set to control. + * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. + */ + // text: function(value) {} -- Generated + + /** + * Sets/gets the disabled state on the control. + * + * @method disabled + * @param {Boolean} state Value to set to control. + * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. + */ + // disabled: function(state) {} -- Generated + + /** + * Sets/gets the active for the control. + * + * @method active + * @param {Boolean} state Value to set to control. + * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. + */ + // active: function(state) {} -- Generated + + /** + * Sets/gets the name for the control. + * + * @method name + * @param {String} value Value to set to control. + * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. + */ + // name: function(value) {} -- Generated + + /** + * Sets/gets the title for the control. + * + * @method title + * @param {String} value Value to set to control. + * @return {String/tinymce.ui.Control} Current control on a set operation or current value on a get. + */ + // title: function(value) {} -- Generated + + /** + * Sets/gets the visible for the control. + * + * @method visible + * @param {Boolean} state Value to set to control. + * @return {Boolean/tinymce.ui.Control} Current control on a set operation or current state on a get. + */ + // visible: function(value) {} -- Generated + }; + + /** + * Setup state properties. + */ + Tools.each('text title visible disabled active value'.split(' '), function(name) { + proto[name] = function(value) { + if (arguments.length === 0) { + return this.state.get(name); + } + + if (typeof value != "undefined") { + this.state.set(name, value); + } + + return this; + }; + }); + + Control = Class.extend(proto); + + function getEventDispatcher(obj) { + if (!obj._eventDispatcher) { + obj._eventDispatcher = new EventDispatcher({ + scope: obj, + toggleEvent: function(name, state) { + if (state && EventDispatcher.isNative(name)) { + if (!obj._nativeEvents) { + obj._nativeEvents = {}; + } + + obj._nativeEvents[name] = true; + + if (obj.state.get('rendered')) { + bindPendingEvents(obj); + } + } + } + }); + } + + return obj._eventDispatcher; + } + + function bindPendingEvents(eventCtrl) { + var i, l, parents, eventRootCtrl, nativeEvents, name; + + function delegate(e) { + var control = eventCtrl.getParentCtrl(e.target); + + if (control) { + control.fire(e.type, e); + } + } + + function mouseLeaveHandler() { + var ctrl = eventRootCtrl._lastHoverCtrl; + + if (ctrl) { + ctrl.fire("mouseleave", {target: ctrl.getEl()}); + + ctrl.parents().each(function(ctrl) { + ctrl.fire("mouseleave", {target: ctrl.getEl()}); + }); + + eventRootCtrl._lastHoverCtrl = null; + } + } + + function mouseEnterHandler(e) { + var ctrl = eventCtrl.getParentCtrl(e.target), lastCtrl = eventRootCtrl._lastHoverCtrl, idx = 0, i, parents, lastParents; + + // Over on a new control + if (ctrl !== lastCtrl) { + eventRootCtrl._lastHoverCtrl = ctrl; + + parents = ctrl.parents().toArray().reverse(); + parents.push(ctrl); + + if (lastCtrl) { + lastParents = lastCtrl.parents().toArray().reverse(); + lastParents.push(lastCtrl); + + for (idx = 0; idx < lastParents.length; idx++) { + if (parents[idx] !== lastParents[idx]) { + break; + } + } + + for (i = lastParents.length - 1; i >= idx; i--) { + lastCtrl = lastParents[i]; + lastCtrl.fire("mouseleave", { + target: lastCtrl.getEl() + }); + } + } + + for (i = idx; i < parents.length; i++) { + ctrl = parents[i]; + ctrl.fire("mouseenter", { + target: ctrl.getEl() + }); + } + } + } + + function fixWheelEvent(e) { + e.preventDefault(); + + if (e.type == "mousewheel") { + e.deltaY = -1 / 40 * e.wheelDelta; + + if (e.wheelDeltaX) { + e.deltaX = -1 / 40 * e.wheelDeltaX; + } + } else { + e.deltaX = 0; + e.deltaY = e.detail; + } + + e = eventCtrl.fire("wheel", e); + } + + nativeEvents = eventCtrl._nativeEvents; + if (nativeEvents) { + // Find event root element if it exists + parents = eventCtrl.parents().toArray(); + parents.unshift(eventCtrl); + for (i = 0, l = parents.length; !eventRootCtrl && i < l; i++) { + eventRootCtrl = parents[i]._eventsRoot; + } + + // Event root wasn't found the use the root control + if (!eventRootCtrl) { + eventRootCtrl = parents[parents.length - 1] || eventCtrl; + } + + // Set the eventsRoot property on children that didn't have it + eventCtrl._eventsRoot = eventRootCtrl; + for (l = i, i = 0; i < l; i++) { + parents[i]._eventsRoot = eventRootCtrl; + } + + var eventRootDelegates = eventRootCtrl._delegates; + if (!eventRootDelegates) { + eventRootDelegates = eventRootCtrl._delegates = {}; + } + + // Bind native event delegates + for (name in nativeEvents) { + if (!nativeEvents) { + return false; + } + + if (name === "wheel" && !hasWheelEventSupport) { + if (hasMouseWheelEventSupport) { + $(eventCtrl.getEl()).on("mousewheel", fixWheelEvent); + } else { + $(eventCtrl.getEl()).on("DOMMouseScroll", fixWheelEvent); + } + + continue; + } + + // Special treatment for mousenter/mouseleave since these doesn't bubble + if (name === "mouseenter" || name === "mouseleave") { + // Fake mousenter/mouseleave + if (!eventRootCtrl._hasMouseEnter) { + $(eventRootCtrl.getEl()).on("mouseleave", mouseLeaveHandler).on("mouseover", mouseEnterHandler); + eventRootCtrl._hasMouseEnter = 1; + } + } else if (!eventRootDelegates[name]) { + $(eventRootCtrl.getEl()).on(name, delegate); + eventRootDelegates[name] = true; + } + + // Remove the event once it's bound + nativeEvents[name] = false; + } + } + } + + return Control; +}); + +// Included from: js/tinymce/classes/ui/Factory.js + +/** + * Factory.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +/** + * This class is a factory for control instances. This enables you + * to create instances of controls without having to require the UI controls directly. + * + * It also allow you to override or add new control types. + * + * @class tinymce.ui.Factory + */ +define("tinymce/ui/Factory", [], function() { + "use strict"; + + var types = {}, namespaceInit; + + return { + /** + * Adds a new control instance type to the factory. + * + * @method add + * @param {String} type Type name for example "button". + * @param {function} typeClass Class type function. + */ + add: function(type, typeClass) { + types[type.toLowerCase()] = typeClass; + }, + + /** + * Returns true/false if the specified type exists or not. + * + * @method has + * @param {String} type Type to look for. + * @return {Boolean} true/false if the control by name exists. + */ + has: function(type) { + return !!types[type.toLowerCase()]; + }, + + /** + * Creates a new control instance based on the settings provided. The instance created will be + * based on the specified type property it can also create whole structures of components out of + * the specified JSON object. + * + * @example + * tinymce.ui.Factory.create({ + * type: 'button', + * text: 'Hello world!' + * }); + * + * @method create + * @param {Object/String} settings Name/Value object with items used to create the type. + * @return {tinymce.ui.Control} Control instance based on the specified type. + */ + create: function(type, settings) { + var ControlType, name, namespace; + + // Build type lookup + if (!namespaceInit) { + namespace = tinymce.ui; + + for (name in namespace) { + types[name.toLowerCase()] = namespace[name]; + } + + namespaceInit = true; + } + + // If string is specified then use it as the type + if (typeof type == 'string') { + settings = settings || {}; + settings.type = type; + } else { + settings = type; + type = settings.type; + } + + // Find control type + type = type.toLowerCase(); + ControlType = types[type]; + + // #if debug + + if (!ControlType) { + throw new Error("Could not find control by type: " + type); + } + + // #endif + + ControlType = new ControlType(settings); + ControlType.type = type; // Set the type on the instance, this will be used by the Selector engine + + return ControlType; + } + }; +}); + +// Included from: js/tinymce/classes/ui/KeyboardNavigation.js + +/** + * KeyboardNavigation.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class handles keyboard navigation of controls and elements. + * + * @class tinymce.ui.KeyboardNavigation + */ +define("tinymce/ui/KeyboardNavigation", [ +], function() { + "use strict"; + + /** + * This class handles all keyboard navigation for WAI-ARIA support. Each root container + * gets an instance of this class. + * + * @constructor + */ + return function(settings) { + var root = settings.root, focusedElement, focusedControl; + + function isElement(node) { + return node && node.nodeType === 1; + } + + try { + focusedElement = document.activeElement; + } catch (ex) { + // IE sometimes fails to return a proper element + focusedElement = document.body; + } + + focusedControl = root.getParentCtrl(focusedElement); + + /** + * Returns the currently focused elements wai aria role of the currently + * focused element or specified element. + * + * @private + * @param {Element} elm Optional element to get role from. + * @return {String} Role of specified element. + */ + function getRole(elm) { + elm = elm || focusedElement; + + if (isElement(elm)) { + return elm.getAttribute('role'); + } + + return null; + } + + /** + * Returns the wai role of the parent element of the currently + * focused element or specified element. + * + * @private + * @param {Element} elm Optional element to get parent role from. + * @return {String} Role of the first parent that has a role. + */ + function getParentRole(elm) { + var role, parent = elm || focusedElement; + + while ((parent = parent.parentNode)) { + if ((role = getRole(parent))) { + return role; + } + } + } + + /** + * Returns a wai aria property by name for example aria-selected. + * + * @private + * @param {String} name Name of the aria property to get for example "disabled". + * @return {String} Aria property value. + */ + function getAriaProp(name) { + var elm = focusedElement; + + if (isElement(elm)) { + return elm.getAttribute('aria-' + name); + } + } + + /** + * Is the element a text input element or not. + * + * @private + * @param {Element} elm Element to check if it's an text input element or not. + * @return {Boolean} True/false if the element is a text element or not. + */ + function isTextInputElement(elm) { + var tagName = elm.tagName.toUpperCase(); + + // Notice: since type can be "email" etc we don't check the type + // So all input elements gets treated as text input elements + return tagName == "INPUT" || tagName == "TEXTAREA" || tagName == "SELECT"; + } + + /** + * Returns true/false if the specified element can be focused or not. + * + * @private + * @param {Element} elm DOM element to check if it can be focused or not. + * @return {Boolean} True/false if the element can have focus. + */ + function canFocus(elm) { + if (isTextInputElement(elm) && !elm.hidden) { + return true; + } + + if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell|slider)$/.test(getRole(elm))) { + return true; + } + + return false; + } + + /** + * Returns an array of focusable visible elements within the specified container element. + * + * @private + * @param {Element} elm DOM element to find focusable elements within. + * @return {Array} Array of focusable elements. + */ + function getFocusElements(elm) { + var elements = []; + + function collect(elm) { + if (elm.nodeType != 1 || elm.style.display == 'none' || elm.disabled) { + return; + } + + if (canFocus(elm)) { + elements.push(elm); + } + + for (var i = 0; i < elm.childNodes.length; i++) { + collect(elm.childNodes[i]); + } + } + + collect(elm || root.getEl()); + + return elements; + } + + /** + * Returns the navigation root control for the specified control. The navigation root + * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group. + * It will look for parents of the specified target control or the currently focused control if this option is omitted. + * + * @private + * @param {tinymce.ui.Control} targetControl Optional target control to find root of. + * @return {tinymce.ui.Control} Navigation root control. + */ + function getNavigationRoot(targetControl) { + var navigationRoot, controls; + + targetControl = targetControl || focusedControl; + controls = targetControl.parents().toArray(); + controls.unshift(targetControl); + + for (var i = 0; i < controls.length; i++) { + navigationRoot = controls[i]; + + if (navigationRoot.settings.ariaRoot) { + break; + } + } + + return navigationRoot; + } + + /** + * Focuses the first item in the specified targetControl element or the last aria index if the + * navigation root has the ariaRemember option enabled. + * + * @private + * @param {tinymce.ui.Control} targetControl Target control to focus the first item in. + */ + function focusFirst(targetControl) { + var navigationRoot = getNavigationRoot(targetControl); + var focusElements = getFocusElements(navigationRoot.getEl()); + + if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) { + moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements); + } else { + moveFocusToIndex(0, focusElements); + } + } + + /** + * Moves the focus to the specified index within the elements list. + * This will scope the index to the size of the element list if it changed. + * + * @private + * @param {Number} idx Specified index to move to. + * @param {Array} elements Array with dom elements to move focus within. + * @return {Number} Input index or a changed index if it was out of range. + */ + function moveFocusToIndex(idx, elements) { + if (idx < 0) { + idx = elements.length - 1; + } else if (idx >= elements.length) { + idx = 0; + } + + if (elements[idx]) { + elements[idx].focus(); + } + + return idx; + } + + /** + * Moves the focus forwards or backwards. + * + * @private + * @param {Number} dir Direction to move in positive means forward, negative means backwards. + * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements. + */ + function moveFocus(dir, elements) { + var idx = -1, navigationRoot = getNavigationRoot(); + + elements = elements || getFocusElements(navigationRoot.getEl()); + + for (var i = 0; i < elements.length; i++) { + if (elements[i] === focusedElement) { + idx = i; + } + } + + idx += dir; + navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements); + } + + /** + * Moves the focus to the left this is called by the left key. + * + * @private + */ + function left() { + var parentRole = getParentRole(); + + if (parentRole == "tablist") { + moveFocus(-1, getFocusElements(focusedElement.parentNode)); + } else if (focusedControl.parent().submenu) { + cancel(); + } else { + moveFocus(-1); + } + } + + /** + * Moves the focus to the right this is called by the right key. + * + * @private + */ + function right() { + var role = getRole(), parentRole = getParentRole(); + + if (parentRole == "tablist") { + moveFocus(1, getFocusElements(focusedElement.parentNode)); + } else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) { + enter(); + } else { + moveFocus(1); + } + } + + /** + * Moves the focus to the up this is called by the up key. + * + * @private + */ + function up() { + moveFocus(-1); + } + + /** + * Moves the focus to the up this is called by the down key. + * + * @private + */ + function down() { + var role = getRole(), parentRole = getParentRole(); + + if (role == "menuitem" && parentRole == "menubar") { + enter(); + } else if (role == "button" && getAriaProp('haspopup')) { + enter({key: 'down'}); + } else { + moveFocus(1); + } + } + + /** + * Moves the focus to the next item or previous item depending on shift key. + * + * @private + * @param {DOMEvent} e DOM event object. + */ + function tab(e) { + var parentRole = getParentRole(); + + if (parentRole == "tablist") { + var elm = getFocusElements(focusedControl.getEl('body'))[0]; + + if (elm) { + elm.focus(); + } + } else { + moveFocus(e.shiftKey ? -1 : 1); + } + } + + /** + * Calls the cancel event on the currently focused control. This is normally done using the Esc key. + * + * @private + */ + function cancel() { + focusedControl.fire('cancel'); + } + + /** + * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys. + * + * @private + * @param {Object} aria Optional aria data to pass along with the enter event. + */ + function enter(aria) { + aria = aria || {}; + focusedControl.fire('click', {target: focusedElement, aria: aria}); + } + + root.on('keydown', function(e) { + function handleNonTabOrEscEvent(e, handler) { + // Ignore non tab keys for text elements + if (isTextInputElement(focusedElement)) { + return; + } + + if (getRole(focusedElement) === 'slider') { + return; + } + + if (handler(e) !== false) { + e.preventDefault(); + } + } + + if (e.isDefaultPrevented()) { + return; + } + + switch (e.keyCode) { + case 37: // DOM_VK_LEFT + handleNonTabOrEscEvent(e, left); + break; + + case 39: // DOM_VK_RIGHT + handleNonTabOrEscEvent(e, right); + break; + + case 38: // DOM_VK_UP + handleNonTabOrEscEvent(e, up); + break; + + case 40: // DOM_VK_DOWN + handleNonTabOrEscEvent(e, down); + break; + + case 27: // DOM_VK_ESCAPE + cancel(); + break; + + case 14: // DOM_VK_ENTER + case 13: // DOM_VK_RETURN + case 32: // DOM_VK_SPACE + handleNonTabOrEscEvent(e, enter); + break; + + case 9: // DOM_VK_TAB + if (tab(e) !== false) { + e.preventDefault(); + } + break; + } + }); + + root.on('focusin', function(e) { + focusedElement = e.target; + focusedControl = e.control; + }); + + return { + focusFirst: focusFirst + }; + }; +}); + +// Included from: js/tinymce/classes/ui/Container.js + +/** + * Container.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Container control. This is extended by all controls that can have + * children such as panels etc. You can also use this class directly as an + * generic container instance. The container doesn't have any specific role or style. + * + * @-x-less Container.less + * @class tinymce.ui.Container + * @extends tinymce.ui.Control + */ +define("tinymce/ui/Container", [ + "tinymce/ui/Control", + "tinymce/ui/Collection", + "tinymce/ui/Selector", + "tinymce/ui/Factory", + "tinymce/ui/KeyboardNavigation", + "tinymce/util/Tools", + "tinymce/dom/DomQuery", + "tinymce/ui/ClassList", + "tinymce/ui/ReflowQueue" +], function(Control, Collection, Selector, Factory, KeyboardNavigation, Tools, $, ClassList, ReflowQueue) { + "use strict"; + + var selectorCache = {}; + + return Control.extend({ + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Array} items Items to add to container in JSON format or control instances. + * @setting {String} layout Layout manager by name to use. + * @setting {Object} defaults Default settings to apply to all items. + */ + init: function(settings) { + var self = this; + + self._super(settings); + settings = self.settings; + + if (settings.fixed) { + self.state.set('fixed', true); + } + + self._items = new Collection(); + + if (self.isRtl()) { + self.classes.add('rtl'); + } + + self.bodyClasses = new ClassList(function() { + if (self.state.get('rendered')) { + self.getEl('body').className = this.toString(); + } + }); + self.bodyClasses.prefix = self.classPrefix; + + self.classes.add('container'); + self.bodyClasses.add('container-body'); + + if (settings.containerCls) { + self.classes.add(settings.containerCls); + } + + self._layout = Factory.create((settings.layout || '') + 'layout'); + + if (self.settings.items) { + self.add(self.settings.items); + } else { + self.add(self.render()); + } + + // TODO: Fix this! + self._hasBody = true; + }, + + /** + * Returns a collection of child items that the container currently have. + * + * @method items + * @return {tinymce.ui.Collection} Control collection direct child controls. + */ + items: function() { + return this._items; + }, + + /** + * Find child controls by selector. + * + * @method find + * @param {String} selector Selector CSS pattern to find children by. + * @return {tinymce.ui.Collection} Control collection with child controls. + */ + find: function(selector) { + selector = selectorCache[selector] = selectorCache[selector] || new Selector(selector); + + return selector.find(this); + }, + + /** + * Adds one or many items to the current container. This will create instances of + * the object representations if needed. + * + * @method add + * @param {Array/Object/tinymce.ui.Control} items Array or item that will be added to the container. + * @return {tinymce.ui.Collection} Current collection control. + */ + add: function(items) { + var self = this; + + self.items().add(self.create(items)).parent(self); + + return self; + }, + + /** + * Focuses the current container instance. This will look + * for the first control in the container and focus that. + * + * @method focus + * @param {Boolean} keyboard Optional true/false if the focus was a keyboard focus or not. + * @return {tinymce.ui.Collection} Current instance. + */ + focus: function(keyboard) { + var self = this, focusCtrl, keyboardNav, items; + + if (keyboard) { + keyboardNav = self.keyboardNav || self.parents().eq(-1)[0].keyboardNav; + + if (keyboardNav) { + keyboardNav.focusFirst(self); + return; + } + } + + items = self.find('*'); + + // TODO: Figure out a better way to auto focus alert dialog buttons + if (self.statusbar) { + items.add(self.statusbar.items()); + } + + items.each(function(ctrl) { + if (ctrl.settings.autofocus) { + focusCtrl = null; + return false; + } + + if (ctrl.canFocus) { + focusCtrl = focusCtrl || ctrl; + } + }); + + if (focusCtrl) { + focusCtrl.focus(); + } + + return self; + }, + + /** + * Replaces the specified child control with a new control. + * + * @method replace + * @param {tinymce.ui.Control} oldItem Old item to be replaced. + * @param {tinymce.ui.Control} newItem New item to be inserted. + */ + replace: function(oldItem, newItem) { + var ctrlElm, items = this.items(), i = items.length; + + // Replace the item in collection + while (i--) { + if (items[i] === oldItem) { + items[i] = newItem; + break; + } + } + + if (i >= 0) { + // Remove new item from DOM + ctrlElm = newItem.getEl(); + if (ctrlElm) { + ctrlElm.parentNode.removeChild(ctrlElm); + } + + // Remove old item from DOM + ctrlElm = oldItem.getEl(); + if (ctrlElm) { + ctrlElm.parentNode.removeChild(ctrlElm); + } + } + + // Adopt the item + newItem.parent(this); + }, + + /** + * Creates the specified items. If any of the items is plain JSON style objects + * it will convert these into real tinymce.ui.Control instances. + * + * @method create + * @param {Array} items Array of items to convert into control instances. + * @return {Array} Array with control instances. + */ + create: function(items) { + var self = this, settings, ctrlItems = []; + + // Non array structure, then force it into an array + if (!Tools.isArray(items)) { + items = [items]; + } + + // Add default type to each child control + Tools.each(items, function(item) { + if (item) { + // Construct item if needed + if (!(item instanceof Control)) { + // Name only then convert it to an object + if (typeof item == "string") { + item = {type: item}; + } + + // Create control instance based on input settings and default settings + settings = Tools.extend({}, self.settings.defaults, item); + item.type = settings.type = settings.type || item.type || self.settings.defaultType || + (settings.defaults ? settings.defaults.type : null); + item = Factory.create(settings); + } + + ctrlItems.push(item); + } + }); + + return ctrlItems; + }, + + /** + * Renders new control instances. + * + * @private + */ + renderNew: function() { + var self = this; + + // Render any new items + self.items().each(function(ctrl, index) { + var containerElm; + + ctrl.parent(self); + + if (!ctrl.state.get('rendered')) { + containerElm = self.getEl('body'); + + // Insert or append the item + if (containerElm.hasChildNodes() && index <= containerElm.childNodes.length - 1) { + $(containerElm.childNodes[index]).before(ctrl.renderHtml()); + } else { + $(containerElm).append(ctrl.renderHtml()); + } + + ctrl.postRender(); + ReflowQueue.add(ctrl); + } + }); + + self._layout.applyClasses(self.items().filter(':visible')); + self._lastRect = null; + + return self; + }, + + /** + * Appends new instances to the current container. + * + * @method append + * @param {Array/tinymce.ui.Collection} items Array if controls to append. + * @return {tinymce.ui.Container} Current container instance. + */ + append: function(items) { + return this.add(items).renderNew(); + }, + + /** + * Prepends new instances to the current container. + * + * @method prepend + * @param {Array/tinymce.ui.Collection} items Array if controls to prepend. + * @return {tinymce.ui.Container} Current container instance. + */ + prepend: function(items) { + var self = this; + + self.items().set(self.create(items).concat(self.items().toArray())); + + return self.renderNew(); + }, + + /** + * Inserts an control at a specific index. + * + * @method insert + * @param {Array/tinymce.ui.Collection} items Array if controls to insert. + * @param {Number} index Index to insert controls at. + * @param {Boolean} [before=false] Inserts controls before the index. + */ + insert: function(items, index, before) { + var self = this, curItems, beforeItems, afterItems; + + items = self.create(items); + curItems = self.items(); + + if (!before && index < curItems.length - 1) { + index += 1; + } + + if (index >= 0 && index < curItems.length) { + beforeItems = curItems.slice(0, index).toArray(); + afterItems = curItems.slice(index).toArray(); + curItems.set(beforeItems.concat(items, afterItems)); + } + + return self.renderNew(); + }, + + /** + * Populates the form fields from the specified JSON data object. + * + * Control items in the form that matches the data will have it's value set. + * + * @method fromJSON + * @param {Object} data JSON data object to set control values by. + * @return {tinymce.ui.Container} Current form instance. + */ + fromJSON: function(data) { + var self = this; + + for (var name in data) { + self.find('#' + name).value(data[name]); + } + + return self; + }, + + /** + * Serializes the form into a JSON object by getting all items + * that has a name and a value. + * + * @method toJSON + * @return {Object} JSON object with form data. + */ + toJSON: function() { + var self = this, data = {}; + + self.find('*').each(function(ctrl) { + var name = ctrl.name(), value = ctrl.value(); + + if (name && typeof value != "undefined") { + data[name] = value; + } + }); + + return data; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, layout = self._layout, role = this.settings.role; + + self.preRender(); + layout.preRender(self); + + return ( + '
' + + '
' + + (self.settings.html || '') + layout.renderHtml(self) + + '
' + + '
' + ); + }, + + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.Container} Current combobox instance. + */ + postRender: function() { + var self = this, box; + + self.items().exec('postRender'); + self._super(); + + self._layout.postRender(self); + self.state.set('rendered', true); + + if (self.settings.style) { + self.$el.css(self.settings.style); + } + + if (self.settings.border) { + box = self.borderBox; + self.$el.css({ + 'border-top-width': box.top, + 'border-right-width': box.right, + 'border-bottom-width': box.bottom, + 'border-left-width': box.left + }); + } + + if (!self.parent()) { + self.keyboardNav = new KeyboardNavigation({ + root: self + }); + } + + return self; + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, layoutRect = self._super(); + + // Recalc container size by asking layout manager + self._layout.recalc(self); + + return layoutRect; + }, + + /** + * Recalculates the positions of the controls in the current container. + * This is invoked by the reflow method and shouldn't be called directly. + * + * @method recalc + */ + recalc: function() { + var self = this, rect = self._layoutRect, lastRect = self._lastRect; + + if (!lastRect || lastRect.w != rect.w || lastRect.h != rect.h) { + self._layout.recalc(self); + rect = self.layoutRect(); + self._lastRect = {x: rect.x, y: rect.y, w: rect.w, h: rect.h}; + return true; + } + }, + + /** + * Reflows the current container and it's children and possible parents. + * This should be used after you for example append children to the current control so + * that the layout managers know that they need to reposition everything. + * + * @example + * container.append({type: 'button', text: 'My button'}).reflow(); + * + * @method reflow + * @return {tinymce.ui.Container} Current container instance. + */ + reflow: function() { + var i; + + ReflowQueue.remove(this); + + if (this.visible()) { + Control.repaintControls = []; + Control.repaintControls.map = {}; + + this.recalc(); + i = Control.repaintControls.length; + + while (i--) { + Control.repaintControls[i].repaint(); + } + + // TODO: Fix me! + if (this.settings.layout !== "flow" && this.settings.layout !== "stack") { + this.repaint(); + } + + Control.repaintControls = []; + } + + return this; + } + }); +}); + +// Included from: js/tinymce/classes/ui/DragHelper.js + +/** + * DragHelper.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Drag/drop helper class. + * + * @example + * var dragHelper = new tinymce.ui.DragHelper('mydiv', { + * start: function(evt) { + * }, + * + * drag: function(evt) { + * }, + * + * end: function(evt) { + * } + * }); + * + * @class tinymce.ui.DragHelper + */ +define("tinymce/ui/DragHelper", [ + "tinymce/dom/DomQuery" +], function($) { + "use strict"; + + function getDocumentSize(doc) { + var documentElement, body, scrollWidth, clientWidth; + var offsetWidth, scrollHeight, clientHeight, offsetHeight, max = Math.max; + + documentElement = doc.documentElement; + body = doc.body; + + scrollWidth = max(documentElement.scrollWidth, body.scrollWidth); + clientWidth = max(documentElement.clientWidth, body.clientWidth); + offsetWidth = max(documentElement.offsetWidth, body.offsetWidth); + + scrollHeight = max(documentElement.scrollHeight, body.scrollHeight); + clientHeight = max(documentElement.clientHeight, body.clientHeight); + offsetHeight = max(documentElement.offsetHeight, body.offsetHeight); + + return { + width: scrollWidth < offsetWidth ? clientWidth : scrollWidth, + height: scrollHeight < offsetHeight ? clientHeight : scrollHeight + }; + } + + function updateWithTouchData(e) { + var keys, i; + + if (e.changedTouches) { + keys = "screenX screenY pageX pageY clientX clientY".split(' '); + for (i = 0; i < keys.length; i++) { + e[keys[i]] = e.changedTouches[0][keys[i]]; + } + } + } + + return function(id, settings) { + var $eventOverlay, doc = settings.document || document, downButton, start, stop, drag, startX, startY; + + settings = settings || {}; + + function getHandleElm() { + return doc.getElementById(settings.handle || id); + } + + start = function(e) { + var docSize = getDocumentSize(doc), handleElm, cursor; + + updateWithTouchData(e); + + e.preventDefault(); + downButton = e.button; + handleElm = getHandleElm(); + startX = e.screenX; + startY = e.screenY; + + // Grab cursor from handle so we can place it on overlay + if (window.getComputedStyle) { + cursor = window.getComputedStyle(handleElm, null).getPropertyValue("cursor"); + } else { + cursor = handleElm.runtimeStyle.cursor; + } + + $eventOverlay = $('
').css({ + position: "absolute", + top: 0, left: 0, + width: docSize.width, + height: docSize.height, + zIndex: 0x7FFFFFFF, + opacity: 0.0001, + cursor: cursor + }).appendTo(doc.body); + + $(doc).on('mousemove touchmove', drag).on('mouseup touchend', stop); + + settings.start(e); + }; + + drag = function(e) { + updateWithTouchData(e); + + if (e.button !== downButton) { + return stop(e); + } + + e.deltaX = e.screenX - startX; + e.deltaY = e.screenY - startY; + + e.preventDefault(); + settings.drag(e); + }; + + stop = function(e) { + updateWithTouchData(e); + + $(doc).off('mousemove touchmove', drag).off('mouseup touchend', stop); + + $eventOverlay.remove(); + + if (settings.stop) { + settings.stop(e); + } + }; + + /** + * Destroys the drag/drop helper instance. + * + * @method destroy + */ + this.destroy = function() { + $(getHandleElm()).off(); + }; + + $(getHandleElm()).on('mousedown touchstart', start); + }; +}); + +// Included from: js/tinymce/classes/ui/Scrollable.js + +/** + * Scrollable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This mixin makes controls scrollable using custom scrollbars. + * + * @-x-less Scrollable.less + * @mixin tinymce.ui.Scrollable + */ +define("tinymce/ui/Scrollable", [ + "tinymce/dom/DomQuery", + "tinymce/ui/DragHelper" +], function($, DragHelper) { + "use strict"; + + return { + init: function() { + var self = this; + self.on('repaint', self.renderScroll); + }, + + renderScroll: function() { + var self = this, margin = 2; + + function repaintScroll() { + var hasScrollH, hasScrollV, bodyElm; + + function repaintAxis(axisName, posName, sizeName, contentSizeName, hasScroll, ax) { + var containerElm, scrollBarElm, scrollThumbElm; + var containerSize, scrollSize, ratio, rect; + var posNameLower, sizeNameLower; + + scrollBarElm = self.getEl('scroll' + axisName); + if (scrollBarElm) { + posNameLower = posName.toLowerCase(); + sizeNameLower = sizeName.toLowerCase(); + + $(self.getEl('absend')).css(posNameLower, self.layoutRect()[contentSizeName] - 1); + + if (!hasScroll) { + $(scrollBarElm).css('display', 'none'); + return; + } + + $(scrollBarElm).css('display', 'block'); + containerElm = self.getEl('body'); + scrollThumbElm = self.getEl('scroll' + axisName + "t"); + containerSize = containerElm["client" + sizeName] - (margin * 2); + containerSize -= hasScrollH && hasScrollV ? scrollBarElm["client" + ax] : 0; + scrollSize = containerElm["scroll" + sizeName]; + ratio = containerSize / scrollSize; + + rect = {}; + rect[posNameLower] = containerElm["offset" + posName] + margin; + rect[sizeNameLower] = containerSize; + $(scrollBarElm).css(rect); + + rect = {}; + rect[posNameLower] = containerElm["scroll" + posName] * ratio; + rect[sizeNameLower] = containerSize * ratio; + $(scrollThumbElm).css(rect); + } + } + + bodyElm = self.getEl('body'); + hasScrollH = bodyElm.scrollWidth > bodyElm.clientWidth; + hasScrollV = bodyElm.scrollHeight > bodyElm.clientHeight; + + repaintAxis("h", "Left", "Width", "contentW", hasScrollH, "Height"); + repaintAxis("v", "Top", "Height", "contentH", hasScrollV, "Width"); + } + + function addScroll() { + function addScrollAxis(axisName, posName, sizeName, deltaPosName, ax) { + var scrollStart, axisId = self._id + '-scroll' + axisName, prefix = self.classPrefix; + + $(self.getEl()).append( + '
' + + '
' + + '
' + ); + + self.draghelper = new DragHelper(axisId + 't', { + start: function() { + scrollStart = self.getEl('body')["scroll" + posName]; + $('#' + axisId).addClass(prefix + 'active'); + }, + + drag: function(e) { + var ratio, hasScrollH, hasScrollV, containerSize, layoutRect = self.layoutRect(); + + hasScrollH = layoutRect.contentW > layoutRect.innerW; + hasScrollV = layoutRect.contentH > layoutRect.innerH; + containerSize = self.getEl('body')["client" + sizeName] - (margin * 2); + containerSize -= hasScrollH && hasScrollV ? self.getEl('scroll' + axisName)["client" + ax] : 0; + + ratio = containerSize / self.getEl('body')["scroll" + sizeName]; + self.getEl('body')["scroll" + posName] = scrollStart + (e["delta" + deltaPosName] / ratio); + }, + + stop: function() { + $('#' + axisId).removeClass(prefix + 'active'); + } + }); + } + + self.classes.add('scroll'); + + addScrollAxis("v", "Top", "Height", "Y", "Width"); + addScrollAxis("h", "Left", "Width", "X", "Height"); + } + + if (self.settings.autoScroll) { + if (!self._hasScroll) { + self._hasScroll = true; + addScroll(); + + self.on('wheel', function(e) { + var bodyEl = self.getEl('body'); + + bodyEl.scrollLeft += (e.deltaX || 0) * 10; + bodyEl.scrollTop += e.deltaY * 10; + + repaintScroll(); + }); + + $(self.getEl('body')).on("scroll", repaintScroll); + } + + repaintScroll(); + } + } + }; +}); + +// Included from: js/tinymce/classes/ui/Panel.js + +/** + * Panel.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new panel. + * + * @-x-less Panel.less + * @class tinymce.ui.Panel + * @extends tinymce.ui.Container + * @mixes tinymce.ui.Scrollable + */ +define("tinymce/ui/Panel", [ + "tinymce/ui/Container", + "tinymce/ui/Scrollable" +], function(Container, Scrollable) { + "use strict"; + + return Container.extend({ + Defaults: { + layout: 'fit', + containerCls: 'panel' + }, + + Mixins: [Scrollable], + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, layout = self._layout, innerHtml = self.settings.html; + + self.preRender(); + layout.preRender(self); + + if (typeof innerHtml == "undefined") { + innerHtml = ( + '
' + + layout.renderHtml(self) + + '
' + ); + } else { + if (typeof innerHtml == 'function') { + innerHtml = innerHtml.call(self); + } + + self._hasBody = false; + } + + return ( + '
' + + (self._preBodyHtml || '') + + innerHtml + + '
' + ); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Movable.js + +/** + * Movable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Movable mixin. Makes controls movable absolute and relative to other elements. + * + * @mixin tinymce.ui.Movable + */ +define("tinymce/ui/Movable", [ + "tinymce/ui/DomUtils" +], function(DomUtils) { + "use strict"; + + function calculateRelativePosition(ctrl, targetElm, rel) { + var ctrlElm, pos, x, y, selfW, selfH, targetW, targetH, viewport, size; + + viewport = DomUtils.getViewPort(); + + // Get pos of target + pos = DomUtils.getPos(targetElm); + x = pos.x; + y = pos.y; + + if (ctrl.state.get('fixed') && DomUtils.getRuntimeStyle(document.body, 'position') == 'static') { + x -= viewport.x; + y -= viewport.y; + } + + // Get size of self + ctrlElm = ctrl.getEl(); + size = DomUtils.getSize(ctrlElm); + selfW = size.width; + selfH = size.height; + + // Get size of target + size = DomUtils.getSize(targetElm); + targetW = size.width; + targetH = size.height; + + // Parse align string + rel = (rel || '').split(''); + + // Target corners + if (rel[0] === 'b') { + y += targetH; + } + + if (rel[1] === 'r') { + x += targetW; + } + + if (rel[0] === 'c') { + y += Math.round(targetH / 2); + } + + if (rel[1] === 'c') { + x += Math.round(targetW / 2); + } + + // Self corners + if (rel[3] === 'b') { + y -= selfH; + } + + if (rel[4] === 'r') { + x -= selfW; + } + + if (rel[3] === 'c') { + y -= Math.round(selfH / 2); + } + + if (rel[4] === 'c') { + x -= Math.round(selfW / 2); + } + + return { + x: x, + y: y, + w: selfW, + h: selfH + }; + } + + return { + /** + * Tests various positions to get the most suitable one. + * + * @method testMoveRel + * @param {DOMElement} elm Element to position against. + * @param {Array} rels Array with relative positions. + * @return {String} Best suitable relative position. + */ + testMoveRel: function(elm, rels) { + var viewPortRect = DomUtils.getViewPort(); + + for (var i = 0; i < rels.length; i++) { + var pos = calculateRelativePosition(this, elm, rels[i]); + + if (this.state.get('fixed')) { + if (pos.x > 0 && pos.x + pos.w < viewPortRect.w && pos.y > 0 && pos.y + pos.h < viewPortRect.h) { + return rels[i]; + } + } else { + if (pos.x > viewPortRect.x && pos.x + pos.w < viewPortRect.w + viewPortRect.x && + pos.y > viewPortRect.y && pos.y + pos.h < viewPortRect.h + viewPortRect.y) { + return rels[i]; + } + } + } + + return rels[0]; + }, + + /** + * Move relative to the specified element. + * + * @method moveRel + * @param {Element} elm Element to move relative to. + * @param {String} rel Relative mode. For example: br-tl. + * @return {tinymce.ui.Control} Current control instance. + */ + moveRel: function(elm, rel) { + if (typeof rel != 'string') { + rel = this.testMoveRel(elm, rel); + } + + var pos = calculateRelativePosition(this, elm, rel); + return this.moveTo(pos.x, pos.y); + }, + + /** + * Move by a relative x, y values. + * + * @method moveBy + * @param {Number} dx Relative x position. + * @param {Number} dy Relative y position. + * @return {tinymce.ui.Control} Current control instance. + */ + moveBy: function(dx, dy) { + var self = this, rect = self.layoutRect(); + + self.moveTo(rect.x + dx, rect.y + dy); + + return self; + }, + + /** + * Move to absolute position. + * + * @method moveTo + * @param {Number} x Absolute x position. + * @param {Number} y Absolute y position. + * @return {tinymce.ui.Control} Current control instance. + */ + moveTo: function(x, y) { + var self = this; + + // TODO: Move this to some global class + function constrain(value, max, size) { + if (value < 0) { + return 0; + } + + if (value + size > max) { + value = max - size; + return value < 0 ? 0 : value; + } + + return value; + } + + if (self.settings.constrainToViewport) { + var viewPortRect = DomUtils.getViewPort(window); + var layoutRect = self.layoutRect(); + + x = constrain(x, viewPortRect.w + viewPortRect.x, layoutRect.w); + y = constrain(y, viewPortRect.h + viewPortRect.y, layoutRect.h); + } + + if (self.state.get('rendered')) { + self.layoutRect({x: x, y: y}).repaint(); + } else { + self.settings.x = x; + self.settings.y = y; + } + + self.fire('move', {x: x, y: y}); + + return self; + } + }; +}); + +// Included from: js/tinymce/classes/ui/Resizable.js + +/** + * Resizable.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Resizable mixin. Enables controls to be resized. + * + * @mixin tinymce.ui.Resizable + */ +define("tinymce/ui/Resizable", [ + "tinymce/ui/DomUtils" +], function(DomUtils) { + "use strict"; + + return { + /** + * Resizes the control to contents. + * + * @method resizeToContent + */ + resizeToContent: function() { + this._layoutRect.autoResize = true; + this._lastRect = null; + this.reflow(); + }, + + /** + * Resizes the control to a specific width/height. + * + * @method resizeTo + * @param {Number} w Control width. + * @param {Number} h Control height. + * @return {tinymce.ui.Control} Current control instance. + */ + resizeTo: function(w, h) { + // TODO: Fix hack + if (w <= 1 || h <= 1) { + var rect = DomUtils.getWindowSize(); + + w = w <= 1 ? w * rect.w : w; + h = h <= 1 ? h * rect.h : h; + } + + this._layoutRect.autoResize = false; + return this.layoutRect({minW: w, minH: h, w: w, h: h}).reflow(); + }, + + /** + * Resizes the control to a specific relative width/height. + * + * @method resizeBy + * @param {Number} dw Relative control width. + * @param {Number} dh Relative control height. + * @return {tinymce.ui.Control} Current control instance. + */ + resizeBy: function(dw, dh) { + var self = this, rect = self.layoutRect(); + + return self.resizeTo(rect.w + dw, rect.h + dh); + } + }; +}); + +// Included from: js/tinymce/classes/ui/FloatPanel.js + +/** + * FloatPanel.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class creates a floating panel. + * + * @-x-less FloatPanel.less + * @class tinymce.ui.FloatPanel + * @extends tinymce.ui.Panel + * @mixes tinymce.ui.Movable + * @mixes tinymce.ui.Resizable + */ +define("tinymce/ui/FloatPanel", [ + "tinymce/ui/Panel", + "tinymce/ui/Movable", + "tinymce/ui/Resizable", + "tinymce/ui/DomUtils", + "tinymce/dom/DomQuery", + "tinymce/util/Delay" +], function(Panel, Movable, Resizable, DomUtils, $, Delay) { + "use strict"; + + var documentClickHandler, documentScrollHandler, windowResizeHandler, visiblePanels = []; + var zOrder = [], hasModal; + + function isChildOf(ctrl, parent) { + while (ctrl) { + if (ctrl == parent) { + return true; + } + + ctrl = ctrl.parent(); + } + } + + function skipOrHidePanels(e) { + // Hide any float panel when a click/focus out is out side that float panel and the + // float panels direct parent for example a click on a menu button + var i = visiblePanels.length; + + while (i--) { + var panel = visiblePanels[i], clickCtrl = panel.getParentCtrl(e.target); + + if (panel.settings.autohide) { + if (clickCtrl) { + if (isChildOf(clickCtrl, panel) || panel.parent() === clickCtrl) { + continue; + } + } + + e = panel.fire('autohide', {target: e.target}); + if (!e.isDefaultPrevented()) { + panel.hide(); + } + } + } + } + + function bindDocumentClickHandler() { + + if (!documentClickHandler) { + documentClickHandler = function(e) { + // Gecko fires click event and in the wrong order on Mac so lets normalize + if (e.button == 2) { + return; + } + + skipOrHidePanels(e); + }; + + $(document).on('click touchstart', documentClickHandler); + } + } + + function bindDocumentScrollHandler() { + if (!documentScrollHandler) { + documentScrollHandler = function() { + var i; + + i = visiblePanels.length; + while (i--) { + repositionPanel(visiblePanels[i]); + } + }; + + $(window).on('scroll', documentScrollHandler); + } + } + + function bindWindowResizeHandler() { + if (!windowResizeHandler) { + var docElm = document.documentElement, clientWidth = docElm.clientWidth, clientHeight = docElm.clientHeight; + + windowResizeHandler = function() { + // Workaround for #7065 IE 7 fires resize events event though the window wasn't resized + if (!document.all || clientWidth != docElm.clientWidth || clientHeight != docElm.clientHeight) { + clientWidth = docElm.clientWidth; + clientHeight = docElm.clientHeight; + FloatPanel.hideAll(); + } + }; + + $(window).on('resize', windowResizeHandler); + } + } + + /** + * Repositions the panel to the top of page if the panel is outside of the visual viewport. It will + * also reposition all child panels of the current panel. + */ + function repositionPanel(panel) { + var scrollY = DomUtils.getViewPort().y; + + function toggleFixedChildPanels(fixed, deltaY) { + var parent; + + for (var i = 0; i < visiblePanels.length; i++) { + if (visiblePanels[i] != panel) { + parent = visiblePanels[i].parent(); + + while (parent && (parent = parent.parent())) { + if (parent == panel) { + visiblePanels[i].fixed(fixed).moveBy(0, deltaY).repaint(); + } + } + } + } + } + + if (panel.settings.autofix) { + if (!panel.state.get('fixed')) { + panel._autoFixY = panel.layoutRect().y; + + if (panel._autoFixY < scrollY) { + panel.fixed(true).layoutRect({y: 0}).repaint(); + toggleFixedChildPanels(true, scrollY - panel._autoFixY); + } + } else { + if (panel._autoFixY > scrollY) { + panel.fixed(false).layoutRect({y: panel._autoFixY}).repaint(); + toggleFixedChildPanels(false, panel._autoFixY - scrollY); + } + } + } + } + + function addRemove(add, ctrl) { + var i, zIndex = FloatPanel.zIndex || 0xFFFF, topModal; + + if (add) { + zOrder.push(ctrl); + } else { + i = zOrder.length; + + while (i--) { + if (zOrder[i] === ctrl) { + zOrder.splice(i, 1); + } + } + } + + if (zOrder.length) { + for (i = 0; i < zOrder.length; i++) { + if (zOrder[i].modal) { + zIndex++; + topModal = zOrder[i]; + } + + zOrder[i].getEl().style.zIndex = zIndex; + zOrder[i].zIndex = zIndex; + zIndex++; + } + } + + var modalBlockEl = $('#' + ctrl.classPrefix + 'modal-block', ctrl.getContainerElm())[0]; + + if (topModal) { + $(modalBlockEl).css('z-index', topModal.zIndex - 1); + } else if (modalBlockEl) { + modalBlockEl.parentNode.removeChild(modalBlockEl); + hasModal = false; + } + + FloatPanel.currentZIndex = zIndex; + } + + var FloatPanel = Panel.extend({ + Mixins: [Movable, Resizable], + + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} autohide Automatically hide the panel. + */ + init: function(settings) { + var self = this; + + self._super(settings); + self._eventsRoot = self; + + self.classes.add('floatpanel'); + + // Hide floatpanes on click out side the root button + if (settings.autohide) { + bindDocumentClickHandler(); + bindWindowResizeHandler(); + visiblePanels.push(self); + } + + if (settings.autofix) { + bindDocumentScrollHandler(); + + self.on('move', function() { + repositionPanel(this); + }); + } + + self.on('postrender show', function(e) { + if (e.control == self) { + var $modalBlockEl, prefix = self.classPrefix; + + if (self.modal && !hasModal) { + $modalBlockEl = $('#' + prefix + 'modal-block', self.getContainerElm()); + if (!$modalBlockEl[0]) { + $modalBlockEl = $( + '
' + ).appendTo(self.getContainerElm()); + } + + Delay.setTimeout(function() { + $modalBlockEl.addClass(prefix + 'in'); + $(self.getEl()).addClass(prefix + 'in'); + }); + + hasModal = true; + } + + addRemove(true, self); + } + }); + + self.on('show', function() { + self.parents().each(function(ctrl) { + if (ctrl.state.get('fixed')) { + self.fixed(true); + return false; + } + }); + }); + + if (settings.popover) { + self._preBodyHtml = '
'; + self.classes.add('popover').add('bottom').add(self.isRtl() ? 'end' : 'start'); + } + + self.aria('label', settings.ariaLabel); + self.aria('labelledby', self._id); + self.aria('describedby', self.describedBy || self._id + '-none'); + }, + + fixed: function(state) { + var self = this; + + if (self.state.get('fixed') != state) { + if (self.state.get('rendered')) { + var viewport = DomUtils.getViewPort(); + + if (state) { + self.layoutRect().y -= viewport.y; + } else { + self.layoutRect().y += viewport.y; + } + } + + self.classes.toggle('fixed', state); + self.state.set('fixed', state); + } + + return self; + }, + + /** + * Shows the current float panel. + * + * @method show + * @return {tinymce.ui.FloatPanel} Current floatpanel instance. + */ + show: function() { + var self = this, i, state = self._super(); + + i = visiblePanels.length; + while (i--) { + if (visiblePanels[i] === self) { + break; + } + } + + if (i === -1) { + visiblePanels.push(self); + } + + return state; + }, + + /** + * Hides the current float panel. + * + * @method hide + * @return {tinymce.ui.FloatPanel} Current floatpanel instance. + */ + hide: function() { + removeVisiblePanel(this); + addRemove(false, this); + + return this._super(); + }, + + /** + * Hide all visible float panels with he autohide setting enabled. This is for + * manually hiding floating menus or panels. + * + * @method hideAll + */ + hideAll: function() { + FloatPanel.hideAll(); + }, + + /** + * Closes the float panel. This will remove the float panel from page and fire the close event. + * + * @method close + */ + close: function() { + var self = this; + + if (!self.fire('close').isDefaultPrevented()) { + self.remove(); + addRemove(false, self); + } + + return self; + }, + + /** + * Removes the float panel from page. + * + * @method remove + */ + remove: function() { + removeVisiblePanel(this); + this._super(); + }, + + postRender: function() { + var self = this; + + if (self.settings.bodyRole) { + this.getEl('body').setAttribute('role', self.settings.bodyRole); + } + + return self._super(); + } + }); + + /** + * Hide all visible float panels with he autohide setting enabled. This is for + * manually hiding floating menus or panels. + * + * @static + * @method hideAll + */ + FloatPanel.hideAll = function() { + var i = visiblePanels.length; + + while (i--) { + var panel = visiblePanels[i]; + + if (panel && panel.settings.autohide) { + panel.hide(); + visiblePanels.splice(i, 1); + } + } + }; + + function removeVisiblePanel(panel) { + var i; + + i = visiblePanels.length; + while (i--) { + if (visiblePanels[i] === panel) { + visiblePanels.splice(i, 1); + } + } + + i = zOrder.length; + while (i--) { + if (zOrder[i] === panel) { + zOrder.splice(i, 1); + } + } + } + + return FloatPanel; +}); + +// Included from: js/tinymce/classes/ui/Window.js + +/** + * Window.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new window. + * + * @-x-less Window.less + * @class tinymce.ui.Window + * @extends tinymce.ui.FloatPanel + */ +define("tinymce/ui/Window", [ + "tinymce/ui/FloatPanel", + "tinymce/ui/Panel", + "tinymce/ui/DomUtils", + "tinymce/dom/DomQuery", + "tinymce/ui/DragHelper", + "tinymce/ui/BoxUtils", + "tinymce/Env", + "tinymce/util/Delay" +], function(FloatPanel, Panel, DomUtils, $, DragHelper, BoxUtils, Env, Delay) { + "use strict"; + + var windows = [], oldMetaValue = ''; + + function toggleFullScreenState(state) { + var noScaleMetaValue = 'width=device-width,initial-scale=1.0,user-scalable=0,minimum-scale=1.0,maximum-scale=1.0', + viewport = $("meta[name=viewport]")[0], + contentValue; + + if (Env.overrideViewPort === false) { + return; + } + + if (!viewport) { + viewport = document.createElement('meta'); + viewport.setAttribute('name', 'viewport'); + document.getElementsByTagName('head')[0].appendChild(viewport); + } + + contentValue = viewport.getAttribute('content'); + if (contentValue && typeof oldMetaValue != 'undefined') { + oldMetaValue = contentValue; + } + + viewport.setAttribute('content', state ? noScaleMetaValue : oldMetaValue); + } + + function toggleBodyFullScreenClasses(classPrefix, state) { + if (checkFullscreenWindows() && state === false) { + $([document.documentElement, document.body]).removeClass(classPrefix + 'fullscreen'); + } + } + + function checkFullscreenWindows() { + for (var i = 0; i < windows.length; i++) { + if (windows[i]._fullscreen) { + return true; + } + } + return false; + } + + function handleWindowResize() { + if (!Env.desktop) { + var lastSize = { + w: window.innerWidth, + h: window.innerHeight + }; + + Delay.setInterval(function() { + var w = window.innerWidth, + h = window.innerHeight; + + if (lastSize.w != w || lastSize.h != h) { + lastSize = { + w: w, + h: h + }; + + $(window).trigger('resize'); + } + }, 100); + } + + function reposition() { + var i, rect = DomUtils.getWindowSize(), layoutRect; + + for (i = 0; i < windows.length; i++) { + layoutRect = windows[i].layoutRect(); + + windows[i].moveTo( + windows[i].settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2), + windows[i].settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2) + ); + } + } + + $(window).on('resize', reposition); + } + + var Window = FloatPanel.extend({ + modal: true, + + Defaults: { + border: 1, + layout: 'flex', + containerCls: 'panel', + role: 'dialog', + callbacks: { + submit: function() { + this.fire('submit', {data: this.toJSON()}); + }, + + close: function() { + this.close(); + } + } + }, + + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + self._super(settings); + + if (self.isRtl()) { + self.classes.add('rtl'); + } + + self.classes.add('window'); + self.bodyClasses.add('window-body'); + self.state.set('fixed', true); + + // Create statusbar + if (settings.buttons) { + self.statusbar = new Panel({ + layout: 'flex', + border: '1 0 0 0', + spacing: 3, + padding: 10, + align: 'center', + pack: self.isRtl() ? 'start' : 'end', + defaults: { + type: 'button' + }, + items: settings.buttons + }); + + self.statusbar.classes.add('foot'); + self.statusbar.parent(self); + } + + self.on('click', function(e) { + var closeClass = self.classPrefix + 'close'; + + if (DomUtils.hasClass(e.target, closeClass) || DomUtils.hasClass(e.target.parentNode, closeClass)) { + self.close(); + } + }); + + self.on('cancel', function() { + self.close(); + }); + + self.aria('describedby', self.describedBy || self._id + '-none'); + self.aria('label', settings.title); + self._fullscreen = false; + }, + + /** + * Recalculates the positions of the controls in the current container. + * This is invoked by the reflow method and shouldn't be called directly. + * + * @method recalc + */ + recalc: function() { + var self = this, statusbar = self.statusbar, layoutRect, width, x, needsRecalc; + + if (self._fullscreen) { + self.layoutRect(DomUtils.getWindowSize()); + self.layoutRect().contentH = self.layoutRect().innerH; + } + + self._super(); + + layoutRect = self.layoutRect(); + + // Resize window based on title width + if (self.settings.title && !self._fullscreen) { + width = layoutRect.headerW; + if (width > layoutRect.w) { + x = layoutRect.x - Math.max(0, width / 2); + self.layoutRect({w: width, x: x}); + needsRecalc = true; + } + } + + // Resize window based on statusbar width + if (statusbar) { + statusbar.layoutRect({w: self.layoutRect().innerW}).recalc(); + + width = statusbar.layoutRect().minW + layoutRect.deltaW; + if (width > layoutRect.w) { + x = layoutRect.x - Math.max(0, width - layoutRect.w); + self.layoutRect({w: width, x: x}); + needsRecalc = true; + } + } + + // Recalc body and disable auto resize + if (needsRecalc) { + self.recalc(); + } + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, layoutRect = self._super(), deltaH = 0, headEl; + + // Reserve vertical space for title + if (self.settings.title && !self._fullscreen) { + headEl = self.getEl('head'); + + var size = DomUtils.getSize(headEl); + + layoutRect.headerW = size.width; + layoutRect.headerH = size.height; + + deltaH += layoutRect.headerH; + } + + // Reserve vertical space for statusbar + if (self.statusbar) { + deltaH += self.statusbar.layoutRect().h; + } + + layoutRect.deltaH += deltaH; + layoutRect.minH += deltaH; + //layoutRect.innerH -= deltaH; + layoutRect.h += deltaH; + + var rect = DomUtils.getWindowSize(); + + layoutRect.x = self.settings.x || Math.max(0, rect.w / 2 - layoutRect.w / 2); + layoutRect.y = self.settings.y || Math.max(0, rect.h / 2 - layoutRect.h / 2); + + return layoutRect; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, layout = self._layout, id = self._id, prefix = self.classPrefix; + var settings = self.settings, headerHtml = '', footerHtml = '', html = settings.html; + + self.preRender(); + layout.preRender(self); + + if (settings.title) { + headerHtml = ( + '
' + + '
' + self.encode(settings.title) + '
' + + '
' + + '' + + '
' + ); + } + + if (settings.url) { + html = ''; + } + + if (typeof html == "undefined") { + html = layout.renderHtml(self); + } + + if (self.statusbar) { + footerHtml = self.statusbar.renderHtml(); + } + + return ( + '
' + + '
' + + headerHtml + + '
' + + html + + '
' + + footerHtml + + '
' + + '
' + ); + }, + + /** + * Switches the window fullscreen mode. + * + * @method fullscreen + * @param {Boolean} state True/false state. + * @return {tinymce.ui.Window} Current window instance. + */ + fullscreen: function(state) { + var self = this, documentElement = document.documentElement, slowRendering, prefix = self.classPrefix, layoutRect; + + if (state != self._fullscreen) { + $(window).on('resize', function() { + var time; + + if (self._fullscreen) { + // Time the layout time if it's to slow use a timeout to not hog the CPU + if (!slowRendering) { + time = new Date().getTime(); + + var rect = DomUtils.getWindowSize(); + self.moveTo(0, 0).resizeTo(rect.w, rect.h); + + if ((new Date().getTime()) - time > 50) { + slowRendering = true; + } + } else { + if (!self._timer) { + self._timer = Delay.setTimeout(function() { + var rect = DomUtils.getWindowSize(); + self.moveTo(0, 0).resizeTo(rect.w, rect.h); + + self._timer = 0; + }, 50); + } + } + } + }); + + layoutRect = self.layoutRect(); + self._fullscreen = state; + + if (!state) { + self.borderBox = BoxUtils.parseBox(self.settings.border); + self.getEl('head').style.display = ''; + layoutRect.deltaH += layoutRect.headerH; + $([documentElement, document.body]).removeClass(prefix + 'fullscreen'); + self.classes.remove('fullscreen'); + self.moveTo(self._initial.x, self._initial.y).resizeTo(self._initial.w, self._initial.h); + } else { + self._initial = {x: layoutRect.x, y: layoutRect.y, w: layoutRect.w, h: layoutRect.h}; + + self.borderBox = BoxUtils.parseBox('0'); + self.getEl('head').style.display = 'none'; + layoutRect.deltaH -= layoutRect.headerH + 2; + $([documentElement, document.body]).addClass(prefix + 'fullscreen'); + self.classes.add('fullscreen'); + + var rect = DomUtils.getWindowSize(); + self.moveTo(0, 0).resizeTo(rect.w, rect.h); + } + } + + return self.reflow(); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, startPos; + + setTimeout(function() { + self.classes.add('in'); + self.fire('open'); + }, 0); + + self._super(); + + if (self.statusbar) { + self.statusbar.postRender(); + } + + self.focus(); + + this.dragHelper = new DragHelper(self._id + '-dragh', { + start: function() { + startPos = { + x: self.layoutRect().x, + y: self.layoutRect().y + }; + }, + + drag: function(e) { + self.moveTo(startPos.x + e.deltaX, startPos.y + e.deltaY); + } + }); + + self.on('submit', function(e) { + if (!e.isDefaultPrevented()) { + self.close(); + } + }); + + windows.push(self); + toggleFullScreenState(true); + }, + + /** + * Fires a submit event with the serialized form. + * + * @method submit + * @return {Object} Event arguments object. + */ + submit: function() { + return this.fire('submit', {data: this.toJSON()}); + }, + + /** + * Removes the current control from DOM and from UI collections. + * + * @method remove + * @return {tinymce.ui.Control} Current control instance. + */ + remove: function() { + var self = this, i; + + self.dragHelper.destroy(); + self._super(); + + if (self.statusbar) { + this.statusbar.remove(); + } + + toggleBodyFullScreenClasses(self.classPrefix, false); + + i = windows.length; + while (i--) { + if (windows[i] === self) { + windows.splice(i, 1); + } + } + + toggleFullScreenState(windows.length > 0); + }, + + /** + * Returns the contentWindow object of the iframe if it exists. + * + * @method getContentWindow + * @return {Window} window object or null. + */ + getContentWindow: function() { + var ifr = this.getEl().getElementsByTagName('iframe')[0]; + return ifr ? ifr.contentWindow : null; + } + }); + + handleWindowResize(); + + return Window; +}); + +// Included from: js/tinymce/classes/ui/MessageBox.js + +/** + * MessageBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class is used to create MessageBoxes like alerts/confirms etc. + * + * @class tinymce.ui.MessageBox + * @extends tinymce.ui.FloatPanel + */ +define("tinymce/ui/MessageBox", [ + "tinymce/ui/Window" +], function(Window) { + "use strict"; + + var MessageBox = Window.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + settings = { + border: 1, + padding: 20, + layout: 'flex', + pack: "center", + align: "center", + containerCls: 'panel', + autoScroll: true, + buttons: {type: "button", text: "Ok", action: "ok"}, + items: { + type: "label", + multiline: true, + maxWidth: 500, + maxHeight: 200 + } + }; + + this._super(settings); + }, + + Statics: { + /** + * Ok buttons constant. + * + * @static + * @final + * @field {Number} OK + */ + OK: 1, + + /** + * Ok/cancel buttons constant. + * + * @static + * @final + * @field {Number} OK_CANCEL + */ + OK_CANCEL: 2, + + /** + * yes/no buttons constant. + * + * @static + * @final + * @field {Number} YES_NO + */ + YES_NO: 3, + + /** + * yes/no/cancel buttons constant. + * + * @static + * @final + * @field {Number} YES_NO_CANCEL + */ + YES_NO_CANCEL: 4, + + /** + * Constructs a new message box and renders it to the body element. + * + * @static + * @method msgBox + * @param {Object} settings Name/value object with settings. + */ + msgBox: function(settings) { + var buttons, callback = settings.callback || function() {}; + + function createButton(text, status, primary) { + return { + type: "button", + text: text, + subtype: primary ? 'primary' : '', + onClick: function(e) { + e.control.parents()[1].close(); + callback(status); + } + }; + } + + switch (settings.buttons) { + case MessageBox.OK_CANCEL: + buttons = [ + createButton('Ok', true, true), + createButton('Cancel', false) + ]; + break; + + case MessageBox.YES_NO: + case MessageBox.YES_NO_CANCEL: + buttons = [ + createButton('Yes', 1, true), + createButton('No', 0) + ]; + + if (settings.buttons == MessageBox.YES_NO_CANCEL) { + buttons.push(createButton('Cancel', -1)); + } + break; + + default: + buttons = [ + createButton('Ok', true, true) + ]; + break; + } + + return new Window({ + padding: 20, + x: settings.x, + y: settings.y, + minWidth: 300, + minHeight: 100, + layout: "flex", + pack: "center", + align: "center", + buttons: buttons, + title: settings.title, + role: 'alertdialog', + items: { + type: "label", + multiline: true, + maxWidth: 500, + maxHeight: 200, + text: settings.text + }, + onPostRender: function() { + this.aria('describedby', this.items()[0]._id); + }, + onClose: settings.onClose, + onCancel: function() { + callback(false); + } + }).renderTo(document.body).reflow(); + }, + + /** + * Creates a new alert dialog. + * + * @method alert + * @param {Object} settings Settings for the alert dialog. + * @param {function} [callback] Callback to execute when the user makes a choice. + */ + alert: function(settings, callback) { + if (typeof settings == "string") { + settings = {text: settings}; + } + + settings.callback = callback; + return MessageBox.msgBox(settings); + }, + + /** + * Creates a new confirm dialog. + * + * @method confirm + * @param {Object} settings Settings for the confirm dialog. + * @param {function} [callback] Callback to execute when the user makes a choice. + */ + confirm: function(settings, callback) { + if (typeof settings == "string") { + settings = {text: settings}; + } + + settings.callback = callback; + settings.buttons = MessageBox.OK_CANCEL; + + return MessageBox.msgBox(settings); + } + } + }); + + return MessageBox; +}); + +// Included from: js/tinymce/classes/WindowManager.js + +/** + * WindowManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class handles the creation of native windows and dialogs. This class can be extended to provide for example inline dialogs. + * + * @class tinymce.WindowManager + * @example + * // Opens a new dialog with the file.htm file and the size 320x240 + * // It also adds a custom parameter this can be retrieved by using tinyMCEPopup.getWindowArg inside the dialog. + * tinymce.activeEditor.windowManager.open({ + * url: 'file.htm', + * width: 320, + * height: 240 + * }, { + * custom_param: 1 + * }); + * + * // Displays an alert box using the active editors window manager instance + * tinymce.activeEditor.windowManager.alert('Hello world!'); + * + * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm + * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) { + * if (s) + * tinymce.activeEditor.windowManager.alert("Ok"); + * else + * tinymce.activeEditor.windowManager.alert("Cancel"); + * }); + */ +define("tinymce/WindowManager", [ + "tinymce/ui/Window", + "tinymce/ui/MessageBox" +], function(Window, MessageBox) { + return function(editor) { + var self = this, windows = []; + + function getTopMostWindow() { + if (windows.length) { + return windows[windows.length - 1]; + } + } + + function fireOpenEvent(win) { + editor.fire('OpenWindow', { + win: win + }); + } + + function fireCloseEvent(win) { + editor.fire('CloseWindow', { + win: win + }); + } + + self.windows = windows; + + editor.on('remove', function() { + var i = windows.length; + + while (i--) { + windows[i].close(); + } + }); + + /** + * Opens a new window. + * + * @method open + * @param {Object} args Optional name/value settings collection contains things like width/height/url etc. + * @param {Object} params Options like title, file, width, height etc. + * @option {String} title Window title. + * @option {String} file URL of the file to open in the window. + * @option {Number} width Width in pixels. + * @option {Number} height Height in pixels. + * @option {Boolean} autoScroll Specifies whether the popup window can have scrollbars if required (i.e. content + * larger than the popup size specified). + */ + self.open = function(args, params) { + var win; + + editor.editorManager.setActive(editor); + + args.title = args.title || ' '; + + // Handle URL + args.url = args.url || args.file; // Legacy + if (args.url) { + args.width = parseInt(args.width || 320, 10); + args.height = parseInt(args.height || 240, 10); + } + + // Handle body + if (args.body) { + args.items = { + defaults: args.defaults, + type: args.bodyType || 'form', + items: args.body, + data: args.data, + callbacks: args.commands + }; + } + + if (!args.url && !args.buttons) { + args.buttons = [ + {text: 'Ok', subtype: 'primary', onclick: function() { + win.find('form')[0].submit(); + }}, + + {text: 'Cancel', onclick: function() { + win.close(); + }} + ]; + } + + win = new Window(args); + windows.push(win); + + win.on('close', function() { + var i = windows.length; + + while (i--) { + if (windows[i] === win) { + windows.splice(i, 1); + } + } + + if (!windows.length) { + editor.focus(); + } + + fireCloseEvent(win); + }); + + // Handle data + if (args.data) { + win.on('postRender', function() { + this.find('*').each(function(ctrl) { + var name = ctrl.name(); + + if (name in args.data) { + ctrl.value(args.data[name]); + } + }); + }); + } + + // store args and parameters + win.features = args || {}; + win.params = params || {}; + + // Takes a snapshot in the FocusManager of the selection before focus is lost to dialog + if (windows.length === 1) { + editor.nodeChanged(); + } + + win = win.renderTo().reflow(); + + fireOpenEvent(win); + + return win; + }; + + /** + * Creates a alert dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method alert + * @param {String} message Text to display in the new alert dialog. + * @param {function} callback Callback function to be executed after the user has selected ok. + * @param {Object} scope Optional scope to execute the callback in. + * @example + * // Displays an alert box using the active editors window manager instance + * tinymce.activeEditor.windowManager.alert('Hello world!'); + */ + self.alert = function(message, callback, scope) { + var win; + + win = MessageBox.alert(message, function() { + if (callback) { + callback.call(scope || this); + } else { + editor.focus(); + } + }); + + win.on('close', function() { + fireCloseEvent(win); + }); + + fireOpenEvent(win); + }; + + /** + * Creates a confirm dialog. Please don't use the blocking behavior of this + * native version use the callback method instead then it can be extended. + * + * @method confirm + * @param {String} message Text to display in the new confirm dialog. + * @param {function} callback Callback function to be executed after the user has selected ok or cancel. + * @param {Object} scope Optional scope to execute the callback in. + * @example + * // Displays an confirm box and an alert message will be displayed depending on what you choose in the confirm + * tinymce.activeEditor.windowManager.confirm("Do you want to do something", function(s) { + * if (s) + * tinymce.activeEditor.windowManager.alert("Ok"); + * else + * tinymce.activeEditor.windowManager.alert("Cancel"); + * }); + */ + self.confirm = function(message, callback, scope) { + var win; + + win = MessageBox.confirm(message, function(state) { + callback.call(scope || this, state); + }); + + win.on('close', function() { + fireCloseEvent(win); + }); + + fireOpenEvent(win); + }; + + /** + * Closes the top most window. + * + * @method close + */ + self.close = function() { + if (getTopMostWindow()) { + getTopMostWindow().close(); + } + }; + + /** + * Returns the params of the last window open call. This can be used in iframe based + * dialog to get params passed from the tinymce plugin. + * + * @example + * var dialogArguments = top.tinymce.activeEditor.windowManager.getParams(); + * + * @method getParams + * @return {Object} Name/value object with parameters passed from windowManager.open call. + */ + self.getParams = function() { + return getTopMostWindow() ? getTopMostWindow().params : null; + }; + + /** + * Sets the params of the last opened window. + * + * @method setParams + * @param {Object} params Params object to set for the last opened window. + */ + self.setParams = function(params) { + if (getTopMostWindow()) { + getTopMostWindow().params = params; + } + }; + + /** + * Returns the currently opened window objects. + * + * @method getWindows + * @return {Array} Array of the currently opened windows. + */ + self.getWindows = function() { + return windows; + }; + }; +}); + +// Included from: js/tinymce/classes/ui/Tooltip.js + +/** + * Tooltip.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a tooltip instance. + * + * @-x-less ToolTip.less + * @class tinymce.ui.ToolTip + * @extends tinymce.ui.Control + * @mixes tinymce.ui.Movable + */ +define("tinymce/ui/Tooltip", [ + "tinymce/ui/Control", + "tinymce/ui/Movable" +], function(Control, Movable) { + return Control.extend({ + Mixins: [Movable], + + Defaults: { + classes: 'widget tooltip tooltip-n' + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, prefix = self.classPrefix; + + return ( + '' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:text', function(e) { + self.getEl().lastChild.innerHTML = self.encode(e.value); + }); + + return self._super(); + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, style, rect; + + style = self.getEl().style; + rect = self._layoutRect; + + style.left = rect.x + 'px'; + style.top = rect.y + 'px'; + style.zIndex = 0xFFFF + 0xFFFF; + } + }); +}); + +// Included from: js/tinymce/classes/ui/Widget.js + +/** + * Widget.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Widget base class a widget is a control that has a tooltip and some basic states. + * + * @class tinymce.ui.Widget + * @extends tinymce.ui.Control + */ +define("tinymce/ui/Widget", [ + "tinymce/ui/Control", + "tinymce/ui/Tooltip" +], function(Control, Tooltip) { + "use strict"; + + var tooltip; + + var Widget = Control.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} tooltip Tooltip text to display when hovering. + * @setting {Boolean} autofocus True if the control should be focused when rendered. + * @setting {String} text Text to display inside widget. + */ + init: function(settings) { + var self = this; + + self._super(settings); + settings = self.settings; + self.canFocus = true; + + if (settings.tooltip && Widget.tooltips !== false) { + self.on('mouseenter', function(e) { + var tooltip = self.tooltip().moveTo(-0xFFFF); + + if (e.control == self) { + var rel = tooltip.text(settings.tooltip).show().testMoveRel(self.getEl(), ['bc-tc', 'bc-tl', 'bc-tr']); + + tooltip.classes.toggle('tooltip-n', rel == 'bc-tc'); + tooltip.classes.toggle('tooltip-nw', rel == 'bc-tl'); + tooltip.classes.toggle('tooltip-ne', rel == 'bc-tr'); + + tooltip.moveRel(self.getEl(), rel); + } else { + tooltip.hide(); + } + }); + + self.on('mouseleave mousedown click', function() { + self.tooltip().hide(); + }); + } + + self.aria('label', settings.ariaLabel || settings.tooltip); + }, + + /** + * Returns the current tooltip instance. + * + * @method tooltip + * @return {tinymce.ui.Tooltip} Tooltip instance. + */ + tooltip: function() { + if (!tooltip) { + tooltip = new Tooltip({type: 'tooltip'}); + tooltip.renderTo(); + } + + return tooltip; + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, settings = self.settings; + + self._super(); + + if (!self.parent() && (settings.width || settings.height)) { + self.initLayoutRect(); + self.repaint(); + } + + if (settings.autofocus) { + self.focus(); + } + }, + + bindStates: function() { + var self = this; + + function disable(state) { + self.aria('disabled', state); + self.classes.toggle('disabled', state); + } + + function active(state) { + self.aria('pressed', state); + self.classes.toggle('active', state); + } + + self.state.on('change:disabled', function(e) { + disable(e.value); + }); + + self.state.on('change:active', function(e) { + active(e.value); + }); + + if (self.state.get('disabled')) { + disable(true); + } + + if (self.state.get('active')) { + active(true); + } + + return self._super(); + }, + + /** + * Removes the current control from DOM and from UI collections. + * + * @method remove + * @return {tinymce.ui.Control} Current control instance. + */ + remove: function() { + this._super(); + + if (tooltip) { + tooltip.remove(); + tooltip = null; + } + } + }); + + return Widget; +}); + +// Included from: js/tinymce/classes/ui/Progress.js + +/** + * Progress.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Progress control. + * + * @-x-less Progress.less + * @class tinymce.ui.Progress + * @extends tinymce.ui.Control + */ +define("tinymce/ui/Progress", [ + "tinymce/ui/Widget" +], function(Widget) { + "use strict"; + + return Widget.extend({ + Defaults: { + value: 0 + }, + + init: function(settings) { + var self = this; + + self._super(settings); + self.classes.add('progress'); + + if (!self.settings.filter) { + self.settings.filter = function(value) { + return Math.round(value); + }; + } + }, + + renderHtml: function() { + var self = this, id = self._id, prefix = this.classPrefix; + + return ( + '
' + + '
' + + '
' + + '
' + + '
0%
' + + '
' + ); + }, + + postRender: function() { + var self = this; + + self._super(); + self.value(self.settings.value); + + return self; + }, + + bindStates: function() { + var self = this; + + function setValue(value) { + value = self.settings.filter(value); + self.getEl().lastChild.innerHTML = value + '%'; + self.getEl().firstChild.firstChild.style.width = value + '%'; + } + + self.state.on('change:value', function(e) { + setValue(e.value); + }); + + setValue(self.state.get('value')); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Notification.js + +/** + * Notification.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a notification instance. + * + * @-x-less Notification.less + * @class tinymce.ui.Notification + * @extends tinymce.ui.Container + * @mixes tinymce.ui.Movable + */ +define("tinymce/ui/Notification", [ + "tinymce/ui/Control", + "tinymce/ui/Movable", + "tinymce/ui/Progress", + "tinymce/util/Delay" +], function(Control, Movable, Progress, Delay) { + return Control.extend({ + Mixins: [Movable], + + Defaults: { + classes: 'widget notification' + }, + + init: function(settings) { + var self = this; + + self._super(settings); + + if (settings.text) { + self.text(settings.text); + } + + if (settings.icon) { + self.icon = settings.icon; + } + + if (settings.color) { + self.color = settings.color; + } + + if (settings.type) { + self.classes.add('notification-' + settings.type); + } + + if (settings.timeout && (settings.timeout < 0 || settings.timeout > 0) && !settings.closeButton) { + self.closeButton = false; + } else { + self.classes.add('has-close'); + self.closeButton = true; + } + + if (settings.progressBar) { + self.progressBar = new Progress(); + } + + self.on('click', function(e) { + if (e.target.className.indexOf(self.classPrefix + 'close') != -1) { + self.close(); + } + }); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, prefix = self.classPrefix, icon = '', closeButton = '', progressBar = '', notificationStyle = ''; + + if (self.icon) { + icon = ''; + } + + if (self.color) { + notificationStyle = ' style="background-color: ' + self.color + '"'; + } + + if (self.closeButton) { + closeButton = ''; + } + + if (self.progressBar) { + progressBar = self.progressBar.renderHtml(); + } + + return ( + '' + ); + }, + + postRender: function() { + var self = this; + + Delay.setTimeout(function() { + self.$el.addClass(self.classPrefix + 'in'); + }); + + return self._super(); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:text', function(e) { + self.getEl().childNodes[1].innerHTML = e.value; + }); + if (self.progressBar) { + self.progressBar.bindStates(); + } + return self._super(); + }, + + close: function() { + var self = this; + + if (!self.fire('close').isDefaultPrevented()) { + self.remove(); + } + + return self; + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, style, rect; + + style = self.getEl().style; + rect = self._layoutRect; + + style.left = rect.x + 'px'; + style.top = rect.y + 'px'; + + // Hardcoded arbitrary z-value because we want the + // notifications under the other windows + style.zIndex = 0xFFFF - 1; + } + }); +}); + +// Included from: js/tinymce/classes/NotificationManager.js + +/** + * NotificationManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class handles the creation of TinyMCE's notifications. + * + * @class tinymce.notificationManager + * @example + * // Opens a new notification of type "error" with text "An error occurred." + * tinymce.activeEditor.notificationManager.open({ + * text: 'An error occurred.', + * type: 'error' + * }); + */ +define("tinymce/NotificationManager", [ + "tinymce/ui/Notification", + "tinymce/util/Delay", + "tinymce/util/Tools" +], function(Notification, Delay, Tools) { + return function(editor) { + var self = this, notifications = []; + + function getLastNotification() { + if (notifications.length) { + return notifications[notifications.length - 1]; + } + } + + self.notifications = notifications; + + function resizeWindowEvent() { + Delay.requestAnimationFrame(function() { + prePositionNotifications(); + positionNotifications(); + }); + } + + // Since the viewport will change based on the present notifications, we need to move them all to the + // top left of the viewport to give an accurate size measurement so we can position them later. + function prePositionNotifications() { + for (var i = 0; i < notifications.length; i++) { + notifications[i].moveTo(0, 0); + } + } + + function positionNotifications() { + if (notifications.length > 0) { + var firstItem = notifications.slice(0, 1)[0]; + var container = editor.inline ? editor.getElement() : editor.getContentAreaContainer(); + firstItem.moveRel(container, 'tc-tc'); + if (notifications.length > 1) { + for (var i = 1; i < notifications.length; i++) { + notifications[i].moveRel(notifications[i - 1].getEl(), 'bc-tc'); + } + } + } + } + + editor.on('remove', function() { + var i = notifications.length; + + while (i--) { + notifications[i].close(); + } + }); + + editor.on('ResizeEditor', positionNotifications); + editor.on('ResizeWindow', resizeWindowEvent); + + /** + * Opens a new notification. + * + * @method open + * @param {Object} args Optional name/value settings collection contains things like timeout/color/message etc. + */ + self.open = function(args) { + // Never open notification if editor has been removed. + if (editor.removed) { + return; + } + + var notif; + + editor.editorManager.setActive(editor); + + var duplicate = findDuplicateMessage(notifications, args); + + if (duplicate === null) { + notif = new Notification(args); + notifications.push(notif); + + //If we have a timeout value + if (args.timeout > 0) { + notif.timer = setTimeout(function() { + notif.close(); + }, args.timeout); + } + + notif.on('close', function() { + var i = notifications.length; + + if (notif.timer) { + editor.getWin().clearTimeout(notif.timer); + } + + while (i--) { + if (notifications[i] === notif) { + notifications.splice(i, 1); + } + } + + positionNotifications(); + }); + + notif.renderTo(); + + positionNotifications(); + } else { + notif = duplicate; + } + + return notif; + }; + + /** + * Closes the top most notification. + * + * @method close + */ + self.close = function() { + if (getLastNotification()) { + getLastNotification().close(); + } + }; + + /** + * Returns the currently opened notification objects. + * + * @method getNotifications + * @return {Array} Array of the currently opened notifications. + */ + self.getNotifications = function() { + return notifications; + }; + + editor.on('SkinLoaded', function() { + var serviceMessage = editor.settings.service_message; + + if (serviceMessage) { + editor.notificationManager.open({ + text: serviceMessage, + type: 'warning', + timeout: 0, + icon: '' + }); + } + }); + + /** + * Finds any existing notification with the same properties as the new one. + * Returns either the found notification or null. + * + * @param {Notification[]} notificationArray - Array of current notifications + * @param {type: string, } newNotification - New notification object + * @returns {?Notification} + */ + function findDuplicateMessage(notificationArray, newNotification) { + if (!isPlainTextNotification(newNotification)) { + return null; + } + + var filteredNotifications = Tools.grep(notificationArray, function (notification) { + return isSameNotification(newNotification, notification); + }); + + return filteredNotifications.length === 0 ? null : filteredNotifications[0]; + } + + /** + * Checks if the passed in args object has the same + * type and text properties as the sent in notification. + * + * @param {type: string, text: string} a - New notification args object + * @param {Notification} b - Old notification + * @returns {boolean} + */ + function isSameNotification(a, b) { + return a.type === b.settings.type && a.text === b.settings.text; + } + + /** + * Checks that the notification does not have a progressBar + * or timeour property. + * + * @param {Notification} notification - Notification to check + * @returns {boolean} + */ + function isPlainTextNotification(notification) { + return !notification.progressBar && !notification.timeout; + } + + //self.positionNotifications = positionNotifications; + }; +}); + +// Included from: js/tinymce/classes/dom/NodePath.js + +/** + * NodePath.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Handles paths of nodes within an element. + * + * @private + * @class tinymce.dom.NodePath + */ +define("tinymce/dom/NodePath", [ + "tinymce/dom/DOMUtils" +], function(DOMUtils) { + function create(rootNode, targetNode, normalized) { + var path = []; + + for (; targetNode && targetNode != rootNode; targetNode = targetNode.parentNode) { + path.push(DOMUtils.nodeIndex(targetNode, normalized)); + } + + return path; + } + + function resolve(rootNode, path) { + var i, node, children; + + for (node = rootNode, i = path.length - 1; i >= 0; i--) { + children = node.childNodes; + + if (path[i] > children.length - 1) { + return null; + } + + node = children[path[i]]; + } + + return node; + } + + return { + create: create, + resolve: resolve + }; +}); + +// Included from: js/tinymce/classes/util/Quirks.js + +/** + * Quirks.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + * + * @ignore-file + */ + +/** + * This file includes fixes for various browser quirks it's made to make it easy to add/remove browser specific fixes. + * + * @private + * @class tinymce.util.Quirks + */ +define("tinymce/util/Quirks", [ + "tinymce/util/VK", + "tinymce/dom/RangeUtils", + "tinymce/dom/TreeWalker", + "tinymce/dom/NodePath", + "tinymce/html/Node", + "tinymce/html/Entities", + "tinymce/Env", + "tinymce/util/Tools", + "tinymce/util/Delay", + "tinymce/caret/CaretContainer", + "tinymce/caret/CaretPosition", + "tinymce/caret/CaretWalker" +], function(VK, RangeUtils, TreeWalker, NodePath, Node, Entities, Env, Tools, Delay, CaretContainer, CaretPosition, CaretWalker) { + return function(editor) { + var each = Tools.each, $ = editor.$; + var BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection, + settings = editor.settings, parser = editor.parser, serializer = editor.serializer; + var isGecko = Env.gecko, isIE = Env.ie, isWebKit = Env.webkit; + var mceInternalUrlPrefix = 'data:text/mce-internal,'; + var mceInternalDataType = isIE ? 'Text' : 'URL'; + + /** + * Executes a command with a specific state this can be to enable/disable browser editing features. + */ + function setEditorCommandState(cmd, state) { + try { + editor.getDoc().execCommand(cmd, false, state); + } catch (ex) { + // Ignore + } + } + + /** + * Returns current IE document mode. + */ + function getDocumentMode() { + var documentMode = editor.getDoc().documentMode; + + return documentMode ? documentMode : 6; + } + + /** + * Returns true/false if the event is prevented or not. + * + * @private + * @param {Event} e Event object. + * @return {Boolean} true/false if the event is prevented or not. + */ + function isDefaultPrevented(e) { + return e.isDefaultPrevented(); + } + + /** + * Sets Text/URL data on the event's dataTransfer object to a special data:text/mce-internal url. + * This is to workaround the inability to set custom contentType on IE and Safari. + * The editor's selected content is encoded into this url so drag and drop between editors will work. + * + * @private + * @param {DragEvent} e Event object + */ + function setMceInternalContent(e) { + var selectionHtml, internalContent; + + if (e.dataTransfer) { + if (editor.selection.isCollapsed() && e.target.tagName == 'IMG') { + selection.select(e.target); + } + + selectionHtml = editor.selection.getContent(); + + // Safari/IE doesn't support custom dataTransfer items so we can only use URL and Text + if (selectionHtml.length > 0) { + internalContent = mceInternalUrlPrefix + escape(editor.id) + ',' + escape(selectionHtml); + e.dataTransfer.setData(mceInternalDataType, internalContent); + } + } + } + + /** + * Gets content of special data:text/mce-internal url on the event's dataTransfer object. + * This is to workaround the inability to set custom contentType on IE and Safari. + * The editor's selected content is encoded into this url so drag and drop between editors will work. + * + * @private + * @param {DragEvent} e Event object + * @returns {String} mce-internal content + */ + function getMceInternalContent(e) { + var internalContent; + + if (e.dataTransfer) { + internalContent = e.dataTransfer.getData(mceInternalDataType); + + if (internalContent && internalContent.indexOf(mceInternalUrlPrefix) >= 0) { + internalContent = internalContent.substr(mceInternalUrlPrefix.length).split(','); + + return { + id: unescape(internalContent[0]), + html: unescape(internalContent[1]) + }; + } + } + + return null; + } + + /** + * Inserts contents using the paste clipboard command if it's available if it isn't it will fallback + * to the core command. + * + * @private + * @param {String} content Content to insert at selection. + */ + function insertClipboardContents(content) { + if (editor.queryCommandSupported('mceInsertClipboardContent')) { + editor.execCommand('mceInsertClipboardContent', false, {content: content}); + } else { + editor.execCommand('mceInsertContent', false, content); + } + } + + /** + * Fixes a WebKit bug when deleting contents using backspace or delete key. + * WebKit will produce a span element if you delete across two block elements. + * + * Example: + *

a

|b

+ * + * Will produce this on backspace: + *

ab

+ * + * This fixes the backspace to produce: + *

a|b

+ * + * See bug: https://bugs.webkit.org/show_bug.cgi?id=45784 + * + * This fixes the following delete scenarios: + * 1. Delete by pressing backspace key. + * 2. Delete by pressing delete key. + * 3. Delete by pressing backspace key with ctrl/cmd (Word delete). + * 4. Delete by pressing delete key with ctrl/cmd (Word delete). + * 5. Delete by drag/dropping contents inside the editor. + * 6. Delete by using Cut Ctrl+X/Cmd+X. + * 7. Delete by selecting contents and writing a character. + * + * This code is a ugly hack since writing full custom delete logic for just this bug + * fix seemed like a huge task. I hope we can remove this before the year 2030. + */ + function cleanupStylesWhenDeleting() { + var doc = editor.getDoc(), dom = editor.dom, selection = editor.selection; + var MutationObserver = window.MutationObserver, olderWebKit, dragStartRng; + + // Add mini polyfill for older WebKits + // TODO: Remove this when old Safari versions gets updated + if (!MutationObserver) { + olderWebKit = true; + + MutationObserver = function() { + var records = [], target; + + function nodeInsert(e) { + var target = e.relatedNode || e.target; + records.push({target: target, addedNodes: [target]}); + } + + function attrModified(e) { + var target = e.relatedNode || e.target; + records.push({target: target, attributeName: e.attrName}); + } + + this.observe = function(node) { + target = node; + target.addEventListener('DOMSubtreeModified', nodeInsert, false); + target.addEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false); + target.addEventListener('DOMNodeInserted', nodeInsert, false); + target.addEventListener('DOMAttrModified', attrModified, false); + }; + + this.disconnect = function() { + target.removeEventListener('DOMSubtreeModified', nodeInsert, false); + target.removeEventListener('DOMNodeInsertedIntoDocument', nodeInsert, false); + target.removeEventListener('DOMNodeInserted', nodeInsert, false); + target.removeEventListener('DOMAttrModified', attrModified, false); + }; + + this.takeRecords = function() { + return records; + }; + }; + } + + function isTrailingBr(node) { + var blockElements = dom.schema.getBlockElements(), rootNode = editor.getBody(); + + if (node.nodeName != 'BR') { + return false; + } + + for (; node != rootNode && !blockElements[node.nodeName]; node = node.parentNode) { + if (node.nextSibling) { + return false; + } + } + + return true; + } + + function isSiblingsIgnoreWhiteSpace(node1, node2) { + var node; + + for (node = node1.nextSibling; node && node != node2; node = node.nextSibling) { + if (node.nodeType == 3 && $.trim(node.data).length === 0) { + continue; + } + + if (node !== node2) { + return false; + } + } + + return node === node2; + } + + function findCaretNode(node, forward, startNode) { + var walker, current, nonEmptyElements; + + // Protect against the possibility we are asked to find a caret node relative + // to a node that is no longer in the DOM tree. In this case attempting to + // select on any match leads to a scenario where selection is completely removed + // from the editor. This scenario is met in real world at a minimum on + // WebKit browsers when selecting all and Cmd-X cutting to delete content. + if (!dom.isChildOf(node, editor.getBody())) { + return; + } + + nonEmptyElements = dom.schema.getNonEmptyElements(); + + walker = new TreeWalker(startNode || node, node); + + while ((current = walker[forward ? 'next' : 'prev']())) { + if (nonEmptyElements[current.nodeName] && !isTrailingBr(current)) { + return current; + } + + if (current.nodeType == 3 && current.data.length > 0) { + return current; + } + } + } + + function deleteRangeBetweenTextBlocks(rng) { + var startBlock, endBlock, caretNodeBefore, caretNodeAfter, textBlockElements; + + if (rng.collapsed) { + return; + } + + startBlock = dom.getParent(RangeUtils.getNode(rng.startContainer, rng.startOffset), dom.isBlock); + endBlock = dom.getParent(RangeUtils.getNode(rng.endContainer, rng.endOffset), dom.isBlock); + textBlockElements = editor.schema.getTextBlockElements(); + + if (startBlock == endBlock) { + return; + } + + if (!textBlockElements[startBlock.nodeName] || !textBlockElements[endBlock.nodeName]) { + return; + } + + if (dom.getContentEditable(startBlock) === "false" || dom.getContentEditable(endBlock) === "false") { + return; + } + + rng.deleteContents(); + + caretNodeBefore = findCaretNode(startBlock, false); + caretNodeAfter = findCaretNode(endBlock, true); + + if (!dom.isEmpty(endBlock)) { + $(startBlock).append(endBlock.childNodes); + } + + $(endBlock).remove(); + + if (caretNodeBefore) { + if (caretNodeBefore.nodeType == 1) { + if (caretNodeBefore.nodeName == "BR") { + rng.setStartBefore(caretNodeBefore); + rng.setEndBefore(caretNodeBefore); + } else { + rng.setStartAfter(caretNodeBefore); + rng.setEndAfter(caretNodeBefore); + } + } else { + rng.setStart(caretNodeBefore, caretNodeBefore.data.length); + rng.setEnd(caretNodeBefore, caretNodeBefore.data.length); + } + } else if (caretNodeAfter) { + if (caretNodeAfter.nodeType == 1) { + rng.setStartBefore(caretNodeAfter); + rng.setEndBefore(caretNodeAfter); + } else { + rng.setStart(caretNodeAfter, 0); + rng.setEnd(caretNodeAfter, 0); + } + } + + selection.setRng(rng); + + return true; + } + + function expandBetweenBlocks(rng, isForward) { + var caretNode, targetCaretNode, textBlock, targetTextBlock, container, offset; + + if (!rng.collapsed) { + return rng; + } + + container = rng.startContainer; + offset = rng.startOffset; + + if (container.nodeType == 3) { + if (isForward) { + if (offset < container.data.length) { + return rng; + } + } else { + if (offset > 0) { + return rng; + } + } + } + + caretNode = RangeUtils.getNode(container, offset); + textBlock = dom.getParent(caretNode, dom.isBlock); + targetCaretNode = findCaretNode(editor.getBody(), isForward, caretNode); + targetTextBlock = dom.getParent(targetCaretNode, dom.isBlock); + var isAfter = container.nodeType === 1 && offset > container.childNodes.length - 1; + + if (!caretNode || !targetCaretNode) { + return rng; + } + + if (targetTextBlock && textBlock != targetTextBlock) { + if (!isForward) { + if (!isSiblingsIgnoreWhiteSpace(targetTextBlock, textBlock)) { + return rng; + } + + if (targetCaretNode.nodeType == 1) { + if (targetCaretNode.nodeName == "BR") { + rng.setStartBefore(targetCaretNode); + } else { + rng.setStartAfter(targetCaretNode); + } + } else { + rng.setStart(targetCaretNode, targetCaretNode.data.length); + } + + if (caretNode.nodeType == 1) { + if (isAfter) { + rng.setEndAfter(caretNode); + } else { + rng.setEndBefore(caretNode); + } + } else { + rng.setEndBefore(caretNode); + } + } else { + if (!isSiblingsIgnoreWhiteSpace(textBlock, targetTextBlock)) { + return rng; + } + + if (caretNode.nodeType == 1) { + if (caretNode.nodeName == "BR") { + rng.setStartBefore(caretNode); + } else { + rng.setStartAfter(caretNode); + } + } else { + rng.setStart(caretNode, caretNode.data.length); + } + + if (targetCaretNode.nodeType == 1) { + rng.setEnd(targetCaretNode, 0); + } else { + rng.setEndBefore(targetCaretNode); + } + } + } + + return rng; + } + + function handleTextBlockMergeDelete(isForward) { + var rng = selection.getRng(); + + rng = expandBetweenBlocks(rng, isForward); + + if (deleteRangeBetweenTextBlocks(rng)) { + return true; + } + } + + /** + * This retains the formatting if the last character is to be deleted. + * + * Backspace on this:

a|

would become

|

in WebKit. + * With this patch:

|

+ */ + function handleLastBlockCharacterDelete(isForward, rng) { + var path, blockElm, newBlockElm, clonedBlockElm, sibling, + container, offset, br, currentFormatNodes; + + function cloneTextBlockWithFormats(blockElm, node) { + currentFormatNodes = $(node).parents().filter(function(idx, node) { + return !!editor.schema.getTextInlineElements()[node.nodeName]; + }); + + newBlockElm = blockElm.cloneNode(false); + + currentFormatNodes = Tools.map(currentFormatNodes, function(formatNode) { + formatNode = formatNode.cloneNode(false); + + if (newBlockElm.hasChildNodes()) { + formatNode.appendChild(newBlockElm.firstChild); + newBlockElm.appendChild(formatNode); + } else { + newBlockElm.appendChild(formatNode); + } + + newBlockElm.appendChild(formatNode); + + return formatNode; + }); + + if (currentFormatNodes.length) { + br = dom.create('br'); + currentFormatNodes[0].appendChild(br); + dom.replace(newBlockElm, blockElm); + + rng.setStartBefore(br); + rng.setEndBefore(br); + editor.selection.setRng(rng); + + return br; + } + + return null; + } + + function isTextBlock(node) { + return node && editor.schema.getTextBlockElements()[node.tagName]; + } + + if (!rng.collapsed) { + return; + } + + container = rng.startContainer; + offset = rng.startOffset; + blockElm = dom.getParent(container, dom.isBlock); + if (!isTextBlock(blockElm)) { + return; + } + + if (container.nodeType == 1) { + container = container.childNodes[offset]; + if (container && container.tagName != 'BR') { + return; + } + + if (isForward) { + sibling = blockElm.nextSibling; + } else { + sibling = blockElm.previousSibling; + } + + if (dom.isEmpty(blockElm) && isTextBlock(sibling) && dom.isEmpty(sibling)) { + if (cloneTextBlockWithFormats(blockElm, container)) { + dom.remove(sibling); + return true; + } + } + } else if (container.nodeType == 3) { + path = NodePath.create(blockElm, container); + clonedBlockElm = blockElm.cloneNode(true); + container = NodePath.resolve(clonedBlockElm, path); + + if (isForward) { + if (offset >= container.data.length) { + return; + } + + container.deleteData(offset, 1); + } else { + if (offset <= 0) { + return; + } + + container.deleteData(offset - 1, 1); + } + + if (dom.isEmpty(clonedBlockElm)) { + return cloneTextBlockWithFormats(blockElm, container); + } + } + } + + function customDelete(isForward) { + var mutationObserver, rng, caretElement; + + if (handleTextBlockMergeDelete(isForward)) { + return; + } + + Tools.each(editor.getBody().getElementsByTagName('*'), function(elm) { + // Mark existing spans + if (elm.tagName == 'SPAN') { + elm.setAttribute('mce-data-marked', 1); + } + + // Make sure all elements has a data-mce-style attribute + if (!elm.hasAttribute('data-mce-style') && elm.hasAttribute('style')) { + editor.dom.setAttrib(elm, 'style', editor.dom.getAttrib(elm, 'style')); + } + }); + + // Observe added nodes and style attribute changes + mutationObserver = new MutationObserver(function() {}); + mutationObserver.observe(editor.getDoc(), { + childList: true, + attributes: true, + subtree: true, + attributeFilter: ['style'] + }); + + editor.getDoc().execCommand(isForward ? 'ForwardDelete' : 'Delete', false, null); + + rng = editor.selection.getRng(); + caretElement = rng.startContainer.parentNode; + + Tools.each(mutationObserver.takeRecords(), function(record) { + if (!dom.isChildOf(record.target, editor.getBody())) { + return; + } + + // Restore style attribute to previous value + if (record.attributeName == "style") { + var oldValue = record.target.getAttribute('data-mce-style'); + + if (oldValue) { + record.target.setAttribute("style", oldValue); + } else { + record.target.removeAttribute("style"); + } + } + + // Remove all spans that aren't marked and retain selection + Tools.each(record.addedNodes, function(node) { + if (node.nodeName == "SPAN" && !node.getAttribute('mce-data-marked')) { + var offset, container; + + if (node == caretElement) { + offset = rng.startOffset; + container = node.firstChild; + } + + dom.remove(node, true); + + if (container) { + rng.setStart(container, offset); + rng.setEnd(container, offset); + editor.selection.setRng(rng); + } + } + }); + }); + + mutationObserver.disconnect(); + + // Remove any left over marks + Tools.each(editor.dom.select('span[mce-data-marked]'), function(span) { + span.removeAttribute('mce-data-marked'); + }); + } + + function transactCustomDelete(isForward) { + editor.undoManager.transact(function () { + customDelete(isForward); + }); + } + + editor.on('keydown', function(e) { + var isForward = e.keyCode == DELETE, isMetaOrCtrl = e.ctrlKey || e.metaKey; + + if (!isDefaultPrevented(e) && (isForward || e.keyCode == BACKSPACE)) { + var rng = editor.selection.getRng(), container = rng.startContainer, offset = rng.startOffset; + + // Shift+Delete is cut + if (isForward && e.shiftKey) { + return; + } + + if (handleLastBlockCharacterDelete(isForward, rng)) { + e.preventDefault(); + return; + } + + // Ignore non meta delete in the where there is text before/after the caret + if (!isMetaOrCtrl && rng.collapsed && container.nodeType == 3) { + if (isForward ? offset < container.data.length : offset > 0) { + return; + } + } + + e.preventDefault(); + + if (isMetaOrCtrl) { + editor.selection.getSel().modify("extend", isForward ? "forward" : "backward", e.metaKey ? "lineboundary" : "word"); + } + + customDelete(isForward); + } + }); + + // Handle case where text is deleted by typing over + editor.on('keypress', function(e) { + if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode > 31 && !VK.metaKeyPressed(e)) { + var rng, currentFormatNodes, fragmentNode, blockParent, caretNode, charText; + + rng = editor.selection.getRng(); + charText = String.fromCharCode(e.charCode); + e.preventDefault(); + + // Keep track of current format nodes + currentFormatNodes = $(rng.startContainer).parents().filter(function(idx, node) { + return !!editor.schema.getTextInlineElements()[node.nodeName]; + }); + + customDelete(true); + + // Check if the browser removed them + currentFormatNodes = currentFormatNodes.filter(function(idx, node) { + return !$.contains(editor.getBody(), node); + }); + + // Then re-add them + if (currentFormatNodes.length) { + fragmentNode = dom.createFragment(); + + currentFormatNodes.each(function(idx, formatNode) { + formatNode = formatNode.cloneNode(false); + + if (fragmentNode.hasChildNodes()) { + formatNode.appendChild(fragmentNode.firstChild); + fragmentNode.appendChild(formatNode); + } else { + caretNode = formatNode; + fragmentNode.appendChild(formatNode); + } + + fragmentNode.appendChild(formatNode); + }); + + caretNode.appendChild(editor.getDoc().createTextNode(charText)); + + // Prevent edge case where older WebKit would add an extra BR element + blockParent = dom.getParent(rng.startContainer, dom.isBlock); + if (dom.isEmpty(blockParent)) { + $(blockParent).empty().append(fragmentNode); + } else { + rng.insertNode(fragmentNode); + } + + rng.setStart(caretNode.firstChild, 1); + rng.setEnd(caretNode.firstChild, 1); + editor.selection.setRng(rng); + } else { + editor.selection.setContent(charText); + } + } + }); + + editor.addCommand('Delete', function() { + customDelete(); + }); + + editor.addCommand('ForwardDelete', function() { + customDelete(true); + }); + + // Older WebKits doesn't properly handle the clipboard so we can't add the rest + if (olderWebKit) { + return; + } + + editor.on('dragstart', function(e) { + dragStartRng = selection.getRng(); + setMceInternalContent(e); + }); + + editor.on('drop', function(e) { + if (!isDefaultPrevented(e)) { + var internalContent = getMceInternalContent(e); + + if (internalContent) { + e.preventDefault(); + + // Safari has a weird issue where drag/dropping images sometimes + // produces a green plus icon. When this happens the caretRangeFromPoint + // will return "null" even though the x, y coordinate is correct. + // But if we detach the insert from the drop event we will get a proper range + Delay.setEditorTimeout(editor, function() { + var pointRng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, doc); + + if (dragStartRng) { + selection.setRng(dragStartRng); + dragStartRng = null; + transactCustomDelete(); + } + + selection.setRng(pointRng); + insertClipboardContents(internalContent.html); + }); + } + } + }); + + editor.on('cut', function(e) { + if (!isDefaultPrevented(e) && e.clipboardData && !editor.selection.isCollapsed()) { + e.preventDefault(); + e.clipboardData.clearData(); + e.clipboardData.setData('text/html', editor.selection.getContent()); + e.clipboardData.setData('text/plain', editor.selection.getContent({format: 'text'})); + + // Needed delay for https://code.google.com/p/chromium/issues/detail?id=363288#c3 + // Nested delete/forwardDelete not allowed on execCommand("cut") + // This is ugly but not sure how to work around it otherwise + Delay.setEditorTimeout(editor, function() { + transactCustomDelete(true); + }); + } + }); + } + + /** + * Makes sure that the editor body becomes empty when backspace or delete is pressed in empty editors. + * + * For example: + *

|

+ * + * Or: + *

|

+ * + * Or: + * [

] + */ + function emptyEditorWhenDeleting() { + function serializeRng(rng) { + var body = dom.create("body"); + var contents = rng.cloneContents(); + body.appendChild(contents); + return selection.serializer.serialize(body, {format: 'html'}); + } + + function allContentsSelected(rng) { + if (!rng.setStart) { + if (rng.item) { + return false; + } + + var bodyRng = rng.duplicate(); + bodyRng.moveToElementText(editor.getBody()); + return RangeUtils.compareRanges(rng, bodyRng); + } + + var selection = serializeRng(rng); + + var allRng = dom.createRng(); + allRng.selectNode(editor.getBody()); + + var allSelection = serializeRng(allRng); + return selection === allSelection; + } + + editor.on('keydown', function(e) { + var keyCode = e.keyCode, isCollapsed, body; + + // Empty the editor if it's needed for example backspace at

|

+ if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) { + isCollapsed = editor.selection.isCollapsed(); + body = editor.getBody(); + + // Selection is collapsed but the editor isn't empty + if (isCollapsed && !dom.isEmpty(body)) { + return; + } + + // Selection isn't collapsed but not all the contents is selected + if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) { + return; + } + + // Manually empty the editor + e.preventDefault(); + editor.setContent(''); + + if (body.firstChild && dom.isBlock(body.firstChild)) { + editor.selection.setCursorLocation(body.firstChild, 0); + } else { + editor.selection.setCursorLocation(body, 0); + } + + editor.nodeChanged(); + } + }); + } + + /** + * WebKit doesn't select all the nodes in the body when you press Ctrl+A. + * IE selects more than the contents [

a

] instead of

[a] see bug #6438 + * This selects the whole body so that backspace/delete logic will delete everything + */ + function selectAll() { + editor.shortcuts.add('meta+a', null, 'SelectAll'); + } + + /** + * WebKit has a weird issue where it some times fails to properly convert keypresses to input method keystrokes. + * The IME on Mac doesn't initialize when it doesn't fire a proper focus event. + * + * This seems to happen when the user manages to click the documentElement element then the window doesn't get proper focus until + * you enter a character into the editor. + * + * It also happens when the first focus in made to the body. + * + * See: https://bugs.webkit.org/show_bug.cgi?id=83566 + */ + function inputMethodFocus() { + if (!editor.settings.content_editable) { + // Case 1 IME doesn't initialize if you focus the document + // Disabled since it was interferring with the cE=false logic + // Also coultn't reproduce the issue on Safari 9 + /*dom.bind(editor.getDoc(), 'focusin', function() { + selection.setRng(selection.getRng()); + });*/ + + // Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event + // Needs to be both down/up due to weird rendering bug on Chrome Windows + dom.bind(editor.getDoc(), 'mousedown mouseup', function(e) { + var rng; + + if (e.target == editor.getDoc().documentElement) { + rng = selection.getRng(); + editor.getBody().focus(); + + if (e.type == 'mousedown') { + if (CaretContainer.isCaretContainer(rng.startContainer)) { + return; + } + + // Edge case for mousedown, drag select and mousedown again within selection on Chrome Windows to render caret + selection.placeCaretAt(e.clientX, e.clientY); + } else { + selection.setRng(rng); + } + } + }); + } + } + + /** + * Backspacing in FireFox/IE from a paragraph into a horizontal rule results in a floating text node because the + * browser just deletes the paragraph - the browser fails to merge the text node with a horizontal rule so it is + * left there. TinyMCE sees a floating text node and wraps it in a paragraph on the key up event (ForceBlocks.js + * addRootBlocks), meaning the action does nothing. With this code, FireFox/IE matche the behaviour of other + * browsers. + * + * It also fixes a bug on Firefox where it's impossible to delete HR elements. + */ + function removeHrOnBackspace() { + editor.on('keydown', function(e) { + if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) { + // Check if there is any HR elements this is faster since getRng on IE 7 & 8 is slow + if (!editor.getBody().getElementsByTagName('hr').length) { + return; + } + + if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) { + var node = selection.getNode(); + var previousSibling = node.previousSibling; + + if (node.nodeName == 'HR') { + dom.remove(node); + e.preventDefault(); + return; + } + + if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") { + dom.remove(previousSibling); + e.preventDefault(); + } + } + } + }); + } + + /** + * Firefox 3.x has an issue where the body element won't get proper focus if you click out + * side it's rectangle. + */ + function focusBody() { + // Fix for a focus bug in FF 3.x where the body element + // wouldn't get proper focus if the user clicked on the HTML element + if (!window.Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4 + editor.on('mousedown', function(e) { + if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") { + var body = editor.getBody(); + + // Blur the body it's focused but not correctly focused + body.blur(); + + // Refocus the body after a little while + Delay.setEditorTimeout(editor, function() { + body.focus(); + }); + } + }); + } + } + + /** + * WebKit has a bug where it isn't possible to select image, hr or anchor elements + * by clicking on them so we need to fake that. + */ + function selectControlElements() { + editor.on('click', function(e) { + var target = e.target; + + // Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250 + // WebKit can't even do simple things like selecting an image + // Needs to be the setBaseAndExtend or it will fail to select floated images + if (/^(IMG|HR)$/.test(target.nodeName) && dom.getContentEditableParent(target) !== "false") { + e.preventDefault(); + selection.getSel().setBaseAndExtent(target, 0, target, 1); + editor.nodeChanged(); + } + + if (target.nodeName == 'A' && dom.hasClass(target, 'mce-item-anchor')) { + e.preventDefault(); + selection.select(target); + } + }); + } + + /** + * Fixes a Gecko bug where the style attribute gets added to the wrong element when deleting between two block elements. + * + * Fixes do backspace/delete on this: + *

bla[ck

r]ed

+ * + * Would become: + *

bla|ed

+ * + * Instead of: + *

bla|ed

+ */ + function removeStylesWhenDeletingAcrossBlockElements() { + function getAttributeApplyFunction() { + var template = dom.getAttribs(selection.getStart().cloneNode(false)); + + return function() { + var target = selection.getStart(); + + if (target !== editor.getBody()) { + dom.setAttrib(target, "style", null); + + each(template, function(attr) { + target.setAttributeNode(attr.cloneNode(true)); + }); + } + }; + } + + function isSelectionAcrossElements() { + return !selection.isCollapsed() && + dom.getParent(selection.getStart(), dom.isBlock) != dom.getParent(selection.getEnd(), dom.isBlock); + } + + editor.on('keypress', function(e) { + var applyAttributes; + + if (!isDefaultPrevented(e) && (e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) { + applyAttributes = getAttributeApplyFunction(); + editor.getDoc().execCommand('delete', false, null); + applyAttributes(); + e.preventDefault(); + return false; + } + }); + + dom.bind(editor.getDoc(), 'cut', function(e) { + var applyAttributes; + + if (!isDefaultPrevented(e) && isSelectionAcrossElements()) { + applyAttributes = getAttributeApplyFunction(); + + Delay.setEditorTimeout(editor, function() { + applyAttributes(); + }); + } + }); + } + + /** + * Screen readers on IE needs to have the role application set on the body. + */ + function ensureBodyHasRoleApplication() { + document.body.setAttribute("role", "application"); + } + + /** + * Backspacing into a table behaves differently depending upon browser type. + * Therefore, disable Backspace when cursor immediately follows a table. + */ + function disableBackspaceIntoATable() { + editor.on('keydown', function(e) { + if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) { + if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) { + var previousSibling = selection.getNode().previousSibling; + if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "table") { + e.preventDefault(); + return false; + } + } + } + }); + } + + /** + * Old IE versions can't properly render BR elements in PRE tags white in contentEditable mode. So this + * logic adds a \n before the BR so that it will get rendered. + */ + function addNewLinesBeforeBrInPre() { + // IE8+ rendering mode does the right thing with BR in PRE + if (getDocumentMode() > 7) { + return; + } + + // Enable display: none in area and add a specific class that hides all BR elements in PRE to + // avoid the caret from getting stuck at the BR elements while pressing the right arrow key + setEditorCommandState('RespectVisibilityInDesign', true); + editor.contentStyles.push('.mceHideBrInPre pre br {display: none}'); + dom.addClass(editor.getBody(), 'mceHideBrInPre'); + + // Adds a \n before all BR elements in PRE to get them visual + parser.addNodeFilter('pre', function(nodes) { + var i = nodes.length, brNodes, j, brElm, sibling; + + while (i--) { + brNodes = nodes[i].getAll('br'); + j = brNodes.length; + while (j--) { + brElm = brNodes[j]; + + // Add \n before BR in PRE elements on older IE:s so the new lines get rendered + sibling = brElm.prev; + if (sibling && sibling.type === 3 && sibling.value.charAt(sibling.value - 1) != '\n') { + sibling.value += '\n'; + } else { + brElm.parent.insert(new Node('#text', 3), brElm, true).value = '\n'; + } + } + } + }); + + // Removes any \n before BR elements in PRE since other browsers and in contentEditable=false mode they will be visible + serializer.addNodeFilter('pre', function(nodes) { + var i = nodes.length, brNodes, j, brElm, sibling; + + while (i--) { + brNodes = nodes[i].getAll('br'); + j = brNodes.length; + while (j--) { + brElm = brNodes[j]; + sibling = brElm.prev; + if (sibling && sibling.type == 3) { + sibling.value = sibling.value.replace(/\r?\n$/, ''); + } + } + } + }); + } + + /** + * Moves style width/height to attribute width/height when the user resizes an image on IE. + */ + function removePreSerializedStylesWhenSelectingControls() { + dom.bind(editor.getBody(), 'mouseup', function() { + var value, node = selection.getNode(); + + // Moved styles to attributes on IMG eements + if (node.nodeName == 'IMG') { + // Convert style width to width attribute + if ((value = dom.getStyle(node, 'width'))) { + dom.setAttrib(node, 'width', value.replace(/[^0-9%]+/g, '')); + dom.setStyle(node, 'width', ''); + } + + // Convert style height to height attribute + if ((value = dom.getStyle(node, 'height'))) { + dom.setAttrib(node, 'height', value.replace(/[^0-9%]+/g, '')); + dom.setStyle(node, 'height', ''); + } + } + }); + } + + /** + * Removes a blockquote when backspace is pressed at the beginning of it. + * + * For example: + *

|x

+ * + * Becomes: + *

|x

+ */ + function removeBlockQuoteOnBackSpace() { + // Add block quote deletion handler + editor.on('keydown', function(e) { + var rng, container, offset, root, parent; + + if (isDefaultPrevented(e) || e.keyCode != VK.BACKSPACE) { + return; + } + + rng = selection.getRng(); + container = rng.startContainer; + offset = rng.startOffset; + root = dom.getRoot(); + parent = container; + + if (!rng.collapsed || offset !== 0) { + return; + } + + while (parent && parent.parentNode && parent.parentNode.firstChild == parent && parent.parentNode != root) { + parent = parent.parentNode; + } + + // Is the cursor at the beginning of a blockquote? + if (parent.tagName === 'BLOCKQUOTE') { + // Remove the blockquote + editor.formatter.toggle('blockquote', null, parent); + + // Move the caret to the beginning of container + rng = dom.createRng(); + rng.setStart(container, 0); + rng.setEnd(container, 0); + selection.setRng(rng); + } + }); + } + + /** + * Sets various Gecko editing options on mouse down and before a execCommand to disable inline table editing that is broken etc. + */ + function setGeckoEditingOptions() { + function setOpts() { + refreshContentEditable(); + + setEditorCommandState("StyleWithCSS", false); + setEditorCommandState("enableInlineTableEditing", false); + + if (!settings.object_resizing) { + setEditorCommandState("enableObjectResizing", false); + } + } + + if (!settings.readonly) { + editor.on('BeforeExecCommand MouseDown', setOpts); + } + } + + /** + * Fixes a gecko link bug, when a link is placed at the end of block elements there is + * no way to move the caret behind the link. This fix adds a bogus br element after the link. + * + * For example this: + *

x

+ * + * Becomes this: + *

x

+ */ + function addBrAfterLastLinks() { + function fixLinks() { + each(dom.select('a'), function(node) { + var parentNode = node.parentNode, root = dom.getRoot(); + + if (parentNode.lastChild === node) { + while (parentNode && !dom.isBlock(parentNode)) { + if (parentNode.parentNode.lastChild !== parentNode || parentNode === root) { + return; + } + + parentNode = parentNode.parentNode; + } + + dom.add(parentNode, 'br', {'data-mce-bogus': 1}); + } + }); + } + + editor.on('SetContent ExecCommand', function(e) { + if (e.type == "setcontent" || e.command === 'mceInsertLink') { + fixLinks(); + } + }); + } + + /** + * WebKit will produce DIV elements here and there by default. But since TinyMCE uses paragraphs by + * default we want to change that behavior. + */ + function setDefaultBlockType() { + if (settings.forced_root_block) { + editor.on('init', function() { + setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block); + }); + } + } + + /** + * Deletes the selected image on IE instead of navigating to previous page. + */ + function deleteControlItemOnBackSpace() { + editor.on('keydown', function(e) { + var rng; + + if (!isDefaultPrevented(e) && e.keyCode == BACKSPACE) { + rng = editor.getDoc().selection.createRange(); + if (rng && rng.item) { + e.preventDefault(); + editor.undoManager.beforeChange(); + dom.remove(rng.item(0)); + editor.undoManager.add(); + } + } + }); + } + + /** + * IE10 doesn't properly render block elements with the right height until you add contents to them. + * This fixes that by adding a padding-right to all empty text block elements. + * See: https://connect.microsoft.com/IE/feedback/details/743881 + */ + function renderEmptyBlocksFix() { + var emptyBlocksCSS; + + // IE10+ + if (getDocumentMode() >= 10) { + emptyBlocksCSS = ''; + each('p div h1 h2 h3 h4 h5 h6'.split(' '), function(name, i) { + emptyBlocksCSS += (i > 0 ? ',' : '') + name + ':empty'; + }); + + editor.contentStyles.push(emptyBlocksCSS + '{padding-right: 1px !important}'); + } + } + + /** + * Old IE versions can't retain contents within noscript elements so this logic will store the contents + * as a attribute and the insert that value as it's raw text when the DOM is serialized. + */ + function keepNoScriptContents() { + if (getDocumentMode() < 9) { + parser.addNodeFilter('noscript', function(nodes) { + var i = nodes.length, node, textNode; + + while (i--) { + node = nodes[i]; + textNode = node.firstChild; + + if (textNode) { + node.attr('data-mce-innertext', textNode.value); + } + } + }); + + serializer.addNodeFilter('noscript', function(nodes) { + var i = nodes.length, node, textNode, value; + + while (i--) { + node = nodes[i]; + textNode = nodes[i].firstChild; + + if (textNode) { + textNode.value = Entities.decode(textNode.value); + } else { + // Old IE can't retain noscript value so an attribute is used to store it + value = node.attributes.map['data-mce-innertext']; + if (value) { + node.attr('data-mce-innertext', null); + textNode = new Node('#text', 3); + textNode.value = value; + textNode.raw = true; + node.append(textNode); + } + } + } + }); + } + } + + /** + * IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode. + */ + function fixCaretSelectionOfDocumentElementOnIe() { + var doc = dom.doc, body = doc.body, started, startRng, htmlElm; + + // Return range from point or null if it failed + function rngFromPoint(x, y) { + var rng = body.createTextRange(); + + try { + rng.moveToPoint(x, y); + } catch (ex) { + // IE sometimes throws and exception, so lets just ignore it + rng = null; + } + + return rng; + } + + // Fires while the selection is changing + function selectionChange(e) { + var pointRng; + + // Check if the button is down or not + if (e.button) { + // Create range from mouse position + pointRng = rngFromPoint(e.x, e.y); + + if (pointRng) { + // Check if pointRange is before/after selection then change the endPoint + if (pointRng.compareEndPoints('StartToStart', startRng) > 0) { + pointRng.setEndPoint('StartToStart', startRng); + } else { + pointRng.setEndPoint('EndToEnd', startRng); + } + + pointRng.select(); + } + } else { + endSelection(); + } + } + + // Removes listeners + function endSelection() { + var rng = doc.selection.createRange(); + + // If the range is collapsed then use the last start range + if (startRng && !rng.item && rng.compareEndPoints('StartToEnd', rng) === 0) { + startRng.select(); + } + + dom.unbind(doc, 'mouseup', endSelection); + dom.unbind(doc, 'mousemove', selectionChange); + startRng = started = 0; + } + + // Make HTML element unselectable since we are going to handle selection by hand + doc.documentElement.unselectable = true; + + // Detect when user selects outside BODY + dom.bind(doc, 'mousedown contextmenu', function(e) { + if (e.target.nodeName === 'HTML') { + if (started) { + endSelection(); + } + + // Detect vertical scrollbar, since IE will fire a mousedown on the scrollbar and have target set as HTML + htmlElm = doc.documentElement; + if (htmlElm.scrollHeight > htmlElm.clientHeight) { + return; + } + + started = 1; + // Setup start position + startRng = rngFromPoint(e.x, e.y); + if (startRng) { + // Listen for selection change events + dom.bind(doc, 'mouseup', endSelection); + dom.bind(doc, 'mousemove', selectionChange); + + dom.getRoot().focus(); + startRng.select(); + } + } + }); + } + + /** + * Fixes selection issues where the caret can be placed between two inline elements like a|b + * this fix will lean the caret right into the closest inline element. + */ + function normalizeSelection() { + // Normalize selection for example a|a becomes a|a except for Ctrl+A since it selects everything + editor.on('keyup focusin mouseup', function(e) { + if (e.keyCode != 65 || !VK.metaKeyPressed(e)) { + selection.normalize(); + } + }, true); + } + + /** + * Forces Gecko to render a broken image icon if it fails to load an image. + */ + function showBrokenImageIcon() { + editor.contentStyles.push( + 'img:-moz-broken {' + + '-moz-force-broken-image-icon:1;' + + 'min-width:24px;' + + 'min-height:24px' + + '}' + ); + } + + /** + * iOS has a bug where it's impossible to type if the document has a touchstart event + * bound and the user touches the document while having the on screen keyboard visible. + * + * The touch event moves the focus to the parent document while having the caret inside the iframe + * this fix moves the focus back into the iframe document. + */ + function restoreFocusOnKeyDown() { + if (!editor.inline) { + editor.on('keydown', function() { + if (document.activeElement == document.body) { + editor.getWin().focus(); + } + }); + } + } + + /** + * IE 11 has an annoying issue where you can't move focus into the editor + * by clicking on the white area HTML element. We used to be able to to fix this with + * the fixCaretSelectionOfDocumentElementOnIe fix. But since M$ removed the selection + * object it's not possible anymore. So we need to hack in a ungly CSS to force the + * body to be at least 150px. If the user clicks the HTML element out side this 150px region + * we simply move the focus into the first paragraph. Not ideal since you loose the + * positioning of the caret but goot enough for most cases. + */ + function bodyHeight() { + if (!editor.inline) { + editor.contentStyles.push('body {min-height: 150px}'); + editor.on('click', function(e) { + var rng; + + if (e.target.nodeName == 'HTML') { + // Edge seems to only need focus if we set the range + // the caret will become invisible and moved out of the iframe!! + if (Env.ie > 11) { + editor.getBody().focus(); + return; + } + + // Need to store away non collapsed ranges since the focus call will mess that up see #7382 + rng = editor.selection.getRng(); + editor.getBody().focus(); + editor.selection.setRng(rng); + editor.selection.normalize(); + editor.nodeChanged(); + } + }); + } + } + + /** + * Firefox on Mac OS will move the browser back to the previous page if you press CMD+Left arrow. + * You might then loose all your work so we need to block that behavior and replace it with our own. + */ + function blockCmdArrowNavigation() { + if (Env.mac) { + editor.on('keydown', function(e) { + if (VK.metaKeyPressed(e) && !e.shiftKey && (e.keyCode == 37 || e.keyCode == 39)) { + e.preventDefault(); + editor.selection.getSel().modify('move', e.keyCode == 37 ? 'backward' : 'forward', 'lineboundary'); + } + }); + } + } + + /** + * Disables the autolinking in IE 9+ this is then re-enabled by the autolink plugin. + */ + function disableAutoUrlDetect() { + setEditorCommandState("AutoUrlDetect", false); + } + + /** + * iOS 7.1 introduced two new bugs: + * 1) It's possible to open links within a contentEditable area by clicking on them. + * 2) If you hold down the finger it will display the link/image touch callout menu. + */ + function tapLinksAndImages() { + editor.on('click', function(e) { + var elm = e.target; + + do { + if (elm.tagName === 'A') { + e.preventDefault(); + return; + } + } while ((elm = elm.parentNode)); + }); + + editor.contentStyles.push('.mce-content-body {-webkit-touch-callout: none}'); + } + + /** + * iOS Safari and possible other browsers have a bug where it won't fire + * a click event when a contentEditable is focused. This function fakes click events + * by using touchstart/touchend and measuring the time and distance travelled. + */ + /* + function touchClickEvent() { + editor.on('touchstart', function(e) { + var elm, time, startTouch, changedTouches; + + elm = e.target; + time = new Date().getTime(); + changedTouches = e.changedTouches; + + if (!changedTouches || changedTouches.length > 1) { + return; + } + + startTouch = changedTouches[0]; + + editor.once('touchend', function(e) { + var endTouch = e.changedTouches[0], args; + + if (new Date().getTime() - time > 500) { + return; + } + + if (Math.abs(startTouch.clientX - endTouch.clientX) > 5) { + return; + } + + if (Math.abs(startTouch.clientY - endTouch.clientY) > 5) { + return; + } + + args = { + target: elm + }; + + each('pageX pageY clientX clientY screenX screenY'.split(' '), function(key) { + args[key] = endTouch[key]; + }); + + args = editor.fire('click', args); + + if (!args.isDefaultPrevented()) { + // iOS WebKit can't place the caret properly once + // you bind touch events so we need to do this manually + // TODO: Expand to the closest word? Touble tap still works. + editor.selection.placeCaretAt(endTouch.clientX, endTouch.clientY); + editor.nodeChanged(); + } + }); + }); + } + */ + + /** + * WebKit has a bug where it will allow forms to be submitted if they are inside a contentEditable element. + * For example this:
'; + } else if (/^(UL|OL)$/.test(body.nodeName)) { + content = '
  • ' + padd + '
  • '; + } + + forcedRootBlockName = self.settings.forced_root_block; + + // Check if forcedRootBlock is configured and that the block is a valid child of the body + if (forcedRootBlockName && self.schema.isValidChild(body.nodeName.toLowerCase(), forcedRootBlockName.toLowerCase())) { + // Padd with bogus BR elements on modern browsers and IE 7 and 8 since they don't render empty P tags properly + content = padd; + content = self.dom.createHTML(forcedRootBlockName, self.settings.forced_root_block_attrs, content); + } else if (!ie && !content) { + // We need to add a BR when forced_root_block is disabled on non IE browsers to place the caret + content = '
    '; + } + + self.dom.setHTML(body, content); + + self.fire('SetContent', args); + } else { + // Parse and serialize the html + if (args.format !== 'raw') { + content = new Serializer({ + validate: self.validate + }, self.schema).serialize( + self.parser.parse(content, {isRootContent: true}) + ); + } + + // Set the new cleaned contents to the editor + args.content = trim(content); + self.dom.setHTML(body, args.content); + + // Do post processing + if (!args.no_events) { + self.fire('SetContent', args); + } + + // Don't normalize selection if the focused element isn't the body in + // content editable mode since it will steal focus otherwise + /*if (!self.settings.content_editable || document.activeElement === self.getBody()) { + self.selection.normalize(); + }*/ + } + + return args.content; + }, + + /** + * Gets the content from the editor instance, this will cleanup the content before it gets returned using + * the different cleanup rules options. + * + * @method getContent + * @param {Object} args Optional content object, this gets passed around through the whole get process. + * @return {String} Cleaned content string, normally HTML contents. + * @example + * // Get the HTML contents of the currently active editor + * console.debug(tinymce.activeEditor.getContent()); + * + * // Get the raw contents of the currently active editor + * tinymce.activeEditor.getContent({format: 'raw'}); + * + * // Get content of a specific editor: + * tinymce.get('content id').getContent() + */ + getContent: function(args) { + var self = this, content, body = self.getBody(); + + // Setup args object + args = args || {}; + args.format = args.format || 'html'; + args.get = true; + args.getInner = true; + + // Do preprocessing + if (!args.no_events) { + self.fire('BeforeGetContent', args); + } + + // Get raw contents or by default the cleaned contents + if (args.format == 'raw') { + content = self.serializer.getTrimmedContent(); + } else if (args.format == 'text') { + content = body.innerText || body.textContent; + } else { + content = self.serializer.serialize(body, args); + } + + // Trim whitespace in beginning/end of HTML + if (args.format != 'text') { + args.content = trim(content); + } else { + args.content = content; + } + + // Do post processing + if (!args.no_events) { + self.fire('GetContent', args); + } + + return args.content; + }, + + /** + * Inserts content at caret position. + * + * @method insertContent + * @param {String} content Content to insert. + * @param {Object} args Optional args to pass to insert call. + */ + insertContent: function(content, args) { + if (args) { + content = extend({content: content}, args); + } + + this.execCommand('mceInsertContent', false, content); + }, + + /** + * Returns true/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents. + * + * The dirty state is automatically set to true if you do modifications to the content in other + * words when new undo levels is created or if you undo/redo to update the contents of the editor. It will also be set + * to false if you call editor.save(). + * + * @method isDirty + * @return {Boolean} True/false if the editor is dirty or not. It will get dirty if the user has made modifications to the contents. + * @example + * if (tinymce.activeEditor.isDirty()) + * alert("You must save your contents."); + */ + isDirty: function() { + return !this.isNotDirty; + }, + + /** + * Explicitly sets the dirty state. This will fire the dirty event if the editor dirty state is changed from false to true + * by invoking this method. + * + * @method setDirty + * @param {Boolean} state True/false if the editor is considered dirty. + * @example + * function ajaxSave() { + * var editor = tinymce.get('elm1'); + * + * // Save contents using some XHR call + * alert(editor.getContent()); + * + * editor.setDirty(false); // Force not dirty state + * } + */ + setDirty: function(state) { + var oldState = !this.isNotDirty; + + this.isNotDirty = !state; + + if (state && state != oldState) { + this.fire('dirty'); + } + }, + + /** + * Sets the editor mode. Mode can be for example "design", "code" or "readonly". + * + * @method setMode + * @param {String} mode Mode to set the editor in. + */ + setMode: function(mode) { + Mode.setMode(this, mode); + }, + + /** + * Returns the editors container element. The container element wrappes in + * all the elements added to the page for the editor. Such as UI, iframe etc. + * + * @method getContainer + * @return {Element} HTML DOM element for the editor container. + */ + getContainer: function() { + var self = this; + + if (!self.container) { + self.container = DOM.get(self.editorContainer || self.id + '_parent'); + } + + return self.container; + }, + + /** + * Returns the editors content area container element. The this element is the one who + * holds the iframe or the editable element. + * + * @method getContentAreaContainer + * @return {Element} HTML DOM element for the editor area container. + */ + getContentAreaContainer: function() { + return this.contentAreaContainer; + }, + + /** + * Returns the target element/textarea that got replaced with a TinyMCE editor instance. + * + * @method getElement + * @return {Element} HTML DOM element for the replaced element. + */ + getElement: function() { + if (!this.targetElm) { + this.targetElm = DOM.get(this.id); + } + + return this.targetElm; + }, + + /** + * Returns the iframes window object. + * + * @method getWin + * @return {Window} Iframe DOM window object. + */ + getWin: function() { + var self = this, elm; + + if (!self.contentWindow) { + elm = self.iframeElement; + + if (elm) { + self.contentWindow = elm.contentWindow; + } + } + + return self.contentWindow; + }, + + /** + * Returns the iframes document object. + * + * @method getDoc + * @return {Document} Iframe DOM document object. + */ + getDoc: function() { + var self = this, win; + + if (!self.contentDocument) { + win = self.getWin(); + + if (win) { + self.contentDocument = win.document; + } + } + + return self.contentDocument; + }, + + /** + * Returns the root element of the editable area. + * For a non-inline iframe-based editor, returns the iframe's body element. + * + * @method getBody + * @return {Element} The root element of the editable area. + */ + getBody: function() { + var doc = this.getDoc(); + return this.bodyElement || (doc ? doc.body : null); + }, + + /** + * URL converter function this gets executed each time a user adds an img, a or + * any other element that has a URL in it. This will be called both by the DOM and HTML + * manipulation functions. + * + * @method convertURL + * @param {string} url URL to convert. + * @param {string} name Attribute name src, href etc. + * @param {string/HTMLElement} elm Tag name or HTML DOM element depending on HTML or DOM insert. + * @return {string} Converted URL string. + */ + convertURL: function(url, name, elm) { + var self = this, settings = self.settings; + + // Use callback instead + if (settings.urlconverter_callback) { + return self.execCallback('urlconverter_callback', url, elm, true, name); + } + + // Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs + if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0 || url.length === 0) { + return url; + } + + // Convert to relative + if (settings.relative_urls) { + return self.documentBaseURI.toRelative(url); + } + + // Convert to absolute + url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host); + + return url; + }, + + /** + * Adds visual aid for tables, anchors etc so they can be more easily edited inside the editor. + * + * @method addVisual + * @param {Element} elm Optional root element to loop though to find tables etc that needs the visual aid. + */ + addVisual: function(elm) { + var self = this, settings = self.settings, dom = self.dom, cls; + + elm = elm || self.getBody(); + + if (self.hasVisual === undefined) { + self.hasVisual = settings.visual; + } + + each(dom.select('table,a', elm), function(elm) { + var value; + + switch (elm.nodeName) { + case 'TABLE': + cls = settings.visual_table_class || 'mce-item-table'; + value = dom.getAttrib(elm, 'border'); + + if ((!value || value == '0') && self.hasVisual) { + dom.addClass(elm, cls); + } else { + dom.removeClass(elm, cls); + } + + return; + + case 'A': + if (!dom.getAttrib(elm, 'href', false)) { + value = dom.getAttrib(elm, 'name') || elm.id; + cls = settings.visual_anchor_class || 'mce-item-anchor'; + + if (value && self.hasVisual) { + dom.addClass(elm, cls); + } else { + dom.removeClass(elm, cls); + } + } + + return; + } + }); + + self.fire('VisualAid', {element: elm, hasVisual: self.hasVisual}); + }, + + /** + * Removes the editor from the dom and tinymce collection. + * + * @method remove + */ + remove: function() { + var self = this; + + if (!self.removed) { + self.save(); + self.removed = 1; + self.unbindAllNativeEvents(); + + // Remove any hidden input + if (self.hasHiddenInput) { + DOM.remove(self.getElement().nextSibling); + } + + if (!self.inline) { + // IE 9 has a bug where the selection stops working if you place the + // caret inside the editor then remove the iframe + if (ie && ie < 10) { + self.getDoc().execCommand('SelectAll', false, null); + } + + DOM.setStyle(self.id, 'display', self.orgDisplay); + self.getBody().onload = null; // Prevent #6816 + } + + self.fire('remove'); + + self.editorManager.remove(self); + DOM.remove(self.getContainer()); + self._selectionOverrides.destroy(); + self.editorUpload.destroy(); + self.destroy(); + } + }, + + /** + * Destroys the editor instance by removing all events, element references or other resources + * that could leak memory. This method will be called automatically when the page is unloaded + * but you can also call it directly if you know what you are doing. + * + * @method destroy + * @param {Boolean} automatic Optional state if the destroy is an automatic destroy or user called one. + */ + destroy: function(automatic) { + var self = this, form; + + // One time is enough + if (self.destroyed) { + return; + } + + // If user manually calls destroy and not remove + // Users seems to have logic that calls destroy instead of remove + if (!automatic && !self.removed) { + self.remove(); + return; + } + + if (!automatic) { + self.editorManager.off('beforeunload', self._beforeUnload); + + // Manual destroy + if (self.theme && self.theme.destroy) { + self.theme.destroy(); + } + + // Destroy controls, selection and dom + self.selection.destroy(); + self.dom.destroy(); + } + + form = self.formElement; + if (form) { + if (form._mceOldSubmit) { + form.submit = form._mceOldSubmit; + form._mceOldSubmit = null; + } + + DOM.unbind(form, 'submit reset', self.formEventDelegate); + } + + self.contentAreaContainer = self.formElement = self.container = self.editorContainer = null; + self.bodyElement = self.contentDocument = self.contentWindow = null; + self.iframeElement = self.targetElm = null; + + if (self.selection) { + self.selection = self.selection.win = self.selection.dom = self.selection.dom.doc = null; + } + + self.destroyed = 1; + }, + + /** + * Uploads all data uri/blob uri images in the editor contents to server. + * + * @method uploadImages + * @param {function} callback Optional callback with images and status for each image. + * @return {tinymce.util.Promise} Promise instance. + */ + uploadImages: function(callback) { + return this.editorUpload.uploadImages(callback); + }, + + // Internal functions + + _scanForImages: function() { + return this.editorUpload.scanForImages(); + } + }; + + extend(Editor.prototype, EditorObservable); + + return Editor; +}); + +// Included from: js/tinymce/classes/util/I18n.js + +/** + * I18n.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * I18n class that handles translation of TinyMCE UI. + * Uses po style with csharp style parameters. + * + * @class tinymce.util.I18n + */ +define("tinymce/util/I18n", [ + "tinymce/util/Tools" +], function(Tools) { + "use strict"; + + var data = {}, code = "en"; + + return { + /** + * Sets the current language code. + * + * @method setCode + * @param {String} newCode Current language code. + */ + setCode: function(newCode) { + if (newCode) { + code = newCode; + this.rtl = this.data[newCode] ? this.data[newCode]._dir === 'rtl' : false; + } + }, + + /** + * Returns the current language code. + * + * @method getCode + * @return {String} Current language code. + */ + getCode: function() { + return code; + }, + + /** + * Property gets set to true if a RTL language pack was loaded. + * + * @property rtl + * @type Boolean + */ + rtl: false, + + /** + * Adds translations for a specific language code. + * + * @method add + * @param {String} code Language code like sv_SE. + * @param {Array} items Name/value array with English en_US to sv_SE. + */ + add: function(code, items) { + var langData = data[code]; + + if (!langData) { + data[code] = langData = {}; + } + + for (var name in items) { + langData[name] = items[name]; + } + + this.setCode(code); + }, + + /** + * Translates the specified text. + * + * It has a few formats: + * I18n.translate("Text"); + * I18n.translate(["Text {0}/{1}", 0, 1]); + * I18n.translate({raw: "Raw string"}); + * + * @method translate + * @param {String/Object/Array} text Text to translate. + * @return {String} String that got translated. + */ + translate: function(text) { + var langData = data[code] || {}; + + /** + * number - string + * null, undefined and empty string - empty string + * array - comma-delimited string + * object - in [object Object] + * function - in [object Function] + * + * @param obj + * @returns {string} + */ + function toString(obj) { + if (Tools.is(obj, 'function')) { + return Object.prototype.toString.call(obj); + } + return !isEmpty(obj) ? '' + obj : ''; + } + + function isEmpty(text) { + return text === '' || text === null || Tools.is(text, 'undefined'); + } + + function getLangData(text) { + // make sure we work on a string and return a string + text = toString(text); + return Tools.hasOwn(langData, text) ? toString(langData[text]) : text; + } + + + if (isEmpty(text)) { + return ''; + } + + if (Tools.is(text, 'object') && Tools.hasOwn(text, 'raw')) { + return toString(text.raw); + } + + if (Tools.is(text, 'array')) { + var values = text.slice(1); + text = getLangData(text[0]).replace(/\{([0-9]+)\}/g, function($1, $2) { + return Tools.hasOwn(values, $2) ? toString(values[$2]) : $1; + }); + } + + return getLangData(text).replace(/{context:\w+}$/, ''); + }, + + data: data + }; +}); + +// Included from: js/tinymce/classes/FocusManager.js + +/** + * FocusManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class manages the focus/blur state of the editor. This class is needed since some + * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar. + * + * This class will fire two events focus and blur on the editor instances that got affected. + * It will also handle the restore of selection when the focus is lost and returned. + * + * @class tinymce.FocusManager + */ +define("tinymce/FocusManager", [ + "tinymce/dom/DOMUtils", + "tinymce/util/Delay", + "tinymce/Env" +], function(DOMUtils, Delay, Env) { + var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM; + + /** + * Constructs a new focus manager instance. + * + * @constructor FocusManager + * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for. + */ + function FocusManager(editorManager) { + function getActiveElement() { + try { + return document.activeElement; + } catch (ex) { + // IE sometimes fails to get the activeElement when resizing table + // TODO: Investigate this + return document.body; + } + } + + // We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object + // TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well. + function createBookmark(dom, rng) { + if (rng && rng.startContainer) { + // Verify that the range is within the root of the editor + if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) { + return; + } + + return { + startContainer: rng.startContainer, + startOffset: rng.startOffset, + endContainer: rng.endContainer, + endOffset: rng.endOffset + }; + } + + return rng; + } + + function bookmarkToRng(editor, bookmark) { + var rng; + + if (bookmark.startContainer) { + rng = editor.getDoc().createRange(); + rng.setStart(bookmark.startContainer, bookmark.startOffset); + rng.setEnd(bookmark.endContainer, bookmark.endOffset); + } else { + rng = bookmark; + } + + return rng; + } + + function isUIElement(elm) { + return !!DOM.getParent(elm, FocusManager.isEditorUIElement); + } + + function registerEvents(e) { + var editor = e.editor; + + editor.on('init', function() { + // Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab + if (editor.inline || Env.ie) { + // Use the onbeforedeactivate event when available since it works better see #7023 + if ("onbeforedeactivate" in document && Env.ie < 9) { + editor.dom.bind(editor.getBody(), 'beforedeactivate', function(e) { + if (e.target != editor.getBody()) { + return; + } + + try { + editor.lastRng = editor.selection.getRng(); + } catch (ex) { + // IE throws "Unexcpected call to method or property access" some times so lets ignore it + } + }); + } else { + // On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes + editor.on('nodechange mouseup keyup', function(e) { + var node = getActiveElement(); + + // Only act on manual nodechanges + if (e.type == 'nodechange' && e.selectionChange) { + return; + } + + // IE 11 reports active element as iframe not body of iframe + if (node && node.id == editor.id + '_ifr') { + node = editor.getBody(); + } + + if (editor.dom.isChildOf(node, editor.getBody())) { + editor.lastRng = editor.selection.getRng(); + } + }); + } + + // Handles the issue with WebKit not retaining selection within inline document + // If the user releases the mouse out side the body since a mouse up event wont occur on the body + if (Env.webkit && !selectionChangeHandler) { + selectionChangeHandler = function() { + var activeEditor = editorManager.activeEditor; + + if (activeEditor && activeEditor.selection) { + var rng = activeEditor.selection.getRng(); + + // Store when it's non collapsed + if (rng && !rng.collapsed) { + editor.lastRng = rng; + } + } + }; + + DOM.bind(document, 'selectionchange', selectionChangeHandler); + } + } + }); + + editor.on('setcontent', function() { + editor.lastRng = null; + }); + + // Remove last selection bookmark on mousedown see #6305 + editor.on('mousedown', function() { + editor.selection.lastFocusBookmark = null; + }); + + editor.on('focusin', function() { + var focusedEditor = editorManager.focusedEditor, lastRng; + + if (editor.selection.lastFocusBookmark) { + lastRng = bookmarkToRng(editor, editor.selection.lastFocusBookmark); + editor.selection.lastFocusBookmark = null; + editor.selection.setRng(lastRng); + } + + if (focusedEditor != editor) { + if (focusedEditor) { + focusedEditor.fire('blur', {focusedEditor: editor}); + } + + editorManager.setActive(editor); + editorManager.focusedEditor = editor; + editor.fire('focus', {blurredEditor: focusedEditor}); + editor.focus(true); + } + + editor.lastRng = null; + }); + + editor.on('focusout', function() { + Delay.setEditorTimeout(editor, function() { + var focusedEditor = editorManager.focusedEditor; + + // Still the same editor the blur was outside any editor UI + if (!isUIElement(getActiveElement()) && focusedEditor == editor) { + editor.fire('blur', {focusedEditor: null}); + editorManager.focusedEditor = null; + + // Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs + if (editor.selection) { + editor.selection.lastFocusBookmark = null; + } + } + }); + }); + + // Check if focus is moved to an element outside the active editor by checking if the target node + // isn't within the body of the activeEditor nor a UI element such as a dialog child control + if (!documentFocusInHandler) { + documentFocusInHandler = function(e) { + var activeEditor = editorManager.activeEditor, target; + + target = e.target; + + if (activeEditor && target.ownerDocument == document) { + // Check to make sure we have a valid selection don't update the bookmark if it's + // a focusin to the body of the editor see #7025 + if (activeEditor.selection && target != activeEditor.getBody()) { + activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng); + } + + // Fire a blur event if the element isn't a UI element + if (target != document.body && !isUIElement(target) && editorManager.focusedEditor == activeEditor) { + activeEditor.fire('blur', {focusedEditor: null}); + editorManager.focusedEditor = null; + } + } + }; + + DOM.bind(document, 'focusin', documentFocusInHandler); + } + + // Handle edge case when user starts the selection inside the editor and releases + // the mouse outside the editor producing a new selection. This weird workaround is needed since + // Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843 + if (editor.inline && !documentMouseUpHandler) { + documentMouseUpHandler = function(e) { + var activeEditor = editorManager.activeEditor, dom = activeEditor.dom; + + if (activeEditor.inline && dom && !dom.isChildOf(e.target, activeEditor.getBody())) { + var rng = activeEditor.selection.getRng(); + + if (!rng.collapsed) { + activeEditor.lastRng = rng; + } + } + }; + + DOM.bind(document, 'mouseup', documentMouseUpHandler); + } + } + + function unregisterDocumentEvents(e) { + if (editorManager.focusedEditor == e.editor) { + editorManager.focusedEditor = null; + } + + if (!editorManager.activeEditor) { + DOM.unbind(document, 'selectionchange', selectionChangeHandler); + DOM.unbind(document, 'focusin', documentFocusInHandler); + DOM.unbind(document, 'mouseup', documentMouseUpHandler); + selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null; + } + } + + editorManager.on('AddEditor', registerEvents); + editorManager.on('RemoveEditor', unregisterDocumentEvents); + } + + /** + * Returns true if the specified element is part of the UI for example an button or text input. + * + * @method isEditorUIElement + * @param {Element} elm Element to check if it's part of the UI or not. + * @return {Boolean} True/false state if the element is part of the UI or not. + */ + FocusManager.isEditorUIElement = function(elm) { + // Needs to be converted to string since svg can have focus: #6776 + return elm.className.toString().indexOf('mce-') !== -1; + }; + + return FocusManager; +}); + +// Included from: js/tinymce/classes/EditorManager.js + +/** + * EditorManager.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class used as a factory for manager for tinymce.Editor instances. + * + * @example + * tinymce.EditorManager.init({}); + * + * @class tinymce.EditorManager + * @mixes tinymce.util.Observable + * @static + */ +define("tinymce/EditorManager", [ + "tinymce/Editor", + "tinymce/dom/DomQuery", + "tinymce/dom/DOMUtils", + "tinymce/util/URI", + "tinymce/Env", + "tinymce/util/Tools", + "tinymce/util/Promise", + "tinymce/util/Observable", + "tinymce/util/I18n", + "tinymce/FocusManager", + "tinymce/AddOnManager" +], function(Editor, $, DOMUtils, URI, Env, Tools, Promise, Observable, I18n, FocusManager, AddOnManager) { + var DOM = DOMUtils.DOM; + var explode = Tools.explode, each = Tools.each, extend = Tools.extend; + var instanceCounter = 0, beforeUnloadDelegate, EditorManager, boundGlobalEvents = false; + + function globalEventDelegate(e) { + each(EditorManager.editors, function(editor) { + if (e.type === 'scroll') { + editor.fire('ScrollWindow', e); + } else { + editor.fire('ResizeWindow', e); + } + }); + } + + function toggleGlobalEvents(editors, state) { + if (state !== boundGlobalEvents) { + if (state) { + $(window).on('resize scroll', globalEventDelegate); + } else { + $(window).off('resize scroll', globalEventDelegate); + } + + boundGlobalEvents = state; + } + } + + function removeEditorFromList(editor) { + var editors = EditorManager.editors, removedFromList; + + delete editors[editor.id]; + + for (var i = 0; i < editors.length; i++) { + if (editors[i] == editor) { + editors.splice(i, 1); + removedFromList = true; + break; + } + } + + // Select another editor since the active one was removed + if (EditorManager.activeEditor == editor) { + EditorManager.activeEditor = editors[0]; + } + + // Clear focusedEditor if necessary, so that we don't try to blur the destroyed editor + if (EditorManager.focusedEditor == editor) { + EditorManager.focusedEditor = null; + } + + return removedFromList; + } + + function purgeDestroyedEditor(editor) { + // User has manually destroyed the editor lets clean up the mess + if (editor && editor.initialized && !(editor.getContainer() || editor.getBody()).parentNode) { + removeEditorFromList(editor); + editor.unbindAllNativeEvents(); + editor.destroy(true); + editor.removed = true; + editor = null; + } + + return editor; + } + + EditorManager = { + /** + * Dom query instance. + * + * @property $ + * @type tinymce.dom.DomQuery + */ + $: $, + + /** + * Major version of TinyMCE build. + * + * @property majorVersion + * @type String + */ + majorVersion: '4', + + /** + * Minor version of TinyMCE build. + * + * @property minorVersion + * @type String + */ + minorVersion: '5.1', + + /** + * Release date of TinyMCE build. + * + * @property releaseDate + * @type String + */ + releaseDate: '2016-12-07', + + /** + * Collection of editor instances. + * + * @property editors + * @type Object + * @example + * for (edId in tinymce.editors) + * tinymce.editors[edId].save(); + */ + editors: [], + + /** + * Collection of language pack data. + * + * @property i18n + * @type Object + */ + i18n: I18n, + + /** + * Currently active editor instance. + * + * @property activeEditor + * @type tinymce.Editor + * @example + * tinyMCE.activeEditor.selection.getContent(); + * tinymce.EditorManager.activeEditor.selection.getContent(); + */ + activeEditor: null, + + setup: function() { + var self = this, baseURL, documentBaseURL, suffix = "", preInit, src; + + // Get base URL for the current document + documentBaseURL = URI.getDocumentBaseUrl(document.location); + + // Check if the URL is a document based format like: http://site/dir/file and file:/// + // leave other formats like applewebdata://... intact + if (/^[^:]+:\/\/\/?[^\/]+\//.test(documentBaseURL)) { + documentBaseURL = documentBaseURL.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, ''); + + if (!/[\/\\]$/.test(documentBaseURL)) { + documentBaseURL += '/'; + } + } + + // If tinymce is defined and has a base use that or use the old tinyMCEPreInit + preInit = window.tinymce || window.tinyMCEPreInit; + if (preInit) { + baseURL = preInit.base || preInit.baseURL; + suffix = preInit.suffix; + } else { + // Get base where the tinymce script is located + var scripts = document.getElementsByTagName('script'); + for (var i = 0; i < scripts.length; i++) { + src = scripts[i].src; + + // Script types supported: + // tinymce.js tinymce.min.js tinymce.dev.js + // tinymce.jquery.js tinymce.jquery.min.js tinymce.jquery.dev.js + // tinymce.full.js tinymce.full.min.js tinymce.full.dev.js + var srcScript = src.substring(src.lastIndexOf('/')); + if (/tinymce(\.full|\.jquery|)(\.min|\.dev|)\.js/.test(src)) { + if (srcScript.indexOf('.min') != -1) { + suffix = '.min'; + } + + baseURL = src.substring(0, src.lastIndexOf('/')); + break; + } + } + + // We didn't find any baseURL by looking at the script elements + // Try to use the document.currentScript as a fallback + if (!baseURL && document.currentScript) { + src = document.currentScript.src; + + if (src.indexOf('.min') != -1) { + suffix = '.min'; + } + + baseURL = src.substring(0, src.lastIndexOf('/')); + } + } + + /** + * Base URL where the root directory if TinyMCE is located. + * + * @property baseURL + * @type String + */ + self.baseURL = new URI(documentBaseURL).toAbsolute(baseURL); + + /** + * Document base URL where the current document is located. + * + * @property documentBaseURL + * @type String + */ + self.documentBaseURL = documentBaseURL; + + /** + * Absolute baseURI for the installation path of TinyMCE. + * + * @property baseURI + * @type tinymce.util.URI + */ + self.baseURI = new URI(self.baseURL); + + /** + * Current suffix to add to each plugin/theme that gets loaded for example ".min". + * + * @property suffix + * @type String + */ + self.suffix = suffix; + + self.focusManager = new FocusManager(self); + }, + + /** + * Overrides the default settings for editor instances. + * + * @method overrideDefaults + * @param {Object} defaultSettings Defaults settings object. + */ + overrideDefaults: function(defaultSettings) { + var baseUrl, suffix; + + baseUrl = defaultSettings.base_url; + if (baseUrl) { + this.baseURL = new URI(this.documentBaseURL).toAbsolute(baseUrl.replace(/\/+$/, '')); + this.baseURI = new URI(this.baseURL); + } + + suffix = defaultSettings.suffix; + if (defaultSettings.suffix) { + this.suffix = suffix; + } + + this.defaultSettings = defaultSettings; + + var pluginBaseUrls = defaultSettings.plugin_base_urls; + for (var name in pluginBaseUrls) { + AddOnManager.PluginManager.urls[name] = pluginBaseUrls[name]; + } + }, + + /** + * Initializes a set of editors. This method will create editors based on various settings. + * + * @method init + * @param {Object} settings Settings object to be passed to each editor instance. + * @return {tinymce.util.Promise} Promise that gets resolved with an array of editors when all editor instances are initialized. + * @example + * // Initializes a editor using the longer method + * tinymce.EditorManager.init({ + * some_settings : 'some value' + * }); + * + * // Initializes a editor instance using the shorter version and with a promise + * tinymce.init({ + * some_settings : 'some value' + * }).then(function(editors) { + * ... + * }); + */ + init: function(settings) { + var self = this, result, invalidInlineTargets; + + invalidInlineTargets = Tools.makeMap( + 'area base basefont br col frame hr img input isindex link meta param embed source wbr track ' + + 'colgroup option tbody tfoot thead tr script noscript style textarea video audio iframe object menu', + ' ' + ); + + function isInvalidInlineTarget(settings, elm) { + return settings.inline && elm.tagName.toLowerCase() in invalidInlineTargets; + } + + function report(msg, elm) { + // Log in a non test environment + if (window.console && !window.test) { + window.console.log(msg, elm); + } + } + + function createId(elm) { + var id = elm.id; + + // Use element id, or unique name or generate a unique id + if (!id) { + id = elm.name; + + if (id && !DOM.get(id)) { + id = elm.name; + } else { + // Generate unique name + id = DOM.uniqueId(); + } + + elm.setAttribute('id', id); + } + + return id; + } + + function execCallback(name) { + var callback = settings[name]; + + if (!callback) { + return; + } + + return callback.apply(self, Array.prototype.slice.call(arguments, 2)); + } + + function hasClass(elm, className) { + return className.constructor === RegExp ? className.test(elm.className) : DOM.hasClass(elm, className); + } + + function findTargets(settings) { + var l, targets = []; + + if (settings.types) { + each(settings.types, function(type) { + targets = targets.concat(DOM.select(type.selector)); + }); + + return targets; + } else if (settings.selector) { + return DOM.select(settings.selector); + } else if (settings.target) { + return [settings.target]; + } + + // Fallback to old setting + switch (settings.mode) { + case "exact": + l = settings.elements || ''; + + if (l.length > 0) { + each(explode(l), function(id) { + var elm; + + if ((elm = DOM.get(id))) { + targets.push(elm); + } else { + each(document.forms, function(f) { + each(f.elements, function(e) { + if (e.name === id) { + id = 'mce_editor_' + instanceCounter++; + DOM.setAttrib(e, 'id', id); + targets.push(e); + } + }); + }); + } + }); + } + break; + + case "textareas": + case "specific_textareas": + each(DOM.select('textarea'), function(elm) { + if (settings.editor_deselector && hasClass(elm, settings.editor_deselector)) { + return; + } + + if (!settings.editor_selector || hasClass(elm, settings.editor_selector)) { + targets.push(elm); + } + }); + break; + } + + return targets; + } + + var provideResults = function(editors) { + result = editors; + }; + + function initEditors() { + var initCount = 0, editors = [], targets; + + function createEditor(id, settings, targetElm) { + var editor = new Editor(id, settings, self); + + editors.push(editor); + + editor.on('init', function() { + if (++initCount === targets.length) { + provideResults(editors); + } + }); + + editor.targetElm = editor.targetElm || targetElm; + editor.render(); + } + + DOM.unbind(window, 'ready', initEditors); + execCallback('onpageload'); + + targets = $.unique(findTargets(settings)); + + // TODO: Deprecate this one + if (settings.types) { + each(settings.types, function(type) { + Tools.each(targets, function(elm) { + if (DOM.is(elm, type.selector)) { + createEditor(createId(elm), extend({}, settings, type), elm); + return false; + } + + return true; + }); + }); + + return; + } + + Tools.each(targets, function(elm) { + purgeDestroyedEditor(self.get(elm.id)); + }); + + targets = Tools.grep(targets, function(elm) { + return !self.get(elm.id); + }); + + each(targets, function(elm) { + if (isInvalidInlineTarget(settings, elm)) { + report('Could not initialize inline editor on invalid inline target element', elm); + } else { + createEditor(createId(elm), settings, elm); + } + }); + } + + self.settings = settings; + DOM.bind(window, 'ready', initEditors); + + return new Promise(function(resolve) { + if (result) { + resolve(result); + } else { + provideResults = function(editors) { + resolve(editors); + }; + } + }); + }, + + /** + * Returns a editor instance by id. + * + * @method get + * @param {String/Number} id Editor instance id or index to return. + * @return {tinymce.Editor} Editor instance to return. + * @example + * // Adds an onclick event to an editor by id (shorter version) + * tinymce.get('mytextbox').on('click', function(e) { + * ed.windowManager.alert('Hello world!'); + * }); + * + * // Adds an onclick event to an editor by id (longer version) + * tinymce.EditorManager.get('mytextbox').on('click', function(e) { + * ed.windowManager.alert('Hello world!'); + * }); + */ + get: function(id) { + if (!arguments.length) { + return this.editors; + } + + return id in this.editors ? this.editors[id] : null; + }, + + /** + * Adds an editor instance to the editor collection. This will also set it as the active editor. + * + * @method add + * @param {tinymce.Editor} editor Editor instance to add to the collection. + * @return {tinymce.Editor} The same instance that got passed in. + */ + add: function(editor) { + var self = this, editors = self.editors; + + // Add named and index editor instance + editors[editor.id] = editor; + editors.push(editor); + + toggleGlobalEvents(editors, true); + + // Doesn't call setActive method since we don't want + // to fire a bunch of activate/deactivate calls while initializing + self.activeEditor = editor; + + self.fire('AddEditor', {editor: editor}); + + if (!beforeUnloadDelegate) { + beforeUnloadDelegate = function() { + self.fire('BeforeUnload'); + }; + + DOM.bind(window, 'beforeunload', beforeUnloadDelegate); + } + + return editor; + }, + + /** + * Creates an editor instance and adds it to the EditorManager collection. + * + * @method createEditor + * @param {String} id Instance id to use for editor. + * @param {Object} settings Editor instance settings. + * @return {tinymce.Editor} Editor instance that got created. + */ + createEditor: function(id, settings) { + return this.add(new Editor(id, settings, this)); + }, + + /** + * Removes a editor or editors form page. + * + * @example + * // Remove all editors bound to divs + * tinymce.remove('div'); + * + * // Remove all editors bound to textareas + * tinymce.remove('textarea'); + * + * // Remove all editors + * tinymce.remove(); + * + * // Remove specific instance by id + * tinymce.remove('#id'); + * + * @method remove + * @param {tinymce.Editor/String/Object} [selector] CSS selector or editor instance to remove. + * @return {tinymce.Editor} The editor that got passed in will be return if it was found otherwise null. + */ + remove: function(selector) { + var self = this, i, editors = self.editors, editor; + + // Remove all editors + if (!selector) { + for (i = editors.length - 1; i >= 0; i--) { + self.remove(editors[i]); + } + + return; + } + + // Remove editors by selector + if (typeof selector == "string") { + selector = selector.selector || selector; + + each(DOM.select(selector), function(elm) { + editor = editors[elm.id]; + + if (editor) { + self.remove(editor); + } + }); + + return; + } + + // Remove specific editor + editor = selector; + + // Not in the collection + if (!editors[editor.id]) { + return null; + } + + if (removeEditorFromList(editor)) { + self.fire('RemoveEditor', {editor: editor}); + } + + if (!editors.length) { + DOM.unbind(window, 'beforeunload', beforeUnloadDelegate); + } + + editor.remove(); + + toggleGlobalEvents(editors, editors.length > 0); + + return editor; + }, + + /** + * Executes a specific command on the currently active editor. + * + * @method execCommand + * @param {String} cmd Command to perform for example Bold. + * @param {Boolean} ui Optional boolean state if a UI should be presented for the command or not. + * @param {String} value Optional value parameter like for example an URL to a link. + * @return {Boolean} true/false if the command was executed or not. + */ + execCommand: function(cmd, ui, value) { + var self = this, editor = self.get(value); + + // Manager commands + switch (cmd) { + case "mceAddEditor": + if (!self.get(value)) { + new Editor(value, self.settings, self).render(); + } + + return true; + + case "mceRemoveEditor": + if (editor) { + editor.remove(); + } + + return true; + + case 'mceToggleEditor': + if (!editor) { + self.execCommand('mceAddEditor', 0, value); + return true; + } + + if (editor.isHidden()) { + editor.show(); + } else { + editor.hide(); + } + + return true; + } + + // Run command on active editor + if (self.activeEditor) { + return self.activeEditor.execCommand(cmd, ui, value); + } + + return false; + }, + + /** + * Calls the save method on all editor instances in the collection. This can be useful when a form is to be submitted. + * + * @method triggerSave + * @example + * // Saves all contents + * tinyMCE.triggerSave(); + */ + triggerSave: function() { + each(this.editors, function(editor) { + editor.save(); + }); + }, + + /** + * Adds a language pack, this gets called by the loaded language files like en.js. + * + * @method addI18n + * @param {String} code Optional language code. + * @param {Object} items Name/value object with translations. + */ + addI18n: function(code, items) { + I18n.add(code, items); + }, + + /** + * Translates the specified string using the language pack items. + * + * @method translate + * @param {String/Array/Object} text String to translate + * @return {String} Translated string. + */ + translate: function(text) { + return I18n.translate(text); + }, + + /** + * Sets the active editor instance and fires the deactivate/activate events. + * + * @method setActive + * @param {tinymce.Editor} editor Editor instance to set as the active instance. + */ + setActive: function(editor) { + var activeEditor = this.activeEditor; + + if (this.activeEditor != editor) { + if (activeEditor) { + activeEditor.fire('deactivate', {relatedTarget: editor}); + } + + editor.fire('activate', {relatedTarget: activeEditor}); + } + + this.activeEditor = editor; + } + }; + + extend(EditorManager, Observable); + + EditorManager.setup(); + + // Export EditorManager as tinymce/tinymce in global namespace + window.tinymce = window.tinyMCE = EditorManager; + + return EditorManager; +}); + +// Included from: js/tinymce/classes/LegacyInput.js + +/** + * LegacyInput.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Converts legacy input to modern HTML. + * + * @class tinymce.LegacyInput + * @private + */ +define("tinymce/LegacyInput", [ + "tinymce/EditorManager", + "tinymce/util/Tools" +], function(EditorManager, Tools) { + var each = Tools.each, explode = Tools.explode; + + EditorManager.on('AddEditor', function(e) { + var editor = e.editor; + + editor.on('preInit', function() { + var filters, fontSizes, dom, settings = editor.settings; + + function replaceWithSpan(node, styles) { + each(styles, function(value, name) { + if (value) { + dom.setStyle(node, name, value); + } + }); + + dom.rename(node, 'span'); + } + + function convert(e) { + dom = editor.dom; + + if (settings.convert_fonts_to_spans) { + each(dom.select('font,u,strike', e.node), function(node) { + filters[node.nodeName.toLowerCase()](dom, node); + }); + } + } + + if (settings.inline_styles) { + fontSizes = explode(settings.font_size_legacy_values); + + filters = { + font: function(dom, node) { + replaceWithSpan(node, { + backgroundColor: node.style.backgroundColor, + color: node.color, + fontFamily: node.face, + fontSize: fontSizes[parseInt(node.size, 10) - 1] + }); + }, + + u: function(dom, node) { + // HTML5 allows U element + if (editor.settings.schema === "html4") { + replaceWithSpan(node, { + textDecoration: 'underline' + }); + } + }, + + strike: function(dom, node) { + replaceWithSpan(node, { + textDecoration: 'line-through' + }); + } + }; + + editor.on('PreProcess SetContent', convert); + } + }); + }); +}); + +// Included from: js/tinymce/classes/util/XHR.js + +/** + * XHR.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class enables you to send XMLHTTPRequests cross browser. + * @class tinymce.util.XHR + * @mixes tinymce.util.Observable + * @static + * @example + * // Sends a low level Ajax request + * tinymce.util.XHR.send({ + * url: 'someurl', + * success: function(text) { + * console.debug(text); + * } + * }); + * + * // Add custom header to XHR request + * tinymce.util.XHR.on('beforeSend', function(e) { + * e.xhr.setRequestHeader('X-Requested-With', 'Something'); + * }); + */ +define("tinymce/util/XHR", [ + "tinymce/util/Observable", + "tinymce/util/Tools" +], function(Observable, Tools) { + var XHR = { + /** + * Sends a XMLHTTPRequest. + * Consult the Wiki for details on what settings this method takes. + * + * @method send + * @param {Object} settings Object will target URL, callbacks and other info needed to make the request. + */ + send: function(settings) { + var xhr, count = 0; + + function ready() { + if (!settings.async || xhr.readyState == 4 || count++ > 10000) { + if (settings.success && count < 10000 && xhr.status == 200) { + settings.success.call(settings.success_scope, '' + xhr.responseText, xhr, settings); + } else if (settings.error) { + settings.error.call(settings.error_scope, count > 10000 ? 'TIMED_OUT' : 'GENERAL', xhr, settings); + } + + xhr = null; + } else { + setTimeout(ready, 10); + } + } + + // Default settings + settings.scope = settings.scope || this; + settings.success_scope = settings.success_scope || settings.scope; + settings.error_scope = settings.error_scope || settings.scope; + settings.async = settings.async === false ? false : true; + settings.data = settings.data || ''; + + XHR.fire('beforeInitialize', {settings: settings}); + + xhr = new XMLHttpRequest(); + + if (xhr) { + if (xhr.overrideMimeType) { + xhr.overrideMimeType(settings.content_type); + } + + xhr.open(settings.type || (settings.data ? 'POST' : 'GET'), settings.url, settings.async); + + if (settings.crossDomain) { + xhr.withCredentials = true; + } + + if (settings.content_type) { + xhr.setRequestHeader('Content-Type', settings.content_type); + } + + if (settings.requestheaders) { + Tools.each(settings.requestheaders, function(header) { + xhr.setRequestHeader(header.key, header.value); + }); + } + + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + + xhr = XHR.fire('beforeSend', {xhr: xhr, settings: settings}).xhr; + xhr.send(settings.data); + + // Syncronous request + if (!settings.async) { + return ready(); + } + + // Wait for response, onReadyStateChange can not be used since it leaks memory in IE + setTimeout(ready, 10); + } + } + }; + + Tools.extend(XHR, Observable); + + return XHR; +}); + +// Included from: js/tinymce/classes/util/JSON.js + +/** + * JSON.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * JSON parser and serializer class. + * + * @class tinymce.util.JSON + * @static + * @example + * // JSON parse a string into an object + * var obj = tinymce.util.JSON.parse(somestring); + * + * // JSON serialize a object into an string + * var str = tinymce.util.JSON.serialize(obj); + */ +define("tinymce/util/JSON", [], function() { + function serialize(o, quote) { + var i, v, t, name; + + quote = quote || '"'; + + if (o === null) { + return 'null'; + } + + t = typeof o; + + if (t == 'string') { + v = '\bb\tt\nn\ff\rr\""\'\'\\\\'; + + /*eslint no-control-regex:0 */ + return quote + o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g, function(a, b) { + // Make sure single quotes never get encoded inside double quotes for JSON compatibility + if (quote === '"' && a === "'") { + return a; + } + + i = v.indexOf(b); + + if (i + 1) { + return '\\' + v.charAt(i + 1); + } + + a = b.charCodeAt().toString(16); + + return '\\u' + '0000'.substring(a.length) + a; + }) + quote; + } + + if (t == 'object') { + if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') { + for (i = 0, v = '['; i < o.length; i++) { + v += (i > 0 ? ',' : '') + serialize(o[i], quote); + } + + return v + ']'; + } + + v = '{'; + + for (name in o) { + if (o.hasOwnProperty(name)) { + v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name + + quote + ':' + serialize(o[name], quote) : ''; + } + } + + return v + '}'; + } + + return '' + o; + } + + return { + /** + * Serializes the specified object as a JSON string. + * + * @method serialize + * @param {Object} obj Object to serialize as a JSON string. + * @param {String} quote Optional quote string defaults to ". + * @return {string} JSON string serialized from input. + */ + serialize: serialize, + + /** + * Unserializes/parses the specified JSON string into a object. + * + * @method parse + * @param {string} s JSON String to parse into a JavaScript object. + * @return {Object} Object from input JSON string or undefined if it failed. + */ + parse: function(text) { + try { + // Trick uglify JS + return window[String.fromCharCode(101) + 'val']('(' + text + ')'); + } catch (ex) { + // Ignore + } + } + + /**#@-*/ + }; +}); + +// Included from: js/tinymce/classes/util/JSONRequest.js + +/** + * JSONRequest.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class enables you to use JSON-RPC to call backend methods. + * + * @class tinymce.util.JSONRequest + * @example + * var json = new tinymce.util.JSONRequest({ + * url: 'somebackend.php' + * }); + * + * // Send RPC call 1 + * json.send({ + * method: 'someMethod1', + * params: ['a', 'b'], + * success: function(result) { + * console.dir(result); + * } + * }); + * + * // Send RPC call 2 + * json.send({ + * method: 'someMethod2', + * params: ['a', 'b'], + * success: function(result) { + * console.dir(result); + * } + * }); + */ +define("tinymce/util/JSONRequest", [ + "tinymce/util/JSON", + "tinymce/util/XHR", + "tinymce/util/Tools" +], function(JSON, XHR, Tools) { + var extend = Tools.extend; + + function JSONRequest(settings) { + this.settings = extend({}, settings); + this.count = 0; + } + + /** + * Simple helper function to send a JSON-RPC request without the need to initialize an object. + * Consult the Wiki API documentation for more details on what you can pass to this function. + * + * @method sendRPC + * @static + * @param {Object} o Call object where there are three field id, method and params this object should also contain callbacks etc. + */ + JSONRequest.sendRPC = function(o) { + return new JSONRequest().send(o); + }; + + JSONRequest.prototype = { + /** + * Sends a JSON-RPC call. Consult the Wiki API documentation for more details on what you can pass to this function. + * + * @method send + * @param {Object} args Call object where there are three field id, method and params this object should also contain callbacks etc. + */ + send: function(args) { + var ecb = args.error, scb = args.success; + + args = extend(this.settings, args); + + args.success = function(c, x) { + c = JSON.parse(c); + + if (typeof c == 'undefined') { + c = { + error: 'JSON Parse error.' + }; + } + + if (c.error) { + ecb.call(args.error_scope || args.scope, c.error, x); + } else { + scb.call(args.success_scope || args.scope, c.result); + } + }; + + args.error = function(ty, x) { + if (ecb) { + ecb.call(args.error_scope || args.scope, ty, x); + } + }; + + args.data = JSON.serialize({ + id: args.id || 'c' + (this.count++), + method: args.method, + params: args.params + }); + + // JSON content type for Ruby on rails. Bug: #1883287 + args.content_type = 'application/json'; + + XHR.send(args); + } + }; + + return JSONRequest; +}); + +// Included from: js/tinymce/classes/util/JSONP.js + +/** + * JSONP.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +define("tinymce/util/JSONP", [ + "tinymce/dom/DOMUtils" +], function(DOMUtils) { + return { + callbacks: {}, + count: 0, + + send: function(settings) { + var self = this, dom = DOMUtils.DOM, count = settings.count !== undefined ? settings.count : self.count; + var id = 'tinymce_jsonp_' + count; + + self.callbacks[count] = function(json) { + dom.remove(id); + delete self.callbacks[count]; + + settings.callback(json); + }; + + dom.add(dom.doc.body, 'script', { + id: id, + src: settings.url, + type: 'text/javascript' + }); + + self.count++; + } + }; +}); + +// Included from: js/tinymce/classes/util/LocalStorage.js + +/** + * LocalStorage.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class will simulate LocalStorage on IE 7 and return the native version on modern browsers. + * Storage is done using userData on IE 7 and a special serialization format. The format is designed + * to be as small as possible by making sure that the keys and values doesn't need to be encoded. This + * makes it possible to store for example HTML data. + * + * Storage format for userData: + * ,,,,... + * + * For example this data key1=value1,key2=value2 would be: + * 4,key1,6,value1,4,key2,6,value2 + * + * @class tinymce.util.LocalStorage + * @static + * @version 4.0 + * @example + * tinymce.util.LocalStorage.setItem('key', 'value'); + * var value = tinymce.util.LocalStorage.getItem('key'); + */ +define("tinymce/util/LocalStorage", [], function() { + var LocalStorage, storageElm, items, keys, userDataKey, hasOldIEDataSupport; + + // Check for native support + try { + if (window.localStorage) { + return localStorage; + } + } catch (ex) { + // Ignore + } + + userDataKey = "tinymce"; + storageElm = document.documentElement; + hasOldIEDataSupport = !!storageElm.addBehavior; + + if (hasOldIEDataSupport) { + storageElm.addBehavior('#default#userData'); + } + + /** + * Gets the keys names and updates LocalStorage.length property. Since IE7 doesn't have any getters/setters. + */ + function updateKeys() { + keys = []; + + for (var key in items) { + keys.push(key); + } + + LocalStorage.length = keys.length; + } + + /** + * Loads the userData string and parses it into the items structure. + */ + function load() { + var key, data, value, pos = 0; + + items = {}; + + // localStorage can be disabled on WebKit/Gecko so make a dummy storage + if (!hasOldIEDataSupport) { + return; + } + + function next(end) { + var value, nextPos; + + nextPos = end !== undefined ? pos + end : data.indexOf(',', pos); + if (nextPos === -1 || nextPos > data.length) { + return null; + } + + value = data.substring(pos, nextPos); + pos = nextPos + 1; + + return value; + } + + storageElm.load(userDataKey); + data = storageElm.getAttribute(userDataKey) || ''; + + do { + var offset = next(); + if (offset === null) { + break; + } + + key = next(parseInt(offset, 32) || 0); + if (key !== null) { + offset = next(); + if (offset === null) { + break; + } + + value = next(parseInt(offset, 32) || 0); + + if (key) { + items[key] = value; + } + } + } while (key !== null); + + updateKeys(); + } + + /** + * Saves the items structure into a the userData format. + */ + function save() { + var value, data = ''; + + // localStorage can be disabled on WebKit/Gecko so make a dummy storage + if (!hasOldIEDataSupport) { + return; + } + + for (var key in items) { + value = items[key]; + data += (data ? ',' : '') + key.length.toString(32) + ',' + key + ',' + value.length.toString(32) + ',' + value; + } + + storageElm.setAttribute(userDataKey, data); + + try { + storageElm.save(userDataKey); + } catch (ex) { + // Ignore disk full + } + + updateKeys(); + } + + LocalStorage = { + /** + * Length of the number of items in storage. + * + * @property length + * @type Number + * @return {Number} Number of items in storage. + */ + //length:0, + + /** + * Returns the key name by index. + * + * @method key + * @param {Number} index Index of key to return. + * @return {String} Key value or null if it wasn't found. + */ + key: function(index) { + return keys[index]; + }, + + /** + * Returns the value if the specified key or null if it wasn't found. + * + * @method getItem + * @param {String} key Key of item to retrieve. + * @return {String} Value of the specified item or null if it wasn't found. + */ + getItem: function(key) { + return key in items ? items[key] : null; + }, + + /** + * Sets the value of the specified item by it's key. + * + * @method setItem + * @param {String} key Key of the item to set. + * @param {String} value Value of the item to set. + */ + setItem: function(key, value) { + items[key] = "" + value; + save(); + }, + + /** + * Removes the specified item by key. + * + * @method removeItem + * @param {String} key Key of item to remove. + */ + removeItem: function(key) { + delete items[key]; + save(); + }, + + /** + * Removes all items. + * + * @method clear + */ + clear: function() { + items = {}; + save(); + } + }; + + load(); + + return LocalStorage; +}); + +// Included from: js/tinymce/classes/Compat.js + +/** + * Compat.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * TinyMCE core class. + * + * @static + * @class tinymce + * @borrow-members tinymce.EditorManager + * @borrow-members tinymce.util.Tools + */ +define("tinymce/Compat", [ + "tinymce/dom/DOMUtils", + "tinymce/dom/EventUtils", + "tinymce/dom/ScriptLoader", + "tinymce/AddOnManager", + "tinymce/util/Tools", + "tinymce/Env" +], function(DOMUtils, EventUtils, ScriptLoader, AddOnManager, Tools, Env) { + var tinymce = window.tinymce; + + /** + * @property {tinymce.dom.DOMUtils} DOM Global DOM instance. + * @property {tinymce.dom.ScriptLoader} ScriptLoader Global ScriptLoader instance. + * @property {tinymce.AddOnManager} PluginManager Global PluginManager instance. + * @property {tinymce.AddOnManager} ThemeManager Global ThemeManager instance. + */ + tinymce.DOM = DOMUtils.DOM; + tinymce.ScriptLoader = ScriptLoader.ScriptLoader; + tinymce.PluginManager = AddOnManager.PluginManager; + tinymce.ThemeManager = AddOnManager.ThemeManager; + + tinymce.dom = tinymce.dom || {}; + tinymce.dom.Event = EventUtils.Event; + + Tools.each( + 'trim isArray is toArray makeMap each map grep inArray extend create walk createNS resolve explode _addCacheSuffix'.split(' '), + function(key) { + tinymce[key] = Tools[key]; + } + ); + + Tools.each('isOpera isWebKit isIE isGecko isMac'.split(' '), function(name) { + tinymce[name] = Env[name.substr(2).toLowerCase()]; + }); + + return {}; +}); + +// Describe the different namespaces + +/** + * Root level namespace this contains classes directly related to the TinyMCE editor. + * + * @namespace tinymce + */ + +/** + * Contains classes for handling the browsers DOM. + * + * @namespace tinymce.dom + */ + +/** + * Contains html parser and serializer logic. + * + * @namespace tinymce.html + */ + +/** + * Contains the different UI types such as buttons, listboxes etc. + * + * @namespace tinymce.ui + */ + +/** + * Contains various utility classes such as json parser, cookies etc. + * + * @namespace tinymce.util + */ + +/** + * Contains modules to handle data binding. + * + * @namespace tinymce.data + */ + +// Included from: js/tinymce/classes/ui/Layout.js + +/** + * Layout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Base layout manager class. + * + * @class tinymce.ui.Layout + */ +define("tinymce/ui/Layout", [ + "tinymce/util/Class", + "tinymce/util/Tools" +], function(Class, Tools) { + "use strict"; + + return Class.extend({ + Defaults: { + firstControlClass: 'first', + lastControlClass: 'last' + }, + + /** + * Constructs a layout instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + this.settings = Tools.extend({}, this.Defaults, settings); + }, + + /** + * This method gets invoked before the layout renders the controls. + * + * @method preRender + * @param {tinymce.ui.Container} container Container instance to preRender. + */ + preRender: function(container) { + container.bodyClasses.add(this.settings.containerClass); + }, + + /** + * Applies layout classes to the container. + * + * @private + */ + applyClasses: function(items) { + var self = this, settings = self.settings, firstClass, lastClass, firstItem, lastItem; + + firstClass = settings.firstControlClass; + lastClass = settings.lastControlClass; + + items.each(function(item) { + item.classes.remove(firstClass).remove(lastClass).add(settings.controlClass); + + if (item.visible()) { + if (!firstItem) { + firstItem = item; + } + + lastItem = item; + } + }); + + if (firstItem) { + firstItem.classes.add(firstClass); + } + + if (lastItem) { + lastItem.classes.add(lastClass); + } + }, + + /** + * Renders the specified container and any layout specific HTML. + * + * @method renderHtml + * @param {tinymce.ui.Container} container Container to render HTML for. + */ + renderHtml: function(container) { + var self = this, html = ''; + + self.applyClasses(container.items()); + + container.items().each(function(item) { + html += item.renderHtml(); + }); + + return html; + }, + + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function() { + }, + + /** + * This method gets invoked after the layout renders the controls. + * + * @method postRender + * @param {tinymce.ui.Container} container Container instance to postRender. + */ + postRender: function() { + }, + + isNative: function() { + return false; + } + }); +}); + +// Included from: js/tinymce/classes/ui/AbsoluteLayout.js + +/** + * AbsoluteLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * LayoutManager for absolute positioning. This layout manager is more of + * a base class for other layouts but can be created and used directly. + * + * @-x-less AbsoluteLayout.less + * @class tinymce.ui.AbsoluteLayout + * @extends tinymce.ui.Layout + */ +define("tinymce/ui/AbsoluteLayout", [ + "tinymce/ui/Layout" +], function(Layout) { + "use strict"; + + return Layout.extend({ + Defaults: { + containerClass: 'abs-layout', + controlClass: 'abs-layout-item' + }, + + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + container.items().filter(':visible').each(function(ctrl) { + var settings = ctrl.settings; + + ctrl.layoutRect({ + x: settings.x, + y: settings.y, + w: settings.w, + h: settings.h + }); + + if (ctrl.recalc) { + ctrl.recalc(); + } + }); + }, + + /** + * Renders the specified container and any layout specific HTML. + * + * @method renderHtml + * @param {tinymce.ui.Container} container Container to render HTML for. + */ + renderHtml: function(container) { + return '
    ' + this._super(container); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Button.js + +/** + * Button.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class is used to create buttons. You can create them directly or through the Factory. + * + * @example + * // Create and render a button to the body element + * tinymce.ui.Factory.create({ + * type: 'button', + * text: 'My button' + * }).renderTo(document.body); + * + * @-x-less Button.less + * @class tinymce.ui.Button + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/Button", [ + "tinymce/ui/Widget" +], function(Widget) { + "use strict"; + + return Widget.extend({ + Defaults: { + classes: "widget btn", + role: "button" + }, + + /** + * Constructs a new button instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} size Size of the button small|medium|large. + * @setting {String} image Image to use for icon. + * @setting {String} icon Icon to use for button. + */ + init: function(settings) { + var self = this, size; + + self._super(settings); + settings = self.settings; + + size = self.settings.size; + + self.on('click mousedown', function(e) { + e.preventDefault(); + }); + + self.on('touchstart', function(e) { + self.fire('click', e); + e.preventDefault(); + }); + + if (settings.subtype) { + self.classes.add(settings.subtype); + } + + if (size) { + self.classes.add('btn-' + size); + } + + if (settings.icon) { + self.icon(settings.icon); + } + }, + + /** + * Sets/gets the current button icon. + * + * @method icon + * @param {String} [icon] New icon identifier. + * @return {String|tinymce.ui.MenuButton} Current icon or current MenuButton instance. + */ + icon: function(icon) { + if (!arguments.length) { + return this.state.get('icon'); + } + + this.state.set('icon', icon); + + return this; + }, + + /** + * Repaints the button for example after it's been resizes by a layout engine. + * + * @method repaint + */ + repaint: function() { + var btnElm = this.getEl().firstChild, + btnStyle; + + if (btnElm) { + btnStyle = btnElm.style; + btnStyle.width = btnStyle.height = "100%"; + } + + this._super(); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, id = self._id, prefix = self.classPrefix; + var icon = self.state.get('icon'), image, text = self.state.get('text'), textHtml = ''; + + image = self.settings.image; + if (image) { + icon = 'none'; + + // Support for [high dpi, low dpi] image sources + if (typeof image != "string") { + image = window.getSelection ? image[0] : image[1]; + } + + image = ' style="background-image: url(\'' + image + '\')"'; + } else { + image = ''; + } + + if (text) { + self.classes.add('btn-has-text'); + textHtml = '' + self.encode(text) + ''; + } + + icon = icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; + + return ( + '
    ' + + '' + + '
    ' + ); + }, + + bindStates: function() { + var self = this, $ = self.$, textCls = self.classPrefix + 'txt'; + + function setButtonText(text) { + var $span = $('span.' + textCls, self.getEl()); + + if (text) { + if (!$span[0]) { + $('button:first', self.getEl()).append(''); + $span = $('span.' + textCls, self.getEl()); + } + + $span.html(self.encode(text)); + } else { + $span.remove(); + } + + self.classes.toggle('btn-has-text', !!text); + } + + self.state.on('change:text', function(e) { + setButtonText(e.value); + }); + + self.state.on('change:icon', function(e) { + var icon = e.value, prefix = self.classPrefix; + + self.settings.icon = icon; + icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; + + var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0]; + + if (icon) { + if (!iconElm || iconElm != btnElm.firstChild) { + iconElm = document.createElement('i'); + btnElm.insertBefore(iconElm, btnElm.firstChild); + } + + iconElm.className = icon; + } else if (iconElm) { + btnElm.removeChild(iconElm); + } + + setButtonText(self.state.get('text')); + }); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/ButtonGroup.js + +/** + * ButtonGroup.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This control enables you to put multiple buttons into a group. This is + * useful when you want to combine similar toolbar buttons into a group. + * + * @example + * // Create and render a buttongroup with two buttons to the body element + * tinymce.ui.Factory.create({ + * type: 'buttongroup', + * items: [ + * {text: 'Button A'}, + * {text: 'Button B'} + * ] + * }).renderTo(document.body); + * + * @-x-less ButtonGroup.less + * @class tinymce.ui.ButtonGroup + * @extends tinymce.ui.Container + */ +define("tinymce/ui/ButtonGroup", [ + "tinymce/ui/Container" +], function(Container) { + "use strict"; + + return Container.extend({ + Defaults: { + defaultType: 'button', + role: 'group' + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, layout = self._layout; + + self.classes.add('btn-group'); + self.preRender(); + layout.preRender(self); + + return ( + '
    ' + + '
    ' + + (self.settings.html || '') + layout.renderHtml(self) + + '
    ' + + '
    ' + ); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Checkbox.js + +/** + * Checkbox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This control creates a custom checkbox. + * + * @example + * // Create and render a checkbox to the body element + * tinymce.ui.Factory.create({ + * type: 'checkbox', + * checked: true, + * text: 'My checkbox' + * }).renderTo(document.body); + * + * @-x-less Checkbox.less + * @class tinymce.ui.Checkbox + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/Checkbox", [ + "tinymce/ui/Widget" +], function(Widget) { + "use strict"; + + return Widget.extend({ + Defaults: { + classes: "checkbox", + role: "checkbox", + checked: false + }, + + /** + * Constructs a new Checkbox instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} checked True if the checkbox should be checked by default. + */ + init: function(settings) { + var self = this; + + self._super(settings); + + self.on('click mousedown', function(e) { + e.preventDefault(); + }); + + self.on('click', function(e) { + e.preventDefault(); + + if (!self.disabled()) { + self.checked(!self.checked()); + } + }); + + self.checked(self.settings.checked); + }, + + /** + * Getter/setter function for the checked state. + * + * @method checked + * @param {Boolean} [state] State to be set. + * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation. + */ + checked: function(state) { + if (!arguments.length) { + return this.state.get('checked'); + } + + this.state.set('checked', state); + + return this; + }, + + /** + * Getter/setter function for the value state. + * + * @method value + * @param {Boolean} [state] State to be set. + * @return {Boolean|tinymce.ui.Checkbox} True/false or checkbox if it's a set operation. + */ + value: function(state) { + if (!arguments.length) { + return this.checked(); + } + + return this.checked(state); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, id = self._id, prefix = self.classPrefix; + + return ( + '
    ' + + '' + + '' + self.encode(self.state.get('text')) + '' + + '
    ' + ); + }, + + bindStates: function() { + var self = this; + + function checked(state) { + self.classes.toggle("checked", state); + self.aria('checked', state); + } + + self.state.on('change:text', function(e) { + self.getEl('al').firstChild.data = self.translate(e.value); + }); + + self.state.on('change:checked change:value', function(e) { + self.fire('change'); + checked(e.value); + }); + + self.state.on('change:icon', function(e) { + var icon = e.value, prefix = self.classPrefix; + + if (typeof icon == 'undefined') { + return self.settings.icon; + } + + self.settings.icon = icon; + icon = icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; + + var btnElm = self.getEl().firstChild, iconElm = btnElm.getElementsByTagName('i')[0]; + + if (icon) { + if (!iconElm || iconElm != btnElm.firstChild) { + iconElm = document.createElement('i'); + btnElm.insertBefore(iconElm, btnElm.firstChild); + } + + iconElm.className = icon; + } else if (iconElm) { + btnElm.removeChild(iconElm); + } + }); + + if (self.state.get('checked')) { + checked(true); + } + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/ComboBox.js + +/** + * ComboBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class creates a combobox control. Select box that you select a value from or + * type a value into. + * + * @-x-less ComboBox.less + * @class tinymce.ui.ComboBox + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/ComboBox", [ + "tinymce/ui/Widget", + "tinymce/ui/Factory", + "tinymce/ui/DomUtils", + "tinymce/dom/DomQuery", + "tinymce/util/VK", + "tinymce/util/Tools" +], function(Widget, Factory, DomUtils, $, VK, Tools) { + "use strict"; + + return Widget.extend({ + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} placeholder Placeholder text to display. + */ + init: function(settings) { + var self = this; + + self._super(settings); + settings = self.settings; + + self.classes.add('combobox'); + self.subinput = true; + self.ariaTarget = 'inp'; // TODO: Figure out a better way + + settings.menu = settings.menu || settings.values; + + if (settings.menu) { + settings.icon = 'caret'; + } + + self.on('click', function(e) { + var elm = e.target, root = self.getEl(); + + if (!$.contains(root, elm) && elm != root) { + return; + } + + while (elm && elm != root) { + if (elm.id && elm.id.indexOf('-open') != -1) { + self.fire('action'); + + if (settings.menu) { + self.showMenu(); + + if (e.aria) { + self.menu.items()[0].focus(); + } + } + } + + elm = elm.parentNode; + } + }); + + // TODO: Rework this + self.on('keydown', function(e) { + var rootControl; + + if (e.keyCode == 13 && e.target.nodeName === 'INPUT') { + e.preventDefault(); + + // Find root control that we can do toJSON on + self.parents().reverse().each(function(ctrl) { + if (ctrl.toJSON) { + rootControl = ctrl; + return false; + } + }); + + // Fire event on current text box with the serialized data of the whole form + self.fire('submit', {data: rootControl.toJSON()}); + } + }); + + self.on('keyup', function(e) { + if (e.target.nodeName == "INPUT") { + var oldValue = self.state.get('value'); + var newValue = e.target.value; + + if (newValue !== oldValue) { + self.state.set('value', newValue); + self.fire('autocomplete', e); + } + } + }); + + self.on('mouseover', function(e) { + var tooltip = self.tooltip().moveTo(-0xFFFF); + + if (self.statusLevel() && e.target.className.indexOf(self.classPrefix + 'status') !== -1) { + var statusMessage = self.statusMessage() || 'Ok'; + var rel = tooltip.text(statusMessage).show().testMoveRel(e.target, ['bc-tc', 'bc-tl', 'bc-tr']); + + tooltip.classes.toggle('tooltip-n', rel == 'bc-tc'); + tooltip.classes.toggle('tooltip-nw', rel == 'bc-tl'); + tooltip.classes.toggle('tooltip-ne', rel == 'bc-tr'); + + tooltip.moveRel(e.target, rel); + } + }); + }, + + statusLevel: function (value) { + if (arguments.length > 0) { + this.state.set('statusLevel', value); + } + + return this.state.get('statusLevel'); + }, + + statusMessage: function (value) { + if (arguments.length > 0) { + this.state.set('statusMessage', value); + } + + return this.state.get('statusMessage'); + }, + + showMenu: function() { + var self = this, settings = self.settings, menu; + + if (!self.menu) { + menu = settings.menu || []; + + // Is menu array then auto constuct menu control + if (menu.length) { + menu = { + type: 'menu', + items: menu + }; + } else { + menu.type = menu.type || 'menu'; + } + + self.menu = Factory.create(menu).parent(self).renderTo(self.getContainerElm()); + self.fire('createmenu'); + self.menu.reflow(); + self.menu.on('cancel', function(e) { + if (e.control === self.menu) { + self.focus(); + } + }); + + self.menu.on('show hide', function(e) { + e.control.items().each(function(ctrl) { + ctrl.active(ctrl.value() == self.value()); + }); + }).fire('show'); + + self.menu.on('select', function(e) { + self.value(e.control.value()); + }); + + self.on('focusin', function(e) { + if (e.target.tagName.toUpperCase() == 'INPUT') { + self.menu.hide(); + } + }); + + self.aria('expanded', true); + } + + self.menu.show(); + self.menu.layoutRect({w: self.layoutRect().w}); + self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); + }, + + /** + * Focuses the input area of the control. + * + * @method focus + */ + focus: function() { + this.getEl('inp').focus(); + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, elm = self.getEl(), openElm = self.getEl('open'), rect = self.layoutRect(); + var width, lineHeight, innerPadding = 0, inputElm = elm.firstChild; + + if (self.statusLevel() && self.statusLevel() !== 'none') { + innerPadding = ( + parseInt(DomUtils.getRuntimeStyle(inputElm, 'padding-right'), 10) - + parseInt(DomUtils.getRuntimeStyle(inputElm, 'padding-left'), 10) + ); + } + + if (openElm) { + width = rect.w - DomUtils.getSize(openElm).width - 10; + } else { + width = rect.w - 10; + } + + // Detect old IE 7+8 add lineHeight to align caret vertically in the middle + var doc = document; + if (doc.all && (!doc.documentMode || doc.documentMode <= 8)) { + lineHeight = (self.layoutRect().h - 2) + 'px'; + } + + $(inputElm).css({ + width: width - innerPadding, + lineHeight: lineHeight + }); + + self._super(); + + return self; + }, + + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.ComboBox} Current combobox instance. + */ + postRender: function() { + var self = this; + + $(this.getEl('inp')).on('change', function(e) { + self.state.set('value', e.target.value); + self.fire('change', e); + }); + + return self._super(); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix; + var value = self.state.get('value') || ''; + var icon, text, openBtnHtml = '', extraAttrs = '', statusHtml = ''; + + if ("spellcheck" in settings) { + extraAttrs += ' spellcheck="' + settings.spellcheck + '"'; + } + + if (settings.maxLength) { + extraAttrs += ' maxlength="' + settings.maxLength + '"'; + } + + if (settings.size) { + extraAttrs += ' size="' + settings.size + '"'; + } + + if (settings.subtype) { + extraAttrs += ' type="' + settings.subtype + '"'; + } + + statusHtml = ''; + + if (self.disabled()) { + extraAttrs += ' disabled="disabled"'; + } + + icon = settings.icon; + if (icon && icon != 'caret') { + icon = prefix + 'ico ' + prefix + 'i-' + settings.icon; + } + + text = self.state.get('text'); + + if (icon || text) { + openBtnHtml = ( + '
    ' + + '' + + '
    ' + ); + + self.classes.add('has-open'); + } + + return ( + '
    ' + + '' + + statusHtml + + openBtnHtml + + '
    ' + ); + }, + + value: function(value) { + if (arguments.length) { + this.state.set('value', value); + return this; + } + + // Make sure the real state is in sync + if (this.state.get('rendered')) { + this.state.set('value', this.getEl('inp').value); + } + + return this.state.get('value'); + }, + + showAutoComplete: function (items, term) { + var self = this; + + if (items.length === 0) { + self.hideMenu(); + return; + } + + var insert = function (value, title) { + return function () { + self.fire('selectitem', { + title: title, + value: value + }); + }; + }; + + if (self.menu) { + self.menu.items().remove(); + } else { + self.menu = Factory.create({ + type: 'menu', + classes: 'combobox-menu', + layout: 'flow' + }).parent(self).renderTo(); + } + + Tools.each(items, function (item) { + self.menu.add({ + text: item.title, + url: item.previewUrl, + match: term, + classes: 'menu-item-ellipsis', + onclick: insert(item.value, item.title) + }); + }); + + self.menu.renderNew(); + self.hideMenu(); + + self.menu.on('cancel', function(e) { + if (e.control.parent() === self.menu) { + e.stopPropagation(); + self.focus(); + self.hideMenu(); + } + }); + + self.menu.on('select', function() { + self.focus(); + }); + + var maxW = self.layoutRect().w; + self.menu.layoutRect({w: maxW, minW: 0, maxW: maxW}); + self.menu.reflow(); + self.menu.show(); + self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); + }, + + hideMenu: function() { + if (this.menu) { + this.menu.hide(); + } + }, + + bindStates: function() { + var self = this; + + self.state.on('change:value', function(e) { + if (self.getEl('inp').value != e.value) { + self.getEl('inp').value = e.value; + } + }); + + self.state.on('change:disabled', function(e) { + self.getEl('inp').disabled = e.value; + }); + + self.state.on('change:statusLevel', function(e) { + var statusIconElm = self.getEl('status'); + var prefix = self.classPrefix, value = e.value; + + DomUtils.css(statusIconElm, 'display', value === 'none' ? 'none' : ''); + DomUtils.toggleClass(statusIconElm, prefix + 'i-checkmark', value === 'ok'); + DomUtils.toggleClass(statusIconElm, prefix + 'i-warning', value === 'warn'); + DomUtils.toggleClass(statusIconElm, prefix + 'i-error', value === 'error'); + self.classes.toggle('has-status', value !== 'none'); + self.repaint(); + }); + + DomUtils.on(self.getEl('status'), 'mouseleave', function () { + self.tooltip().hide(); + }); + + self.on('cancel', function (e) { + if (self.menu && self.menu.visible()) { + e.stopPropagation(); + self.hideMenu(); + } + }); + + var focusIdx = function (idx, menu) { + if (menu && menu.items().length > 0) { + menu.items().eq(idx)[0].focus(); + } + }; + + self.on('keydown', function (e) { + var keyCode = e.keyCode; + + if (e.target.nodeName === 'INPUT') { + if (keyCode === VK.DOWN) { + e.preventDefault(); + self.fire('autocomplete'); + focusIdx(0, self.menu); + } else if (keyCode === VK.UP) { + e.preventDefault(); + focusIdx(-1, self.menu); + } + } + }); + + return self._super(); + }, + + remove: function() { + $(this.getEl('inp')).off(); + + if (this.menu) { + this.menu.remove(); + } + + this._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/ColorBox.js + +/** + * ColorBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This widget lets you enter colors and browse for colors by pressing the color button. It also displays + * a preview of the current color. + * + * @-x-less ColorBox.less + * @class tinymce.ui.ColorBox + * @extends tinymce.ui.ComboBox + */ +define("tinymce/ui/ColorBox", [ + "tinymce/ui/ComboBox" +], function(ComboBox) { + "use strict"; + + return ComboBox.extend({ + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + settings.spellcheck = false; + + if (settings.onaction) { + settings.icon = 'none'; + } + + self._super(settings); + + self.classes.add('colorbox'); + self.on('change keyup postrender', function() { + self.repaintColor(self.value()); + }); + }, + + repaintColor: function(value) { + var openElm = this.getEl('open'); + var elm = openElm ? openElm.getElementsByTagName('i')[0] : null; + + if (elm) { + try { + elm.style.background = value; + } catch (ex) { + // Ignore + } + } + }, + + bindStates: function() { + var self = this; + + self.state.on('change:value', function(e) { + if (self.state.get('rendered')) { + self.repaintColor(e.value); + } + }); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/PanelButton.js + +/** + * PanelButton.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new panel button. + * + * @class tinymce.ui.PanelButton + * @extends tinymce.ui.Button + */ +define("tinymce/ui/PanelButton", [ + "tinymce/ui/Button", + "tinymce/ui/FloatPanel" +], function(Button, FloatPanel) { + "use strict"; + + return Button.extend({ + /** + * Shows the panel for the button. + * + * @method showPanel + */ + showPanel: function() { + var self = this, settings = self.settings; + + self.active(true); + + if (!self.panel) { + var panelSettings = settings.panel; + + // Wrap panel in grid layout if type if specified + // This makes it possible to add forms or other containers directly in the panel option + if (panelSettings.type) { + panelSettings = { + layout: 'grid', + items: panelSettings + }; + } + + panelSettings.role = panelSettings.role || 'dialog'; + panelSettings.popover = true; + panelSettings.autohide = true; + panelSettings.ariaRoot = true; + + self.panel = new FloatPanel(panelSettings).on('hide', function() { + self.active(false); + }).on('cancel', function(e) { + e.stopPropagation(); + self.focus(); + self.hidePanel(); + }).parent(self).renderTo(self.getContainerElm()); + + self.panel.fire('show'); + self.panel.reflow(); + } else { + self.panel.show(); + } + + self.panel.moveRel(self.getEl(), settings.popoverAlign || (self.isRtl() ? ['bc-tr', 'bc-tc'] : ['bc-tl', 'bc-tc'])); + }, + + /** + * Hides the panel for the button. + * + * @method hidePanel + */ + hidePanel: function() { + var self = this; + + if (self.panel) { + self.panel.hide(); + } + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self.aria('haspopup', true); + + self.on('click', function(e) { + if (e.control === self) { + if (self.panel && self.panel.visible()) { + self.hidePanel(); + } else { + self.showPanel(); + self.panel.focus(!!e.aria); + } + } + }); + + return self._super(); + }, + + remove: function() { + if (this.panel) { + this.panel.remove(); + this.panel = null; + } + + return this._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/ColorButton.js + +/** + * ColorButton.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class creates a color button control. This is a split button in which the main + * button has a visual representation of the currently selected color. When clicked + * the caret button displays a color picker, allowing the user to select a new color. + * + * @-x-less ColorButton.less + * @class tinymce.ui.ColorButton + * @extends tinymce.ui.PanelButton + */ +define("tinymce/ui/ColorButton", [ + "tinymce/ui/PanelButton", + "tinymce/dom/DOMUtils" +], function(PanelButton, DomUtils) { + "use strict"; + + var DOM = DomUtils.DOM; + + return PanelButton.extend({ + /** + * Constructs a new ColorButton instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + this._super(settings); + this.classes.add('colorbutton'); + }, + + /** + * Getter/setter for the current color. + * + * @method color + * @param {String} [color] Color to set. + * @return {String|tinymce.ui.ColorButton} Current color or current instance. + */ + color: function(color) { + if (color) { + this._color = color; + this.getEl('preview').style.backgroundColor = color; + return this; + } + + return this._color; + }, + + /** + * Resets the current color. + * + * @method resetColor + * @return {tinymce.ui.ColorButton} Current instance. + */ + resetColor: function() { + this._color = null; + this.getEl('preview').style.backgroundColor = null; + return this; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, id = self._id, prefix = self.classPrefix, text = self.state.get('text'); + var icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + self.settings.icon : ''; + var image = self.settings.image ? ' style="background-image: url(\'' + self.settings.image + '\')"' : '', + textHtml = ''; + + if (text) { + self.classes.add('btn-has-text'); + textHtml = '' + self.encode(text) + ''; + } + + return ( + '
    ' + + '' + + '' + + '
    ' + ); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, onClickHandler = self.settings.onclick; + + self.on('click', function(e) { + if (e.aria && e.aria.key == 'down') { + return; + } + + if (e.control == self && !DOM.getParent(e.target, '.' + self.classPrefix + 'open')) { + e.stopImmediatePropagation(); + onClickHandler.call(self, e); + } + }); + + delete self.settings.onclick; + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/util/Color.js + +/** + * Color.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class lets you parse/serialize colors and convert rgb/hsb. + * + * @class tinymce.util.Color + * @example + * var white = new tinymce.util.Color({r: 255, g: 255, b: 255}); + * var red = new tinymce.util.Color('#FF0000'); + * + * console.log(white.toHex(), red.toHsv()); + */ +define("tinymce/util/Color", [], function() { + var min = Math.min, max = Math.max, round = Math.round; + + /** + * Constructs a new color instance. + * + * @constructor + * @method Color + * @param {String} value Optional initial value to parse. + */ + function Color(value) { + var self = this, r = 0, g = 0, b = 0; + + function rgb2hsv(r, g, b) { + var h, s, v, d, minRGB, maxRGB; + + h = 0; + s = 0; + v = 0; + r = r / 255; + g = g / 255; + b = b / 255; + + minRGB = min(r, min(g, b)); + maxRGB = max(r, max(g, b)); + + if (minRGB == maxRGB) { + v = minRGB; + + return { + h: 0, + s: 0, + v: v * 100 + }; + } + + /*eslint no-nested-ternary:0 */ + d = (r == minRGB) ? g - b : ((b == minRGB) ? r - g : b - r); + h = (r == minRGB) ? 3 : ((b == minRGB) ? 1 : 5); + h = 60 * (h - d / (maxRGB - minRGB)); + s = (maxRGB - minRGB) / maxRGB; + v = maxRGB; + + return { + h: round(h), + s: round(s * 100), + v: round(v * 100) + }; + } + + function hsvToRgb(hue, saturation, brightness) { + var side, chroma, x, match; + + hue = (parseInt(hue, 10) || 0) % 360; + saturation = parseInt(saturation, 10) / 100; + brightness = parseInt(brightness, 10) / 100; + saturation = max(0, min(saturation, 1)); + brightness = max(0, min(brightness, 1)); + + if (saturation === 0) { + r = g = b = round(255 * brightness); + return; + } + + side = hue / 60; + chroma = brightness * saturation; + x = chroma * (1 - Math.abs(side % 2 - 1)); + match = brightness - chroma; + + switch (Math.floor(side)) { + case 0: + r = chroma; + g = x; + b = 0; + break; + + case 1: + r = x; + g = chroma; + b = 0; + break; + + case 2: + r = 0; + g = chroma; + b = x; + break; + + case 3: + r = 0; + g = x; + b = chroma; + break; + + case 4: + r = x; + g = 0; + b = chroma; + break; + + case 5: + r = chroma; + g = 0; + b = x; + break; + + default: + r = g = b = 0; + } + + r = round(255 * (r + match)); + g = round(255 * (g + match)); + b = round(255 * (b + match)); + } + + /** + * Returns the hex string of the current color. For example: #ff00ff + * + * @method toHex + * @return {String} Hex string of current color. + */ + function toHex() { + function hex(val) { + val = parseInt(val, 10).toString(16); + + return val.length > 1 ? val : '0' + val; + } + + return '#' + hex(r) + hex(g) + hex(b); + } + + /** + * Returns the r, g, b values of the color. Each channel has a range from 0-255. + * + * @method toRgb + * @return {Object} Object with r, g, b fields. + */ + function toRgb() { + return { + r: r, + g: g, + b: b + }; + } + + /** + * Returns the h, s, v values of the color. Ranges: h=0-360, s=0-100, v=0-100. + * + * @method toHsv + * @return {Object} Object with h, s, v fields. + */ + function toHsv() { + return rgb2hsv(r, g, b); + } + + /** + * Parses the specified value and populates the color instance. + * + * Supported format examples: + * * rbg(255,0,0) + * * #ff0000 + * * #fff + * * {r: 255, g: 0, b: 0} + * * {h: 360, s: 100, v: 100} + * + * @method parse + * @param {Object/String} value Color value to parse. + * @return {tinymce.util.Color} Current color instance. + */ + function parse(value) { + var matches; + + if (typeof value == 'object') { + if ("r" in value) { + r = value.r; + g = value.g; + b = value.b; + } else if ("v" in value) { + hsvToRgb(value.h, value.s, value.v); + } + } else { + if ((matches = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)[^\)]*\)/gi.exec(value))) { + r = parseInt(matches[1], 10); + g = parseInt(matches[2], 10); + b = parseInt(matches[3], 10); + } else if ((matches = /#([0-F]{2})([0-F]{2})([0-F]{2})/gi.exec(value))) { + r = parseInt(matches[1], 16); + g = parseInt(matches[2], 16); + b = parseInt(matches[3], 16); + } else if ((matches = /#([0-F])([0-F])([0-F])/gi.exec(value))) { + r = parseInt(matches[1] + matches[1], 16); + g = parseInt(matches[2] + matches[2], 16); + b = parseInt(matches[3] + matches[3], 16); + } + } + + r = r < 0 ? 0 : (r > 255 ? 255 : r); + g = g < 0 ? 0 : (g > 255 ? 255 : g); + b = b < 0 ? 0 : (b > 255 ? 255 : b); + + return self; + } + + if (value) { + parse(value); + } + + self.toRgb = toRgb; + self.toHsv = toHsv; + self.toHex = toHex; + self.parse = parse; + } + + return Color; +}); + +// Included from: js/tinymce/classes/ui/ColorPicker.js + +/** + * ColorPicker.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Color picker widget lets you select colors. + * + * @-x-less ColorPicker.less + * @class tinymce.ui.ColorPicker + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/ColorPicker", [ + "tinymce/ui/Widget", + "tinymce/ui/DragHelper", + "tinymce/ui/DomUtils", + "tinymce/util/Color" +], function(Widget, DragHelper, DomUtils, Color) { + "use strict"; + + return Widget.extend({ + Defaults: { + classes: "widget colorpicker" + }, + + /** + * Constructs a new colorpicker instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} color Initial color value. + */ + init: function(settings) { + this._super(settings); + }, + + postRender: function() { + var self = this, color = self.color(), hsv, hueRootElm, huePointElm, svRootElm, svPointElm; + + hueRootElm = self.getEl('h'); + huePointElm = self.getEl('hp'); + svRootElm = self.getEl('sv'); + svPointElm = self.getEl('svp'); + + function getPos(elm, event) { + var pos = DomUtils.getPos(elm), x, y; + + x = event.pageX - pos.x; + y = event.pageY - pos.y; + + x = Math.max(0, Math.min(x / elm.clientWidth, 1)); + y = Math.max(0, Math.min(y / elm.clientHeight, 1)); + + return { + x: x, + y: y + }; + } + + function updateColor(hsv, hueUpdate) { + var hue = (360 - hsv.h) / 360; + + DomUtils.css(huePointElm, { + top: (hue * 100) + '%' + }); + + if (!hueUpdate) { + DomUtils.css(svPointElm, { + left: hsv.s + '%', + top: (100 - hsv.v) + '%' + }); + } + + svRootElm.style.background = new Color({s: 100, v: 100, h: hsv.h}).toHex(); + self.color().parse({s: hsv.s, v: hsv.v, h: hsv.h}); + } + + function updateSaturationAndValue(e) { + var pos; + + pos = getPos(svRootElm, e); + hsv.s = pos.x * 100; + hsv.v = (1 - pos.y) * 100; + + updateColor(hsv); + self.fire('change'); + } + + function updateHue(e) { + var pos; + + pos = getPos(hueRootElm, e); + hsv = color.toHsv(); + hsv.h = (1 - pos.y) * 360; + updateColor(hsv, true); + self.fire('change'); + } + + self._repaint = function() { + hsv = color.toHsv(); + updateColor(hsv); + }; + + self._super(); + + self._svdraghelper = new DragHelper(self._id + '-sv', { + start: updateSaturationAndValue, + drag: updateSaturationAndValue + }); + + self._hdraghelper = new DragHelper(self._id + '-h', { + start: updateHue, + drag: updateHue + }); + + self._repaint(); + }, + + rgb: function() { + return this.color().toRgb(); + }, + + value: function(value) { + var self = this; + + if (arguments.length) { + self.color().parse(value); + + if (self._rendered) { + self._repaint(); + } + } else { + return self.color().toHex(); + } + }, + + color: function() { + if (!this._color) { + this._color = new Color(); + } + + return this._color; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, id = self._id, prefix = self.classPrefix, hueHtml; + var stops = '#ff0000,#ff0080,#ff00ff,#8000ff,#0000ff,#0080ff,#00ffff,#00ff80,#00ff00,#80ff00,#ffff00,#ff8000,#ff0000'; + + function getOldIeFallbackHtml() { + var i, l, html = '', gradientPrefix, stopsList; + + gradientPrefix = 'filter:progid:DXImageTransform.Microsoft.gradient(GradientType=0,startColorstr='; + stopsList = stops.split(','); + for (i = 0, l = stopsList.length - 1; i < l; i++) { + html += ( + '
    ' + ); + } + + return html; + } + + var gradientCssText = ( + 'background: -ms-linear-gradient(top,' + stops + ');' + + 'background: linear-gradient(to bottom,' + stops + ');' + ); + + hueHtml = ( + '
    ' + + getOldIeFallbackHtml() + + '
    ' + + '
    ' + ); + + return ( + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + '
    ' + + hueHtml + + '
    ' + ); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Path.js + +/** + * Path.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new path control. + * + * @-x-less Path.less + * @class tinymce.ui.Path + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/Path", [ + "tinymce/ui/Widget" +], function(Widget) { + "use strict"; + + return Widget.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {String} delimiter Delimiter to display between row in path. + */ + init: function(settings) { + var self = this; + + if (!settings.delimiter) { + settings.delimiter = '\u00BB'; + } + + self._super(settings); + self.classes.add('path'); + self.canFocus = true; + + self.on('click', function(e) { + var index, target = e.target; + + if ((index = target.getAttribute('data-index'))) { + self.fire('select', {value: self.row()[index], index: index}); + } + }); + + self.row(self.settings.row); + }, + + /** + * Focuses the current control. + * + * @method focus + * @return {tinymce.ui.Control} Current control instance. + */ + focus: function() { + var self = this; + + self.getEl().firstChild.focus(); + + return self; + }, + + /** + * Sets/gets the data to be used for the path. + * + * @method row + * @param {Array} row Array with row name is rendered to path. + */ + row: function(row) { + if (!arguments.length) { + return this.state.get('row'); + } + + this.state.set('row', row); + + return this; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this; + + return ( + '
    ' + + self._getDataPathHtml(self.state.get('row')) + + '
    ' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:row', function(e) { + self.innerHtml(self._getDataPathHtml(e.value)); + }); + + return self._super(); + }, + + _getDataPathHtml: function(data) { + var self = this, parts = data || [], i, l, html = '', prefix = self.classPrefix; + + for (i = 0, l = parts.length; i < l; i++) { + html += ( + (i > 0 ? '' : '') + + '
    ' + parts[i].name + '
    ' + ); + } + + if (!html) { + html = '
    \u00a0
    '; + } + + return html; + } + }); +}); + +// Included from: js/tinymce/classes/ui/ElementPath.js + +/** + * ElementPath.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This control creates an path for the current selections parent elements in TinyMCE. + * + * @class tinymce.ui.ElementPath + * @extends tinymce.ui.Path + */ +define("tinymce/ui/ElementPath", [ + "tinymce/ui/Path" +], function(Path) { + return Path.extend({ + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.ElementPath} Current combobox instance. + */ + postRender: function() { + var self = this, editor = self.settings.editor; + + function isHidden(elm) { + if (elm.nodeType === 1) { + if (elm.nodeName == "BR" || !!elm.getAttribute('data-mce-bogus')) { + return true; + } + + if (elm.getAttribute('data-mce-type') === 'bookmark') { + return true; + } + } + + return false; + } + + if (editor.settings.elementpath !== false) { + self.on('select', function(e) { + editor.focus(); + editor.selection.select(this.row()[e.index].element); + editor.nodeChanged(); + }); + + editor.on('nodeChange', function(e) { + var outParents = [], parents = e.parents, i = parents.length; + + while (i--) { + if (parents[i].nodeType == 1 && !isHidden(parents[i])) { + var args = editor.fire('ResolveName', { + name: parents[i].nodeName.toLowerCase(), + target: parents[i] + }); + + if (!args.isDefaultPrevented()) { + outParents.push({name: args.name, element: parents[i]}); + } + + if (args.isPropagationStopped()) { + break; + } + } + } + + self.row(outParents); + }); + } + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/FormItem.js + +/** + * FormItem.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class is a container created by the form element with + * a label and control item. + * + * @class tinymce.ui.FormItem + * @extends tinymce.ui.Container + * @setting {String} label Label to display for the form item. + */ +define("tinymce/ui/FormItem", [ + "tinymce/ui/Container" +], function(Container) { + "use strict"; + + return Container.extend({ + Defaults: { + layout: 'flex', + align: 'center', + defaults: { + flex: 1 + } + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, layout = self._layout, prefix = self.classPrefix; + + self.classes.add('formitem'); + layout.preRender(self); + + return ( + '
    ' + + (self.settings.title ? ('
    ' + + self.settings.title + '
    ') : '') + + '
    ' + + (self.settings.html || '') + layout.renderHtml(self) + + '
    ' + + '
    ' + ); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Form.js + +/** + * Form.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class creates a form container. A form container has the ability + * to automatically wrap items in tinymce.ui.FormItem instances. + * + * Each FormItem instance is a container for the label and the item. + * + * @example + * tinymce.ui.Factory.create({ + * type: 'form', + * items: [ + * {type: 'textbox', label: 'My text box'} + * ] + * }).renderTo(document.body); + * + * @class tinymce.ui.Form + * @extends tinymce.ui.Container + */ +define("tinymce/ui/Form", [ + "tinymce/ui/Container", + "tinymce/ui/FormItem", + "tinymce/util/Tools" +], function(Container, FormItem, Tools) { + "use strict"; + + return Container.extend({ + Defaults: { + containerCls: 'form', + layout: 'flex', + direction: 'column', + align: 'stretch', + flex: 1, + padding: 20, + labelGap: 30, + spacing: 10, + callbacks: { + submit: function() { + this.submit(); + } + } + }, + + /** + * This method gets invoked before the control is rendered. + * + * @method preRender + */ + preRender: function() { + var self = this, items = self.items(); + + if (!self.settings.formItemDefaults) { + self.settings.formItemDefaults = { + layout: 'flex', + autoResize: "overflow", + defaults: {flex: 1} + }; + } + + // Wrap any labeled items in FormItems + items.each(function(ctrl) { + var formItem, label = ctrl.settings.label; + + if (label) { + formItem = new FormItem(Tools.extend({ + items: { + type: 'label', + id: ctrl._id + '-l', + text: label, + flex: 0, + forId: ctrl._id, + disabled: ctrl.disabled() + } + }, self.settings.formItemDefaults)); + + formItem.type = 'formitem'; + ctrl.aria('labelledby', ctrl._id + '-l'); + + if (typeof ctrl.settings.flex == "undefined") { + ctrl.settings.flex = 1; + } + + self.replace(ctrl, formItem); + formItem.add(ctrl); + } + }); + }, + + /** + * Fires a submit event with the serialized form. + * + * @method submit + * @return {Object} Event arguments object. + */ + submit: function() { + return this.fire('submit', {data: this.toJSON()}); + }, + + /** + * Post render method. Called after the control has been rendered to the target. + * + * @method postRender + * @return {tinymce.ui.ComboBox} Current combobox instance. + */ + postRender: function() { + var self = this; + + self._super(); + self.fromJSON(self.settings.data); + }, + + bindStates: function() { + var self = this; + + self._super(); + + function recalcLabels() { + var maxLabelWidth = 0, labels = [], i, labelGap, items; + + if (self.settings.labelGapCalc === false) { + return; + } + + if (self.settings.labelGapCalc == "children") { + items = self.find('formitem'); + } else { + items = self.items(); + } + + items.filter('formitem').each(function(item) { + var labelCtrl = item.items()[0], labelWidth = labelCtrl.getEl().clientWidth; + + maxLabelWidth = labelWidth > maxLabelWidth ? labelWidth : maxLabelWidth; + labels.push(labelCtrl); + }); + + labelGap = self.settings.labelGap || 0; + + i = labels.length; + while (i--) { + labels[i].settings.minWidth = maxLabelWidth + labelGap; + } + } + + self.on('show', recalcLabels); + recalcLabels(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/FieldSet.js + +/** + * FieldSet.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class creates fieldset containers. + * + * @-x-less FieldSet.less + * @class tinymce.ui.FieldSet + * @extends tinymce.ui.Form + */ +define("tinymce/ui/FieldSet", [ + "tinymce/ui/Form" +], function(Form) { + "use strict"; + + return Form.extend({ + Defaults: { + containerCls: 'fieldset', + layout: 'flex', + direction: 'column', + align: 'stretch', + flex: 1, + padding: "25 15 5 15", + labelGap: 30, + spacing: 10, + border: 1 + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, layout = self._layout, prefix = self.classPrefix; + + self.preRender(); + layout.preRender(self); + + return ( + '
    ' + + (self.settings.title ? ('' + + self.settings.title + '') : '') + + '
    ' + + (self.settings.html || '') + layout.renderHtml(self) + + '
    ' + + '
    ' + ); + } + }); +}); + +// Included from: js/tinymce/classes/content/LinkTargets.js + +/** + * LinkTargets.js + * + * Released under LGPL License. + * Copyright (c) 1999-2016 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This module is enables you to get anything that you can link to in a element. + * + * @private + * @class tinymce.content.LinkTargets + */ +define('tinymce/content/LinkTargets', [ + 'tinymce/dom/DOMUtils', + 'tinymce/util/Fun', + 'tinymce/util/Arr', + 'tinymce/util/Uuid', + 'tinymce/util/Tools', + 'tinymce/dom/NodeType' +], function( + DOMUtils, + Fun, + Arr, + Uuid, + Tools, + NodeType +) { + var trim = Tools.trim; + + var create = function (type, title, url, level, attach) { + return { + type: type, + title: title, + url: url, + level: level, + attach: attach + }; + }; + + var isChildOfContentEditableTrue = function (node) { + while ((node = node.parentNode)) { + var value = node.contentEditable; + if (value && value !== 'inherit') { + return NodeType.isContentEditableTrue(node); + } + } + + return false; + }; + + var select = function (selector, root) { + return DOMUtils.DOM.select(selector, root); + }; + + var getElementText = function (elm) { + return elm.innerText || elm.textContent; + }; + + var getOrGenerateId = function (elm) { + return elm.id ? elm.id : Uuid.uuid('h'); + }; + + var isAnchor = function (elm) { + return elm && elm.nodeName === 'A' && (elm.id || elm.name); + }; + + var isValidAnchor = function (elm) { + return isAnchor(elm) && isEditable(elm); + }; + + var isHeader = function (elm) { + return elm && /^(H[1-6])$/.test(elm.nodeName); + }; + + var isEditable = function (elm) { + return isChildOfContentEditableTrue(elm) && !NodeType.isContentEditableFalse(elm); + }; + + var isValidHeader = function (elm) { + return isHeader(elm) && isEditable(elm); + }; + + var getLevel = function (elm) { + return isHeader(elm) ? parseInt(elm.nodeName.substr(1), 10) : 0; + }; + + var headerTarget = function (elm) { + var headerId = getOrGenerateId(elm); + + var attach = function () { + elm.id = headerId; + }; + + return create('header', getElementText(elm), '#' + headerId, getLevel(elm), attach); + }; + + var anchorTarget = function (elm) { + var anchorId = elm.id || elm.name; + var anchorText = getElementText(elm); + + return create('anchor', anchorText ? anchorText : '#' + anchorId, '#' + anchorId, 0, Fun.noop); + }; + + var getHeaderTargets = function (elms) { + return Arr.map(Arr.filter(elms, isValidHeader), headerTarget); + }; + + var getAnchorTargets = function (elms) { + return Arr.map(Arr.filter(elms, isValidAnchor), anchorTarget); + }; + + var getTargetElements = function (elm) { + var elms = select('h1,h2,h3,h4,h5,h6,a:not([href])', elm); + return elms; + }; + + var hasTitle = function (target) { + return trim(target.title).length > 0; + }; + + var find = function (elm) { + var elms = getTargetElements(elm); + return Arr.filter(getHeaderTargets(elms).concat(getAnchorTargets(elms)), hasTitle); + }; + + return { + find: find + }; +}); + +// Included from: js/tinymce/classes/ui/FilePicker.js + +/** + * FilePicker.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*global tinymce:true */ + +/** + * This class creates a file picker control. + * + * @class tinymce.ui.FilePicker + * @extends tinymce.ui.ComboBox + */ +define("tinymce/ui/FilePicker", [ + "tinymce/ui/ComboBox", + "tinymce/util/Tools", + "tinymce/util/Arr", + "tinymce/util/Fun", + "tinymce/util/VK", + "tinymce/content/LinkTargets" +], function(ComboBox, Tools, Arr, Fun, VK, LinkTargets) { + "use strict"; + + var history = {}; + var HISTORY_LENGTH = 5; + + var toMenuItem = function (target) { + return { + title: target.title, + value: { + title: {raw: target.title}, + url: target.url, + attach: target.attach + } + }; + }; + + var toMenuItems = function (targets) { + return Tools.map(targets, toMenuItem); + }; + + var staticMenuItem = function (title, url) { + return { + title: title, + value: { + title: title, + url: url, + attach: Fun.noop + } + }; + }; + + var isUniqueUrl = function (url, targets) { + var foundTarget = Arr.find(targets, function (target) { + return target.url === url; + }); + + return !foundTarget; + }; + + var getSetting = function (editorSettings, name, defaultValue) { + var value = name in editorSettings ? editorSettings[name] : defaultValue; + return value === false ? null : value; + }; + + var createMenuItems = function (term, targets, fileType, editorSettings) { + var separator = {title: '-'}; + + var fromHistoryMenuItems = function (history) { + var uniqueHistory = Arr.filter(history[fileType], function (url) { + return isUniqueUrl(url, targets); + }); + + return Tools.map(uniqueHistory, function (url) { + return { + title: url, + value: { + title: url, + url: url, + attach: Fun.noop + } + }; + }); + }; + + var fromMenuItems = function (type) { + var filteredTargets = Arr.filter(targets, function (target) { + return target.type == type; + }); + + return toMenuItems(filteredTargets); + }; + + var anchorMenuItems = function () { + var anchorMenuItems = fromMenuItems('anchor'); + var topAnchor = getSetting(editorSettings, 'anchor_top', '#top'); + var bottomAchor = getSetting(editorSettings, 'anchor_bottom', '#bottom'); + + if (topAnchor !== null) { + anchorMenuItems.unshift(staticMenuItem('', topAnchor)); + } + + if (bottomAchor !== null) { + anchorMenuItems.push(staticMenuItem('', bottomAchor)); + } + + return anchorMenuItems; + }; + + var join = function (items) { + return Arr.reduce(items, function (a, b) { + var bothEmpty = a.length === 0 || b.length === 0; + return bothEmpty ? a.concat(b) : a.concat(separator, b); + }, []); + }; + + if (editorSettings.typeahead_urls === false) { + return []; + } + + return fileType === 'file' ? join([ + filterByQuery(term, fromHistoryMenuItems(history)), + filterByQuery(term, fromMenuItems('header')), + filterByQuery(term, anchorMenuItems()) + ]) : filterByQuery(term, fromHistoryMenuItems(history)); + }; + + var addToHistory = function (url, fileType) { + var items = history[fileType]; + + if (!/^https?/.test(url)) { + return; + } + + if (items) { + if (Arr.indexOf(items, url) === -1) { + history[fileType] = items.slice(0, HISTORY_LENGTH).concat(url); + } + } else { + history[fileType] = [url]; + } + }; + + var filterByQuery = function (term, menuItems) { + var lowerCaseTerm = term.toLowerCase(); + var result = Tools.grep(menuItems, function (item) { + return item.title.toLowerCase().indexOf(lowerCaseTerm) !== -1; + }); + + return result.length === 1 && result[0].title === term ? [] : result; + }; + + var getTitle = function (linkDetails) { + var title = linkDetails.title; + return title.raw ? title.raw : title; + }; + + var setupAutoCompleteHandler = function (ctrl, editorSettings, bodyElm, fileType) { + var autocomplete = function (term) { + var linkTargets = LinkTargets.find(bodyElm); + var menuItems = createMenuItems(term, linkTargets, fileType, editorSettings); + ctrl.showAutoComplete(menuItems, term); + }; + + ctrl.on('autocomplete', function () { + autocomplete(ctrl.value()); + }); + + ctrl.on('selectitem', function (e) { + var linkDetails = e.value; + + ctrl.value(linkDetails.url); + var title = getTitle(linkDetails); + + if (fileType === 'image') { + ctrl.fire('change', {meta: {alt: title, attach: linkDetails.attach}}); + } else { + ctrl.fire('change', {meta: {text: title, attach: linkDetails.attach}}); + } + + ctrl.focus(); + }); + + ctrl.on('click', function (e) { + if (ctrl.value().length === 0 && e.target.nodeName === 'INPUT') { + autocomplete(''); + } + }); + + ctrl.on('PostRender', function () { + ctrl.getRoot().on('submit', function (e) { + if (!e.isDefaultPrevented()) { + addToHistory(ctrl.value(), fileType); + } + }); + }); + }; + + var statusToUiState = function (result) { + var status = result.status, message = result.message; + + if (status === 'valid') { + return {status: 'ok', message: message}; + } else if (status === 'unknown') { + return {status: 'warn', message: message}; + } else if (status === 'invalid') { + return {status: 'warn', message: message}; + } else { + return {status: 'none', message: ''}; + } + }; + + var setupLinkValidatorHandler = function (ctrl, editorSettings, fileType) { + var validatorHandler = editorSettings.filepicker_validator_handler; + if (validatorHandler) { + var validateUrl = function (url) { + if (url.length === 0) { + ctrl.statusLevel('none'); + return; + } + + validatorHandler({ + url: url, + type: fileType + }, function (result) { + var uiState = statusToUiState(result); + + ctrl.statusMessage(uiState.message); + ctrl.statusLevel(uiState.status); + }); + }; + + ctrl.state.on('change:value', function (e) { + validateUrl(e.value); + }); + } + }; + + return ComboBox.extend({ + /** + * Constructs a new control instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this, editor = tinymce.activeEditor, editorSettings = editor.settings; + var actionCallback, fileBrowserCallback, fileBrowserCallbackTypes; + var fileType = settings.filetype; + + settings.spellcheck = false; + + fileBrowserCallbackTypes = editorSettings.file_picker_types || editorSettings.file_browser_callback_types; + if (fileBrowserCallbackTypes) { + fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/); + } + + if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType]) { + fileBrowserCallback = editorSettings.file_picker_callback; + if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType])) { + actionCallback = function() { + var meta = self.fire('beforecall').meta; + + meta = Tools.extend({filetype: fileType}, meta); + + // file_picker_callback(callback, currentValue, metaData) + fileBrowserCallback.call( + editor, + function(value, meta) { + self.value(value).fire('change', {meta: meta}); + }, + self.value(), + meta + ); + }; + } else { + // Legacy callback: file_picker_callback(id, currentValue, filetype, window) + fileBrowserCallback = editorSettings.file_browser_callback; + if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[fileType])) { + actionCallback = function() { + fileBrowserCallback( + self.getEl('inp').id, + self.value(), + fileType, + window + ); + }; + } + } + } + + if (actionCallback) { + settings.icon = 'browse'; + settings.onaction = actionCallback; + } + + self._super(settings); + + setupAutoCompleteHandler(self, editorSettings, editor.getBody(), fileType); + setupLinkValidatorHandler(self, editorSettings, fileType); + } + }); +}); + +// Included from: js/tinymce/classes/ui/FitLayout.js + +/** + * FitLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This layout manager will resize the control to be the size of it's parent container. + * In other words width: 100% and height: 100%. + * + * @-x-less FitLayout.less + * @class tinymce.ui.FitLayout + * @extends tinymce.ui.AbsoluteLayout + */ +define("tinymce/ui/FitLayout", [ + "tinymce/ui/AbsoluteLayout" +], function(AbsoluteLayout) { + "use strict"; + + return AbsoluteLayout.extend({ + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + var contLayoutRect = container.layoutRect(), paddingBox = container.paddingBox; + + container.items().filter(':visible').each(function(ctrl) { + ctrl.layoutRect({ + x: paddingBox.left, + y: paddingBox.top, + w: contLayoutRect.innerW - paddingBox.right - paddingBox.left, + h: contLayoutRect.innerH - paddingBox.top - paddingBox.bottom + }); + + if (ctrl.recalc) { + ctrl.recalc(); + } + }); + } + }); +}); + +// Included from: js/tinymce/classes/ui/FlexLayout.js + +/** + * FlexLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This layout manager works similar to the CSS flex box. + * + * @setting {String} direction row|row-reverse|column|column-reverse + * @setting {Number} flex A positive-number to flex by. + * @setting {String} align start|end|center|stretch + * @setting {String} pack start|end|justify + * + * @class tinymce.ui.FlexLayout + * @extends tinymce.ui.AbsoluteLayout + */ +define("tinymce/ui/FlexLayout", [ + "tinymce/ui/AbsoluteLayout" +], function(AbsoluteLayout) { + "use strict"; + + return AbsoluteLayout.extend({ + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + // A ton of variables, needs to be in the same scope for performance + var i, l, items, contLayoutRect, contPaddingBox, contSettings, align, pack, spacing, totalFlex, availableSpace, direction; + var ctrl, ctrlLayoutRect, ctrlSettings, flex, maxSizeItems = [], size, maxSize, ratio, rect, pos, maxAlignEndPos; + var sizeName, minSizeName, posName, maxSizeName, beforeName, innerSizeName, deltaSizeName, contentSizeName; + var alignAxisName, alignInnerSizeName, alignSizeName, alignMinSizeName, alignBeforeName, alignAfterName; + var alignDeltaSizeName, alignContentSizeName; + var max = Math.max, min = Math.min; + + // Get container items, properties and settings + items = container.items().filter(':visible'); + contLayoutRect = container.layoutRect(); + contPaddingBox = container.paddingBox; + contSettings = container.settings; + direction = container.isRtl() ? (contSettings.direction || 'row-reversed') : contSettings.direction; + align = contSettings.align; + pack = container.isRtl() ? (contSettings.pack || 'end') : contSettings.pack; + spacing = contSettings.spacing || 0; + + if (direction == "row-reversed" || direction == "column-reverse") { + items = items.set(items.toArray().reverse()); + direction = direction.split('-')[0]; + } + + // Setup axis variable name for row/column direction since the calculations is the same + if (direction == "column") { + posName = "y"; + sizeName = "h"; + minSizeName = "minH"; + maxSizeName = "maxH"; + innerSizeName = "innerH"; + beforeName = 'top'; + deltaSizeName = "deltaH"; + contentSizeName = "contentH"; + + alignBeforeName = "left"; + alignSizeName = "w"; + alignAxisName = "x"; + alignInnerSizeName = "innerW"; + alignMinSizeName = "minW"; + alignAfterName = "right"; + alignDeltaSizeName = "deltaW"; + alignContentSizeName = "contentW"; + } else { + posName = "x"; + sizeName = "w"; + minSizeName = "minW"; + maxSizeName = "maxW"; + innerSizeName = "innerW"; + beforeName = 'left'; + deltaSizeName = "deltaW"; + contentSizeName = "contentW"; + + alignBeforeName = "top"; + alignSizeName = "h"; + alignAxisName = "y"; + alignInnerSizeName = "innerH"; + alignMinSizeName = "minH"; + alignAfterName = "bottom"; + alignDeltaSizeName = "deltaH"; + alignContentSizeName = "contentH"; + } + + // Figure out total flex, availableSpace and collect any max size elements + availableSpace = contLayoutRect[innerSizeName] - contPaddingBox[beforeName] - contPaddingBox[beforeName]; + maxAlignEndPos = totalFlex = 0; + for (i = 0, l = items.length; i < l; i++) { + ctrl = items[i]; + ctrlLayoutRect = ctrl.layoutRect(); + ctrlSettings = ctrl.settings; + flex = ctrlSettings.flex; + availableSpace -= (i < l - 1 ? spacing : 0); + + if (flex > 0) { + totalFlex += flex; + + // Flexed item has a max size then we need to check if we will hit that size + if (ctrlLayoutRect[maxSizeName]) { + maxSizeItems.push(ctrl); + } + + ctrlLayoutRect.flex = flex; + } + + availableSpace -= ctrlLayoutRect[minSizeName]; + + // Calculate the align end position to be used to check for overflow/underflow + size = contPaddingBox[alignBeforeName] + ctrlLayoutRect[alignMinSizeName] + contPaddingBox[alignAfterName]; + if (size > maxAlignEndPos) { + maxAlignEndPos = size; + } + } + + // Calculate minW/minH + rect = {}; + if (availableSpace < 0) { + rect[minSizeName] = contLayoutRect[minSizeName] - availableSpace + contLayoutRect[deltaSizeName]; + } else { + rect[minSizeName] = contLayoutRect[innerSizeName] - availableSpace + contLayoutRect[deltaSizeName]; + } + + rect[alignMinSizeName] = maxAlignEndPos + contLayoutRect[alignDeltaSizeName]; + + rect[contentSizeName] = contLayoutRect[innerSizeName] - availableSpace; + rect[alignContentSizeName] = maxAlignEndPos; + rect.minW = min(rect.minW, contLayoutRect.maxW); + rect.minH = min(rect.minH, contLayoutRect.maxH); + rect.minW = max(rect.minW, contLayoutRect.startMinWidth); + rect.minH = max(rect.minH, contLayoutRect.startMinHeight); + + // Resize container container if minSize was changed + if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) { + rect.w = rect.minW; + rect.h = rect.minH; + + container.layoutRect(rect); + this.recalc(container); + + // Forced recalc for example if items are hidden/shown + if (container._lastRect === null) { + var parentCtrl = container.parent(); + if (parentCtrl) { + parentCtrl._lastRect = null; + parentCtrl.recalc(); + } + } + + return; + } + + // Handle max size elements, check if they will become to wide with current options + ratio = availableSpace / totalFlex; + for (i = 0, l = maxSizeItems.length; i < l; i++) { + ctrl = maxSizeItems[i]; + ctrlLayoutRect = ctrl.layoutRect(); + maxSize = ctrlLayoutRect[maxSizeName]; + size = ctrlLayoutRect[minSizeName] + ctrlLayoutRect.flex * ratio; + + if (size > maxSize) { + availableSpace -= (ctrlLayoutRect[maxSizeName] - ctrlLayoutRect[minSizeName]); + totalFlex -= ctrlLayoutRect.flex; + ctrlLayoutRect.flex = 0; + ctrlLayoutRect.maxFlexSize = maxSize; + } else { + ctrlLayoutRect.maxFlexSize = 0; + } + } + + // Setup new ratio, target layout rect, start position + ratio = availableSpace / totalFlex; + pos = contPaddingBox[beforeName]; + rect = {}; + + // Handle pack setting moves the start position to end, center + if (totalFlex === 0) { + if (pack == "end") { + pos = availableSpace + contPaddingBox[beforeName]; + } else if (pack == "center") { + pos = Math.round( + (contLayoutRect[innerSizeName] / 2) - ((contLayoutRect[innerSizeName] - availableSpace) / 2) + ) + contPaddingBox[beforeName]; + + if (pos < 0) { + pos = contPaddingBox[beforeName]; + } + } else if (pack == "justify") { + pos = contPaddingBox[beforeName]; + spacing = Math.floor(availableSpace / (items.length - 1)); + } + } + + // Default aligning (start) the other ones needs to be calculated while doing the layout + rect[alignAxisName] = contPaddingBox[alignBeforeName]; + + // Start laying out controls + for (i = 0, l = items.length; i < l; i++) { + ctrl = items[i]; + ctrlLayoutRect = ctrl.layoutRect(); + size = ctrlLayoutRect.maxFlexSize || ctrlLayoutRect[minSizeName]; + + // Align the control on the other axis + if (align === "center") { + rect[alignAxisName] = Math.round((contLayoutRect[alignInnerSizeName] / 2) - (ctrlLayoutRect[alignSizeName] / 2)); + } else if (align === "stretch") { + rect[alignSizeName] = max( + ctrlLayoutRect[alignMinSizeName] || 0, + contLayoutRect[alignInnerSizeName] - contPaddingBox[alignBeforeName] - contPaddingBox[alignAfterName] + ); + rect[alignAxisName] = contPaddingBox[alignBeforeName]; + } else if (align === "end") { + rect[alignAxisName] = contLayoutRect[alignInnerSizeName] - ctrlLayoutRect[alignSizeName] - contPaddingBox.top; + } + + // Calculate new size based on flex + if (ctrlLayoutRect.flex > 0) { + size += ctrlLayoutRect.flex * ratio; + } + + rect[sizeName] = size; + rect[posName] = pos; + ctrl.layoutRect(rect); + + // Recalculate containers + if (ctrl.recalc) { + ctrl.recalc(); + } + + // Move x/y position + pos += size + spacing; + } + } + }); +}); + +// Included from: js/tinymce/classes/ui/FlowLayout.js + +/** + * FlowLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This layout manager will place the controls by using the browsers native layout. + * + * @-x-less FlowLayout.less + * @class tinymce.ui.FlowLayout + * @extends tinymce.ui.Layout + */ +define("tinymce/ui/FlowLayout", [ + "tinymce/ui/Layout" +], function(Layout) { + return Layout.extend({ + Defaults: { + containerClass: 'flow-layout', + controlClass: 'flow-layout-item', + endClass: 'break' + }, + + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + container.items().filter(':visible').each(function(ctrl) { + if (ctrl.recalc) { + ctrl.recalc(); + } + }); + }, + + isNative: function() { + return true; + } + }); +}); + +// Included from: js/tinymce/classes/ui/FormatControls.js + +/** + * FormatControls.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Internal class containing all TinyMCE specific control types such as + * format listboxes, fontlist boxes, toolbar buttons etc. + * + * @class tinymce.ui.FormatControls + */ +define("tinymce/ui/FormatControls", [ + "tinymce/ui/Control", + "tinymce/ui/Widget", + "tinymce/ui/FloatPanel", + "tinymce/util/Tools", + "tinymce/util/Arr", + "tinymce/dom/DOMUtils", + "tinymce/EditorManager", + "tinymce/Env" +], function(Control, Widget, FloatPanel, Tools, Arr, DOMUtils, EditorManager, Env) { + var each = Tools.each; + + var flatten = function (ar) { + return Arr.reduce(ar, function (result, item) { + return result.concat(item); + }, []); + }; + + EditorManager.on('AddEditor', function(e) { + var editor = e.editor; + + setupRtlMode(editor); + registerControls(editor); + setupContainer(editor); + }); + + Control.translate = function(text) { + return EditorManager.translate(text); + }; + + Widget.tooltips = !Env.iOS; + + function setupContainer(editor) { + if (editor.settings.ui_container) { + Env.container = DOMUtils.DOM.select(editor.settings.ui_container)[0]; + } + } + + function setupRtlMode(editor) { + editor.on('ScriptsLoaded', function () { + if (editor.rtl) { + Control.rtl = true; + } + }); + } + + function registerControls(editor) { + var formatMenu; + + function createListBoxChangeHandler(items, formatName) { + return function() { + var self = this; + + editor.on('nodeChange', function(e) { + var formatter = editor.formatter; + var value = null; + + each(e.parents, function(node) { + each(items, function(item) { + if (formatName) { + if (formatter.matchNode(node, formatName, {value: item.value})) { + value = item.value; + } + } else { + if (formatter.matchNode(node, item.value)) { + value = item.value; + } + } + + if (value) { + return false; + } + }); + + if (value) { + return false; + } + }); + + self.value(value); + }); + }; + } + + function createFormats(formats) { + formats = formats.replace(/;$/, '').split(';'); + + var i = formats.length; + while (i--) { + formats[i] = formats[i].split('='); + } + + return formats; + } + + function createFormatMenu() { + var count = 0, newFormats = []; + + var defaultStyleFormats = [ + {title: 'Headings', items: [ + {title: 'Heading 1', format: 'h1'}, + {title: 'Heading 2', format: 'h2'}, + {title: 'Heading 3', format: 'h3'}, + {title: 'Heading 4', format: 'h4'}, + {title: 'Heading 5', format: 'h5'}, + {title: 'Heading 6', format: 'h6'} + ]}, + + {title: 'Inline', items: [ + {title: 'Bold', icon: 'bold', format: 'bold'}, + {title: 'Italic', icon: 'italic', format: 'italic'}, + {title: 'Underline', icon: 'underline', format: 'underline'}, + {title: 'Strikethrough', icon: 'strikethrough', format: 'strikethrough'}, + {title: 'Superscript', icon: 'superscript', format: 'superscript'}, + {title: 'Subscript', icon: 'subscript', format: 'subscript'}, + {title: 'Code', icon: 'code', format: 'code'} + ]}, + + {title: 'Blocks', items: [ + {title: 'Paragraph', format: 'p'}, + {title: 'Blockquote', format: 'blockquote'}, + {title: 'Div', format: 'div'}, + {title: 'Pre', format: 'pre'} + ]}, + + {title: 'Alignment', items: [ + {title: 'Left', icon: 'alignleft', format: 'alignleft'}, + {title: 'Center', icon: 'aligncenter', format: 'aligncenter'}, + {title: 'Right', icon: 'alignright', format: 'alignright'}, + {title: 'Justify', icon: 'alignjustify', format: 'alignjustify'} + ]} + ]; + + function createMenu(formats) { + var menu = []; + + if (!formats) { + return; + } + + each(formats, function(format) { + var menuItem = { + text: format.title, + icon: format.icon + }; + + if (format.items) { + menuItem.menu = createMenu(format.items); + } else { + var formatName = format.format || "custom" + count++; + + if (!format.format) { + format.name = formatName; + newFormats.push(format); + } + + menuItem.format = formatName; + menuItem.cmd = format.cmd; + } + + menu.push(menuItem); + }); + + return menu; + } + + function createStylesMenu() { + var menu; + + if (editor.settings.style_formats_merge) { + if (editor.settings.style_formats) { + menu = createMenu(defaultStyleFormats.concat(editor.settings.style_formats)); + } else { + menu = createMenu(defaultStyleFormats); + } + } else { + menu = createMenu(editor.settings.style_formats || defaultStyleFormats); + } + + return menu; + } + + editor.on('init', function() { + each(newFormats, function(format) { + editor.formatter.register(format.name, format); + }); + }); + + return { + type: 'menu', + items: createStylesMenu(), + onPostRender: function(e) { + editor.fire('renderFormatsMenu', {control: e.control}); + }, + itemDefaults: { + preview: true, + + textStyle: function() { + if (this.settings.format) { + return editor.formatter.getCssText(this.settings.format); + } + }, + + onPostRender: function() { + var self = this; + + self.parent().on('show', function() { + var formatName, command; + + formatName = self.settings.format; + if (formatName) { + self.disabled(!editor.formatter.canApply(formatName)); + self.active(editor.formatter.match(formatName)); + } + + command = self.settings.cmd; + if (command) { + self.active(editor.queryCommandState(command)); + } + }); + }, + + onclick: function() { + if (this.settings.format) { + toggleFormat(this.settings.format); + } + + if (this.settings.cmd) { + editor.execCommand(this.settings.cmd); + } + } + } + }; + } + + formatMenu = createFormatMenu(); + + function initOnPostRender(name) { + return function() { + var self = this; + + // TODO: Fix this + if (editor.formatter) { + editor.formatter.formatChanged(name, function(state) { + self.active(state); + }); + } else { + editor.on('init', function() { + editor.formatter.formatChanged(name, function(state) { + self.active(state); + }); + }); + } + }; + } + + // Simple format controls : + each({ + bold: 'Bold', + italic: 'Italic', + underline: 'Underline', + strikethrough: 'Strikethrough', + subscript: 'Subscript', + superscript: 'Superscript' + }, function(text, name) { + editor.addButton(name, { + tooltip: text, + onPostRender: initOnPostRender(name), + onclick: function() { + toggleFormat(name); + } + }); + }); + + // Simple command controls :[,] + each({ + outdent: ['Decrease indent', 'Outdent'], + indent: ['Increase indent', 'Indent'], + cut: ['Cut', 'Cut'], + copy: ['Copy', 'Copy'], + paste: ['Paste', 'Paste'], + help: ['Help', 'mceHelp'], + selectall: ['Select all', 'SelectAll'], + removeformat: ['Clear formatting', 'RemoveFormat'], + visualaid: ['Visual aids', 'mceToggleVisualAid'], + newdocument: ['New document', 'mceNewDocument'] + }, function(item, name) { + editor.addButton(name, { + tooltip: item[0], + cmd: item[1] + }); + }); + + // Simple command controls with format state + each({ + blockquote: ['Blockquote', 'mceBlockQuote'], + subscript: ['Subscript', 'Subscript'], + superscript: ['Superscript', 'Superscript'], + alignleft: ['Align left', 'JustifyLeft'], + aligncenter: ['Align center', 'JustifyCenter'], + alignright: ['Align right', 'JustifyRight'], + alignjustify: ['Justify', 'JustifyFull'], + alignnone: ['No alignment', 'JustifyNone'] + }, function(item, name) { + editor.addButton(name, { + tooltip: item[0], + cmd: item[1], + onPostRender: initOnPostRender(name) + }); + }); + + function toggleUndoRedoState(type) { + return function() { + var self = this; + + type = type == 'redo' ? 'hasRedo' : 'hasUndo'; + + function checkState() { + return editor.undoManager ? editor.undoManager[type]() : false; + } + + self.disabled(!checkState()); + editor.on('Undo Redo AddUndo TypingUndo ClearUndos SwitchMode', function() { + self.disabled(editor.readonly || !checkState()); + }); + }; + } + + function toggleVisualAidState() { + var self = this; + + editor.on('VisualAid', function(e) { + self.active(e.hasVisual); + }); + + self.active(editor.hasVisual); + } + + var trimMenuItems = function (menuItems) { + var outputMenuItems = menuItems; + + if (outputMenuItems.length > 0 && outputMenuItems[0].text === '-') { + outputMenuItems = outputMenuItems.slice(1); + } + + if (outputMenuItems.length > 0 && outputMenuItems[outputMenuItems.length - 1].text === '-') { + outputMenuItems = outputMenuItems.slice(0, outputMenuItems.length - 1); + } + + return outputMenuItems; + }; + + var createCustomMenuItems = function (names) { + var items, nameList; + + if (typeof names === 'string') { + nameList = names.split(' '); + } else if (Tools.isArray(names)) { + return flatten(Tools.map(names, createCustomMenuItems)); + } + + items = Tools.grep(nameList, function (name) { + return name === '|' || name in editor.menuItems; + }); + + return Tools.map(items, function (name) { + return name === '|' ? {text: '-'} : editor.menuItems[name]; + }); + }; + + var createContextMenuItems = function (context) { + var outputMenuItems = [{text: '-'}]; + var menuItems = Tools.grep(editor.menuItems, function (menuItem) { + return menuItem.context === context; + }); + + Tools.each(menuItems, function (menuItem) { + if (menuItem.separator == 'before') { + outputMenuItems.push({text: '|'}); + } + + if (menuItem.prependToContext) { + outputMenuItems.unshift(menuItem); + } else { + outputMenuItems.push(menuItem); + } + + if (menuItem.separator == 'after') { + outputMenuItems.push({text: '|'}); + } + }); + + return outputMenuItems; + }; + + var createInsertMenu = function (editorSettings) { + if (editorSettings.insert_button_items) { + return trimMenuItems(createCustomMenuItems(editorSettings.insert_button_items)); + } else { + return trimMenuItems(createContextMenuItems('insert')); + } + }; + + editor.addButton('undo', { + tooltip: 'Undo', + onPostRender: toggleUndoRedoState('undo'), + cmd: 'undo' + }); + + editor.addButton('redo', { + tooltip: 'Redo', + onPostRender: toggleUndoRedoState('redo'), + cmd: 'redo' + }); + + editor.addMenuItem('newdocument', { + text: 'New document', + icon: 'newdocument', + cmd: 'mceNewDocument' + }); + + editor.addMenuItem('undo', { + text: 'Undo', + icon: 'undo', + shortcut: 'Meta+Z', + onPostRender: toggleUndoRedoState('undo'), + cmd: 'undo' + }); + + editor.addMenuItem('redo', { + text: 'Redo', + icon: 'redo', + shortcut: 'Meta+Y', + onPostRender: toggleUndoRedoState('redo'), + cmd: 'redo' + }); + + editor.addMenuItem('visualaid', { + text: 'Visual aids', + selectable: true, + onPostRender: toggleVisualAidState, + cmd: 'mceToggleVisualAid' + }); + + editor.addButton('remove', { + tooltip: 'Remove', + icon: 'remove', + cmd: 'Delete' + }); + + editor.addButton('insert', { + type: 'menubutton', + icon: 'insert', + menu: [], + oncreatemenu: function () { + this.menu.add(createInsertMenu(editor.settings)); + this.menu.renderNew(); + } + }); + + each({ + cut: ['Cut', 'Cut', 'Meta+X'], + copy: ['Copy', 'Copy', 'Meta+C'], + paste: ['Paste', 'Paste', 'Meta+V'], + selectall: ['Select all', 'SelectAll', 'Meta+A'], + bold: ['Bold', 'Bold', 'Meta+B'], + italic: ['Italic', 'Italic', 'Meta+I'], + underline: ['Underline', 'Underline'], + strikethrough: ['Strikethrough', 'Strikethrough'], + subscript: ['Subscript', 'Subscript'], + superscript: ['Superscript', 'Superscript'], + removeformat: ['Clear formatting', 'RemoveFormat'] + }, function(item, name) { + editor.addMenuItem(name, { + text: item[0], + icon: name, + shortcut: item[2], + cmd: item[1] + }); + }); + + editor.on('mousedown', function() { + FloatPanel.hideAll(); + }); + + function toggleFormat(fmt) { + if (fmt.control) { + fmt = fmt.control.value(); + } + + if (fmt) { + editor.execCommand('mceToggleFormat', false, fmt); + } + } + + function hideMenuObjects(menu) { + var count = menu.length; + + Tools.each(menu, function (item) { + if (item.menu) { + item.hidden = hideMenuObjects(item.menu) === 0; + } + + var formatName = item.format; + if (formatName) { + item.hidden = !editor.formatter.canApply(formatName); + } + + if (item.hidden) { + count--; + } + }); + + return count; + } + + function hideFormatMenuItems(menu) { + var count = menu.items().length; + + menu.items().each(function (item) { + if (item.menu) { + item.visible(hideFormatMenuItems(item.menu) > 0); + } + + if (!item.menu && item.settings.menu) { + item.visible(hideMenuObjects(item.settings.menu) > 0); + } + + var formatName = item.settings.format; + if (formatName) { + item.visible(editor.formatter.canApply(formatName)); + } + + if (!item.visible()) { + count--; + } + }); + + return count; + } + + editor.addButton('styleselect', { + type: 'menubutton', + text: 'Formats', + menu: formatMenu, + onShowMenu: function () { + if (editor.settings.style_formats_autohide) { + hideFormatMenuItems(this.menu); + } + } + }); + + editor.addButton('formatselect', function() { + var items = [], blocks = createFormats(editor.settings.block_formats || + 'Paragraph=p;' + + 'Heading 1=h1;' + + 'Heading 2=h2;' + + 'Heading 3=h3;' + + 'Heading 4=h4;' + + 'Heading 5=h5;' + + 'Heading 6=h6;' + + 'Preformatted=pre' + ); + + each(blocks, function(block) { + items.push({ + text: block[0], + value: block[1], + textStyle: function() { + return editor.formatter.getCssText(block[1]); + } + }); + }); + + return { + type: 'listbox', + text: blocks[0][0], + values: items, + fixedWidth: true, + onselect: toggleFormat, + onPostRender: createListBoxChangeHandler(items) + }; + }); + + editor.addButton('fontselect', function() { + var defaultFontsFormats = + 'Andale Mono=andale mono,monospace;' + + 'Arial=arial,helvetica,sans-serif;' + + 'Arial Black=arial black,sans-serif;' + + 'Book Antiqua=book antiqua,palatino,serif;' + + 'Comic Sans MS=comic sans ms,sans-serif;' + + 'Courier New=courier new,courier,monospace;' + + 'Georgia=georgia,palatino,serif;' + + 'Helvetica=helvetica,arial,sans-serif;' + + 'Impact=impact,sans-serif;' + + 'Symbol=symbol;' + + 'Tahoma=tahoma,arial,helvetica,sans-serif;' + + 'Terminal=terminal,monaco,monospace;' + + 'Times New Roman=times new roman,times,serif;' + + 'Trebuchet MS=trebuchet ms,geneva,sans-serif;' + + 'Verdana=verdana,geneva,sans-serif;' + + 'Webdings=webdings;' + + 'Wingdings=wingdings,zapf dingbats'; + + var items = [], fonts = createFormats(editor.settings.font_formats || defaultFontsFormats); + + each(fonts, function(font) { + items.push({ + text: {raw: font[0]}, + value: font[1], + textStyle: font[1].indexOf('dings') == -1 ? 'font-family:' + font[1] : '' + }); + }); + + return { + type: 'listbox', + text: 'Font Family', + tooltip: 'Font Family', + values: items, + fixedWidth: true, + onPostRender: createListBoxChangeHandler(items, 'fontname'), + onselect: function(e) { + if (e.control.settings.value) { + editor.execCommand('FontName', false, e.control.settings.value); + } + } + }; + }); + + editor.addButton('fontsizeselect', function() { + var items = [], defaultFontsizeFormats = '8pt 10pt 12pt 14pt 18pt 24pt 36pt'; + var fontsize_formats = editor.settings.fontsize_formats || defaultFontsizeFormats; + + each(fontsize_formats.split(' '), function(item) { + var text = item, value = item; + // Allow text=value font sizes. + var values = item.split('='); + if (values.length > 1) { + text = values[0]; + value = values[1]; + } + items.push({text: text, value: value}); + }); + + return { + type: 'listbox', + text: 'Font Sizes', + tooltip: 'Font Sizes', + values: items, + fixedWidth: true, + onPostRender: createListBoxChangeHandler(items, 'fontsize'), + onclick: function(e) { + if (e.control.settings.value) { + editor.execCommand('FontSize', false, e.control.settings.value); + } + } + }; + }); + + editor.addMenuItem('formats', { + text: 'Formats', + menu: formatMenu + }); + } +}); + +// Included from: js/tinymce/classes/ui/GridLayout.js + +/** + * GridLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This layout manager places controls in a grid. + * + * @setting {Number} spacing Spacing between controls. + * @setting {Number} spacingH Horizontal spacing between controls. + * @setting {Number} spacingV Vertical spacing between controls. + * @setting {Number} columns Number of columns to use. + * @setting {String/Array} alignH start|end|center|stretch or array of values for each column. + * @setting {String/Array} alignV start|end|center|stretch or array of values for each column. + * @setting {String} pack start|end + * + * @class tinymce.ui.GridLayout + * @extends tinymce.ui.AbsoluteLayout + */ +define("tinymce/ui/GridLayout", [ + "tinymce/ui/AbsoluteLayout" +], function(AbsoluteLayout) { + "use strict"; + + return AbsoluteLayout.extend({ + /** + * Recalculates the positions of the controls in the specified container. + * + * @method recalc + * @param {tinymce.ui.Container} container Container instance to recalc. + */ + recalc: function(container) { + var settings, rows, cols, items, contLayoutRect, width, height, rect, + ctrlLayoutRect, ctrl, x, y, posX, posY, ctrlSettings, contPaddingBox, align, spacingH, spacingV, alignH, alignV, maxX, maxY, + colWidths = [], rowHeights = [], ctrlMinWidth, ctrlMinHeight, availableWidth, availableHeight, reverseRows, idx; + + // Get layout settings + settings = container.settings; + items = container.items().filter(':visible'); + contLayoutRect = container.layoutRect(); + cols = settings.columns || Math.ceil(Math.sqrt(items.length)); + rows = Math.ceil(items.length / cols); + spacingH = settings.spacingH || settings.spacing || 0; + spacingV = settings.spacingV || settings.spacing || 0; + alignH = settings.alignH || settings.align; + alignV = settings.alignV || settings.align; + contPaddingBox = container.paddingBox; + reverseRows = 'reverseRows' in settings ? settings.reverseRows : container.isRtl(); + + if (alignH && typeof alignH == "string") { + alignH = [alignH]; + } + + if (alignV && typeof alignV == "string") { + alignV = [alignV]; + } + + // Zero padd columnWidths + for (x = 0; x < cols; x++) { + colWidths.push(0); + } + + // Zero padd rowHeights + for (y = 0; y < rows; y++) { + rowHeights.push(0); + } + + // Calculate columnWidths and rowHeights + for (y = 0; y < rows; y++) { + for (x = 0; x < cols; x++) { + ctrl = items[y * cols + x]; + + // Out of bounds + if (!ctrl) { + break; + } + + ctrlLayoutRect = ctrl.layoutRect(); + ctrlMinWidth = ctrlLayoutRect.minW; + ctrlMinHeight = ctrlLayoutRect.minH; + + colWidths[x] = ctrlMinWidth > colWidths[x] ? ctrlMinWidth : colWidths[x]; + rowHeights[y] = ctrlMinHeight > rowHeights[y] ? ctrlMinHeight : rowHeights[y]; + } + } + + // Calculate maxX + availableWidth = contLayoutRect.innerW - contPaddingBox.left - contPaddingBox.right; + for (maxX = 0, x = 0; x < cols; x++) { + maxX += colWidths[x] + (x > 0 ? spacingH : 0); + availableWidth -= (x > 0 ? spacingH : 0) + colWidths[x]; + } + + // Calculate maxY + availableHeight = contLayoutRect.innerH - contPaddingBox.top - contPaddingBox.bottom; + for (maxY = 0, y = 0; y < rows; y++) { + maxY += rowHeights[y] + (y > 0 ? spacingV : 0); + availableHeight -= (y > 0 ? spacingV : 0) + rowHeights[y]; + } + + maxX += contPaddingBox.left + contPaddingBox.right; + maxY += contPaddingBox.top + contPaddingBox.bottom; + + // Calculate minW/minH + rect = {}; + rect.minW = maxX + (contLayoutRect.w - contLayoutRect.innerW); + rect.minH = maxY + (contLayoutRect.h - contLayoutRect.innerH); + + rect.contentW = rect.minW - contLayoutRect.deltaW; + rect.contentH = rect.minH - contLayoutRect.deltaH; + rect.minW = Math.min(rect.minW, contLayoutRect.maxW); + rect.minH = Math.min(rect.minH, contLayoutRect.maxH); + rect.minW = Math.max(rect.minW, contLayoutRect.startMinWidth); + rect.minH = Math.max(rect.minH, contLayoutRect.startMinHeight); + + // Resize container container if minSize was changed + if (contLayoutRect.autoResize && (rect.minW != contLayoutRect.minW || rect.minH != contLayoutRect.minH)) { + rect.w = rect.minW; + rect.h = rect.minH; + + container.layoutRect(rect); + this.recalc(container); + + // Forced recalc for example if items are hidden/shown + if (container._lastRect === null) { + var parentCtrl = container.parent(); + if (parentCtrl) { + parentCtrl._lastRect = null; + parentCtrl.recalc(); + } + } + + return; + } + + // Update contentW/contentH so absEnd moves correctly + if (contLayoutRect.autoResize) { + rect = container.layoutRect(rect); + rect.contentW = rect.minW - contLayoutRect.deltaW; + rect.contentH = rect.minH - contLayoutRect.deltaH; + } + + var flexV; + + if (settings.packV == 'start') { + flexV = 0; + } else { + flexV = availableHeight > 0 ? Math.floor(availableHeight / rows) : 0; + } + + // Calculate totalFlex + var totalFlex = 0; + var flexWidths = settings.flexWidths; + if (flexWidths) { + for (x = 0; x < flexWidths.length; x++) { + totalFlex += flexWidths[x]; + } + } else { + totalFlex = cols; + } + + // Calculate new column widths based on flex values + var ratio = availableWidth / totalFlex; + for (x = 0; x < cols; x++) { + colWidths[x] += flexWidths ? flexWidths[x] * ratio : ratio; + } + + // Move/resize controls + posY = contPaddingBox.top; + for (y = 0; y < rows; y++) { + posX = contPaddingBox.left; + height = rowHeights[y] + flexV; + + for (x = 0; x < cols; x++) { + if (reverseRows) { + idx = y * cols + cols - 1 - x; + } else { + idx = y * cols + x; + } + + ctrl = items[idx]; + + // No more controls to render then break + if (!ctrl) { + break; + } + + // Get control settings and calculate x, y + ctrlSettings = ctrl.settings; + ctrlLayoutRect = ctrl.layoutRect(); + width = Math.max(colWidths[x], ctrlLayoutRect.startMinWidth); + ctrlLayoutRect.x = posX; + ctrlLayoutRect.y = posY; + + // Align control horizontal + align = ctrlSettings.alignH || (alignH ? (alignH[x] || alignH[0]) : null); + if (align == "center") { + ctrlLayoutRect.x = posX + (width / 2) - (ctrlLayoutRect.w / 2); + } else if (align == "right") { + ctrlLayoutRect.x = posX + width - ctrlLayoutRect.w; + } else if (align == "stretch") { + ctrlLayoutRect.w = width; + } + + // Align control vertical + align = ctrlSettings.alignV || (alignV ? (alignV[x] || alignV[0]) : null); + if (align == "center") { + ctrlLayoutRect.y = posY + (height / 2) - (ctrlLayoutRect.h / 2); + } else if (align == "bottom") { + ctrlLayoutRect.y = posY + height - ctrlLayoutRect.h; + } else if (align == "stretch") { + ctrlLayoutRect.h = height; + } + + ctrl.layoutRect(ctrlLayoutRect); + + posX += width + spacingH; + + if (ctrl.recalc) { + ctrl.recalc(); + } + } + + posY += height + spacingV; + } + } + }); +}); + +// Included from: js/tinymce/classes/ui/Iframe.js + +/** + * Iframe.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/*jshint scripturl:true */ + +/** + * This class creates an iframe. + * + * @setting {String} url Url to open in the iframe. + * + * @-x-less Iframe.less + * @class tinymce.ui.Iframe + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/Iframe", [ + "tinymce/ui/Widget", + "tinymce/util/Delay" +], function(Widget, Delay) { + "use strict"; + + return Widget.extend({ + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this; + + self.classes.add('iframe'); + self.canFocus = false; + + /*eslint no-script-url:0 */ + return ( + '' + ); + }, + + /** + * Setter for the iframe source. + * + * @method src + * @param {String} src Source URL for iframe. + */ + src: function(src) { + this.getEl().src = src; + }, + + /** + * Inner HTML for the iframe. + * + * @method html + * @param {String} html HTML string to set as HTML inside the iframe. + * @param {function} callback Optional callback to execute when the iframe body is filled with contents. + * @return {tinymce.ui.Iframe} Current iframe control. + */ + html: function(html, callback) { + var self = this, body = this.getEl().contentWindow.document.body; + + // Wait for iframe to initialize IE 10 takes time + if (!body) { + Delay.setTimeout(function() { + self.html(html); + }); + } else { + body.innerHTML = html; + + if (callback) { + callback(); + } + } + + return this; + } + }); +}); + +// Included from: js/tinymce/classes/ui/InfoBox.js + +/** + * InfoBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2016 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * .... + * + * @-x-less InfoBox.less + * @class tinymce.ui.InfoBox + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/InfoBox", [ + "tinymce/ui/Widget" +], function(Widget) { + "use strict"; + + return Widget.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} multiline Multiline label. + */ + init: function(settings) { + var self = this; + + self._super(settings); + self.classes.add('widget').add('infobox'); + self.canFocus = false; + }, + + severity: function(level) { + this.classes.remove('error'); + this.classes.remove('warning'); + this.classes.remove('success'); + this.classes.add(level); + }, + + help: function(state) { + this.state.set('help', state); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, prefix = self.classPrefix; + + return ( + '
    ' + + '
    ' + + self.encode(self.state.get('text')) + + '' + + '
    ' + + '
    ' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:text', function(e) { + self.getEl('body').firstChild.data = self.encode(e.value); + + if (self.state.get('rendered')) { + self.updateLayoutRect(); + } + }); + + self.state.on('change:help', function(e) { + self.classes.toggle('has-help', e.value); + + if (self.state.get('rendered')) { + self.updateLayoutRect(); + } + }); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Label.js + +/** + * Label.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class creates a label element. A label is a simple text control + * that can be bound to other controls. + * + * @-x-less Label.less + * @class tinymce.ui.Label + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/Label", [ + "tinymce/ui/Widget", + "tinymce/ui/DomUtils" +], function(Widget, DomUtils) { + "use strict"; + + return Widget.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} multiline Multiline label. + */ + init: function(settings) { + var self = this; + + self._super(settings); + self.classes.add('widget').add('label'); + self.canFocus = false; + + if (settings.multiline) { + self.classes.add('autoscroll'); + } + + if (settings.strong) { + self.classes.add('strong'); + } + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, layoutRect = self._super(); + + if (self.settings.multiline) { + var size = DomUtils.getSize(self.getEl()); + + // Check if the text fits within maxW if not then try word wrapping it + if (size.width > layoutRect.maxW) { + layoutRect.minW = layoutRect.maxW; + self.classes.add('multiline'); + } + + self.getEl().style.width = layoutRect.minW + 'px'; + layoutRect.startMinH = layoutRect.h = layoutRect.minH = Math.min(layoutRect.maxH, DomUtils.getSize(self.getEl()).height); + } + + return layoutRect; + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this; + + if (!self.settings.multiline) { + self.getEl().style.lineHeight = self.layoutRect().h + 'px'; + } + + return self._super(); + }, + + severity: function(level) { + this.classes.remove('error'); + this.classes.remove('warning'); + this.classes.remove('success'); + this.classes.add(level); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, targetCtrl, forName, forId = self.settings.forId; + + if (!forId && (forName = self.settings.forName)) { + targetCtrl = self.getRoot().find('#' + forName)[0]; + + if (targetCtrl) { + forId = targetCtrl._id; + } + } + + if (forId) { + return ( + '' + ); + } + + return ( + '' + + self.encode(self.state.get('text')) + + '' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:text', function(e) { + self.innerHtml(self.encode(e.value)); + + if (self.state.get('rendered')) { + self.updateLayoutRect(); + } + }); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Toolbar.js + +/** + * Toolbar.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new toolbar. + * + * @class tinymce.ui.Toolbar + * @extends tinymce.ui.Container + */ +define("tinymce/ui/Toolbar", [ + "tinymce/ui/Container" +], function(Container) { + "use strict"; + + return Container.extend({ + Defaults: { + role: 'toolbar', + layout: 'flow' + }, + + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + self._super(settings); + self.classes.add('toolbar'); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self.items().each(function(ctrl) { + ctrl.classes.add('toolbar-item'); + }); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/MenuBar.js + +/** + * MenuBar.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new menubar. + * + * @-x-less MenuBar.less + * @class tinymce.ui.MenuBar + * @extends tinymce.ui.Container + */ +define("tinymce/ui/MenuBar", [ + "tinymce/ui/Toolbar" +], function(Toolbar) { + "use strict"; + + return Toolbar.extend({ + Defaults: { + role: 'menubar', + containerCls: 'menubar', + ariaRoot: true, + defaults: { + type: 'menubutton' + } + } + }); +}); + +// Included from: js/tinymce/classes/ui/MenuButton.js + +/** + * MenuButton.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new menu button. + * + * @-x-less MenuButton.less + * @class tinymce.ui.MenuButton + * @extends tinymce.ui.Button + */ +define("tinymce/ui/MenuButton", [ + "tinymce/ui/Button", + "tinymce/ui/Factory", + "tinymce/ui/MenuBar" +], function(Button, Factory, MenuBar) { + "use strict"; + + // TODO: Maybe add as some global function + function isChildOf(node, parent) { + while (node) { + if (parent === node) { + return true; + } + + node = node.parentNode; + } + + return false; + } + + var MenuButton = Button.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + self._renderOpen = true; + + self._super(settings); + settings = self.settings; + + self.classes.add('menubtn'); + + if (settings.fixedWidth) { + self.classes.add('fixed-width'); + } + + self.aria('haspopup', true); + + self.state.set('menu', settings.menu || self.render()); + }, + + /** + * Shows the menu for the button. + * + * @method showMenu + */ + showMenu: function() { + var self = this, menu; + + if (self.menu && self.menu.visible()) { + return self.hideMenu(); + } + + if (!self.menu) { + menu = self.state.get('menu') || []; + + // Is menu array then auto constuct menu control + if (menu.length) { + menu = { + type: 'menu', + items: menu + }; + } else { + menu.type = menu.type || 'menu'; + } + + if (!menu.renderTo) { + self.menu = Factory.create(menu).parent(self).renderTo(); + } else { + self.menu = menu.parent(self).show().renderTo(); + } + + self.fire('createmenu'); + self.menu.reflow(); + self.menu.on('cancel', function(e) { + if (e.control.parent() === self.menu) { + e.stopPropagation(); + self.focus(); + self.hideMenu(); + } + }); + + // Move focus to button when a menu item is selected/clicked + self.menu.on('select', function() { + self.focus(); + }); + + self.menu.on('show hide', function(e) { + if (e.control == self.menu) { + self.activeMenu(e.type == 'show'); + } + + self.aria('expanded', e.type == 'show'); + }).fire('show'); + } + + self.menu.show(); + self.menu.layoutRect({w: self.layoutRect().w}); + self.menu.moveRel(self.getEl(), self.isRtl() ? ['br-tr', 'tr-br'] : ['bl-tl', 'tl-bl']); + self.fire('showmenu'); + }, + + /** + * Hides the menu for the button. + * + * @method hideMenu + */ + hideMenu: function() { + var self = this; + + if (self.menu) { + self.menu.items().each(function(item) { + if (item.hideMenu) { + item.hideMenu(); + } + }); + + self.menu.hide(); + } + }, + + /** + * Sets the active menu state. + * + * @private + */ + activeMenu: function(state) { + this.classes.toggle('active', state); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, id = self._id, prefix = self.classPrefix; + var icon = self.settings.icon, image, text = self.state.get('text'), + textHtml = ''; + + image = self.settings.image; + if (image) { + icon = 'none'; + + // Support for [high dpi, low dpi] image sources + if (typeof image != "string") { + image = window.getSelection ? image[0] : image[1]; + } + + image = ' style="background-image: url(\'' + image + '\')"'; + } else { + image = ''; + } + + if (text) { + self.classes.add('btn-has-text'); + textHtml = '' + self.encode(text) + ''; + } + + icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; + + self.aria('role', self.parent() instanceof MenuBar ? 'menuitem' : 'button'); + + return ( + '
    ' + + '' + + '
    ' + ); + }, + + /** + * Gets invoked after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self.on('click', function(e) { + if (e.control === self && isChildOf(e.target, self.getEl())) { + self.showMenu(); + + if (e.aria) { + self.menu.items().filter(':visible')[0].focus(); + } + } + }); + + self.on('mouseenter', function(e) { + var overCtrl = e.control, parent = self.parent(), hasVisibleSiblingMenu; + + if (overCtrl && parent && overCtrl instanceof MenuButton && overCtrl.parent() == parent) { + parent.items().filter('MenuButton').each(function(ctrl) { + if (ctrl.hideMenu && ctrl != overCtrl) { + if (ctrl.menu && ctrl.menu.visible()) { + hasVisibleSiblingMenu = true; + } + + ctrl.hideMenu(); + } + }); + + if (hasVisibleSiblingMenu) { + overCtrl.focus(); // Fix for: #5887 + overCtrl.showMenu(); + } + } + }); + + return self._super(); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:menu', function() { + if (self.menu) { + self.menu.remove(); + } + + self.menu = null; + }); + + return self._super(); + }, + + /** + * Removes the control and it's menus. + * + * @method remove + */ + remove: function() { + this._super(); + + if (this.menu) { + this.menu.remove(); + } + } + }); + + return MenuButton; +}); + +// Included from: js/tinymce/classes/ui/MenuItem.js + +/** + * MenuItem.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new menu item. + * + * @-x-less MenuItem.less + * @class tinymce.ui.MenuItem + * @extends tinymce.ui.Control + */ +define("tinymce/ui/MenuItem", [ + "tinymce/ui/Widget", + "tinymce/ui/Factory", + "tinymce/Env", + "tinymce/util/Delay" +], function(Widget, Factory, Env, Delay) { + "use strict"; + + return Widget.extend({ + Defaults: { + border: 0, + role: 'menuitem' + }, + + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} selectable Selectable menu. + * @setting {Array} menu Submenu array with items. + * @setting {String} shortcut Shortcut to display for menu item. Example: Ctrl+X + */ + init: function(settings) { + var self = this, text; + + self._super(settings); + + settings = self.settings; + + self.classes.add('menu-item'); + + if (settings.menu) { + self.classes.add('menu-item-expand'); + } + + if (settings.preview) { + self.classes.add('menu-item-preview'); + } + + text = self.state.get('text'); + if (text === '-' || text === '|') { + self.classes.add('menu-item-sep'); + self.aria('role', 'separator'); + self.state.set('text', '-'); + } + + if (settings.selectable) { + self.aria('role', 'menuitemcheckbox'); + self.classes.add('menu-item-checkbox'); + settings.icon = 'selected'; + } + + if (!settings.preview && !settings.selectable) { + self.classes.add('menu-item-normal'); + } + + self.on('mousedown', function(e) { + e.preventDefault(); + }); + + if (settings.menu && !settings.ariaHideMenu) { + self.aria('haspopup', true); + } + }, + + /** + * Returns true/false if the menuitem has sub menu. + * + * @method hasMenus + * @return {Boolean} True/false state if it has submenu. + */ + hasMenus: function() { + return !!this.settings.menu; + }, + + /** + * Shows the menu for the menu item. + * + * @method showMenu + */ + showMenu: function() { + var self = this, settings = self.settings, menu, parent = self.parent(); + + parent.items().each(function(ctrl) { + if (ctrl !== self) { + ctrl.hideMenu(); + } + }); + + if (settings.menu) { + menu = self.menu; + + if (!menu) { + menu = settings.menu; + + // Is menu array then auto constuct menu control + if (menu.length) { + menu = { + type: 'menu', + items: menu + }; + } else { + menu.type = menu.type || 'menu'; + } + + if (parent.settings.itemDefaults) { + menu.itemDefaults = parent.settings.itemDefaults; + } + + menu = self.menu = Factory.create(menu).parent(self).renderTo(); + menu.reflow(); + menu.on('cancel', function(e) { + e.stopPropagation(); + self.focus(); + menu.hide(); + }); + menu.on('show hide', function(e) { + if (e.control.items) { + e.control.items().each(function(ctrl) { + ctrl.active(ctrl.settings.selected); + }); + } + }).fire('show'); + + menu.on('hide', function(e) { + if (e.control === menu) { + self.classes.remove('selected'); + } + }); + + menu.submenu = true; + } else { + menu.show(); + } + + menu._parentMenu = parent; + + menu.classes.add('menu-sub'); + + var rel = menu.testMoveRel( + self.getEl(), + self.isRtl() ? ['tl-tr', 'bl-br', 'tr-tl', 'br-bl'] : ['tr-tl', 'br-bl', 'tl-tr', 'bl-br'] + ); + + menu.moveRel(self.getEl(), rel); + menu.rel = rel; + + rel = 'menu-sub-' + rel; + menu.classes.remove(menu._lastRel).add(rel); + menu._lastRel = rel; + + self.classes.add('selected'); + self.aria('expanded', true); + } + }, + + /** + * Hides the menu for the menu item. + * + * @method hideMenu + */ + hideMenu: function() { + var self = this; + + if (self.menu) { + self.menu.items().each(function(item) { + if (item.hideMenu) { + item.hideMenu(); + } + }); + + self.menu.hide(); + self.aria('expanded', false); + } + + return self; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix, text = self.state.get('text'); + var icon = self.settings.icon, image = '', shortcut = settings.shortcut; + var url = self.encode(settings.url), iconHtml = ''; + + // Converts shortcut format to Mac/PC variants + function convertShortcut(shortcut) { + var i, value, replace = {}; + + if (Env.mac) { + replace = { + alt: '⌥', + ctrl: '⌘', + shift: '⇧', + meta: '⌘' + }; + } else { + replace = { + meta: 'Ctrl' + }; + } + + shortcut = shortcut.split('+'); + + for (i = 0; i < shortcut.length; i++) { + value = replace[shortcut[i].toLowerCase()]; + + if (value) { + shortcut[i] = value; + } + } + + return shortcut.join('+'); + } + + function escapeRegExp(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); + } + + function markMatches(text) { + var match = settings.match || ''; + + return match ? text.replace(new RegExp(escapeRegExp(match), 'gi'), function (match) { + return '!mce~match[' + match + ']mce~match!'; + }) : text; + } + + function boldMatches(text) { + return text. + replace(new RegExp(escapeRegExp('!mce~match['), 'g'), ''). + replace(new RegExp(escapeRegExp(']mce~match!'), 'g'), ''); + } + + if (icon) { + self.parent().classes.add('menu-has-icons'); + } + + if (settings.image) { + image = ' style="background-image: url(\'' + settings.image + '\')"'; + } + + if (shortcut) { + shortcut = convertShortcut(shortcut); + } + + icon = prefix + 'ico ' + prefix + 'i-' + (self.settings.icon || 'none'); + iconHtml = (text !== '-' ? '\u00a0' : ''); + + text = boldMatches(self.encode(markMatches(text))); + url = boldMatches(self.encode(markMatches(url))); + + return ( + '
    ' + + iconHtml + + (text !== '-' ? '' + text + '' : '') + + (shortcut ? '
    ' + shortcut + '
    ' : '') + + (settings.menu ? '
    ' : '') + + (url ? '' : '') + + '
    ' + ); + }, + + /** + * Gets invoked after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, settings = self.settings; + + var textStyle = settings.textStyle; + if (typeof textStyle == "function") { + textStyle = textStyle.call(this); + } + + if (textStyle) { + var textElm = self.getEl('text'); + if (textElm) { + textElm.setAttribute('style', textStyle); + } + } + + self.on('mouseenter click', function(e) { + if (e.control === self) { + if (!settings.menu && e.type === 'click') { + self.fire('select'); + + // Edge will crash if you stress it see #2660 + Delay.requestAnimationFrame(function() { + self.parent().hideAll(); + }); + } else { + self.showMenu(); + + if (e.aria) { + self.menu.focus(true); + } + } + } + }); + + self._super(); + + return self; + }, + + hover: function() { + var self = this; + + self.parent().items().each(function(ctrl) { + ctrl.classes.remove('selected'); + }); + + self.classes.toggle('selected', true); + + return self; + }, + + active: function(state) { + if (typeof state != "undefined") { + this.aria('checked', state); + } + + return this._super(state); + }, + + /** + * Removes the control and it's menus. + * + * @method remove + */ + remove: function() { + this._super(); + + if (this.menu) { + this.menu.remove(); + } + } + }); +}); + +// Included from: js/tinymce/classes/ui/Throbber.js + +/** + * Throbber.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This class enables you to display a Throbber for any element. + * + * @-x-less Throbber.less + * @class tinymce.ui.Throbber + */ +define("tinymce/ui/Throbber", [ + "tinymce/dom/DomQuery", + "tinymce/ui/Control", + "tinymce/util/Delay" +], function($, Control, Delay) { + "use strict"; + + /** + * Constructs a new throbber. + * + * @constructor + * @param {Element} elm DOM Html element to display throbber in. + * @param {Boolean} inline Optional true/false state if the throbber should be appended to end of element for infinite scroll. + */ + return function(elm, inline) { + var self = this, state, classPrefix = Control.classPrefix, timer; + + /** + * Shows the throbber. + * + * @method show + * @param {Number} [time] Time to wait before showing. + * @param {function} [callback] Optional callback to execute when the throbber is shown. + * @return {tinymce.ui.Throbber} Current throbber instance. + */ + self.show = function(time, callback) { + function render() { + if (state) { + $(elm).append( + '
    ' + ); + + if (callback) { + callback(); + } + } + } + + self.hide(); + + state = true; + + if (time) { + timer = Delay.setTimeout(render, time); + } else { + render(); + } + + return self; + }; + + /** + * Hides the throbber. + * + * @method hide + * @return {tinymce.ui.Throbber} Current throbber instance. + */ + self.hide = function() { + var child = elm.lastChild; + + Delay.clearTimeout(timer); + + if (child && child.className.indexOf('throbber') != -1) { + child.parentNode.removeChild(child); + } + + state = false; + + return self; + }; + }; +}); + +// Included from: js/tinymce/classes/ui/Menu.js + +/** + * Menu.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new menu. + * + * @-x-less Menu.less + * @class tinymce.ui.Menu + * @extends tinymce.ui.FloatPanel + */ +define("tinymce/ui/Menu", [ + "tinymce/ui/FloatPanel", + "tinymce/ui/MenuItem", + "tinymce/ui/Throbber", + "tinymce/util/Tools" +], function(FloatPanel, MenuItem, Throbber, Tools) { + "use strict"; + + return FloatPanel.extend({ + Defaults: { + defaultType: 'menuitem', + border: 1, + layout: 'stack', + role: 'application', + bodyRole: 'menu', + ariaRoot: true + }, + + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + */ + init: function(settings) { + var self = this; + + settings.autohide = true; + settings.constrainToViewport = true; + + if (typeof settings.items === 'function') { + settings.itemsFactory = settings.items; + settings.items = []; + } + + if (settings.itemDefaults) { + var items = settings.items, i = items.length; + + while (i--) { + items[i] = Tools.extend({}, settings.itemDefaults, items[i]); + } + } + + self._super(settings); + self.classes.add('menu'); + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + this.classes.toggle('menu-align', true); + + this._super(); + + this.getEl().style.height = ''; + this.getEl('body').style.height = ''; + + return this; + }, + + /** + * Hides/closes the menu. + * + * @method cancel + */ + cancel: function() { + var self = this; + + self.hideAll(); + self.fire('select'); + }, + + /** + * Loads new items from the factory items function. + * + * @method load + */ + load: function() { + var self = this, time, factory; + + function hideThrobber() { + if (self.throbber) { + self.throbber.hide(); + self.throbber = null; + } + } + + factory = self.settings.itemsFactory; + if (!factory) { + return; + } + + if (!self.throbber) { + self.throbber = new Throbber(self.getEl('body'), true); + + if (self.items().length === 0) { + self.throbber.show(); + self.fire('loading'); + } else { + self.throbber.show(100, function() { + self.items().remove(); + self.fire('loading'); + }); + } + + self.on('hide close', hideThrobber); + } + + self.requestTime = time = new Date().getTime(); + + self.settings.itemsFactory(function(items) { + if (items.length === 0) { + self.hide(); + return; + } + + if (self.requestTime !== time) { + return; + } + + self.getEl().style.width = ''; + self.getEl('body').style.width = ''; + + hideThrobber(); + self.items().remove(); + self.getEl('body').innerHTML = ''; + + self.add(items); + self.renderNew(); + self.fire('loaded'); + }); + }, + + /** + * Hide menu and all sub menus. + * + * @method hideAll + */ + hideAll: function() { + var self = this; + + this.find('menuitem').exec('hideMenu'); + + return self._super(); + }, + + /** + * Invoked before the menu is rendered. + * + * @method preRender + */ + preRender: function() { + var self = this; + + self.items().each(function(ctrl) { + var settings = ctrl.settings; + + if (settings.icon || settings.image || settings.selectable) { + self._hasIcons = true; + return false; + } + }); + + if (self.settings.itemsFactory) { + self.on('postrender', function() { + if (self.settings.itemsFactory) { + self.load(); + } + }); + } + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/ListBox.js + +/** + * ListBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new list box control. + * + * @-x-less ListBox.less + * @class tinymce.ui.ListBox + * @extends tinymce.ui.MenuButton + */ +define("tinymce/ui/ListBox", [ + "tinymce/ui/MenuButton", + "tinymce/ui/Menu" +], function(MenuButton, Menu) { + "use strict"; + + return MenuButton.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Array} values Array with values to add to list box. + */ + init: function(settings) { + var self = this, values, selected, selectedText, lastItemCtrl; + + function setSelected(menuValues) { + // Try to find a selected value + for (var i = 0; i < menuValues.length; i++) { + selected = menuValues[i].selected || settings.value === menuValues[i].value; + + if (selected) { + selectedText = selectedText || menuValues[i].text; + self.state.set('value', menuValues[i].value); + return true; + } + + // If the value has a submenu, try to find the selected values in that menu + if (menuValues[i].menu) { + if (setSelected(menuValues[i].menu)) { + return true; + } + } + } + } + + self._super(settings); + settings = self.settings; + + self._values = values = settings.values; + if (values) { + if (typeof settings.value != "undefined") { + setSelected(values); + } + + // Default with first item + if (!selected && values.length > 0) { + selectedText = values[0].text; + self.state.set('value', values[0].value); + } + + self.state.set('menu', values); + } + + self.state.set('text', settings.text || selectedText); + + self.classes.add('listbox'); + + self.on('select', function(e) { + var ctrl = e.control; + + if (lastItemCtrl) { + e.lastControl = lastItemCtrl; + } + + if (settings.multiple) { + ctrl.active(!ctrl.active()); + } else { + self.value(e.control.value()); + } + + lastItemCtrl = ctrl; + }); + }, + + /** + * Getter/setter function for the control value. + * + * @method value + * @param {String} [value] Value to be set. + * @return {Boolean/tinymce.ui.ListBox} Value or self if it's a set operation. + */ + bindStates: function() { + var self = this; + + function activateMenuItemsByValue(menu, value) { + if (menu instanceof Menu) { + menu.items().each(function(ctrl) { + if (!ctrl.hasMenus()) { + ctrl.active(ctrl.value() === value); + } + }); + } + } + + function getSelectedItem(menuValues, value) { + var selectedItem; + + if (!menuValues) { + return; + } + + for (var i = 0; i < menuValues.length; i++) { + if (menuValues[i].value === value) { + return menuValues[i]; + } + + if (menuValues[i].menu) { + selectedItem = getSelectedItem(menuValues[i].menu, value); + if (selectedItem) { + return selectedItem; + } + } + } + } + + self.on('show', function(e) { + activateMenuItemsByValue(e.control, self.value()); + }); + + self.state.on('change:value', function(e) { + var selectedItem = getSelectedItem(self.state.get('menu'), e.value); + + if (selectedItem) { + self.text(selectedItem.text); + } else { + self.text(self.settings.text); + } + }); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Radio.js + +/** + * Radio.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new radio button. + * + * @-x-less Radio.less + * @class tinymce.ui.Radio + * @extends tinymce.ui.Checkbox + */ +define("tinymce/ui/Radio", [ + "tinymce/ui/Checkbox" +], function(Checkbox) { + "use strict"; + + return Checkbox.extend({ + Defaults: { + classes: "radio", + role: "radio" + } + }); +}); + +// Included from: js/tinymce/classes/ui/ResizeHandle.js + +/** + * ResizeHandle.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Renders a resize handle that fires ResizeStart, Resize and ResizeEnd events. + * + * @-x-less ResizeHandle.less + * @class tinymce.ui.ResizeHandle + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/ResizeHandle", [ + "tinymce/ui/Widget", + "tinymce/ui/DragHelper" +], function(Widget, DragHelper) { + "use strict"; + + return Widget.extend({ + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, prefix = self.classPrefix; + + self.classes.add('resizehandle'); + + if (self.settings.direction == "both") { + self.classes.add('resizehandle-both'); + } + + self.canFocus = false; + + return ( + '
    ' + + '' + + '
    ' + ); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self._super(); + + self.resizeDragHelper = new DragHelper(this._id, { + start: function() { + self.fire('ResizeStart'); + }, + + drag: function(e) { + if (self.settings.direction != "both") { + e.deltaX = 0; + } + + self.fire('Resize', e); + }, + + stop: function() { + self.fire('ResizeEnd'); + } + }); + }, + + remove: function() { + if (this.resizeDragHelper) { + this.resizeDragHelper.destroy(); + } + + return this._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/SelectBox.js + +/** + * SelectBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new select box control. + * + * @-x-less SelectBox.less + * @class tinymce.ui.SelectBox + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/SelectBox", [ + "tinymce/ui/Widget" +], function(Widget) { + "use strict"; + + function createOptions(options) { + var strOptions = ''; + if (options) { + for (var i = 0; i < options.length; i++) { + strOptions += ''; + } + } + return strOptions; + } + + return Widget.extend({ + Defaults: { + classes: "selectbox", + role: "selectbox", + options: [] + }, + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Array} options Array with options to add to the select box. + */ + init: function(settings) { + var self = this; + + self._super(settings); + + if (self.settings.size) { + self.size = self.settings.size; + } + + if (self.settings.options) { + self._options = self.settings.options; + } + + self.on('keydown', function(e) { + var rootControl; + + if (e.keyCode == 13) { + e.preventDefault(); + + // Find root control that we can do toJSON on + self.parents().reverse().each(function(ctrl) { + if (ctrl.toJSON) { + rootControl = ctrl; + return false; + } + }); + + // Fire event on current text box with the serialized data of the whole form + self.fire('submit', {data: rootControl.toJSON()}); + } + }); + }, + + /** + * Getter/setter function for the options state. + * + * @method options + * @param {Array} [state] State to be set. + * @return {Array|tinymce.ui.SelectBox} Array of string options. + */ + options: function(state) { + if (!arguments.length) { + return this.state.get('options'); + } + + this.state.set('options', state); + + return this; + }, + + renderHtml: function() { + var self = this, options, size = ''; + + options = createOptions(self._options); + + if (self.size) { + size = ' size = "' + self.size + '"'; + } + + return ( + '' + ); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:options', function(e) { + self.getEl().innerHTML = createOptions(e.value); + }); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Slider.js + +/** + * Slider.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Slider control. + * + * @-x-less Slider.less + * @class tinymce.ui.Slider + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/Slider", [ + "tinymce/ui/Widget", + "tinymce/ui/DragHelper", + "tinymce/ui/DomUtils" +], function(Widget, DragHelper, DomUtils) { + "use strict"; + + function constrain(value, minVal, maxVal) { + if (value < minVal) { + value = minVal; + } + + if (value > maxVal) { + value = maxVal; + } + + return value; + } + + function setAriaProp(el, name, value) { + el.setAttribute('aria-' + name, value); + } + + function updateSliderHandle(ctrl, value) { + var maxHandlePos, shortSizeName, sizeName, stylePosName, styleValue, handleEl; + + if (ctrl.settings.orientation == "v") { + stylePosName = "top"; + sizeName = "height"; + shortSizeName = "h"; + } else { + stylePosName = "left"; + sizeName = "width"; + shortSizeName = "w"; + } + + handleEl = ctrl.getEl('handle'); + maxHandlePos = (ctrl.layoutRect()[shortSizeName] || 100) - DomUtils.getSize(handleEl)[sizeName]; + + styleValue = (maxHandlePos * ((value - ctrl._minValue) / (ctrl._maxValue - ctrl._minValue))) + 'px'; + handleEl.style[stylePosName] = styleValue; + handleEl.style.height = ctrl.layoutRect().h + 'px'; + + setAriaProp(handleEl, 'valuenow', value); + setAriaProp(handleEl, 'valuetext', '' + ctrl.settings.previewFilter(value)); + setAriaProp(handleEl, 'valuemin', ctrl._minValue); + setAriaProp(handleEl, 'valuemax', ctrl._maxValue); + } + + return Widget.extend({ + init: function(settings) { + var self = this; + + if (!settings.previewFilter) { + settings.previewFilter = function(value) { + return Math.round(value * 100) / 100.0; + }; + } + + self._super(settings); + self.classes.add('slider'); + + if (settings.orientation == "v") { + self.classes.add('vertical'); + } + + self._minValue = settings.minValue || 0; + self._maxValue = settings.maxValue || 100; + self._initValue = self.state.get('value'); + }, + + renderHtml: function() { + var self = this, id = self._id, prefix = self.classPrefix; + + return ( + '
    ' + + '
    ' + + '
    ' + ); + }, + + reset: function() { + this.value(this._initValue).repaint(); + }, + + postRender: function() { + var self = this, minValue, maxValue, screenCordName, + stylePosName, sizeName, shortSizeName; + + function toFraction(min, max, val) { + return (val + min) / (max - min); + } + + function fromFraction(min, max, val) { + return (val * (max - min)) - min; + } + + function handleKeyboard(minValue, maxValue) { + function alter(delta) { + var value; + + value = self.value(); + value = fromFraction(minValue, maxValue, toFraction(minValue, maxValue, value) + (delta * 0.05)); + value = constrain(value, minValue, maxValue); + + self.value(value); + + self.fire('dragstart', {value: value}); + self.fire('drag', {value: value}); + self.fire('dragend', {value: value}); + } + + self.on('keydown', function(e) { + switch (e.keyCode) { + case 37: + case 38: + alter(-1); + break; + + case 39: + case 40: + alter(1); + break; + } + }); + } + + function handleDrag(minValue, maxValue, handleEl) { + var startPos, startHandlePos, maxHandlePos, handlePos, value; + + self._dragHelper = new DragHelper(self._id, { + handle: self._id + "-handle", + + start: function(e) { + startPos = e[screenCordName]; + startHandlePos = parseInt(self.getEl('handle').style[stylePosName], 10); + maxHandlePos = (self.layoutRect()[shortSizeName] || 100) - DomUtils.getSize(handleEl)[sizeName]; + self.fire('dragstart', {value: value}); + }, + + drag: function(e) { + var delta = e[screenCordName] - startPos; + + handlePos = constrain(startHandlePos + delta, 0, maxHandlePos); + handleEl.style[stylePosName] = handlePos + 'px'; + + value = minValue + (handlePos / maxHandlePos) * (maxValue - minValue); + self.value(value); + + self.tooltip().text('' + self.settings.previewFilter(value)).show().moveRel(handleEl, 'bc tc'); + + self.fire('drag', {value: value}); + }, + + stop: function() { + self.tooltip().hide(); + self.fire('dragend', {value: value}); + } + }); + } + + minValue = self._minValue; + maxValue = self._maxValue; + + if (self.settings.orientation == "v") { + screenCordName = "screenY"; + stylePosName = "top"; + sizeName = "height"; + shortSizeName = "h"; + } else { + screenCordName = "screenX"; + stylePosName = "left"; + sizeName = "width"; + shortSizeName = "w"; + } + + self._super(); + + handleKeyboard(minValue, maxValue, self.getEl('handle')); + handleDrag(minValue, maxValue, self.getEl('handle')); + }, + + repaint: function() { + this._super(); + updateSliderHandle(this, this.value()); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:value', function(e) { + updateSliderHandle(self, e.value); + }); + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/Spacer.js + +/** + * Spacer.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a spacer. This control is used in flex layouts for example. + * + * @-x-less Spacer.less + * @class tinymce.ui.Spacer + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/Spacer", [ + "tinymce/ui/Widget" +], function(Widget) { + "use strict"; + + return Widget.extend({ + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this; + + self.classes.add('spacer'); + self.canFocus = false; + + return '
    '; + } + }); +}); + +// Included from: js/tinymce/classes/ui/SplitButton.js + +/** + * SplitButton.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a split button. + * + * @-x-less SplitButton.less + * @class tinymce.ui.SplitButton + * @extends tinymce.ui.Button + */ +define("tinymce/ui/SplitButton", [ + "tinymce/ui/MenuButton", + "tinymce/ui/DomUtils", + "tinymce/dom/DomQuery" +], function(MenuButton, DomUtils, $) { + return MenuButton.extend({ + Defaults: { + classes: "widget btn splitbtn", + role: "button" + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, elm = self.getEl(), rect = self.layoutRect(), mainButtonElm, menuButtonElm; + + self._super(); + + mainButtonElm = elm.firstChild; + menuButtonElm = elm.lastChild; + + $(mainButtonElm).css({ + width: rect.w - DomUtils.getSize(menuButtonElm).width, + height: rect.h - 2 + }); + + $(menuButtonElm).css({ + height: rect.h - 2 + }); + + return self; + }, + + /** + * Sets the active menu state. + * + * @private + */ + activeMenu: function(state) { + var self = this; + + $(self.getEl().lastChild).toggleClass(self.classPrefix + 'active', state); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, id = self._id, prefix = self.classPrefix, image; + var icon = self.state.get('icon'), text = self.state.get('text'), + textHtml = ''; + + image = self.settings.image; + if (image) { + icon = 'none'; + + // Support for [high dpi, low dpi] image sources + if (typeof image != "string") { + image = window.getSelection ? image[0] : image[1]; + } + + image = ' style="background-image: url(\'' + image + '\')"'; + } else { + image = ''; + } + + icon = self.settings.icon ? prefix + 'ico ' + prefix + 'i-' + icon : ''; + + if (text) { + self.classes.add('btn-has-text'); + textHtml = '' + self.encode(text) + ''; + } + + return ( + '
    ' + + '' + + '' + + '
    ' + ); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this, onClickHandler = self.settings.onclick; + + self.on('click', function(e) { + var node = e.target; + + if (e.control == this) { + // Find clicks that is on the main button + while (node) { + if ((e.aria && e.aria.key != 'down') || (node.nodeName == 'BUTTON' && node.className.indexOf('open') == -1)) { + e.stopImmediatePropagation(); + + if (onClickHandler) { + onClickHandler.call(this, e); + } + + return; + } + + node = node.parentNode; + } + } + }); + + delete self.settings.onclick; + + return self._super(); + } + }); +}); + +// Included from: js/tinymce/classes/ui/StackLayout.js + +/** + * StackLayout.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This layout uses the browsers layout when the items are blocks. + * + * @-x-less StackLayout.less + * @class tinymce.ui.StackLayout + * @extends tinymce.ui.FlowLayout + */ +define("tinymce/ui/StackLayout", [ + "tinymce/ui/FlowLayout" +], function(FlowLayout) { + "use strict"; + + return FlowLayout.extend({ + Defaults: { + containerClass: 'stack-layout', + controlClass: 'stack-layout-item', + endClass: 'break' + }, + + isNative: function() { + return true; + } + }); +}); + +// Included from: js/tinymce/classes/ui/TabPanel.js + +/** + * TabPanel.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a tab panel control. + * + * @-x-less TabPanel.less + * @class tinymce.ui.TabPanel + * @extends tinymce.ui.Panel + * + * @setting {Number} activeTab Active tab index. + */ +define("tinymce/ui/TabPanel", [ + "tinymce/ui/Panel", + "tinymce/dom/DomQuery", + "tinymce/ui/DomUtils" +], function(Panel, $, DomUtils) { + "use strict"; + + return Panel.extend({ + Defaults: { + layout: 'absolute', + defaults: { + type: 'panel' + } + }, + + /** + * Activates the specified tab by index. + * + * @method activateTab + * @param {Number} idx Index of the tab to activate. + */ + activateTab: function(idx) { + var activeTabElm; + + if (this.activeTabId) { + activeTabElm = this.getEl(this.activeTabId); + $(activeTabElm).removeClass(this.classPrefix + 'active'); + activeTabElm.setAttribute('aria-selected', "false"); + } + + this.activeTabId = 't' + idx; + + activeTabElm = this.getEl('t' + idx); + activeTabElm.setAttribute('aria-selected', "true"); + $(activeTabElm).addClass(this.classPrefix + 'active'); + + this.items()[idx].show().fire('showtab'); + this.reflow(); + + this.items().each(function(item, i) { + if (idx != i) { + item.hide(); + } + }); + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, layout = self._layout, tabsHtml = '', prefix = self.classPrefix; + + self.preRender(); + layout.preRender(self); + + self.items().each(function(ctrl, i) { + var id = self._id + '-t' + i; + + ctrl.aria('role', 'tabpanel'); + ctrl.aria('labelledby', id); + + tabsHtml += ( + '' + ); + }); + + return ( + '
    ' + + '
    ' + + tabsHtml + + '
    ' + + '
    ' + + layout.renderHtml(self) + + '
    ' + + '
    ' + ); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self._super(); + + self.settings.activeTab = self.settings.activeTab || 0; + self.activateTab(self.settings.activeTab); + + this.on('click', function(e) { + var targetParent = e.target.parentNode; + + if (targetParent && targetParent.id == self._id + '-head') { + var i = targetParent.childNodes.length; + + while (i--) { + if (targetParent.childNodes[i] == e.target) { + self.activateTab(i); + } + } + } + }); + }, + + /** + * Initializes the current controls layout rect. + * This will be executed by the layout managers to determine the + * default minWidth/minHeight etc. + * + * @method initLayoutRect + * @return {Object} Layout rect instance. + */ + initLayoutRect: function() { + var self = this, rect, minW, minH; + + minW = DomUtils.getSize(self.getEl('head')).width; + minW = minW < 0 ? 0 : minW; + minH = 0; + + self.items().each(function(item) { + minW = Math.max(minW, item.layoutRect().minW); + minH = Math.max(minH, item.layoutRect().minH); + }); + + self.items().each(function(ctrl) { + ctrl.settings.x = 0; + ctrl.settings.y = 0; + ctrl.settings.w = minW; + ctrl.settings.h = minH; + + ctrl.layoutRect({ + x: 0, + y: 0, + w: minW, + h: minH + }); + }); + + var headH = DomUtils.getSize(self.getEl('head')).height; + + self.settings.minWidth = minW; + self.settings.minHeight = minH + headH; + + rect = self._super(); + rect.deltaH += headH; + rect.innerH = rect.h - rect.deltaH; + + return rect; + } + }); +}); + +// Included from: js/tinymce/classes/ui/TextBox.js + +/** + * TextBox.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * Creates a new textbox. + * + * @-x-less TextBox.less + * @class tinymce.ui.TextBox + * @extends tinymce.ui.Widget + */ +define("tinymce/ui/TextBox", [ + "tinymce/ui/Widget", + "tinymce/util/Tools", + "tinymce/ui/DomUtils" +], function(Widget, Tools, DomUtils) { + return Widget.extend({ + /** + * Constructs a instance with the specified settings. + * + * @constructor + * @param {Object} settings Name/value object with settings. + * @setting {Boolean} multiline True if the textbox is a multiline control. + * @setting {Number} maxLength Max length for the textbox. + * @setting {Number} size Size of the textbox in characters. + */ + init: function(settings) { + var self = this; + + self._super(settings); + + self.classes.add('textbox'); + + if (settings.multiline) { + self.classes.add('multiline'); + } else { + self.on('keydown', function(e) { + var rootControl; + + if (e.keyCode == 13) { + e.preventDefault(); + + // Find root control that we can do toJSON on + self.parents().reverse().each(function(ctrl) { + if (ctrl.toJSON) { + rootControl = ctrl; + return false; + } + }); + + // Fire event on current text box with the serialized data of the whole form + self.fire('submit', {data: rootControl.toJSON()}); + } + }); + + self.on('keyup', function(e) { + self.state.set('value', e.target.value); + }); + } + }, + + /** + * Repaints the control after a layout operation. + * + * @method repaint + */ + repaint: function() { + var self = this, style, rect, borderBox, borderW, borderH = 0, lastRepaintRect; + + style = self.getEl().style; + rect = self._layoutRect; + lastRepaintRect = self._lastRepaintRect || {}; + + // Detect old IE 7+8 add lineHeight to align caret vertically in the middle + var doc = document; + if (!self.settings.multiline && doc.all && (!doc.documentMode || doc.documentMode <= 8)) { + style.lineHeight = (rect.h - borderH) + 'px'; + } + + borderBox = self.borderBox; + borderW = borderBox.left + borderBox.right + 8; + borderH = borderBox.top + borderBox.bottom + (self.settings.multiline ? 8 : 0); + + if (rect.x !== lastRepaintRect.x) { + style.left = rect.x + 'px'; + lastRepaintRect.x = rect.x; + } + + if (rect.y !== lastRepaintRect.y) { + style.top = rect.y + 'px'; + lastRepaintRect.y = rect.y; + } + + if (rect.w !== lastRepaintRect.w) { + style.width = (rect.w - borderW) + 'px'; + lastRepaintRect.w = rect.w; + } + + if (rect.h !== lastRepaintRect.h) { + style.height = (rect.h - borderH) + 'px'; + lastRepaintRect.h = rect.h; + } + + self._lastRepaintRect = lastRepaintRect; + self.fire('repaint', {}, false); + + return self; + }, + + /** + * Renders the control as a HTML string. + * + * @method renderHtml + * @return {String} HTML representing the control. + */ + renderHtml: function() { + var self = this, settings = self.settings, attrs, elm; + + attrs = { + id: self._id, + hidefocus: '1' + }; + + Tools.each([ + 'rows', 'spellcheck', 'maxLength', 'size', 'readonly', 'min', + 'max', 'step', 'list', 'pattern', 'placeholder', 'required', 'multiple' + ], function(name) { + attrs[name] = settings[name]; + }); + + if (self.disabled()) { + attrs.disabled = 'disabled'; + } + + if (settings.subtype) { + attrs.type = settings.subtype; + } + + elm = DomUtils.create(settings.multiline ? 'textarea' : 'input', attrs); + elm.value = self.state.get('value'); + elm.className = self.classes; + + return elm.outerHTML; + }, + + value: function(value) { + if (arguments.length) { + this.state.set('value', value); + return this; + } + + // Make sure the real state is in sync + if (this.state.get('rendered')) { + this.state.set('value', this.getEl().value); + } + + return this.state.get('value'); + }, + + /** + * Called after the control has been rendered. + * + * @method postRender + */ + postRender: function() { + var self = this; + + self.getEl().value = self.state.get('value'); + self._super(); + + self.$el.on('change', function(e) { + self.state.set('value', e.target.value); + self.fire('change', e); + }); + }, + + bindStates: function() { + var self = this; + + self.state.on('change:value', function(e) { + if (self.getEl().value != e.value) { + self.getEl().value = e.value; + } + }); + + self.state.on('change:disabled', function(e) { + self.getEl().disabled = e.value; + }); + + return self._super(); + }, + + remove: function() { + this.$el.off(); + this._super(); + } + }); +}); + +// Included from: js/tinymce/classes/Register.js + +/** + * Register.js + * + * Released under LGPL License. + * Copyright (c) 1999-2015 Ephox Corp. All rights reserved + * + * License: http://www.tinymce.com/license + * Contributing: http://www.tinymce.com/contributing + */ + +/** + * This registers tinymce in common module loaders. + * + * @private + * @class tinymce.Register + */ +define("tinymce/Register", [ +], function() { + /*eslint consistent-this: 0 */ + var context = this || window; + + var tinymce = function() { + return context.tinymce; + }; + + if (typeof context.define === "function") { + // Bolt + if (!context.define.amd) { + context.define("ephox/tinymce", [], tinymce); + } + } + + if (typeof module === 'object') { + /* global module */ + module.exports = window.tinymce; + } + + return {}; +}); + +expose(["tinymce/geom/Rect","tinymce/util/Promise","tinymce/util/Delay","tinymce/Env","tinymce/dom/EventUtils","tinymce/dom/Sizzle","tinymce/util/Tools","tinymce/dom/DomQuery","tinymce/html/Styles","tinymce/dom/TreeWalker","tinymce/html/Entities","tinymce/dom/DOMUtils","tinymce/dom/ScriptLoader","tinymce/AddOnManager","tinymce/dom/RangeUtils","tinymce/html/Node","tinymce/html/Schema","tinymce/html/SaxParser","tinymce/html/DomParser","tinymce/html/Writer","tinymce/html/Serializer","tinymce/dom/Serializer","tinymce/util/VK","tinymce/dom/ControlSelection","tinymce/dom/BookmarkManager","tinymce/dom/Selection","tinymce/Formatter","tinymce/UndoManager","tinymce/EditorCommands","tinymce/util/URI","tinymce/util/Class","tinymce/util/EventDispatcher","tinymce/util/Observable","tinymce/ui/Selector","tinymce/ui/Collection","tinymce/ui/ReflowQueue","tinymce/ui/Control","tinymce/ui/Factory","tinymce/ui/KeyboardNavigation","tinymce/ui/Container","tinymce/ui/DragHelper","tinymce/ui/Scrollable","tinymce/ui/Panel","tinymce/ui/Movable","tinymce/ui/Resizable","tinymce/ui/FloatPanel","tinymce/ui/Window","tinymce/ui/MessageBox","tinymce/WindowManager","tinymce/ui/Tooltip","tinymce/ui/Widget","tinymce/ui/Progress","tinymce/ui/Notification","tinymce/NotificationManager","tinymce/EditorObservable","tinymce/Shortcuts","tinymce/Editor","tinymce/util/I18n","tinymce/FocusManager","tinymce/EditorManager","tinymce/util/XHR","tinymce/util/JSON","tinymce/util/JSONRequest","tinymce/util/JSONP","tinymce/util/LocalStorage","tinymce/Compat","tinymce/ui/Layout","tinymce/ui/AbsoluteLayout","tinymce/ui/Button","tinymce/ui/ButtonGroup","tinymce/ui/Checkbox","tinymce/ui/ComboBox","tinymce/ui/ColorBox","tinymce/ui/PanelButton","tinymce/ui/ColorButton","tinymce/util/Color","tinymce/ui/ColorPicker","tinymce/ui/Path","tinymce/ui/ElementPath","tinymce/ui/FormItem","tinymce/ui/Form","tinymce/ui/FieldSet","tinymce/ui/FilePicker","tinymce/ui/FitLayout","tinymce/ui/FlexLayout","tinymce/ui/FlowLayout","tinymce/ui/FormatControls","tinymce/ui/GridLayout","tinymce/ui/Iframe","tinymce/ui/InfoBox","tinymce/ui/Label","tinymce/ui/Toolbar","tinymce/ui/MenuBar","tinymce/ui/MenuButton","tinymce/ui/MenuItem","tinymce/ui/Throbber","tinymce/ui/Menu","tinymce/ui/ListBox","tinymce/ui/Radio","tinymce/ui/ResizeHandle","tinymce/ui/SelectBox","tinymce/ui/Slider","tinymce/ui/Spacer","tinymce/ui/SplitButton","tinymce/ui/StackLayout","tinymce/ui/TabPanel","tinymce/ui/TextBox"]); +})(window); +!function(e){function t(){function t(e){"remove"===e&&this.each(function(e,t){var n=i(t);n&&n.remove()}),this.find("span.mceEditor,div.mceEditor").each(function(e,t){var n=tinymce.get(t.id.replace(/_parent$/,""));n&&n.remove()})}function r(e){var n,r=this;if(null!=e)t.call(r),r.each(function(t,n){var r;(r=tinymce.get(n.id))&&r.setContent(e)});else if(r.length>0&&(n=tinymce.get(r[0].id)))return n.getContent()}function i(e){var t=null;return e&&e.id&&a.tinymce&&(t=tinymce.get(e.id)),t}function o(e){return!!(e&&e.length&&a.tinymce&&e.is(":tinymce"))}var s={};e.each(["text","html","val"],function(t,a){var l=s[a]=e.fn[a],c="text"===a;e.fn[a]=function(t){var a=this;if(!o(a))return l.apply(a,arguments);if(t!==n)return r.call(a.filter(":tinymce"),t),l.apply(a.not(":tinymce"),arguments),a;var s="",u=arguments;return(c?a:a.eq(0)).each(function(t,n){var r=i(n);s+=r?c?r.getContent().replace(/<(?:"[^"]*"|'[^']*'|[^'">])*>/g,""):r.getContent({save:!0}):l.apply(e(n),u)}),s}}),e.each(["append","prepend"],function(t,r){var a=s[r]=e.fn[r],l="prepend"===r;e.fn[r]=function(e){var t=this;return o(t)?e!==n?("string"==typeof e&&t.filter(":tinymce").each(function(t,n){var r=i(n);r&&r.setContent(l?e+r.getContent():r.getContent()+e)}),a.apply(t.not(":tinymce"),arguments),t):void 0:a.apply(t,arguments)}}),e.each(["remove","replaceWith","replaceAll","empty"],function(n,r){var i=s[r]=e.fn[r];e.fn[r]=function(){return t.call(this,r),i.apply(this,arguments)}}),s.attr=e.fn.attr,e.fn.attr=function(t,a){var l=this,c=arguments;if(!t||"value"!==t||!o(l))return a!==n?s.attr.apply(l,c):s.attr.apply(l,c);if(a!==n)return r.call(l.filter(":tinymce"),a),s.attr.apply(l.not(":tinymce"),c),l;var u=l[0],d=i(u);return d?d.getContent({save:!0}):s.attr.apply(e(u),c)}}var n,r,i,o=[],a=window;e.fn.tinymce=function(n){function s(){var r=[],o=0;i||(t(),i=!0),d.each(function(e,t){var i,a=t.id,s=n.oninit;a||(t.id=a=tinymce.DOM.uniqueId()),tinymce.get(a)||(i=new tinymce.Editor(a,n,tinymce.EditorManager),r.push(i),i.on("init",function(){var e,t=s;d.css("visibility",""),s&&++o==r.length&&("string"==typeof t&&(e=t.indexOf(".")===-1?null:tinymce.resolve(t.replace(/\.\w+$/,"")),t=tinymce.resolve(t)),t.apply(e||tinymce,r))}))}),e.each(r,function(e,t){t.render()})}var l,c,u,d=this,f="";if(!d.length)return d;if(!n)return window.tinymce?tinymce.get(d[0].id):null;if(d.css("visibility","hidden"),a.tinymce||r||!(l=n.script_url))1===r?o.push(s):s();else{r=1,c=l.substring(0,l.lastIndexOf("/")),l.indexOf(".min")!=-1&&(f=".min"),a.tinymce=a.tinyMCEPreInit||{base:c,suffix:f},l.indexOf("gzip")!=-1&&(u=n.language||"en",l=l+(/\?/.test(l)?"&":"?")+"js=true&core=true&suffix="+escape(f)+"&themes="+escape(n.theme||"modern")+"&plugins="+escape(n.plugins||"")+"&languages="+(u||""),a.tinyMCE_GZ||(a.tinyMCE_GZ={start:function(){function t(e){tinymce.ScriptLoader.markDone(tinymce.baseURI.toAbsolute(e))}t("langs/"+u+".js"),t("themes/"+n.theme+"/theme"+f+".js"),t("themes/"+n.theme+"/langs/"+u+".js"),e.each(n.plugins.split(","),function(e,n){n&&(t("plugins/"+n+"/plugin"+f+".js"),t("plugins/"+n+"/langs/"+u+".js"))})},end:function(){}}));var h=document.createElement("script");h.type="text/javascript",h.onload=h.onreadystatechange=function(t){t=t||window.event,2===r||"load"!=t.type&&!/complete|loaded/.test(h.readyState)||(tinymce.dom.Event.domLoaded=1,r=2,n.script_loaded&&n.script_loaded(),s(),e.each(o,function(e,t){t()}))},h.src=l,document.body.appendChild(h)}return d},e.extend(e.expr[":"],{tinymce:function(e){var t;return!!(e.id&&"tinymce"in window&&(t=tinymce.get(e.id),t&&t.editorManager===tinymce))}})}(jQuery); + + + +/*! + * Modernizr v2.7.1 + * www.modernizr.com + * + * Copyright (c) Faruk Ates, Paul Irish, Alex Sexton + * Available under the BSD and MIT licenses: www.modernizr.com/license/ + */ + +/* + * Modernizr tests which native CSS3 and HTML5 features are available in + * the current UA and makes the results available to you in two ways: + * as properties on a global Modernizr object, and as classes on the + * element. This information allows you to progressively enhance + * your pages with a granular level of control over the experience. + * + * Modernizr has an optional (not included) conditional resource loader + * called Modernizr.load(), based on Yepnope.js (yepnopejs.com). + * To get a build that includes Modernizr.load(), as well as choosing + * which tests to include, go to www.modernizr.com/download/ + * + * Authors Faruk Ates, Paul Irish, Alex Sexton + * Contributors Ryan Seddon, Ben Alman + */ + + +window.Modernizr = (function( window, document, undefined ) { + + var version = '2.7.1', + + Modernizr = {}, + + /*>>cssclasses*/ + // option for enabling the HTML classes to be added + enableClasses = true, + /*>>cssclasses*/ + + docElement = document.documentElement, + + /** + * Create our "modernizr" element that we do most feature tests on. + */ + mod = 'modernizr', + modElem = document.createElement(mod), + mStyle = modElem.style, + + /** + * Create the input element for various Web Forms feature tests. + */ + inputElem /*>>inputelem*/ = document.createElement('input') /*>>inputelem*/ , + + /*>>smile*/ + smile = ':)', + /*>>smile*/ + + toString = {}.toString, + + // TODO :: make the prefixes more granular + /*>>prefixes*/ + // List of property values to set for css tests. See ticket #21 + prefixes = ' -webkit- -moz- -o- -ms- '.split(' '), + /*>>prefixes*/ + + /*>>domprefixes*/ + // Following spec is to expose vendor-specific style properties as: + // elem.style.WebkitBorderRadius + // and the following would be incorrect: + // elem.style.webkitBorderRadius + + // Webkit ghosts their properties in lowercase but Opera & Moz do not. + // Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+ + // erik.eae.net/archives/2008/03/10/21.48.10/ + + // More here: github.com/Modernizr/Modernizr/issues/issue/21 + omPrefixes = 'Webkit Moz O ms', + + cssomPrefixes = omPrefixes.split(' '), + + domPrefixes = omPrefixes.toLowerCase().split(' '), + /*>>domprefixes*/ + + /*>>ns*/ + ns = {'svg': 'http://www.w3.org/2000/svg'}, + /*>>ns*/ + + tests = {}, + inputs = {}, + attrs = {}, + + classes = [], + + slice = classes.slice, + + featureName, // used in testing loop + + + /*>>teststyles*/ + // Inject element with style element and some CSS rules + injectElementWithStyles = function( rule, callback, nodes, testnames ) { + + var style, ret, node, docOverflow, + div = document.createElement('div'), + // After page load injecting a fake body doesn't work so check if body exists + body = document.body, + // IE6 and 7 won't return offsetWidth or offsetHeight unless it's in the body element, so we fake it. + fakeBody = body || document.createElement('body'); + + if ( parseInt(nodes, 10) ) { + // In order not to give false positives we create a node for each test + // This also allows the method to scale for unspecified uses + while ( nodes-- ) { + node = document.createElement('div'); + node.id = testnames ? testnames[nodes] : mod + (nodes + 1); + div.appendChild(node); + } + } + + // '].join(''); + div.id = mod; + // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody. + // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270 + (body ? div : fakeBody).innerHTML += style; + fakeBody.appendChild(div); + if ( !body ) { + //avoid crashing IE8, if background image is used + fakeBody.style.background = ''; + //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible + fakeBody.style.overflow = 'hidden'; + docOverflow = docElement.style.overflow; + docElement.style.overflow = 'hidden'; + docElement.appendChild(fakeBody); + } + + ret = callback(div, rule); + // If this is done after page load we don't want to remove the body so check if body exists + if ( !body ) { + fakeBody.parentNode.removeChild(fakeBody); + docElement.style.overflow = docOverflow; + } else { + div.parentNode.removeChild(div); + } + + return !!ret; + + }, + /*>>teststyles*/ + + /*>>mq*/ + // adapted from matchMedia polyfill + // by Scott Jehl and Paul Irish + // gist.github.com/786768 + testMediaQuery = function( mq ) { + + var matchMedia = window.matchMedia || window.msMatchMedia; + if ( matchMedia ) { + return matchMedia(mq).matches; + } + + var bool; + + injectElementWithStyles('@media ' + mq + ' { #' + mod + ' { position: absolute; } }', function( node ) { + bool = (window.getComputedStyle ? + getComputedStyle(node, null) : + node.currentStyle)['position'] == 'absolute'; + }); + + return bool; + + }, + /*>>mq*/ + + + /*>>hasevent*/ + // + // isEventSupported determines if a given element supports the given event + // kangax.github.com/iseventsupported/ + // + // The following results are known incorrects: + // Modernizr.hasEvent("webkitTransitionEnd", elem) // false negative + // Modernizr.hasEvent("textInput") // in Webkit. github.com/Modernizr/Modernizr/issues/333 + // ... + isEventSupported = (function() { + + var TAGNAMES = { + 'select': 'input', 'change': 'input', + 'submit': 'form', 'reset': 'form', + 'error': 'img', 'load': 'img', 'abort': 'img' + }; + + function isEventSupported( eventName, element ) { + + element = element || document.createElement(TAGNAMES[eventName] || 'div'); + eventName = 'on' + eventName; + + // When using `setAttribute`, IE skips "unload", WebKit skips "unload" and "resize", whereas `in` "catches" those + var isSupported = eventName in element; + + if ( !isSupported ) { + // If it has no `setAttribute` (i.e. doesn't implement Node interface), try generic element + if ( !element.setAttribute ) { + element = document.createElement('div'); + } + if ( element.setAttribute && element.removeAttribute ) { + element.setAttribute(eventName, ''); + isSupported = is(element[eventName], 'function'); + + // If property was created, "remove it" (by setting value to `undefined`) + if ( !is(element[eventName], 'undefined') ) { + element[eventName] = undefined; + } + element.removeAttribute(eventName); + } + } + + element = null; + return isSupported; + } + return isEventSupported; + })(), + /*>>hasevent*/ + + // TODO :: Add flag for hasownprop ? didn't last time + + // hasOwnProperty shim by kangax needed for Safari 2.0 support + _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp; + + if ( !is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined') ) { + hasOwnProp = function (object, property) { + return _hasOwnProperty.call(object, property); + }; + } + else { + hasOwnProp = function (object, property) { /* yes, this can give false positives/negatives, but most of the time we don't care about those */ + return ((property in object) && is(object.constructor.prototype[property], 'undefined')); + }; + } + + // Adapted from ES5-shim https://github.com/kriskowal/es5-shim/blob/master/es5-shim.js + // es5.github.com/#x15.3.4.5 + + if (!Function.prototype.bind) { + Function.prototype.bind = function bind(that) { + + var target = this; + + if (typeof target != "function") { + throw new TypeError(); + } + + var args = slice.call(arguments, 1), + bound = function () { + + if (this instanceof bound) { + + var F = function(){}; + F.prototype = target.prototype; + var self = new F(); + + var result = target.apply( + self, + args.concat(slice.call(arguments)) + ); + if (Object(result) === result) { + return result; + } + return self; + + } else { + + return target.apply( + that, + args.concat(slice.call(arguments)) + ); + + } + + }; + + return bound; + }; + } + + /** + * setCss applies given styles to the Modernizr DOM node. + */ + function setCss( str ) { + mStyle.cssText = str; + } + + /** + * setCssAll extrapolates all vendor-specific css strings. + */ + function setCssAll( str1, str2 ) { + return setCss(prefixes.join(str1 + ';') + ( str2 || '' )); + } + + /** + * is returns a boolean for if typeof obj is exactly type. + */ + function is( obj, type ) { + return typeof obj === type; + } + + /** + * contains returns a boolean for if substr is found within str. + */ + function contains( str, substr ) { + return !!~('' + str).indexOf(substr); + } + + /*>>testprop*/ + + // testProps is a generic CSS / DOM property test. + + // In testing support for a given CSS property, it's legit to test: + // `elem.style[styleName] !== undefined` + // If the property is supported it will return an empty string, + // if unsupported it will return undefined. + + // We'll take advantage of this quick test and skip setting a style + // on our modernizr element, but instead just testing undefined vs + // empty string. + + // Because the testing of the CSS property names (with "-", as + // opposed to the camelCase DOM properties) is non-portable and + // non-standard but works in WebKit and IE (but not Gecko or Opera), + // we explicitly reject properties with dashes so that authors + // developing in WebKit or IE first don't end up with + // browser-specific content by accident. + + function testProps( props, prefixed ) { + for ( var i in props ) { + var prop = props[i]; + if ( !contains(prop, "-") && mStyle[prop] !== undefined ) { + return prefixed == 'pfx' ? prop : true; + } + } + return false; + } + /*>>testprop*/ + + // TODO :: add testDOMProps + /** + * testDOMProps is a generic DOM property test; if a browser supports + * a certain property, it won't return undefined for it. + */ + function testDOMProps( props, obj, elem ) { + for ( var i in props ) { + var item = obj[props[i]]; + if ( item !== undefined) { + + // return the property name as a string + if (elem === false) return props[i]; + + // let's bind a function + if (is(item, 'function')){ + // default to autobind unless override + return item.bind(elem || obj); + } + + // return the unbound function or obj or value + return item; + } + } + return false; + } + + /*>>testallprops*/ + /** + * testPropsAll tests a list of DOM properties we want to check against. + * We specify literally ALL possible (known and/or likely) properties on + * the element including the non-vendor prefixed one, for forward- + * compatibility. + */ + function testPropsAll( prop, prefixed, elem ) { + + var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), + props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); + + // did they call .prefixed('boxSizing') or are we just testing a prop? + if(is(prefixed, "string") || is(prefixed, "undefined")) { + return testProps(props, prefixed); + + // otherwise, they called .prefixed('requestAnimationFrame', window[, elem]) + } else { + props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' '); + return testDOMProps(props, prefixed, elem); + } + } + /*>>testallprops*/ + + + /** + * Tests + * ----- + */ + + // The *new* flexbox + // dev.w3.org/csswg/css3-flexbox + + tests['flexbox'] = function() { + return testPropsAll('flexWrap'); + }; + + // The *old* flexbox + // www.w3.org/TR/2009/WD-css3-flexbox-20090723/ + + tests['flexboxlegacy'] = function() { + return testPropsAll('boxDirection'); + }; + + // On the S60 and BB Storm, getContext exists, but always returns undefined + // so we actually have to call getContext() to verify + // github.com/Modernizr/Modernizr/issues/issue/97/ + + tests['canvas'] = function() { + var elem = document.createElement('canvas'); + return !!(elem.getContext && elem.getContext('2d')); + }; + + tests['canvastext'] = function() { + return !!(Modernizr['canvas'] && is(document.createElement('canvas').getContext('2d').fillText, 'function')); + }; + + // webk.it/70117 is tracking a legit WebGL feature detect proposal + + // We do a soft detect which may false positive in order to avoid + // an expensive context creation: bugzil.la/732441 + + tests['webgl'] = function() { + return !!window.WebGLRenderingContext; + }; + + /* + * The Modernizr.touch test only indicates if the browser supports + * touch events, which does not necessarily reflect a touchscreen + * device, as evidenced by tablets running Windows 7 or, alas, + * the Palm Pre / WebOS (touch) phones. + * + * Additionally, Chrome (desktop) used to lie about its support on this, + * but that has since been rectified: crbug.com/36415 + * + * We also test for Firefox 4 Multitouch Support. + * + * For more info, see: modernizr.github.com/Modernizr/touch.html + */ + + tests['touch'] = function() { + var bool; + + if(('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) { + bool = true; + } else { + injectElementWithStyles(['@media (',prefixes.join('touch-enabled),('),mod,')','{#modernizr{top:9px;position:absolute}}'].join(''), function( node ) { + bool = node.offsetTop === 9; + }); + } + + return bool; + }; + + + // geolocation is often considered a trivial feature detect... + // Turns out, it's quite tricky to get right: + // + // Using !!navigator.geolocation does two things we don't want. It: + // 1. Leaks memory in IE9: github.com/Modernizr/Modernizr/issues/513 + // 2. Disables page caching in WebKit: webk.it/43956 + // + // Meanwhile, in Firefox < 8, an about:config setting could expose + // a false positive that would throw an exception: bugzil.la/688158 + + tests['geolocation'] = function() { + return 'geolocation' in navigator; + }; + + + tests['postmessage'] = function() { + return !!window.postMessage; + }; + + + // Chrome incognito mode used to throw an exception when using openDatabase + // It doesn't anymore. + tests['websqldatabase'] = function() { + return !!window.openDatabase; + }; + + // Vendors had inconsistent prefixing with the experimental Indexed DB: + // - Webkit's implementation is accessible through webkitIndexedDB + // - Firefox shipped moz_indexedDB before FF4b9, but since then has been mozIndexedDB + // For speed, we don't test the legacy (and beta-only) indexedDB + tests['indexedDB'] = function() { + return !!testPropsAll("indexedDB", window); + }; + + // documentMode logic from YUI to filter out IE8 Compat Mode + // which false positives. + tests['hashchange'] = function() { + return isEventSupported('hashchange', window) && (document.documentMode === undefined || document.documentMode > 7); + }; + + // Per 1.6: + // This used to be Modernizr.historymanagement but the longer + // name has been deprecated in favor of a shorter and property-matching one. + // The old API is still available in 1.6, but as of 2.0 will throw a warning, + // and in the first release thereafter disappear entirely. + tests['history'] = function() { + return !!(window.history && history.pushState); + }; + + tests['draganddrop'] = function() { + var div = document.createElement('div'); + return ('draggable' in div) || ('ondragstart' in div && 'ondrop' in div); + }; + + // FF3.6 was EOL'ed on 4/24/12, but the ESR version of FF10 + // will be supported until FF19 (2/12/13), at which time, ESR becomes FF17. + // FF10 still uses prefixes, so check for it until then. + // for more ESR info, see: mozilla.org/en-US/firefox/organizations/faq/ + tests['websockets'] = function() { + return 'WebSocket' in window || 'MozWebSocket' in window; + }; + + + // css-tricks.com/rgba-browser-support/ + tests['rgba'] = function() { + // Set an rgba() color and check the returned value + + setCss('background-color:rgba(150,255,150,.5)'); + + return contains(mStyle.backgroundColor, 'rgba'); + }; + + tests['hsla'] = function() { + // Same as rgba(), in fact, browsers re-map hsla() to rgba() internally, + // except IE9 who retains it as hsla + + setCss('background-color:hsla(120,40%,100%,.5)'); + + return contains(mStyle.backgroundColor, 'rgba') || contains(mStyle.backgroundColor, 'hsla'); + }; + + tests['multiplebgs'] = function() { + // Setting multiple images AND a color on the background shorthand property + // and then querying the style.background property value for the number of + // occurrences of "url(" is a reliable method for detecting ACTUAL support for this! + + setCss('background:url(https://),url(https://),red url(https://)'); + + // If the UA supports multiple backgrounds, there should be three occurrences + // of the string "url(" in the return value for elemStyle.background + + return (/(url\s*\(.*?){3}/).test(mStyle.background); + }; + + + + // this will false positive in Opera Mini + // github.com/Modernizr/Modernizr/issues/396 + + tests['backgroundsize'] = function() { + return testPropsAll('backgroundSize'); + }; + + tests['borderimage'] = function() { + return testPropsAll('borderImage'); + }; + + + // Super comprehensive table about all the unique implementations of + // border-radius: muddledramblings.com/table-of-css3-border-radius-compliance + + tests['borderradius'] = function() { + return testPropsAll('borderRadius'); + }; + + // WebOS unfortunately false positives on this test. + tests['boxshadow'] = function() { + return testPropsAll('boxShadow'); + }; + + // FF3.0 will false positive on this test + tests['textshadow'] = function() { + return document.createElement('div').style.textShadow === ''; + }; + + + tests['opacity'] = function() { + // Browsers that actually have CSS Opacity implemented have done so + // according to spec, which means their return values are within the + // range of [0.0,1.0] - including the leading zero. + + setCssAll('opacity:.55'); + + // The non-literal . in this regex is intentional: + // German Chrome returns this value as 0,55 + // github.com/Modernizr/Modernizr/issues/#issue/59/comment/516632 + return (/^0.55$/).test(mStyle.opacity); + }; + + + // Note, Android < 4 will pass this test, but can only animate + // a single property at a time + // daneden.me/2011/12/putting-up-with-androids-bullshit/ + tests['cssanimations'] = function() { + return testPropsAll('animationName'); + }; + + + tests['csscolumns'] = function() { + return testPropsAll('columnCount'); + }; + + + tests['cssgradients'] = function() { + /** + * For CSS Gradients syntax, please see: + * webkit.org/blog/175/introducing-css-gradients/ + * developer.mozilla.org/en/CSS/-moz-linear-gradient + * developer.mozilla.org/en/CSS/-moz-radial-gradient + * dev.w3.org/csswg/css3-images/#gradients- + */ + + var str1 = 'background-image:', + str2 = 'gradient(linear,left top,right bottom,from(#9f9),to(white));', + str3 = 'linear-gradient(left top,#9f9, white);'; + + setCss( + // legacy webkit syntax (FIXME: remove when syntax not in use anymore) + (str1 + '-webkit- '.split(' ').join(str2 + str1) + + // standard syntax // trailing 'background-image:' + prefixes.join(str3 + str1)).slice(0, -str1.length) + ); + + return contains(mStyle.backgroundImage, 'gradient'); + }; + + + tests['cssreflections'] = function() { + return testPropsAll('boxReflect'); + }; + + + tests['csstransforms'] = function() { + return !!testPropsAll('transform'); + }; + + + tests['csstransforms3d'] = function() { + + var ret = !!testPropsAll('perspective'); + + // Webkit's 3D transforms are passed off to the browser's own graphics renderer. + // It works fine in Safari on Leopard and Snow Leopard, but not in Chrome in + // some conditions. As a result, Webkit typically recognizes the syntax but + // will sometimes throw a false positive, thus we must do a more thorough check: + if ( ret && 'webkitPerspective' in docElement.style ) { + + // Webkit allows this media query to succeed only if the feature is enabled. + // `@media (transform-3d),(-webkit-transform-3d){ ... }` + injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function( node, rule ) { + ret = node.offsetLeft === 9 && node.offsetHeight === 3; + }); + } + return ret; + }; + + + tests['csstransitions'] = function() { + return testPropsAll('transition'); + }; + + + /*>>fontface*/ + // @font-face detection routine by Diego Perini + // javascript.nwbox.com/CSSSupport/ + + // false positives: + // WebOS github.com/Modernizr/Modernizr/issues/342 + // WP7 github.com/Modernizr/Modernizr/issues/538 + tests['fontface'] = function() { + var bool; + + injectElementWithStyles('@font-face {font-family:"font";src:url("https://")}', function( node, rule ) { + var style = document.getElementById('smodernizr'), + sheet = style.sheet || style.styleSheet, + cssText = sheet ? (sheet.cssRules && sheet.cssRules[0] ? sheet.cssRules[0].cssText : sheet.cssText || '') : ''; + + bool = /src/i.test(cssText) && cssText.indexOf(rule.split(' ')[0]) === 0; + }); + + return bool; + }; + /*>>fontface*/ + + // CSS generated content detection + tests['generatedcontent'] = function() { + var bool; + + injectElementWithStyles(['#',mod,'{font:0/0 a}#',mod,':after{content:"',smile,'";visibility:hidden;font:3px/1 a}'].join(''), function( node ) { + bool = node.offsetHeight >= 3; + }); + + return bool; + }; + + + + // These tests evaluate support of the video/audio elements, as well as + // testing what types of content they support. + // + // We're using the Boolean constructor here, so that we can extend the value + // e.g. Modernizr.video // true + // Modernizr.video.ogg // 'probably' + // + // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845 + // thx to NielsLeenheer and zcorpan + + // Note: in some older browsers, "no" was a return value instead of empty string. + // It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2 + // It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5 + + tests['video'] = function() { + var elem = document.createElement('video'), + bool = false; + + // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224 + try { + if ( bool = !!elem.canPlayType ) { + bool = new Boolean(bool); + bool.ogg = elem.canPlayType('video/ogg; codecs="theora"') .replace(/^no$/,''); + + // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546 + bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"') .replace(/^no$/,''); + + bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,''); + } + + } catch(e) { } + + return bool; + }; + + tests['audio'] = function() { + var elem = document.createElement('audio'), + bool = false; + + try { + if ( bool = !!elem.canPlayType ) { + bool = new Boolean(bool); + bool.ogg = elem.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,''); + bool.mp3 = elem.canPlayType('audio/mpeg;') .replace(/^no$/,''); + + // Mimetypes accepted: + // developer.mozilla.org/En/Media_formats_supported_by_the_audio_and_video_elements + // bit.ly/iphoneoscodecs + bool.wav = elem.canPlayType('audio/wav; codecs="1"') .replace(/^no$/,''); + bool.m4a = ( elem.canPlayType('audio/x-m4a;') || + elem.canPlayType('audio/aac;')) .replace(/^no$/,''); + } + } catch(e) { } + + return bool; + }; + + + // In FF4, if disabled, window.localStorage should === null. + + // Normally, we could not test that directly and need to do a + // `('localStorage' in window) && ` test first because otherwise Firefox will + // throw bugzil.la/365772 if cookies are disabled + + // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem + // will throw the exception: + // QUOTA_EXCEEDED_ERRROR DOM Exception 22. + // Peculiarly, getItem and removeItem calls do not throw. + + // Because we are forced to try/catch this, we'll go aggressive. + + // Just FWIW: IE8 Compat mode supports these features completely: + // www.quirksmode.org/dom/html5.html + // But IE8 doesn't support either with local files + + tests['localstorage'] = function() { + try { + localStorage.setItem(mod, mod); + localStorage.removeItem(mod); + return true; + } catch(e) { + return false; + } + }; + + tests['sessionstorage'] = function() { + try { + sessionStorage.setItem(mod, mod); + sessionStorage.removeItem(mod); + return true; + } catch(e) { + return false; + } + }; + + + tests['webworkers'] = function() { + return !!window.Worker; + }; + + + tests['applicationcache'] = function() { + return !!window.applicationCache; + }; + + + // Thanks to Erik Dahlstrom + tests['svg'] = function() { + return !!document.createElementNS && !!document.createElementNS(ns.svg, 'svg').createSVGRect; + }; + + // specifically for SVG inline in HTML, not within XHTML + // test page: paulirish.com/demo/inline-svg + tests['inlinesvg'] = function() { + var div = document.createElement('div'); + div.innerHTML = ''; + return (div.firstChild && div.firstChild.namespaceURI) == ns.svg; + }; + + // SVG SMIL animation + tests['smil'] = function() { + return !!document.createElementNS && /SVGAnimate/.test(toString.call(document.createElementNS(ns.svg, 'animate'))); + }; + + // This test is only for clip paths in SVG proper, not clip paths on HTML content + // demo: srufaculty.sru.edu/david.dailey/svg/newstuff/clipPath4.svg + + // However read the comments to dig into applying SVG clippaths to HTML content here: + // github.com/Modernizr/Modernizr/issues/213#issuecomment-1149491 + tests['svgclippaths'] = function() { + return !!document.createElementNS && /SVGClipPath/.test(toString.call(document.createElementNS(ns.svg, 'clipPath'))); + }; + + /*>>webforms*/ + // input features and input types go directly onto the ret object, bypassing the tests loop. + // Hold this guy to execute in a moment. + function webforms() { + /*>>input*/ + // Run through HTML5's new input attributes to see if the UA understands any. + // We're using f which is the element created early on + // Mike Taylr has created a comprehensive resource for testing these attributes + // when applied to all input types: + // miketaylr.com/code/input-type-attr.html + // spec: www.whatwg.org/specs/web-apps/current-work/multipage/the-input-element.html#input-type-attr-summary + + // Only input placeholder is tested while textarea's placeholder is not. + // Currently Safari 4 and Opera 11 have support only for the input placeholder + // Both tests are available in feature-detects/forms-placeholder.js + Modernizr['input'] = (function( props ) { + for ( var i = 0, len = props.length; i < len; i++ ) { + attrs[ props[i] ] = !!(props[i] in inputElem); + } + if (attrs.list){ + // safari false positive's on datalist: webk.it/74252 + // see also github.com/Modernizr/Modernizr/issues/146 + attrs.list = !!(document.createElement('datalist') && window.HTMLDataListElement); + } + return attrs; + })('autocomplete autofocus list placeholder max min multiple pattern required step'.split(' ')); + /*>>input*/ + + /*>>inputtypes*/ + // Run through HTML5's new input types to see if the UA understands any. + // This is put behind the tests runloop because it doesn't return a + // true/false like all the other tests; instead, it returns an object + // containing each input type with its corresponding true/false value + + // Big thanks to @miketaylr for the html5 forms expertise. miketaylr.com/ + Modernizr['inputtypes'] = (function(props) { + + for ( var i = 0, bool, inputElemType, defaultView, len = props.length; i < len; i++ ) { + + inputElem.setAttribute('type', inputElemType = props[i]); + bool = inputElem.type !== 'text'; + + // We first check to see if the type we give it sticks.. + // If the type does, we feed it a textual value, which shouldn't be valid. + // If the value doesn't stick, we know there's input sanitization which infers a custom UI + if ( bool ) { + + inputElem.value = smile; + inputElem.style.cssText = 'position:absolute;visibility:hidden;'; + + if ( /^range$/.test(inputElemType) && inputElem.style.WebkitAppearance !== undefined ) { + + docElement.appendChild(inputElem); + defaultView = document.defaultView; + + // Safari 2-4 allows the smiley as a value, despite making a slider + bool = defaultView.getComputedStyle && + defaultView.getComputedStyle(inputElem, null).WebkitAppearance !== 'textfield' && + // Mobile android web browser has false positive, so must + // check the height to see if the widget is actually there. + (inputElem.offsetHeight !== 0); + + docElement.removeChild(inputElem); + + } else if ( /^(search|tel)$/.test(inputElemType) ){ + // Spec doesn't define any special parsing or detectable UI + // behaviors so we pass these through as true + + // Interestingly, opera fails the earlier test, so it doesn't + // even make it here. + + } else if ( /^(url|email)$/.test(inputElemType) ) { + // Real url and email support comes with prebaked validation. + bool = inputElem.checkValidity && inputElem.checkValidity() === false; + + } else { + // If the upgraded input compontent rejects the :) text, we got a winner + bool = inputElem.value != smile; + } + } + + inputs[ props[i] ] = !!bool; + } + return inputs; + })('search tel url email datetime date month week time datetime-local number range color'.split(' ')); + /*>>inputtypes*/ + } + /*>>webforms*/ + + + // End of test definitions + // ----------------------- + + + + // Run through all tests and detect their support in the current UA. + // todo: hypothetically we could be doing an array of tests and use a basic loop here. + for ( var feature in tests ) { + if ( hasOwnProp(tests, feature) ) { + // run the test, throw the return value into the Modernizr, + // then based on that boolean, define an appropriate className + // and push it into an array of classes we'll join later. + featureName = feature.toLowerCase(); + Modernizr[featureName] = tests[feature](); + + classes.push((Modernizr[featureName] ? '' : 'no-') + featureName); + } + } + + /*>>webforms*/ + // input tests need to run. + Modernizr.input || webforms(); + /*>>webforms*/ + + + /** + * addTest allows the user to define their own feature tests + * the result will be added onto the Modernizr object, + * as well as an appropriate className set on the html element + * + * @param feature - String naming the feature + * @param test - Function returning true if feature is supported, false if not + */ + Modernizr.addTest = function ( feature, test ) { + if ( typeof feature == 'object' ) { + for ( var key in feature ) { + if ( hasOwnProp( feature, key ) ) { + Modernizr.addTest( key, feature[ key ] ); + } + } + } else { + + feature = feature.toLowerCase(); + + if ( Modernizr[feature] !== undefined ) { + // we're going to quit if you're trying to overwrite an existing test + // if we were to allow it, we'd do this: + // var re = new RegExp("\\b(no-)?" + feature + "\\b"); + // docElement.className = docElement.className.replace( re, '' ); + // but, no rly, stuff 'em. + return Modernizr; + } + + test = typeof test == 'function' ? test() : test; + + if (typeof enableClasses !== "undefined" && enableClasses) { + docElement.className += ' ' + (test ? '' : 'no-') + feature; + } + Modernizr[feature] = test; + + } + + return Modernizr; // allow chaining. + }; + + + // Reset modElem.cssText to nothing to reduce memory footprint. + setCss(''); + modElem = inputElem = null; + + /*>>shiv*/ + /** + * @preserve HTML5 Shiv prev3.7.1 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed + */ + ;(function(window, document) { + /*jshint evil:true */ + /** version */ + var version = '3.7.0'; + + /** Preset options */ + var options = window.html5 || {}; + + /** Used to skip problem elements */ + var reSkip = /^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i; + + /** Not all elements can be cloned in IE **/ + var saveClones = /^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i; + + /** Detect whether the browser supports default html5 styles */ + var supportsHtml5Styles; + + /** Name of the expando, to work with multiple documents or to re-shiv one document */ + var expando = '_html5shiv'; + + /** The id for the the documents expando */ + var expanID = 0; + + /** Cached data for each document */ + var expandoData = {}; + + /** Detect whether the browser supports unknown elements */ + var supportsUnknownElements; + + (function() { + try { + var a = document.createElement('a'); + a.innerHTML = ''; + //if the hidden property is implemented we can assume, that the browser supports basic HTML5 Styles + supportsHtml5Styles = ('hidden' in a); + + supportsUnknownElements = a.childNodes.length == 1 || (function() { + // assign a false positive if unable to shiv + (document.createElement)('a'); + var frag = document.createDocumentFragment(); + return ( + typeof frag.cloneNode == 'undefined' || + typeof frag.createDocumentFragment == 'undefined' || + typeof frag.createElement == 'undefined' + ); + }()); + } catch(e) { + // assign a false positive if detection fails => unable to shiv + supportsHtml5Styles = true; + supportsUnknownElements = true; + } + + }()); + + /*--------------------------------------------------------------------------*/ + + /** + * Creates a style sheet with the given CSS text and adds it to the document. + * @private + * @param {Document} ownerDocument The document. + * @param {String} cssText The CSS text. + * @returns {StyleSheet} The style element. + */ + function addStyleSheet(ownerDocument, cssText) { + var p = ownerDocument.createElement('p'), + parent = ownerDocument.getElementsByTagName('head')[0] || ownerDocument.documentElement; + + p.innerHTML = 'x'; + return parent.insertBefore(p.lastChild, parent.firstChild); + } + + /** + * Returns the value of `html5.elements` as an array. + * @private + * @returns {Array} An array of shived element node names. + */ + function getElements() { + var elements = html5.elements; + return typeof elements == 'string' ? elements.split(' ') : elements; + } + + /** + * Returns the data associated to the given document + * @private + * @param {Document} ownerDocument The document. + * @returns {Object} An object of data. + */ + function getExpandoData(ownerDocument) { + var data = expandoData[ownerDocument[expando]]; + if (!data) { + data = {}; + expanID++; + ownerDocument[expando] = expanID; + expandoData[expanID] = data; + } + return data; + } + + /** + * returns a shived element for the given nodeName and document + * @memberOf html5 + * @param {String} nodeName name of the element + * @param {Document} ownerDocument The context document. + * @returns {Object} The shived element. + */ + function createElement(nodeName, ownerDocument, data){ + if (!ownerDocument) { + ownerDocument = document; + } + if(supportsUnknownElements){ + return ownerDocument.createElement(nodeName); + } + if (!data) { + data = getExpandoData(ownerDocument); + } + var node; + + if (data.cache[nodeName]) { + node = data.cache[nodeName].cloneNode(); + } else if (saveClones.test(nodeName)) { + node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); + } else { + node = data.createElem(nodeName); + } + + // Avoid adding some elements to fragments in IE < 9 because + // * Attributes like `name` or `type` cannot be set/changed once an element + // is inserted into a document/fragment + // * Link elements with `src` attributes that are inaccessible, as with + // a 403 response, will cause the tab/window to crash + // * Script elements appended to fragments will execute when their `src` + // or `text` property is set + return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; + } + + /** + * returns a shived DocumentFragment for the given document + * @memberOf html5 + * @param {Document} ownerDocument The context document. + * @returns {Object} The shived DocumentFragment. + */ + function createDocumentFragment(ownerDocument, data){ + if (!ownerDocument) { + ownerDocument = document; + } + if(supportsUnknownElements){ + return ownerDocument.createDocumentFragment(); + } + data = data || getExpandoData(ownerDocument); + var clone = data.frag.cloneNode(), + i = 0, + elems = getElements(), + l = elems.length; + for(;i>shiv*/ + + // Assign private properties to the return object with prefix + Modernizr._version = version; + + // expose these for the plugin API. Look in the source for how to join() them against your input + /*>>prefixes*/ + Modernizr._prefixes = prefixes; + /*>>prefixes*/ + /*>>domprefixes*/ + Modernizr._domPrefixes = domPrefixes; + Modernizr._cssomPrefixes = cssomPrefixes; + /*>>domprefixes*/ + + /*>>mq*/ + // Modernizr.mq tests a given media query, live against the current state of the window + // A few important notes: + // * If a browser does not support media queries at all (eg. oldIE) the mq() will always return false + // * A max-width or orientation query will be evaluated against the current state, which may change later. + // * You must specify values. Eg. If you are testing support for the min-width media query use: + // Modernizr.mq('(min-width:0)') + // usage: + // Modernizr.mq('only screen and (max-width:768)') + Modernizr.mq = testMediaQuery; + /*>>mq*/ + + /*>>hasevent*/ + // Modernizr.hasEvent() detects support for a given event, with an optional element to test on + // Modernizr.hasEvent('gesturestart', elem) + Modernizr.hasEvent = isEventSupported; + /*>>hasevent*/ + + /*>>testprop*/ + // Modernizr.testProp() investigates whether a given style property is recognized + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testProp('pointerEvents') + Modernizr.testProp = function(prop){ + return testProps([prop]); + }; + /*>>testprop*/ + + /*>>testallprops*/ + // Modernizr.testAllProps() investigates whether a given style property, + // or any of its vendor-prefixed variants, is recognized + // Note that the property names must be provided in the camelCase variant. + // Modernizr.testAllProps('boxSizing') + Modernizr.testAllProps = testPropsAll; + /*>>testallprops*/ + + + /*>>teststyles*/ + // Modernizr.testStyles() allows you to add custom styles to the document and test an element afterwards + // Modernizr.testStyles('#modernizr { position:absolute }', function(elem, rule){ ... }) + Modernizr.testStyles = injectElementWithStyles; + /*>>teststyles*/ + + + /*>>prefixed*/ + // Modernizr.prefixed() returns the prefixed or nonprefixed property name variant of your input + // Modernizr.prefixed('boxSizing') // 'MozBoxSizing' + + // Properties must be passed as dom-style camelcase, rather than `box-sizing` hypentated style. + // Return values will also be the camelCase variant, if you need to translate that to hypenated style use: + // + // str.replace(/([A-Z])/g, function(str,m1){ return '-' + m1.toLowerCase(); }).replace(/^ms-/,'-ms-'); + + // If you're trying to ascertain which transition end event to bind to, you might do something like... + // + // var transEndEventNames = { + // 'WebkitTransition' : 'webkitTransitionEnd', + // 'MozTransition' : 'transitionend', + // 'OTransition' : 'oTransitionEnd', + // 'msTransition' : 'MSTransitionEnd', + // 'transition' : 'transitionend' + // }, + // transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ]; + + Modernizr.prefixed = function(prop, obj, elem){ + if(!obj) { + return testPropsAll(prop, 'pfx'); + } else { + // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame' + return testPropsAll(prop, obj, elem); + } + }; + /*>>prefixed*/ + + + /*>>cssclasses*/ + // Remove "no-js" class from element, if it exists: + docElement.className = docElement.className.replace(/(^|\s)no-js(\s|$)/, '$1$2') + + + // Add the new classes to the element. + (enableClasses ? ' js ' + classes.join(' ') : ''); + /*>>cssclasses*/ + + return Modernizr; + +})(this, this.document); +/* + Leaflet, a JavaScript library for mobile-friendly interactive maps. http://leafletjs.com + (c) 2010-2013, Vladimir Agafonkin + (c) 2010-2011, CloudMade +*/ + + + + + +(function (window, document, undefined) { +var oldL = window.L, + L = {}; + +L.version = '0.7.7'; + +// define Leaflet for Node module pattern loaders, including Browserify +if (typeof module === 'object' && typeof module.exports === 'object') { + module.exports = L; + +// define Leaflet as an AMD module +} else if (typeof define === 'function' && define.amd) { + define(L); +} + +// define Leaflet as a global L variable, saving the original L to restore later if needed + +L.noConflict = function () { + window.L = oldL; + return this; +}; + +window.L = L; + + +/* + * L.Util contains various utility functions used throughout Leaflet code. + */ + +L.Util = { + extend: function (dest) { // (Object[, Object, ...]) -> + var sources = Array.prototype.slice.call(arguments, 1), + i, j, len, src; + + for (j = 0, len = sources.length; j < len; j++) { + src = sources[j] || {}; + for (i in src) { + if (src.hasOwnProperty(i)) { + dest[i] = src[i]; + } + } + } + return dest; + }, + + bind: function (fn, obj) { // (Function, Object) -> Function + var args = arguments.length > 2 ? Array.prototype.slice.call(arguments, 2) : null; + return function () { + return fn.apply(obj, args || arguments); + }; + }, + + stamp: (function () { + var lastId = 0, + key = '_leaflet_id'; + return function (obj) { + obj[key] = obj[key] || ++lastId; + return obj[key]; + }; + }()), + + invokeEach: function (obj, method, context) { + var i, args; + + if (typeof obj === 'object') { + args = Array.prototype.slice.call(arguments, 3); + + for (i in obj) { + method.apply(context, [i, obj[i]].concat(args)); + } + return true; + } + + return false; + }, + + limitExecByInterval: function (fn, time, context) { + var lock, execOnUnlock; + + return function wrapperFn() { + var args = arguments; + + if (lock) { + execOnUnlock = true; + return; + } + + lock = true; + + setTimeout(function () { + lock = false; + + if (execOnUnlock) { + wrapperFn.apply(context, args); + execOnUnlock = false; + } + }, time); + + fn.apply(context, args); + }; + }, + + falseFn: function () { + return false; + }, + + formatNum: function (num, digits) { + var pow = Math.pow(10, digits || 5); + return Math.round(num * pow) / pow; + }, + + trim: function (str) { + return str.trim ? str.trim() : str.replace(/^\s+|\s+$/g, ''); + }, + + splitWords: function (str) { + return L.Util.trim(str).split(/\s+/); + }, + + setOptions: function (obj, options) { + obj.options = L.extend({}, obj.options, options); + return obj.options; + }, + + getParamString: function (obj, existingUrl, uppercase) { + var params = []; + for (var i in obj) { + params.push(encodeURIComponent(uppercase ? i.toUpperCase() : i) + '=' + encodeURIComponent(obj[i])); + } + return ((!existingUrl || existingUrl.indexOf('?') === -1) ? '?' : '&') + params.join('&'); + }, + template: function (str, data) { + return str.replace(/\{ *([\w_]+) *\}/g, function (str, key) { + var value = data[key]; + if (value === undefined) { + throw new Error('No value provided for variable ' + str); + } else if (typeof value === 'function') { + value = value(data); + } + return value; + }); + }, + + isArray: Array.isArray || function (obj) { + return (Object.prototype.toString.call(obj) === '[object Array]'); + }, + + emptyImageUrl: 'data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=' +}; + +(function () { + + // inspired by http://paulirish.com/2011/requestanimationframe-for-smart-animating/ + + function getPrefixed(name) { + var i, fn, + prefixes = ['webkit', 'moz', 'o', 'ms']; + + for (i = 0; i < prefixes.length && !fn; i++) { + fn = window[prefixes[i] + name]; + } + + return fn; + } + + var lastTime = 0; + + function timeoutDefer(fn) { + var time = +new Date(), + timeToCall = Math.max(0, 16 - (time - lastTime)); + + lastTime = time + timeToCall; + return window.setTimeout(fn, timeToCall); + } + + var requestFn = window.requestAnimationFrame || + getPrefixed('RequestAnimationFrame') || timeoutDefer; + + var cancelFn = window.cancelAnimationFrame || + getPrefixed('CancelAnimationFrame') || + getPrefixed('CancelRequestAnimationFrame') || + function (id) { window.clearTimeout(id); }; + + + L.Util.requestAnimFrame = function (fn, context, immediate, element) { + fn = L.bind(fn, context); + + if (immediate && requestFn === timeoutDefer) { + fn(); + } else { + return requestFn.call(window, fn, element); + } + }; + + L.Util.cancelAnimFrame = function (id) { + if (id) { + cancelFn.call(window, id); + } + }; + +}()); + +// shortcuts for most used utility functions +L.extend = L.Util.extend; +L.bind = L.Util.bind; +L.stamp = L.Util.stamp; +L.setOptions = L.Util.setOptions; + + +/* + * L.Class powers the OOP facilities of the library. + * Thanks to John Resig and Dean Edwards for inspiration! + */ + +L.Class = function () {}; + +L.Class.extend = function (props) { + + // extended class with the new prototype + var NewClass = function () { + + // call the constructor + if (this.initialize) { + this.initialize.apply(this, arguments); + } + + // call all constructor hooks + if (this._initHooks) { + this.callInitHooks(); + } + }; + + // instantiate class without calling constructor + var F = function () {}; + F.prototype = this.prototype; + + var proto = new F(); + proto.constructor = NewClass; + + NewClass.prototype = proto; + + //inherit parent's statics + for (var i in this) { + if (this.hasOwnProperty(i) && i !== 'prototype') { + NewClass[i] = this[i]; + } + } + + // mix static properties into the class + if (props.statics) { + L.extend(NewClass, props.statics); + delete props.statics; + } + + // mix includes into the prototype + if (props.includes) { + L.Util.extend.apply(null, [proto].concat(props.includes)); + delete props.includes; + } + + // merge options + if (props.options && proto.options) { + props.options = L.extend({}, proto.options, props.options); + } + + // mix given properties into the prototype + L.extend(proto, props); + + proto._initHooks = []; + + var parent = this; + // jshint camelcase: false + NewClass.__super__ = parent.prototype; + + // add method for calling all hooks + proto.callInitHooks = function () { + + if (this._initHooksCalled) { return; } + + if (parent.prototype.callInitHooks) { + parent.prototype.callInitHooks.call(this); + } + + this._initHooksCalled = true; + + for (var i = 0, len = proto._initHooks.length; i < len; i++) { + proto._initHooks[i].call(this); + } + }; + + return NewClass; +}; + + +// method for adding properties to prototype +L.Class.include = function (props) { + L.extend(this.prototype, props); +}; + +// merge new default options to the Class +L.Class.mergeOptions = function (options) { + L.extend(this.prototype.options, options); +}; + +// add a constructor hook +L.Class.addInitHook = function (fn) { // (Function) || (String, args...) + var args = Array.prototype.slice.call(arguments, 1); + + var init = typeof fn === 'function' ? fn : function () { + this[fn].apply(this, args); + }; + + this.prototype._initHooks = this.prototype._initHooks || []; + this.prototype._initHooks.push(init); +}; + + +/* + * L.Mixin.Events is used to add custom events functionality to Leaflet classes. + */ + +var eventsKey = '_leaflet_events'; + +L.Mixin = {}; + +L.Mixin.Events = { + + addEventListener: function (types, fn, context) { // (String, Function[, Object]) or (Object[, Object]) + + // types can be a map of types/handlers + if (L.Util.invokeEach(types, this.addEventListener, this, fn, context)) { return this; } + + var events = this[eventsKey] = this[eventsKey] || {}, + contextId = context && context !== this && L.stamp(context), + i, len, event, type, indexKey, indexLenKey, typeIndex; + + // types can be a string of space-separated words + types = L.Util.splitWords(types); + + for (i = 0, len = types.length; i < len; i++) { + event = { + action: fn, + context: context || this + }; + type = types[i]; + + if (contextId) { + // store listeners of a particular context in a separate hash (if it has an id) + // gives a major performance boost when removing thousands of map layers + + indexKey = type + '_idx'; + indexLenKey = indexKey + '_len'; + + typeIndex = events[indexKey] = events[indexKey] || {}; + + if (!typeIndex[contextId]) { + typeIndex[contextId] = []; + + // keep track of the number of keys in the index to quickly check if it's empty + events[indexLenKey] = (events[indexLenKey] || 0) + 1; + } + + typeIndex[contextId].push(event); + + + } else { + events[type] = events[type] || []; + events[type].push(event); + } + } + + return this; + }, + + hasEventListeners: function (type) { // (String) -> Boolean + var events = this[eventsKey]; + return !!events && ((type in events && events[type].length > 0) || + (type + '_idx' in events && events[type + '_idx_len'] > 0)); + }, + + removeEventListener: function (types, fn, context) { // ([String, Function, Object]) or (Object[, Object]) + + if (!this[eventsKey]) { + return this; + } + + if (!types) { + return this.clearAllEventListeners(); + } + + if (L.Util.invokeEach(types, this.removeEventListener, this, fn, context)) { return this; } + + var events = this[eventsKey], + contextId = context && context !== this && L.stamp(context), + i, len, type, listeners, j, indexKey, indexLenKey, typeIndex, removed; + + types = L.Util.splitWords(types); + + for (i = 0, len = types.length; i < len; i++) { + type = types[i]; + indexKey = type + '_idx'; + indexLenKey = indexKey + '_len'; + + typeIndex = events[indexKey]; + + if (!fn) { + // clear all listeners for a type if function isn't specified + delete events[type]; + delete events[indexKey]; + delete events[indexLenKey]; + + } else { + listeners = contextId && typeIndex ? typeIndex[contextId] : events[type]; + + if (listeners) { + for (j = listeners.length - 1; j >= 0; j--) { + if ((listeners[j].action === fn) && (!context || (listeners[j].context === context))) { + removed = listeners.splice(j, 1); + // set the old action to a no-op, because it is possible + // that the listener is being iterated over as part of a dispatch + removed[0].action = L.Util.falseFn; + } + } + + if (context && typeIndex && (listeners.length === 0)) { + delete typeIndex[contextId]; + events[indexLenKey]--; + } + } + } + } + + return this; + }, + + clearAllEventListeners: function () { + delete this[eventsKey]; + return this; + }, + + fireEvent: function (type, data) { // (String[, Object]) + if (!this.hasEventListeners(type)) { + return this; + } + + var event = L.Util.extend({}, data, { type: type, target: this }); + + var events = this[eventsKey], + listeners, i, len, typeIndex, contextId; + + if (events[type]) { + // make sure adding/removing listeners inside other listeners won't cause infinite loop + listeners = events[type].slice(); + + for (i = 0, len = listeners.length; i < len; i++) { + listeners[i].action.call(listeners[i].context, event); + } + } + + // fire event for the context-indexed listeners as well + typeIndex = events[type + '_idx']; + + for (contextId in typeIndex) { + listeners = typeIndex[contextId].slice(); + + if (listeners) { + for (i = 0, len = listeners.length; i < len; i++) { + listeners[i].action.call(listeners[i].context, event); + } + } + } + + return this; + }, + + addOneTimeEventListener: function (types, fn, context) { + + if (L.Util.invokeEach(types, this.addOneTimeEventListener, this, fn, context)) { return this; } + + var handler = L.bind(function () { + this + .removeEventListener(types, fn, context) + .removeEventListener(types, handler, context); + }, this); + + return this + .addEventListener(types, fn, context) + .addEventListener(types, handler, context); + } +}; + +L.Mixin.Events.on = L.Mixin.Events.addEventListener; +L.Mixin.Events.off = L.Mixin.Events.removeEventListener; +L.Mixin.Events.once = L.Mixin.Events.addOneTimeEventListener; +L.Mixin.Events.fire = L.Mixin.Events.fireEvent; + + +/* + * L.Browser handles different browser and feature detections for internal Leaflet use. + */ + +(function () { + + var ie = 'ActiveXObject' in window, + ielt9 = ie && !document.addEventListener, + + // terrible browser detection to work around Safari / iOS / Android browser bugs + ua = navigator.userAgent.toLowerCase(), + webkit = ua.indexOf('webkit') !== -1, + chrome = ua.indexOf('chrome') !== -1, + phantomjs = ua.indexOf('phantom') !== -1, + android = ua.indexOf('android') !== -1, + android23 = ua.search('android [23]') !== -1, + gecko = ua.indexOf('gecko') !== -1, + + mobile = typeof orientation !== undefined + '', + msPointer = !window.PointerEvent && window.MSPointerEvent, + pointer = (window.PointerEvent && window.navigator.pointerEnabled) || + msPointer, + retina = ('devicePixelRatio' in window && window.devicePixelRatio > 1) || + ('matchMedia' in window && window.matchMedia('(min-resolution:144dpi)') && + window.matchMedia('(min-resolution:144dpi)').matches), + + doc = document.documentElement, + ie3d = ie && ('transition' in doc.style), + webkit3d = ('WebKitCSSMatrix' in window) && ('m11' in new window.WebKitCSSMatrix()) && !android23, + gecko3d = 'MozPerspective' in doc.style, + opera3d = 'OTransition' in doc.style, + any3d = !window.L_DISABLE_3D && (ie3d || webkit3d || gecko3d || opera3d) && !phantomjs; + + var touch = !window.L_NO_TOUCH && !phantomjs && (pointer || 'ontouchstart' in window || + (window.DocumentTouch && document instanceof window.DocumentTouch)); + + L.Browser = { + ie: ie, + ielt9: ielt9, + webkit: webkit, + gecko: gecko && !webkit && !window.opera && !ie, + + android: android, + android23: android23, + + chrome: chrome, + + ie3d: ie3d, + webkit3d: webkit3d, + gecko3d: gecko3d, + opera3d: opera3d, + any3d: any3d, + + mobile: mobile, + mobileWebkit: mobile && webkit, + mobileWebkit3d: mobile && webkit3d, + mobileOpera: mobile && window.opera, + + touch: touch, + msPointer: msPointer, + pointer: pointer, + + retina: retina + }; + +}()); + + +/* + * L.Point represents a point with x and y coordinates. + */ + +L.Point = function (/*Number*/ x, /*Number*/ y, /*Boolean*/ round) { + this.x = (round ? Math.round(x) : x); + this.y = (round ? Math.round(y) : y); +}; + +L.Point.prototype = { + + clone: function () { + return new L.Point(this.x, this.y); + }, + + // non-destructive, returns a new point + add: function (point) { + return this.clone()._add(L.point(point)); + }, + + // destructive, used directly for performance in situations where it's safe to modify existing point + _add: function (point) { + this.x += point.x; + this.y += point.y; + return this; + }, + + subtract: function (point) { + return this.clone()._subtract(L.point(point)); + }, + + _subtract: function (point) { + this.x -= point.x; + this.y -= point.y; + return this; + }, + + divideBy: function (num) { + return this.clone()._divideBy(num); + }, + + _divideBy: function (num) { + this.x /= num; + this.y /= num; + return this; + }, + + multiplyBy: function (num) { + return this.clone()._multiplyBy(num); + }, + + _multiplyBy: function (num) { + this.x *= num; + this.y *= num; + return this; + }, + + round: function () { + return this.clone()._round(); + }, + + _round: function () { + this.x = Math.round(this.x); + this.y = Math.round(this.y); + return this; + }, + + floor: function () { + return this.clone()._floor(); + }, + + _floor: function () { + this.x = Math.floor(this.x); + this.y = Math.floor(this.y); + return this; + }, + + distanceTo: function (point) { + point = L.point(point); + + var x = point.x - this.x, + y = point.y - this.y; + + return Math.sqrt(x * x + y * y); + }, + + equals: function (point) { + point = L.point(point); + + return point.x === this.x && + point.y === this.y; + }, + + contains: function (point) { + point = L.point(point); + + return Math.abs(point.x) <= Math.abs(this.x) && + Math.abs(point.y) <= Math.abs(this.y); + }, + + toString: function () { + return 'Point(' + + L.Util.formatNum(this.x) + ', ' + + L.Util.formatNum(this.y) + ')'; + } +}; + +L.point = function (x, y, round) { + if (x instanceof L.Point) { + return x; + } + if (L.Util.isArray(x)) { + return new L.Point(x[0], x[1]); + } + if (x === undefined || x === null) { + return x; + } + return new L.Point(x, y, round); +}; + + +/* + * L.Bounds represents a rectangular area on the screen in pixel coordinates. + */ + +L.Bounds = function (a, b) { //(Point, Point) or Point[] + if (!a) { return; } + + var points = b ? [a, b] : a; + + for (var i = 0, len = points.length; i < len; i++) { + this.extend(points[i]); + } +}; + +L.Bounds.prototype = { + // extend the bounds to contain the given point + extend: function (point) { // (Point) + point = L.point(point); + + if (!this.min && !this.max) { + this.min = point.clone(); + this.max = point.clone(); + } else { + this.min.x = Math.min(point.x, this.min.x); + this.max.x = Math.max(point.x, this.max.x); + this.min.y = Math.min(point.y, this.min.y); + this.max.y = Math.max(point.y, this.max.y); + } + return this; + }, + + getCenter: function (round) { // (Boolean) -> Point + return new L.Point( + (this.min.x + this.max.x) / 2, + (this.min.y + this.max.y) / 2, round); + }, + + getBottomLeft: function () { // -> Point + return new L.Point(this.min.x, this.max.y); + }, + + getTopRight: function () { // -> Point + return new L.Point(this.max.x, this.min.y); + }, + + getSize: function () { + return this.max.subtract(this.min); + }, + + contains: function (obj) { // (Bounds) or (Point) -> Boolean + var min, max; + + if (typeof obj[0] === 'number' || obj instanceof L.Point) { + obj = L.point(obj); + } else { + obj = L.bounds(obj); + } + + if (obj instanceof L.Bounds) { + min = obj.min; + max = obj.max; + } else { + min = max = obj; + } + + return (min.x >= this.min.x) && + (max.x <= this.max.x) && + (min.y >= this.min.y) && + (max.y <= this.max.y); + }, + + intersects: function (bounds) { // (Bounds) -> Boolean + bounds = L.bounds(bounds); + + var min = this.min, + max = this.max, + min2 = bounds.min, + max2 = bounds.max, + xIntersects = (max2.x >= min.x) && (min2.x <= max.x), + yIntersects = (max2.y >= min.y) && (min2.y <= max.y); + + return xIntersects && yIntersects; + }, + + isValid: function () { + return !!(this.min && this.max); + } +}; + +L.bounds = function (a, b) { // (Bounds) or (Point, Point) or (Point[]) + if (!a || a instanceof L.Bounds) { + return a; + } + return new L.Bounds(a, b); +}; + + +/* + * L.Transformation is an utility class to perform simple point transformations through a 2d-matrix. + */ + +L.Transformation = function (a, b, c, d) { + this._a = a; + this._b = b; + this._c = c; + this._d = d; +}; + +L.Transformation.prototype = { + transform: function (point, scale) { // (Point, Number) -> Point + return this._transform(point.clone(), scale); + }, + + // destructive transform (faster) + _transform: function (point, scale) { + scale = scale || 1; + point.x = scale * (this._a * point.x + this._b); + point.y = scale * (this._c * point.y + this._d); + return point; + }, + + untransform: function (point, scale) { + scale = scale || 1; + return new L.Point( + (point.x / scale - this._b) / this._a, + (point.y / scale - this._d) / this._c); + } +}; + + +/* + * L.DomUtil contains various utility functions for working with DOM. + */ + +L.DomUtil = { + get: function (id) { + return (typeof id === 'string' ? document.getElementById(id) : id); + }, + + getStyle: function (el, style) { + + var value = el.style[style]; + + if (!value && el.currentStyle) { + value = el.currentStyle[style]; + } + + if ((!value || value === 'auto') && document.defaultView) { + var css = document.defaultView.getComputedStyle(el, null); + value = css ? css[style] : null; + } + + return value === 'auto' ? null : value; + }, + + getViewportOffset: function (element) { + + var top = 0, + left = 0, + el = element, + docBody = document.body, + docEl = document.documentElement, + pos; + + do { + top += el.offsetTop || 0; + left += el.offsetLeft || 0; + + //add borders + top += parseInt(L.DomUtil.getStyle(el, 'borderTopWidth'), 10) || 0; + left += parseInt(L.DomUtil.getStyle(el, 'borderLeftWidth'), 10) || 0; + + pos = L.DomUtil.getStyle(el, 'position'); + + if (el.offsetParent === docBody && pos === 'absolute') { break; } + + if (pos === 'fixed') { + top += docBody.scrollTop || docEl.scrollTop || 0; + left += docBody.scrollLeft || docEl.scrollLeft || 0; + break; + } + + if (pos === 'relative' && !el.offsetLeft) { + var width = L.DomUtil.getStyle(el, 'width'), + maxWidth = L.DomUtil.getStyle(el, 'max-width'), + r = el.getBoundingClientRect(); + + if (width !== 'none' || maxWidth !== 'none') { + left += r.left + el.clientLeft; + } + + //calculate full y offset since we're breaking out of the loop + top += r.top + (docBody.scrollTop || docEl.scrollTop || 0); + + break; + } + + el = el.offsetParent; + + } while (el); + + el = element; + + do { + if (el === docBody) { break; } + + top -= el.scrollTop || 0; + left -= el.scrollLeft || 0; + + el = el.parentNode; + } while (el); + + return new L.Point(left, top); + }, + + documentIsLtr: function () { + if (!L.DomUtil._docIsLtrCached) { + L.DomUtil._docIsLtrCached = true; + L.DomUtil._docIsLtr = L.DomUtil.getStyle(document.body, 'direction') === 'ltr'; + } + return L.DomUtil._docIsLtr; + }, + + create: function (tagName, className, container) { + + var el = document.createElement(tagName); + el.className = className; + + if (container) { + container.appendChild(el); + } + + return el; + }, + + hasClass: function (el, name) { + if (el.classList !== undefined) { + return el.classList.contains(name); + } + var className = L.DomUtil._getClass(el); + return className.length > 0 && new RegExp('(^|\\s)' + name + '(\\s|$)').test(className); + }, + + addClass: function (el, name) { + if (el.classList !== undefined) { + var classes = L.Util.splitWords(name); + for (var i = 0, len = classes.length; i < len; i++) { + el.classList.add(classes[i]); + } + } else if (!L.DomUtil.hasClass(el, name)) { + var className = L.DomUtil._getClass(el); + L.DomUtil._setClass(el, (className ? className + ' ' : '') + name); + } + }, + + removeClass: function (el, name) { + if (el.classList !== undefined) { + el.classList.remove(name); + } else { + L.DomUtil._setClass(el, L.Util.trim((' ' + L.DomUtil._getClass(el) + ' ').replace(' ' + name + ' ', ' '))); + } + }, + + _setClass: function (el, name) { + if (el.className.baseVal === undefined) { + el.className = name; + } else { + // in case of SVG element + el.className.baseVal = name; + } + }, + + _getClass: function (el) { + return el.className.baseVal === undefined ? el.className : el.className.baseVal; + }, + + setOpacity: function (el, value) { + + if ('opacity' in el.style) { + el.style.opacity = value; + + } else if ('filter' in el.style) { + + var filter = false, + filterName = 'DXImageTransform.Microsoft.Alpha'; + + // filters collection throws an error if we try to retrieve a filter that doesn't exist + try { + filter = el.filters.item(filterName); + } catch (e) { + // don't set opacity to 1 if we haven't already set an opacity, + // it isn't needed and breaks transparent pngs. + if (value === 1) { return; } + } + + value = Math.round(value * 100); + + if (filter) { + filter.Enabled = (value !== 100); + filter.Opacity = value; + } else { + el.style.filter += ' progid:' + filterName + '(opacity=' + value + ')'; + } + } + }, + + testProp: function (props) { + + var style = document.documentElement.style; + + for (var i = 0; i < props.length; i++) { + if (props[i] in style) { + return props[i]; + } + } + return false; + }, + + getTranslateString: function (point) { + // on WebKit browsers (Chrome/Safari/iOS Safari/Android) using translate3d instead of translate + // makes animation smoother as it ensures HW accel is used. Firefox 13 doesn't care + // (same speed either way), Opera 12 doesn't support translate3d + + var is3d = L.Browser.webkit3d, + open = 'translate' + (is3d ? '3d' : '') + '(', + close = (is3d ? ',0' : '') + ')'; + + return open + point.x + 'px,' + point.y + 'px' + close; + }, + + getScaleString: function (scale, origin) { + + var preTranslateStr = L.DomUtil.getTranslateString(origin.add(origin.multiplyBy(-1 * scale))), + scaleStr = ' scale(' + scale + ') '; + + return preTranslateStr + scaleStr; + }, + + setPosition: function (el, point, disable3D) { // (HTMLElement, Point[, Boolean]) + + // jshint camelcase: false + el._leaflet_pos = point; + + if (!disable3D && L.Browser.any3d) { + el.style[L.DomUtil.TRANSFORM] = L.DomUtil.getTranslateString(point); + } else { + el.style.left = point.x + 'px'; + el.style.top = point.y + 'px'; + } + }, + + getPosition: function (el) { + // this method is only used for elements previously positioned using setPosition, + // so it's safe to cache the position for performance + + // jshint camelcase: false + return el._leaflet_pos; + } +}; + + +// prefix style property names + +L.DomUtil.TRANSFORM = L.DomUtil.testProp( + ['transform', 'WebkitTransform', 'OTransform', 'MozTransform', 'msTransform']); + +// webkitTransition comes first because some browser versions that drop vendor prefix don't do +// the same for the transitionend event, in particular the Android 4.1 stock browser + +L.DomUtil.TRANSITION = L.DomUtil.testProp( + ['webkitTransition', 'transition', 'OTransition', 'MozTransition', 'msTransition']); + +L.DomUtil.TRANSITION_END = + L.DomUtil.TRANSITION === 'webkitTransition' || L.DomUtil.TRANSITION === 'OTransition' ? + L.DomUtil.TRANSITION + 'End' : 'transitionend'; + +(function () { + if ('onselectstart' in document) { + L.extend(L.DomUtil, { + disableTextSelection: function () { + L.DomEvent.on(window, 'selectstart', L.DomEvent.preventDefault); + }, + + enableTextSelection: function () { + L.DomEvent.off(window, 'selectstart', L.DomEvent.preventDefault); + } + }); + } else { + var userSelectProperty = L.DomUtil.testProp( + ['userSelect', 'WebkitUserSelect', 'OUserSelect', 'MozUserSelect', 'msUserSelect']); + + L.extend(L.DomUtil, { + disableTextSelection: function () { + if (userSelectProperty) { + var style = document.documentElement.style; + this._userSelect = style[userSelectProperty]; + style[userSelectProperty] = 'none'; + } + }, + + enableTextSelection: function () { + if (userSelectProperty) { + document.documentElement.style[userSelectProperty] = this._userSelect; + delete this._userSelect; + } + } + }); + } + + L.extend(L.DomUtil, { + disableImageDrag: function () { + L.DomEvent.on(window, 'dragstart', L.DomEvent.preventDefault); + }, + + enableImageDrag: function () { + L.DomEvent.off(window, 'dragstart', L.DomEvent.preventDefault); + } + }); +})(); + + +/* + * L.LatLng represents a geographical point with latitude and longitude coordinates. + */ + +L.LatLng = function (lat, lng, alt) { // (Number, Number, Number) + lat = parseFloat(lat); + lng = parseFloat(lng); + + if (isNaN(lat) || isNaN(lng)) { + throw new Error('Invalid LatLng object: (' + lat + ', ' + lng + ')'); + } + + this.lat = lat; + this.lng = lng; + + if (alt !== undefined) { + this.alt = parseFloat(alt); + } +}; + +L.extend(L.LatLng, { + DEG_TO_RAD: Math.PI / 180, + RAD_TO_DEG: 180 / Math.PI, + MAX_MARGIN: 1.0E-9 // max margin of error for the "equals" check +}); + +L.LatLng.prototype = { + equals: function (obj) { // (LatLng) -> Boolean + if (!obj) { return false; } + + obj = L.latLng(obj); + + var margin = Math.max( + Math.abs(this.lat - obj.lat), + Math.abs(this.lng - obj.lng)); + + return margin <= L.LatLng.MAX_MARGIN; + }, + + toString: function (precision) { // (Number) -> String + return 'LatLng(' + + L.Util.formatNum(this.lat, precision) + ', ' + + L.Util.formatNum(this.lng, precision) + ')'; + }, + + // Haversine distance formula, see http://en.wikipedia.org/wiki/Haversine_formula + // TODO move to projection code, LatLng shouldn't know about Earth + distanceTo: function (other) { // (LatLng) -> Number + other = L.latLng(other); + + var R = 6378137, // earth radius in meters + d2r = L.LatLng.DEG_TO_RAD, + dLat = (other.lat - this.lat) * d2r, + dLon = (other.lng - this.lng) * d2r, + lat1 = this.lat * d2r, + lat2 = other.lat * d2r, + sin1 = Math.sin(dLat / 2), + sin2 = Math.sin(dLon / 2); + + var a = sin1 * sin1 + sin2 * sin2 * Math.cos(lat1) * Math.cos(lat2); + + return R * 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + }, + + wrap: function (a, b) { // (Number, Number) -> LatLng + var lng = this.lng; + + a = a || -180; + b = b || 180; + + lng = (lng + b) % (b - a) + (lng < a || lng === b ? b : a); + + return new L.LatLng(this.lat, lng); + } +}; + +L.latLng = function (a, b) { // (LatLng) or ([Number, Number]) or (Number, Number) + if (a instanceof L.LatLng) { + return a; + } + if (L.Util.isArray(a)) { + if (typeof a[0] === 'number' || typeof a[0] === 'string') { + return new L.LatLng(a[0], a[1], a[2]); + } else { + return null; + } + } + if (a === undefined || a === null) { + return a; + } + if (typeof a === 'object' && 'lat' in a) { + return new L.LatLng(a.lat, 'lng' in a ? a.lng : a.lon); + } + if (b === undefined) { + return null; + } + return new L.LatLng(a, b); +}; + + + +/* + * L.LatLngBounds represents a rectangular area on the map in geographical coordinates. + */ + +L.LatLngBounds = function (southWest, northEast) { // (LatLng, LatLng) or (LatLng[]) + if (!southWest) { return; } + + var latlngs = northEast ? [southWest, northEast] : southWest; + + for (var i = 0, len = latlngs.length; i < len; i++) { + this.extend(latlngs[i]); + } +}; + +L.LatLngBounds.prototype = { + // extend the bounds to contain the given point or bounds + extend: function (obj) { // (LatLng) or (LatLngBounds) + if (!obj) { return this; } + + var latLng = L.latLng(obj); + if (latLng !== null) { + obj = latLng; + } else { + obj = L.latLngBounds(obj); + } + + if (obj instanceof L.LatLng) { + if (!this._southWest && !this._northEast) { + this._southWest = new L.LatLng(obj.lat, obj.lng); + this._northEast = new L.LatLng(obj.lat, obj.lng); + } else { + this._southWest.lat = Math.min(obj.lat, this._southWest.lat); + this._southWest.lng = Math.min(obj.lng, this._southWest.lng); + + this._northEast.lat = Math.max(obj.lat, this._northEast.lat); + this._northEast.lng = Math.max(obj.lng, this._northEast.lng); + } + } else if (obj instanceof L.LatLngBounds) { + this.extend(obj._southWest); + this.extend(obj._northEast); + } + return this; + }, + + // extend the bounds by a percentage + pad: function (bufferRatio) { // (Number) -> LatLngBounds + var sw = this._southWest, + ne = this._northEast, + heightBuffer = Math.abs(sw.lat - ne.lat) * bufferRatio, + widthBuffer = Math.abs(sw.lng - ne.lng) * bufferRatio; + + return new L.LatLngBounds( + new L.LatLng(sw.lat - heightBuffer, sw.lng - widthBuffer), + new L.LatLng(ne.lat + heightBuffer, ne.lng + widthBuffer)); + }, + + getCenter: function () { // -> LatLng + return new L.LatLng( + (this._southWest.lat + this._northEast.lat) / 2, + (this._southWest.lng + this._northEast.lng) / 2); + }, + + getSouthWest: function () { + return this._southWest; + }, + + getNorthEast: function () { + return this._northEast; + }, + + getNorthWest: function () { + return new L.LatLng(this.getNorth(), this.getWest()); + }, + + getSouthEast: function () { + return new L.LatLng(this.getSouth(), this.getEast()); + }, + + getWest: function () { + return this._southWest.lng; + }, + + getSouth: function () { + return this._southWest.lat; + }, + + getEast: function () { + return this._northEast.lng; + }, + + getNorth: function () { + return this._northEast.lat; + }, + + contains: function (obj) { // (LatLngBounds) or (LatLng) -> Boolean + if (typeof obj[0] === 'number' || obj instanceof L.LatLng) { + obj = L.latLng(obj); + } else { + obj = L.latLngBounds(obj); + } + + var sw = this._southWest, + ne = this._northEast, + sw2, ne2; + + if (obj instanceof L.LatLngBounds) { + sw2 = obj.getSouthWest(); + ne2 = obj.getNorthEast(); + } else { + sw2 = ne2 = obj; + } + + return (sw2.lat >= sw.lat) && (ne2.lat <= ne.lat) && + (sw2.lng >= sw.lng) && (ne2.lng <= ne.lng); + }, + + intersects: function (bounds) { // (LatLngBounds) + bounds = L.latLngBounds(bounds); + + var sw = this._southWest, + ne = this._northEast, + sw2 = bounds.getSouthWest(), + ne2 = bounds.getNorthEast(), + + latIntersects = (ne2.lat >= sw.lat) && (sw2.lat <= ne.lat), + lngIntersects = (ne2.lng >= sw.lng) && (sw2.lng <= ne.lng); + + return latIntersects && lngIntersects; + }, + + toBBoxString: function () { + return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(','); + }, + + equals: function (bounds) { // (LatLngBounds) + if (!bounds) { return false; } + + bounds = L.latLngBounds(bounds); + + return this._southWest.equals(bounds.getSouthWest()) && + this._northEast.equals(bounds.getNorthEast()); + }, + + isValid: function () { + return !!(this._southWest && this._northEast); + } +}; + +//TODO International date line? + +L.latLngBounds = function (a, b) { // (LatLngBounds) or (LatLng, LatLng) + if (!a || a instanceof L.LatLngBounds) { + return a; + } + return new L.LatLngBounds(a, b); +}; + + +/* + * L.Projection contains various geographical projections used by CRS classes. + */ + +L.Projection = {}; + + +/* + * Spherical Mercator is the most popular map projection, used by EPSG:3857 CRS used by default. + */ + +L.Projection.SphericalMercator = { + MAX_LATITUDE: 85.0511287798, + + project: function (latlng) { // (LatLng) -> Point + var d = L.LatLng.DEG_TO_RAD, + max = this.MAX_LATITUDE, + lat = Math.max(Math.min(max, latlng.lat), -max), + x = latlng.lng * d, + y = lat * d; + + y = Math.log(Math.tan((Math.PI / 4) + (y / 2))); + + return new L.Point(x, y); + }, + + unproject: function (point) { // (Point, Boolean) -> LatLng + var d = L.LatLng.RAD_TO_DEG, + lng = point.x * d, + lat = (2 * Math.atan(Math.exp(point.y)) - (Math.PI / 2)) * d; + + return new L.LatLng(lat, lng); + } +}; + + +/* + * Simple equirectangular (Plate Carree) projection, used by CRS like EPSG:4326 and Simple. + */ + +L.Projection.LonLat = { + project: function (latlng) { + return new L.Point(latlng.lng, latlng.lat); + }, + + unproject: function (point) { + return new L.LatLng(point.y, point.x); + } +}; + + +/* + * L.CRS is a base object for all defined CRS (Coordinate Reference Systems) in Leaflet. + */ + +L.CRS = { + latLngToPoint: function (latlng, zoom) { // (LatLng, Number) -> Point + var projectedPoint = this.projection.project(latlng), + scale = this.scale(zoom); + + return this.transformation._transform(projectedPoint, scale); + }, + + pointToLatLng: function (point, zoom) { // (Point, Number[, Boolean]) -> LatLng + var scale = this.scale(zoom), + untransformedPoint = this.transformation.untransform(point, scale); + + return this.projection.unproject(untransformedPoint); + }, + + project: function (latlng) { + return this.projection.project(latlng); + }, + + scale: function (zoom) { + return 256 * Math.pow(2, zoom); + }, + + getSize: function (zoom) { + var s = this.scale(zoom); + return L.point(s, s); + } +}; + + +/* + * A simple CRS that can be used for flat non-Earth maps like panoramas or game maps. + */ + +L.CRS.Simple = L.extend({}, L.CRS, { + projection: L.Projection.LonLat, + transformation: new L.Transformation(1, 0, -1, 0), + + scale: function (zoom) { + return Math.pow(2, zoom); + } +}); + + +/* + * L.CRS.EPSG3857 (Spherical Mercator) is the most common CRS for web mapping + * and is used by Leaflet by default. + */ + +L.CRS.EPSG3857 = L.extend({}, L.CRS, { + code: 'EPSG:3857', + + projection: L.Projection.SphericalMercator, + transformation: new L.Transformation(0.5 / Math.PI, 0.5, -0.5 / Math.PI, 0.5), + + project: function (latlng) { // (LatLng) -> Point + var projectedPoint = this.projection.project(latlng), + earthRadius = 6378137; + return projectedPoint.multiplyBy(earthRadius); + } +}); + +L.CRS.EPSG900913 = L.extend({}, L.CRS.EPSG3857, { + code: 'EPSG:900913' +}); + + +/* + * L.CRS.EPSG4326 is a CRS popular among advanced GIS specialists. + */ + +L.CRS.EPSG4326 = L.extend({}, L.CRS, { + code: 'EPSG:4326', + + projection: L.Projection.LonLat, + transformation: new L.Transformation(1 / 360, 0.5, -1 / 360, 0.5) +}); + + +/* + * L.Map is the central class of the API - it is used to create a map. + */ + +L.Map = L.Class.extend({ + + includes: L.Mixin.Events, + + options: { + crs: L.CRS.EPSG3857, + + /* + center: LatLng, + zoom: Number, + layers: Array, + */ + + fadeAnimation: L.DomUtil.TRANSITION && !L.Browser.android23, + trackResize: true, + markerZoomAnimation: L.DomUtil.TRANSITION && L.Browser.any3d + }, + + initialize: function (id, options) { // (HTMLElement or String, Object) + options = L.setOptions(this, options); + + + this._initContainer(id); + this._initLayout(); + + // hack for https://github.com/Leaflet/Leaflet/issues/1980 + this._onResize = L.bind(this._onResize, this); + + this._initEvents(); + + if (options.maxBounds) { + this.setMaxBounds(options.maxBounds); + } + + if (options.center && options.zoom !== undefined) { + this.setView(L.latLng(options.center), options.zoom, {reset: true}); + } + + this._handlers = []; + + this._layers = {}; + this._zoomBoundLayers = {}; + this._tileLayersNum = 0; + + this.callInitHooks(); + + this._addLayers(options.layers); + }, + + + // public methods that modify map state + + // replaced by animation-powered implementation in Map.PanAnimation.js + setView: function (center, zoom) { + zoom = zoom === undefined ? this.getZoom() : zoom; + this._resetView(L.latLng(center), this._limitZoom(zoom)); + return this; + }, + + setZoom: function (zoom, options) { + if (!this._loaded) { + this._zoom = this._limitZoom(zoom); + return this; + } + return this.setView(this.getCenter(), zoom, {zoom: options}); + }, + + zoomIn: function (delta, options) { + return this.setZoom(this._zoom + (delta || 1), options); + }, + + zoomOut: function (delta, options) { + return this.setZoom(this._zoom - (delta || 1), options); + }, + + setZoomAround: function (latlng, zoom, options) { + var scale = this.getZoomScale(zoom), + viewHalf = this.getSize().divideBy(2), + containerPoint = latlng instanceof L.Point ? latlng : this.latLngToContainerPoint(latlng), + + centerOffset = containerPoint.subtract(viewHalf).multiplyBy(1 - 1 / scale), + newCenter = this.containerPointToLatLng(viewHalf.add(centerOffset)); + + return this.setView(newCenter, zoom, {zoom: options}); + }, + + fitBounds: function (bounds, options) { + + options = options || {}; + bounds = bounds.getBounds ? bounds.getBounds() : L.latLngBounds(bounds); + + var paddingTL = L.point(options.paddingTopLeft || options.padding || [0, 0]), + paddingBR = L.point(options.paddingBottomRight || options.padding || [0, 0]), + + zoom = this.getBoundsZoom(bounds, false, paddingTL.add(paddingBR)); + + zoom = (options.maxZoom) ? Math.min(options.maxZoom, zoom) : zoom; + + var paddingOffset = paddingBR.subtract(paddingTL).divideBy(2), + + swPoint = this.project(bounds.getSouthWest(), zoom), + nePoint = this.project(bounds.getNorthEast(), zoom), + center = this.unproject(swPoint.add(nePoint).divideBy(2).add(paddingOffset), zoom); + + return this.setView(center, zoom, options); + }, + + fitWorld: function (options) { + return this.fitBounds([[-90, -180], [90, 180]], options); + }, + + panTo: function (center, options) { // (LatLng) + return this.setView(center, this._zoom, {pan: options}); + }, + + panBy: function (offset) { // (Point) + // replaced with animated panBy in Map.PanAnimation.js + this.fire('movestart'); + + this._rawPanBy(L.point(offset)); + + this.fire('move'); + return this.fire('moveend'); + }, + + setMaxBounds: function (bounds) { + bounds = L.latLngBounds(bounds); + + this.options.maxBounds = bounds; + + if (!bounds) { + return this.off('moveend', this._panInsideMaxBounds, this); + } + + if (this._loaded) { + this._panInsideMaxBounds(); + } + + return this.on('moveend', this._panInsideMaxBounds, this); + }, + + panInsideBounds: function (bounds, options) { + var center = this.getCenter(), + newCenter = this._limitCenter(center, this._zoom, bounds); + + if (center.equals(newCenter)) { return this; } + + return this.panTo(newCenter, options); + }, + + addLayer: function (layer) { + // TODO method is too big, refactor + + var id = L.stamp(layer); + + if (this._layers[id]) { return this; } + + this._layers[id] = layer; + + // TODO getMaxZoom, getMinZoom in ILayer (instead of options) + if (layer.options && (!isNaN(layer.options.maxZoom) || !isNaN(layer.options.minZoom))) { + this._zoomBoundLayers[id] = layer; + this._updateZoomLevels(); + } + + // TODO looks ugly, refactor!!! + if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) { + this._tileLayersNum++; + this._tileLayersToLoad++; + layer.on('load', this._onTileLayerLoad, this); + } + + if (this._loaded) { + this._layerAdd(layer); + } + + return this; + }, + + removeLayer: function (layer) { + var id = L.stamp(layer); + + if (!this._layers[id]) { return this; } + + if (this._loaded) { + layer.onRemove(this); + } + + delete this._layers[id]; + + if (this._loaded) { + this.fire('layerremove', {layer: layer}); + } + + if (this._zoomBoundLayers[id]) { + delete this._zoomBoundLayers[id]; + this._updateZoomLevels(); + } + + // TODO looks ugly, refactor + if (this.options.zoomAnimation && L.TileLayer && (layer instanceof L.TileLayer)) { + this._tileLayersNum--; + this._tileLayersToLoad--; + layer.off('load', this._onTileLayerLoad, this); + } + + return this; + }, + + hasLayer: function (layer) { + if (!layer) { return false; } + + return (L.stamp(layer) in this._layers); + }, + + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + invalidateSize: function (options) { + if (!this._loaded) { return this; } + + options = L.extend({ + animate: false, + pan: true + }, options === true ? {animate: true} : options); + + var oldSize = this.getSize(); + this._sizeChanged = true; + this._initialCenter = null; + + var newSize = this.getSize(), + oldCenter = oldSize.divideBy(2).round(), + newCenter = newSize.divideBy(2).round(), + offset = oldCenter.subtract(newCenter); + + if (!offset.x && !offset.y) { return this; } + + if (options.animate && options.pan) { + this.panBy(offset); + + } else { + if (options.pan) { + this._rawPanBy(offset); + } + + this.fire('move'); + + if (options.debounceMoveend) { + clearTimeout(this._sizeTimer); + this._sizeTimer = setTimeout(L.bind(this.fire, this, 'moveend'), 200); + } else { + this.fire('moveend'); + } + } + + return this.fire('resize', { + oldSize: oldSize, + newSize: newSize + }); + }, + + // TODO handler.addTo + addHandler: function (name, HandlerClass) { + if (!HandlerClass) { return this; } + + var handler = this[name] = new HandlerClass(this); + + this._handlers.push(handler); + + if (this.options[name]) { + handler.enable(); + } + + return this; + }, + + remove: function () { + if (this._loaded) { + this.fire('unload'); + } + + this._initEvents('off'); + + try { + // throws error in IE6-8 + delete this._container._leaflet; + } catch (e) { + this._container._leaflet = undefined; + } + + this._clearPanes(); + if (this._clearControlPos) { + this._clearControlPos(); + } + + this._clearHandlers(); + + return this; + }, + + + // public methods for getting map state + + getCenter: function () { // (Boolean) -> LatLng + this._checkIfLoaded(); + + if (this._initialCenter && !this._moved()) { + return this._initialCenter; + } + return this.layerPointToLatLng(this._getCenterLayerPoint()); + }, + + getZoom: function () { + return this._zoom; + }, + + getBounds: function () { + var bounds = this.getPixelBounds(), + sw = this.unproject(bounds.getBottomLeft()), + ne = this.unproject(bounds.getTopRight()); + + return new L.LatLngBounds(sw, ne); + }, + + getMinZoom: function () { + return this.options.minZoom === undefined ? + (this._layersMinZoom === undefined ? 0 : this._layersMinZoom) : + this.options.minZoom; + }, + + getMaxZoom: function () { + return this.options.maxZoom === undefined ? + (this._layersMaxZoom === undefined ? Infinity : this._layersMaxZoom) : + this.options.maxZoom; + }, + + getBoundsZoom: function (bounds, inside, padding) { // (LatLngBounds[, Boolean, Point]) -> Number + bounds = L.latLngBounds(bounds); + + var zoom = this.getMinZoom() - (inside ? 1 : 0), + maxZoom = this.getMaxZoom(), + size = this.getSize(), + + nw = bounds.getNorthWest(), + se = bounds.getSouthEast(), + + zoomNotFound = true, + boundsSize; + + padding = L.point(padding || [0, 0]); + + do { + zoom++; + boundsSize = this.project(se, zoom).subtract(this.project(nw, zoom)).add(padding); + zoomNotFound = !inside ? size.contains(boundsSize) : boundsSize.x < size.x || boundsSize.y < size.y; + + } while (zoomNotFound && zoom <= maxZoom); + + if (zoomNotFound && inside) { + return null; + } + + return inside ? zoom : zoom - 1; + }, + + getSize: function () { + if (!this._size || this._sizeChanged) { + this._size = new L.Point( + this._container.clientWidth, + this._container.clientHeight); + + this._sizeChanged = false; + } + return this._size.clone(); + }, + + getPixelBounds: function () { + var topLeftPoint = this._getTopLeftPoint(); + return new L.Bounds(topLeftPoint, topLeftPoint.add(this.getSize())); + }, + + getPixelOrigin: function () { + this._checkIfLoaded(); + return this._initialTopLeftPoint; + }, + + getPanes: function () { + return this._panes; + }, + + getContainer: function () { + return this._container; + }, + + + // TODO replace with universal implementation after refactoring projections + + getZoomScale: function (toZoom) { + var crs = this.options.crs; + return crs.scale(toZoom) / crs.scale(this._zoom); + }, + + getScaleZoom: function (scale) { + return this._zoom + (Math.log(scale) / Math.LN2); + }, + + + // conversion methods + + project: function (latlng, zoom) { // (LatLng[, Number]) -> Point + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.latLngToPoint(L.latLng(latlng), zoom); + }, + + unproject: function (point, zoom) { // (Point[, Number]) -> LatLng + zoom = zoom === undefined ? this._zoom : zoom; + return this.options.crs.pointToLatLng(L.point(point), zoom); + }, + + layerPointToLatLng: function (point) { // (Point) + var projectedPoint = L.point(point).add(this.getPixelOrigin()); + return this.unproject(projectedPoint); + }, + + latLngToLayerPoint: function (latlng) { // (LatLng) + var projectedPoint = this.project(L.latLng(latlng))._round(); + return projectedPoint._subtract(this.getPixelOrigin()); + }, + + containerPointToLayerPoint: function (point) { // (Point) + return L.point(point).subtract(this._getMapPanePos()); + }, + + layerPointToContainerPoint: function (point) { // (Point) + return L.point(point).add(this._getMapPanePos()); + }, + + containerPointToLatLng: function (point) { + var layerPoint = this.containerPointToLayerPoint(L.point(point)); + return this.layerPointToLatLng(layerPoint); + }, + + latLngToContainerPoint: function (latlng) { + return this.layerPointToContainerPoint(this.latLngToLayerPoint(L.latLng(latlng))); + }, + + mouseEventToContainerPoint: function (e) { // (MouseEvent) + return L.DomEvent.getMousePosition(e, this._container); + }, + + mouseEventToLayerPoint: function (e) { // (MouseEvent) + return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(e)); + }, + + mouseEventToLatLng: function (e) { // (MouseEvent) + return this.layerPointToLatLng(this.mouseEventToLayerPoint(e)); + }, + + + // map initialization methods + + _initContainer: function (id) { + var container = this._container = L.DomUtil.get(id); + + if (!container) { + throw new Error('Map container not found.'); + } else if (container._leaflet) { + throw new Error('Map container is already initialized.'); + } + + container._leaflet = true; + }, + + _initLayout: function () { + var container = this._container; + + L.DomUtil.addClass(container, 'leaflet-container' + + (L.Browser.touch ? ' leaflet-touch' : '') + + (L.Browser.retina ? ' leaflet-retina' : '') + + (L.Browser.ielt9 ? ' leaflet-oldie' : '') + + (this.options.fadeAnimation ? ' leaflet-fade-anim' : '')); + + var position = L.DomUtil.getStyle(container, 'position'); + + if (position !== 'absolute' && position !== 'relative' && position !== 'fixed') { + container.style.position = 'relative'; + } + + this._initPanes(); + + if (this._initControlPos) { + this._initControlPos(); + } + }, + + _initPanes: function () { + var panes = this._panes = {}; + + this._mapPane = panes.mapPane = this._createPane('leaflet-map-pane', this._container); + + this._tilePane = panes.tilePane = this._createPane('leaflet-tile-pane', this._mapPane); + panes.objectsPane = this._createPane('leaflet-objects-pane', this._mapPane); + panes.shadowPane = this._createPane('leaflet-shadow-pane'); + panes.overlayPane = this._createPane('leaflet-overlay-pane'); + panes.markerPane = this._createPane('leaflet-marker-pane'); + panes.popupPane = this._createPane('leaflet-popup-pane'); + + var zoomHide = ' leaflet-zoom-hide'; + + if (!this.options.markerZoomAnimation) { + L.DomUtil.addClass(panes.markerPane, zoomHide); + L.DomUtil.addClass(panes.shadowPane, zoomHide); + L.DomUtil.addClass(panes.popupPane, zoomHide); + } + }, + + _createPane: function (className, container) { + return L.DomUtil.create('div', className, container || this._panes.objectsPane); + }, + + _clearPanes: function () { + this._container.removeChild(this._mapPane); + }, + + _addLayers: function (layers) { + layers = layers ? (L.Util.isArray(layers) ? layers : [layers]) : []; + + for (var i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + }, + + + // private methods that modify map state + + _resetView: function (center, zoom, preserveMapOffset, afterZoomAnim) { + + var zoomChanged = (this._zoom !== zoom); + + if (!afterZoomAnim) { + this.fire('movestart'); + + if (zoomChanged) { + this.fire('zoomstart'); + } + } + + this._zoom = zoom; + this._initialCenter = center; + + this._initialTopLeftPoint = this._getNewTopLeftPoint(center); + + if (!preserveMapOffset) { + L.DomUtil.setPosition(this._mapPane, new L.Point(0, 0)); + } else { + this._initialTopLeftPoint._add(this._getMapPanePos()); + } + + this._tileLayersToLoad = this._tileLayersNum; + + var loading = !this._loaded; + this._loaded = true; + + this.fire('viewreset', {hard: !preserveMapOffset}); + + if (loading) { + this.fire('load'); + this.eachLayer(this._layerAdd, this); + } + + this.fire('move'); + + if (zoomChanged || afterZoomAnim) { + this.fire('zoomend'); + } + + this.fire('moveend', {hard: !preserveMapOffset}); + }, + + _rawPanBy: function (offset) { + L.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(offset)); + }, + + _getZoomSpan: function () { + return this.getMaxZoom() - this.getMinZoom(); + }, + + _updateZoomLevels: function () { + var i, + minZoom = Infinity, + maxZoom = -Infinity, + oldZoomSpan = this._getZoomSpan(); + + for (i in this._zoomBoundLayers) { + var layer = this._zoomBoundLayers[i]; + if (!isNaN(layer.options.minZoom)) { + minZoom = Math.min(minZoom, layer.options.minZoom); + } + if (!isNaN(layer.options.maxZoom)) { + maxZoom = Math.max(maxZoom, layer.options.maxZoom); + } + } + + if (i === undefined) { // we have no tilelayers + this._layersMaxZoom = this._layersMinZoom = undefined; + } else { + this._layersMaxZoom = maxZoom; + this._layersMinZoom = minZoom; + } + + if (oldZoomSpan !== this._getZoomSpan()) { + this.fire('zoomlevelschange'); + } + }, + + _panInsideMaxBounds: function () { + this.panInsideBounds(this.options.maxBounds); + }, + + _checkIfLoaded: function () { + if (!this._loaded) { + throw new Error('Set map center and zoom first.'); + } + }, + + // map events + + _initEvents: function (onOff) { + if (!L.DomEvent) { return; } + + onOff = onOff || 'on'; + + L.DomEvent[onOff](this._container, 'click', this._onMouseClick, this); + + var events = ['dblclick', 'mousedown', 'mouseup', 'mouseenter', + 'mouseleave', 'mousemove', 'contextmenu'], + i, len; + + for (i = 0, len = events.length; i < len; i++) { + L.DomEvent[onOff](this._container, events[i], this._fireMouseEvent, this); + } + + if (this.options.trackResize) { + L.DomEvent[onOff](window, 'resize', this._onResize, this); + } + }, + + _onResize: function () { + L.Util.cancelAnimFrame(this._resizeRequest); + this._resizeRequest = L.Util.requestAnimFrame( + function () { this.invalidateSize({debounceMoveend: true}); }, this, false, this._container); + }, + + _onMouseClick: function (e) { + if (!this._loaded || (!e._simulated && + ((this.dragging && this.dragging.moved()) || + (this.boxZoom && this.boxZoom.moved()))) || + L.DomEvent._skipped(e)) { return; } + + this.fire('preclick'); + this._fireMouseEvent(e); + }, + + _fireMouseEvent: function (e) { + if (!this._loaded || L.DomEvent._skipped(e)) { return; } + + var type = e.type; + + type = (type === 'mouseenter' ? 'mouseover' : (type === 'mouseleave' ? 'mouseout' : type)); + + if (!this.hasEventListeners(type)) { return; } + + if (type === 'contextmenu') { + L.DomEvent.preventDefault(e); + } + + var containerPoint = this.mouseEventToContainerPoint(e), + layerPoint = this.containerPointToLayerPoint(containerPoint), + latlng = this.layerPointToLatLng(layerPoint); + + this.fire(type, { + latlng: latlng, + layerPoint: layerPoint, + containerPoint: containerPoint, + originalEvent: e + }); + }, + + _onTileLayerLoad: function () { + this._tileLayersToLoad--; + if (this._tileLayersNum && !this._tileLayersToLoad) { + this.fire('tilelayersload'); + } + }, + + _clearHandlers: function () { + for (var i = 0, len = this._handlers.length; i < len; i++) { + this._handlers[i].disable(); + } + }, + + whenReady: function (callback, context) { + if (this._loaded) { + callback.call(context || this, this); + } else { + this.on('load', callback, context); + } + return this; + }, + + _layerAdd: function (layer) { + layer.onAdd(this); + this.fire('layeradd', {layer: layer}); + }, + + + // private methods for getting map state + + _getMapPanePos: function () { + return L.DomUtil.getPosition(this._mapPane); + }, + + _moved: function () { + var pos = this._getMapPanePos(); + return pos && !pos.equals([0, 0]); + }, + + _getTopLeftPoint: function () { + return this.getPixelOrigin().subtract(this._getMapPanePos()); + }, + + _getNewTopLeftPoint: function (center, zoom) { + var viewHalf = this.getSize()._divideBy(2); + // TODO round on display, not calculation to increase precision? + return this.project(center, zoom)._subtract(viewHalf)._round(); + }, + + _latLngToNewLayerPoint: function (latlng, newZoom, newCenter) { + var topLeft = this._getNewTopLeftPoint(newCenter, newZoom).add(this._getMapPanePos()); + return this.project(latlng, newZoom)._subtract(topLeft); + }, + + // layer point of the current center + _getCenterLayerPoint: function () { + return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); + }, + + // offset of the specified place to the current center in pixels + _getCenterOffset: function (latlng) { + return this.latLngToLayerPoint(latlng).subtract(this._getCenterLayerPoint()); + }, + + // adjust center for view to get inside bounds + _limitCenter: function (center, zoom, bounds) { + + if (!bounds) { return center; } + + var centerPoint = this.project(center, zoom), + viewHalf = this.getSize().divideBy(2), + viewBounds = new L.Bounds(centerPoint.subtract(viewHalf), centerPoint.add(viewHalf)), + offset = this._getBoundsOffset(viewBounds, bounds, zoom); + + return this.unproject(centerPoint.add(offset), zoom); + }, + + // adjust offset for view to get inside bounds + _limitOffset: function (offset, bounds) { + if (!bounds) { return offset; } + + var viewBounds = this.getPixelBounds(), + newBounds = new L.Bounds(viewBounds.min.add(offset), viewBounds.max.add(offset)); + + return offset.add(this._getBoundsOffset(newBounds, bounds)); + }, + + // returns offset needed for pxBounds to get inside maxBounds at a specified zoom + _getBoundsOffset: function (pxBounds, maxBounds, zoom) { + var nwOffset = this.project(maxBounds.getNorthWest(), zoom).subtract(pxBounds.min), + seOffset = this.project(maxBounds.getSouthEast(), zoom).subtract(pxBounds.max), + + dx = this._rebound(nwOffset.x, -seOffset.x), + dy = this._rebound(nwOffset.y, -seOffset.y); + + return new L.Point(dx, dy); + }, + + _rebound: function (left, right) { + return left + right > 0 ? + Math.round(left - right) / 2 : + Math.max(0, Math.ceil(left)) - Math.max(0, Math.floor(right)); + }, + + _limitZoom: function (zoom) { + var min = this.getMinZoom(), + max = this.getMaxZoom(); + + return Math.max(min, Math.min(max, zoom)); + } +}); + +L.map = function (id, options) { + return new L.Map(id, options); +}; + + +/* + * Mercator projection that takes into account that the Earth is not a perfect sphere. + * Less popular than spherical mercator; used by projections like EPSG:3395. + */ + +L.Projection.Mercator = { + MAX_LATITUDE: 85.0840591556, + + R_MINOR: 6356752.314245179, + R_MAJOR: 6378137, + + project: function (latlng) { // (LatLng) -> Point + var d = L.LatLng.DEG_TO_RAD, + max = this.MAX_LATITUDE, + lat = Math.max(Math.min(max, latlng.lat), -max), + r = this.R_MAJOR, + r2 = this.R_MINOR, + x = latlng.lng * d * r, + y = lat * d, + tmp = r2 / r, + eccent = Math.sqrt(1.0 - tmp * tmp), + con = eccent * Math.sin(y); + + con = Math.pow((1 - con) / (1 + con), eccent * 0.5); + + var ts = Math.tan(0.5 * ((Math.PI * 0.5) - y)) / con; + y = -r * Math.log(ts); + + return new L.Point(x, y); + }, + + unproject: function (point) { // (Point, Boolean) -> LatLng + var d = L.LatLng.RAD_TO_DEG, + r = this.R_MAJOR, + r2 = this.R_MINOR, + lng = point.x * d / r, + tmp = r2 / r, + eccent = Math.sqrt(1 - (tmp * tmp)), + ts = Math.exp(- point.y / r), + phi = (Math.PI / 2) - 2 * Math.atan(ts), + numIter = 15, + tol = 1e-7, + i = numIter, + dphi = 0.1, + con; + + while ((Math.abs(dphi) > tol) && (--i > 0)) { + con = eccent * Math.sin(phi); + dphi = (Math.PI / 2) - 2 * Math.atan(ts * + Math.pow((1.0 - con) / (1.0 + con), 0.5 * eccent)) - phi; + phi += dphi; + } + + return new L.LatLng(phi * d, lng); + } +}; + + + +L.CRS.EPSG3395 = L.extend({}, L.CRS, { + code: 'EPSG:3395', + + projection: L.Projection.Mercator, + + transformation: (function () { + var m = L.Projection.Mercator, + r = m.R_MAJOR, + scale = 0.5 / (Math.PI * r); + + return new L.Transformation(scale, 0.5, -scale, 0.5); + }()) +}); + + +/* + * L.TileLayer is used for standard xyz-numbered tile layers. + */ + +L.TileLayer = L.Class.extend({ + includes: L.Mixin.Events, + + options: { + minZoom: 0, + maxZoom: 18, + tileSize: 256, + subdomains: 'abc', + errorTileUrl: '', + attribution: '', + zoomOffset: 0, + opacity: 1, + /* + maxNativeZoom: null, + zIndex: null, + tms: false, + continuousWorld: false, + noWrap: false, + zoomReverse: false, + detectRetina: false, + reuseTiles: false, + bounds: false, + */ + unloadInvisibleTiles: L.Browser.mobile, + updateWhenIdle: L.Browser.mobile + }, + + initialize: function (url, options) { + options = L.setOptions(this, options); + + // detecting retina displays, adjusting tileSize and zoom levels + if (options.detectRetina && L.Browser.retina && options.maxZoom > 0) { + + options.tileSize = Math.floor(options.tileSize / 2); + options.zoomOffset++; + + if (options.minZoom > 0) { + options.minZoom--; + } + this.options.maxZoom--; + } + + if (options.bounds) { + options.bounds = L.latLngBounds(options.bounds); + } + + this._url = url; + + var subdomains = this.options.subdomains; + + if (typeof subdomains === 'string') { + this.options.subdomains = subdomains.split(''); + } + }, + + onAdd: function (map) { + this._map = map; + this._animated = map._zoomAnimated; + + // create a container div for tiles + this._initContainer(); + + // set up events + map.on({ + 'viewreset': this._reset, + 'moveend': this._update + }, this); + + if (this._animated) { + map.on({ + 'zoomanim': this._animateZoom, + 'zoomend': this._endZoomAnim + }, this); + } + + if (!this.options.updateWhenIdle) { + this._limitedUpdate = L.Util.limitExecByInterval(this._update, 150, this); + map.on('move', this._limitedUpdate, this); + } + + this._reset(); + this._update(); + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + onRemove: function (map) { + this._container.parentNode.removeChild(this._container); + + map.off({ + 'viewreset': this._reset, + 'moveend': this._update + }, this); + + if (this._animated) { + map.off({ + 'zoomanim': this._animateZoom, + 'zoomend': this._endZoomAnim + }, this); + } + + if (!this.options.updateWhenIdle) { + map.off('move', this._limitedUpdate, this); + } + + this._container = null; + this._map = null; + }, + + bringToFront: function () { + var pane = this._map._panes.tilePane; + + if (this._container) { + pane.appendChild(this._container); + this._setAutoZIndex(pane, Math.max); + } + + return this; + }, + + bringToBack: function () { + var pane = this._map._panes.tilePane; + + if (this._container) { + pane.insertBefore(this._container, pane.firstChild); + this._setAutoZIndex(pane, Math.min); + } + + return this; + }, + + getAttribution: function () { + return this.options.attribution; + }, + + getContainer: function () { + return this._container; + }, + + setOpacity: function (opacity) { + this.options.opacity = opacity; + + if (this._map) { + this._updateOpacity(); + } + + return this; + }, + + setZIndex: function (zIndex) { + this.options.zIndex = zIndex; + this._updateZIndex(); + + return this; + }, + + setUrl: function (url, noRedraw) { + this._url = url; + + if (!noRedraw) { + this.redraw(); + } + + return this; + }, + + redraw: function () { + if (this._map) { + this._reset({hard: true}); + this._update(); + } + return this; + }, + + _updateZIndex: function () { + if (this._container && this.options.zIndex !== undefined) { + this._container.style.zIndex = this.options.zIndex; + } + }, + + _setAutoZIndex: function (pane, compare) { + + var layers = pane.children, + edgeZIndex = -compare(Infinity, -Infinity), // -Infinity for max, Infinity for min + zIndex, i, len; + + for (i = 0, len = layers.length; i < len; i++) { + + if (layers[i] !== this._container) { + zIndex = parseInt(layers[i].style.zIndex, 10); + + if (!isNaN(zIndex)) { + edgeZIndex = compare(edgeZIndex, zIndex); + } + } + } + + this.options.zIndex = this._container.style.zIndex = + (isFinite(edgeZIndex) ? edgeZIndex : 0) + compare(1, -1); + }, + + _updateOpacity: function () { + var i, + tiles = this._tiles; + + if (L.Browser.ielt9) { + for (i in tiles) { + L.DomUtil.setOpacity(tiles[i], this.options.opacity); + } + } else { + L.DomUtil.setOpacity(this._container, this.options.opacity); + } + }, + + _initContainer: function () { + var tilePane = this._map._panes.tilePane; + + if (!this._container) { + this._container = L.DomUtil.create('div', 'leaflet-layer'); + + this._updateZIndex(); + + if (this._animated) { + var className = 'leaflet-tile-container'; + + this._bgBuffer = L.DomUtil.create('div', className, this._container); + this._tileContainer = L.DomUtil.create('div', className, this._container); + + } else { + this._tileContainer = this._container; + } + + tilePane.appendChild(this._container); + + if (this.options.opacity < 1) { + this._updateOpacity(); + } + } + }, + + _reset: function (e) { + for (var key in this._tiles) { + this.fire('tileunload', {tile: this._tiles[key]}); + } + + this._tiles = {}; + this._tilesToLoad = 0; + + if (this.options.reuseTiles) { + this._unusedTiles = []; + } + + this._tileContainer.innerHTML = ''; + + if (this._animated && e && e.hard) { + this._clearBgBuffer(); + } + + this._initContainer(); + }, + + _getTileSize: function () { + var map = this._map, + zoom = map.getZoom() + this.options.zoomOffset, + zoomN = this.options.maxNativeZoom, + tileSize = this.options.tileSize; + + if (zoomN && zoom > zoomN) { + tileSize = Math.round(map.getZoomScale(zoom) / map.getZoomScale(zoomN) * tileSize); + } + + return tileSize; + }, + + _update: function () { + + if (!this._map) { return; } + + var map = this._map, + bounds = map.getPixelBounds(), + zoom = map.getZoom(), + tileSize = this._getTileSize(); + + if (zoom > this.options.maxZoom || zoom < this.options.minZoom) { + return; + } + + var tileBounds = L.bounds( + bounds.min.divideBy(tileSize)._floor(), + bounds.max.divideBy(tileSize)._floor()); + + this._addTilesFromCenterOut(tileBounds); + + if (this.options.unloadInvisibleTiles || this.options.reuseTiles) { + this._removeOtherTiles(tileBounds); + } + }, + + _addTilesFromCenterOut: function (bounds) { + var queue = [], + center = bounds.getCenter(); + + var j, i, point; + + for (j = bounds.min.y; j <= bounds.max.y; j++) { + for (i = bounds.min.x; i <= bounds.max.x; i++) { + point = new L.Point(i, j); + + if (this._tileShouldBeLoaded(point)) { + queue.push(point); + } + } + } + + var tilesToLoad = queue.length; + + if (tilesToLoad === 0) { return; } + + // load tiles in order of their distance to center + queue.sort(function (a, b) { + return a.distanceTo(center) - b.distanceTo(center); + }); + + var fragment = document.createDocumentFragment(); + + // if its the first batch of tiles to load + if (!this._tilesToLoad) { + this.fire('loading'); + } + + this._tilesToLoad += tilesToLoad; + + for (i = 0; i < tilesToLoad; i++) { + this._addTile(queue[i], fragment); + } + + this._tileContainer.appendChild(fragment); + }, + + _tileShouldBeLoaded: function (tilePoint) { + if ((tilePoint.x + ':' + tilePoint.y) in this._tiles) { + return false; // already loaded + } + + var options = this.options; + + if (!options.continuousWorld) { + var limit = this._getWrapTileNum(); + + // don't load if exceeds world bounds + if ((options.noWrap && (tilePoint.x < 0 || tilePoint.x >= limit.x)) || + tilePoint.y < 0 || tilePoint.y >= limit.y) { return false; } + } + + if (options.bounds) { + var tileSize = this._getTileSize(), + nwPoint = tilePoint.multiplyBy(tileSize), + sePoint = nwPoint.add([tileSize, tileSize]), + nw = this._map.unproject(nwPoint), + se = this._map.unproject(sePoint); + + // TODO temporary hack, will be removed after refactoring projections + // https://github.com/Leaflet/Leaflet/issues/1618 + if (!options.continuousWorld && !options.noWrap) { + nw = nw.wrap(); + se = se.wrap(); + } + + if (!options.bounds.intersects([nw, se])) { return false; } + } + + return true; + }, + + _removeOtherTiles: function (bounds) { + var kArr, x, y, key; + + for (key in this._tiles) { + kArr = key.split(':'); + x = parseInt(kArr[0], 10); + y = parseInt(kArr[1], 10); + + // remove tile if it's out of bounds + if (x < bounds.min.x || x > bounds.max.x || y < bounds.min.y || y > bounds.max.y) { + this._removeTile(key); + } + } + }, + + _removeTile: function (key) { + var tile = this._tiles[key]; + + this.fire('tileunload', {tile: tile, url: tile.src}); + + if (this.options.reuseTiles) { + L.DomUtil.removeClass(tile, 'leaflet-tile-loaded'); + this._unusedTiles.push(tile); + + } else if (tile.parentNode === this._tileContainer) { + this._tileContainer.removeChild(tile); + } + + // for https://github.com/CloudMade/Leaflet/issues/137 + if (!L.Browser.android) { + tile.onload = null; + tile.src = L.Util.emptyImageUrl; + } + + delete this._tiles[key]; + }, + + _addTile: function (tilePoint, container) { + var tilePos = this._getTilePos(tilePoint); + + // get unused tile - or create a new tile + var tile = this._getTile(); + + /* + Chrome 20 layouts much faster with top/left (verify with timeline, frames) + Android 4 browser has display issues with top/left and requires transform instead + (other browsers don't currently care) - see debug/hacks/jitter.html for an example + */ + L.DomUtil.setPosition(tile, tilePos, L.Browser.chrome); + + this._tiles[tilePoint.x + ':' + tilePoint.y] = tile; + + this._loadTile(tile, tilePoint); + + if (tile.parentNode !== this._tileContainer) { + container.appendChild(tile); + } + }, + + _getZoomForUrl: function () { + + var options = this.options, + zoom = this._map.getZoom(); + + if (options.zoomReverse) { + zoom = options.maxZoom - zoom; + } + + zoom += options.zoomOffset; + + return options.maxNativeZoom ? Math.min(zoom, options.maxNativeZoom) : zoom; + }, + + _getTilePos: function (tilePoint) { + var origin = this._map.getPixelOrigin(), + tileSize = this._getTileSize(); + + return tilePoint.multiplyBy(tileSize).subtract(origin); + }, + + // image-specific code (override to implement e.g. Canvas or SVG tile layer) + + getTileUrl: function (tilePoint) { + return L.Util.template(this._url, L.extend({ + s: this._getSubdomain(tilePoint), + z: tilePoint.z, + x: tilePoint.x, + y: tilePoint.y + }, this.options)); + }, + + _getWrapTileNum: function () { + var crs = this._map.options.crs, + size = crs.getSize(this._map.getZoom()); + return size.divideBy(this._getTileSize())._floor(); + }, + + _adjustTilePoint: function (tilePoint) { + + var limit = this._getWrapTileNum(); + + // wrap tile coordinates + if (!this.options.continuousWorld && !this.options.noWrap) { + tilePoint.x = ((tilePoint.x % limit.x) + limit.x) % limit.x; + } + + if (this.options.tms) { + tilePoint.y = limit.y - tilePoint.y - 1; + } + + tilePoint.z = this._getZoomForUrl(); + }, + + _getSubdomain: function (tilePoint) { + var index = Math.abs(tilePoint.x + tilePoint.y) % this.options.subdomains.length; + return this.options.subdomains[index]; + }, + + _getTile: function () { + if (this.options.reuseTiles && this._unusedTiles.length > 0) { + var tile = this._unusedTiles.pop(); + this._resetTile(tile); + return tile; + } + return this._createTile(); + }, + + // Override if data stored on a tile needs to be cleaned up before reuse + _resetTile: function (/*tile*/) {}, + + _createTile: function () { + var tile = L.DomUtil.create('img', 'leaflet-tile'); + tile.style.width = tile.style.height = this._getTileSize() + 'px'; + tile.galleryimg = 'no'; + + tile.onselectstart = tile.onmousemove = L.Util.falseFn; + + if (L.Browser.ielt9 && this.options.opacity !== undefined) { + L.DomUtil.setOpacity(tile, this.options.opacity); + } + // without this hack, tiles disappear after zoom on Chrome for Android + // https://github.com/Leaflet/Leaflet/issues/2078 + if (L.Browser.mobileWebkit3d) { + tile.style.WebkitBackfaceVisibility = 'hidden'; + } + return tile; + }, + + _loadTile: function (tile, tilePoint) { + tile._layer = this; + tile.onload = this._tileOnLoad; + tile.onerror = this._tileOnError; + + this._adjustTilePoint(tilePoint); + tile.src = this.getTileUrl(tilePoint); + + this.fire('tileloadstart', { + tile: tile, + url: tile.src + }); + }, + + _tileLoaded: function () { + this._tilesToLoad--; + + if (this._animated) { + L.DomUtil.addClass(this._tileContainer, 'leaflet-zoom-animated'); + } + + if (!this._tilesToLoad) { + this.fire('load'); + + if (this._animated) { + // clear scaled tiles after all new tiles are loaded (for performance) + clearTimeout(this._clearBgBufferTimer); + this._clearBgBufferTimer = setTimeout(L.bind(this._clearBgBuffer, this), 500); + } + } + }, + + _tileOnLoad: function () { + var layer = this._layer; + + //Only if we are loading an actual image + if (this.src !== L.Util.emptyImageUrl) { + L.DomUtil.addClass(this, 'leaflet-tile-loaded'); + + layer.fire('tileload', { + tile: this, + url: this.src + }); + } + + layer._tileLoaded(); + }, + + _tileOnError: function () { + var layer = this._layer; + + layer.fire('tileerror', { + tile: this, + url: this.src + }); + + var newUrl = layer.options.errorTileUrl; + if (newUrl) { + this.src = newUrl; + } + + layer._tileLoaded(); + } +}); + +L.tileLayer = function (url, options) { + return new L.TileLayer(url, options); +}; + + +/* + * L.TileLayer.WMS is used for putting WMS tile layers on the map. + */ + +L.TileLayer.WMS = L.TileLayer.extend({ + + defaultWmsParams: { + service: 'WMS', + request: 'GetMap', + version: '1.1.1', + layers: '', + styles: '', + format: 'image/jpeg', + transparent: false + }, + + initialize: function (url, options) { // (String, Object) + + this._url = url; + + var wmsParams = L.extend({}, this.defaultWmsParams), + tileSize = options.tileSize || this.options.tileSize; + + if (options.detectRetina && L.Browser.retina) { + wmsParams.width = wmsParams.height = tileSize * 2; + } else { + wmsParams.width = wmsParams.height = tileSize; + } + + for (var i in options) { + // all keys that are not TileLayer options go to WMS params + if (!this.options.hasOwnProperty(i) && i !== 'crs') { + wmsParams[i] = options[i]; + } + } + + this.wmsParams = wmsParams; + + L.setOptions(this, options); + }, + + onAdd: function (map) { + + this._crs = this.options.crs || map.options.crs; + + this._wmsVersion = parseFloat(this.wmsParams.version); + + var projectionKey = this._wmsVersion >= 1.3 ? 'crs' : 'srs'; + this.wmsParams[projectionKey] = this._crs.code; + + L.TileLayer.prototype.onAdd.call(this, map); + }, + + getTileUrl: function (tilePoint) { // (Point, Number) -> String + + var map = this._map, + tileSize = this.options.tileSize, + + nwPoint = tilePoint.multiplyBy(tileSize), + sePoint = nwPoint.add([tileSize, tileSize]), + + nw = this._crs.project(map.unproject(nwPoint, tilePoint.z)), + se = this._crs.project(map.unproject(sePoint, tilePoint.z)), + bbox = this._wmsVersion >= 1.3 && this._crs === L.CRS.EPSG4326 ? + [se.y, nw.x, nw.y, se.x].join(',') : + [nw.x, se.y, se.x, nw.y].join(','), + + url = L.Util.template(this._url, {s: this._getSubdomain(tilePoint)}); + + return url + L.Util.getParamString(this.wmsParams, url, true) + '&BBOX=' + bbox; + }, + + setParams: function (params, noRedraw) { + + L.extend(this.wmsParams, params); + + if (!noRedraw) { + this.redraw(); + } + + return this; + } +}); + +L.tileLayer.wms = function (url, options) { + return new L.TileLayer.WMS(url, options); +}; + + +/* + * L.TileLayer.Canvas is a class that you can use as a base for creating + * dynamically drawn Canvas-based tile layers. + */ + +L.TileLayer.Canvas = L.TileLayer.extend({ + options: { + async: false + }, + + initialize: function (options) { + L.setOptions(this, options); + }, + + redraw: function () { + if (this._map) { + this._reset({hard: true}); + this._update(); + } + + for (var i in this._tiles) { + this._redrawTile(this._tiles[i]); + } + return this; + }, + + _redrawTile: function (tile) { + this.drawTile(tile, tile._tilePoint, this._map._zoom); + }, + + _createTile: function () { + var tile = L.DomUtil.create('canvas', 'leaflet-tile'); + tile.width = tile.height = this.options.tileSize; + tile.onselectstart = tile.onmousemove = L.Util.falseFn; + return tile; + }, + + _loadTile: function (tile, tilePoint) { + tile._layer = this; + tile._tilePoint = tilePoint; + + this._redrawTile(tile); + + if (!this.options.async) { + this.tileDrawn(tile); + } + }, + + drawTile: function (/*tile, tilePoint*/) { + // override with rendering code + }, + + tileDrawn: function (tile) { + this._tileOnLoad.call(tile); + } +}); + + +L.tileLayer.canvas = function (options) { + return new L.TileLayer.Canvas(options); +}; + + +/* + * L.ImageOverlay is used to overlay images over the map (to specific geographical bounds). + */ + +L.ImageOverlay = L.Class.extend({ + includes: L.Mixin.Events, + + options: { + opacity: 1 + }, + + initialize: function (url, bounds, options) { // (String, LatLngBounds, Object) + this._url = url; + this._bounds = L.latLngBounds(bounds); + + L.setOptions(this, options); + }, + + onAdd: function (map) { + this._map = map; + + if (!this._image) { + this._initImage(); + } + + map._panes.overlayPane.appendChild(this._image); + + map.on('viewreset', this._reset, this); + + if (map.options.zoomAnimation && L.Browser.any3d) { + map.on('zoomanim', this._animateZoom, this); + } + + this._reset(); + }, + + onRemove: function (map) { + map.getPanes().overlayPane.removeChild(this._image); + + map.off('viewreset', this._reset, this); + + if (map.options.zoomAnimation) { + map.off('zoomanim', this._animateZoom, this); + } + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + setOpacity: function (opacity) { + this.options.opacity = opacity; + this._updateOpacity(); + return this; + }, + + // TODO remove bringToFront/bringToBack duplication from TileLayer/Path + bringToFront: function () { + if (this._image) { + this._map._panes.overlayPane.appendChild(this._image); + } + return this; + }, + + bringToBack: function () { + var pane = this._map._panes.overlayPane; + if (this._image) { + pane.insertBefore(this._image, pane.firstChild); + } + return this; + }, + + setUrl: function (url) { + this._url = url; + this._image.src = this._url; + }, + + getAttribution: function () { + return this.options.attribution; + }, + + _initImage: function () { + this._image = L.DomUtil.create('img', 'leaflet-image-layer'); + + if (this._map.options.zoomAnimation && L.Browser.any3d) { + L.DomUtil.addClass(this._image, 'leaflet-zoom-animated'); + } else { + L.DomUtil.addClass(this._image, 'leaflet-zoom-hide'); + } + + this._updateOpacity(); + + //TODO createImage util method to remove duplication + L.extend(this._image, { + galleryimg: 'no', + onselectstart: L.Util.falseFn, + onmousemove: L.Util.falseFn, + onload: L.bind(this._onImageLoad, this), + src: this._url + }); + }, + + _animateZoom: function (e) { + var map = this._map, + image = this._image, + scale = map.getZoomScale(e.zoom), + nw = this._bounds.getNorthWest(), + se = this._bounds.getSouthEast(), + + topLeft = map._latLngToNewLayerPoint(nw, e.zoom, e.center), + size = map._latLngToNewLayerPoint(se, e.zoom, e.center)._subtract(topLeft), + origin = topLeft._add(size._multiplyBy((1 / 2) * (1 - 1 / scale))); + + image.style[L.DomUtil.TRANSFORM] = + L.DomUtil.getTranslateString(origin) + ' scale(' + scale + ') '; + }, + + _reset: function () { + var image = this._image, + topLeft = this._map.latLngToLayerPoint(this._bounds.getNorthWest()), + size = this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(topLeft); + + L.DomUtil.setPosition(image, topLeft); + + image.style.width = size.x + 'px'; + image.style.height = size.y + 'px'; + }, + + _onImageLoad: function () { + this.fire('load'); + }, + + _updateOpacity: function () { + L.DomUtil.setOpacity(this._image, this.options.opacity); + } +}); + +L.imageOverlay = function (url, bounds, options) { + return new L.ImageOverlay(url, bounds, options); +}; + + +/* + * L.Icon is an image-based icon class that you can use with L.Marker for custom markers. + */ + +L.Icon = L.Class.extend({ + options: { + /* + iconUrl: (String) (required) + iconRetinaUrl: (String) (optional, used for retina devices if detected) + iconSize: (Point) (can be set through CSS) + iconAnchor: (Point) (centered by default, can be set in CSS with negative margins) + popupAnchor: (Point) (if not specified, popup opens in the anchor point) + shadowUrl: (String) (no shadow by default) + shadowRetinaUrl: (String) (optional, used for retina devices if detected) + shadowSize: (Point) + shadowAnchor: (Point) + */ + className: '' + }, + + initialize: function (options) { + L.setOptions(this, options); + }, + + createIcon: function (oldIcon) { + return this._createIcon('icon', oldIcon); + }, + + createShadow: function (oldIcon) { + return this._createIcon('shadow', oldIcon); + }, + + _createIcon: function (name, oldIcon) { + var src = this._getIconUrl(name); + + if (!src) { + if (name === 'icon') { + throw new Error('iconUrl not set in Icon options (see the docs).'); + } + return null; + } + + var img; + if (!oldIcon || oldIcon.tagName !== 'IMG') { + img = this._createImg(src); + } else { + img = this._createImg(src, oldIcon); + } + this._setIconStyles(img, name); + + return img; + }, + + _setIconStyles: function (img, name) { + var options = this.options, + size = L.point(options[name + 'Size']), + anchor; + + if (name === 'shadow') { + anchor = L.point(options.shadowAnchor || options.iconAnchor); + } else { + anchor = L.point(options.iconAnchor); + } + + if (!anchor && size) { + anchor = size.divideBy(2, true); + } + + img.className = 'leaflet-marker-' + name + ' ' + options.className; + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + } + + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; + } + }, + + _createImg: function (src, el) { + el = el || document.createElement('img'); + el.src = src; + return el; + }, + + _getIconUrl: function (name) { + if (L.Browser.retina && this.options[name + 'RetinaUrl']) { + return this.options[name + 'RetinaUrl']; + } + return this.options[name + 'Url']; + } +}); + +L.icon = function (options) { + return new L.Icon(options); +}; + + +/* + * L.Icon.Default is the blue marker icon used by default in Leaflet. + */ + +L.Icon.Default = L.Icon.extend({ + + options: { + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + + shadowSize: [41, 41] + }, + + _getIconUrl: function (name) { + var key = name + 'Url'; + + if (this.options[key]) { + return this.options[key]; + } + + if (L.Browser.retina && name === 'icon') { + return "/assets/marker-icon-2x-454dc479e82b487529b6b93d6a9b29ac69ca7b4f5a9d5fdf8e01871f6d216113.png"; + } + + if (name == 'shadow') { + return "/assets/marker-shadow-4f340d2d61746333dffe056e074ce1704ae4e47fec5a7de98322fbdbcfcb2b6d.png"; + } else { + return "/assets/marker-icon-915e83a6fc798c599e5c9e3f759d6bc065d65151019acd0410d1f4731bcaaf72.png"; + } + } +}); + +L.Icon.Default.imagePath = (function () { + var scripts = document.getElementsByTagName('script'), + leafletRe = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/; + + var i, len, src, matches, path; + + for (i = 0, len = scripts.length; i < len; i++) { + src = scripts[i].src; + matches = src.match(leafletRe); + + if (matches) { + path = src.split(leafletRe)[0]; + return (path ? path + '/' : ''); + } + } +}()); + + +/* + * L.Marker is used to display clickable/draggable icons on the map. + */ + +L.Marker = L.Class.extend({ + + includes: L.Mixin.Events, + + options: { + icon: new L.Icon.Default(), + title: '', + alt: '', + clickable: true, + draggable: false, + keyboard: true, + zIndexOffset: 0, + opacity: 1, + riseOnHover: false, + riseOffset: 250 + }, + + initialize: function (latlng, options) { + L.setOptions(this, options); + this._latlng = L.latLng(latlng); + }, + + onAdd: function (map) { + this._map = map; + + map.on('viewreset', this.update, this); + + this._initIcon(); + this.update(); + this.fire('add'); + + if (map.options.zoomAnimation && map.options.markerZoomAnimation) { + map.on('zoomanim', this._animateZoom, this); + } + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + onRemove: function (map) { + if (this.dragging) { + this.dragging.disable(); + } + + this._removeIcon(); + this._removeShadow(); + + this.fire('remove'); + + map.off({ + 'viewreset': this.update, + 'zoomanim': this._animateZoom + }, this); + + this._map = null; + }, + + getLatLng: function () { + return this._latlng; + }, + + setLatLng: function (latlng) { + this._latlng = L.latLng(latlng); + + this.update(); + + return this.fire('move', { latlng: this._latlng }); + }, + + setZIndexOffset: function (offset) { + this.options.zIndexOffset = offset; + this.update(); + + return this; + }, + + setIcon: function (icon) { + + this.options.icon = icon; + + if (this._map) { + this._initIcon(); + this.update(); + } + + if (this._popup) { + this.bindPopup(this._popup); + } + + return this; + }, + + update: function () { + if (this._icon) { + this._setPos(this._map.latLngToLayerPoint(this._latlng).round()); + } + return this; + }, + + _initIcon: function () { + var options = this.options, + map = this._map, + animation = (map.options.zoomAnimation && map.options.markerZoomAnimation), + classToAdd = animation ? 'leaflet-zoom-animated' : 'leaflet-zoom-hide'; + + var icon = options.icon.createIcon(this._icon), + addIcon = false; + + // if we're not reusing the icon, remove the old one and init new one + if (icon !== this._icon) { + if (this._icon) { + this._removeIcon(); + } + addIcon = true; + + if (options.title) { + icon.title = options.title; + } + + if (options.alt) { + icon.alt = options.alt; + } + } + + L.DomUtil.addClass(icon, classToAdd); + + if (options.keyboard) { + icon.tabIndex = '0'; + } + + this._icon = icon; + + this._initInteraction(); + + if (options.riseOnHover) { + L.DomEvent + .on(icon, 'mouseover', this._bringToFront, this) + .on(icon, 'mouseout', this._resetZIndex, this); + } + + var newShadow = options.icon.createShadow(this._shadow), + addShadow = false; + + if (newShadow !== this._shadow) { + this._removeShadow(); + addShadow = true; + } + + if (newShadow) { + L.DomUtil.addClass(newShadow, classToAdd); + } + this._shadow = newShadow; + + + if (options.opacity < 1) { + this._updateOpacity(); + } + + + var panes = this._map._panes; + + if (addIcon) { + panes.markerPane.appendChild(this._icon); + } + + if (newShadow && addShadow) { + panes.shadowPane.appendChild(this._shadow); + } + }, + + _removeIcon: function () { + if (this.options.riseOnHover) { + L.DomEvent + .off(this._icon, 'mouseover', this._bringToFront) + .off(this._icon, 'mouseout', this._resetZIndex); + } + + this._map._panes.markerPane.removeChild(this._icon); + + this._icon = null; + }, + + _removeShadow: function () { + if (this._shadow) { + this._map._panes.shadowPane.removeChild(this._shadow); + } + this._shadow = null; + }, + + _setPos: function (pos) { + L.DomUtil.setPosition(this._icon, pos); + + if (this._shadow) { + L.DomUtil.setPosition(this._shadow, pos); + } + + this._zIndex = pos.y + this.options.zIndexOffset; + + this._resetZIndex(); + }, + + _updateZIndex: function (offset) { + this._icon.style.zIndex = this._zIndex + offset; + }, + + _animateZoom: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round(); + + this._setPos(pos); + }, + + _initInteraction: function () { + + if (!this.options.clickable) { return; } + + // TODO refactor into something shared with Map/Path/etc. to DRY it up + + var icon = this._icon, + events = ['dblclick', 'mousedown', 'mouseover', 'mouseout', 'contextmenu']; + + L.DomUtil.addClass(icon, 'leaflet-clickable'); + L.DomEvent.on(icon, 'click', this._onMouseClick, this); + L.DomEvent.on(icon, 'keypress', this._onKeyPress, this); + + for (var i = 0; i < events.length; i++) { + L.DomEvent.on(icon, events[i], this._fireMouseEvent, this); + } + + if (L.Handler.MarkerDrag) { + this.dragging = new L.Handler.MarkerDrag(this); + + if (this.options.draggable) { + this.dragging.enable(); + } + } + }, + + _onMouseClick: function (e) { + var wasDragged = this.dragging && this.dragging.moved(); + + if (this.hasEventListeners(e.type) || wasDragged) { + L.DomEvent.stopPropagation(e); + } + + if (wasDragged) { return; } + + if ((!this.dragging || !this.dragging._enabled) && this._map.dragging && this._map.dragging.moved()) { return; } + + this.fire(e.type, { + originalEvent: e, + latlng: this._latlng + }); + }, + + _onKeyPress: function (e) { + if (e.keyCode === 13) { + this.fire('click', { + originalEvent: e, + latlng: this._latlng + }); + } + }, + + _fireMouseEvent: function (e) { + + this.fire(e.type, { + originalEvent: e, + latlng: this._latlng + }); + + // TODO proper custom event propagation + // this line will always be called if marker is in a FeatureGroup + if (e.type === 'contextmenu' && this.hasEventListeners(e.type)) { + L.DomEvent.preventDefault(e); + } + if (e.type !== 'mousedown') { + L.DomEvent.stopPropagation(e); + } else { + L.DomEvent.preventDefault(e); + } + }, + + setOpacity: function (opacity) { + this.options.opacity = opacity; + if (this._map) { + this._updateOpacity(); + } + + return this; + }, + + _updateOpacity: function () { + L.DomUtil.setOpacity(this._icon, this.options.opacity); + if (this._shadow) { + L.DomUtil.setOpacity(this._shadow, this.options.opacity); + } + }, + + _bringToFront: function () { + this._updateZIndex(this.options.riseOffset); + }, + + _resetZIndex: function () { + this._updateZIndex(0); + } +}); + +L.marker = function (latlng, options) { + return new L.Marker(latlng, options); +}; + + +/* + * L.DivIcon is a lightweight HTML-based icon class (as opposed to the image-based L.Icon) + * to use with L.Marker. + */ + +L.DivIcon = L.Icon.extend({ + options: { + iconSize: [12, 12], // also can be set through CSS + /* + iconAnchor: (Point) + popupAnchor: (Point) + html: (String) + bgPos: (Point) + */ + className: 'leaflet-div-icon', + html: false + }, + + createIcon: function (oldIcon) { + var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), + options = this.options; + + if (options.html !== false) { + div.innerHTML = options.html; + } else { + div.innerHTML = ''; + } + + if (options.bgPos) { + div.style.backgroundPosition = + (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px'; + } + + this._setIconStyles(div, 'icon'); + return div; + }, + + createShadow: function () { + return null; + } +}); + +L.divIcon = function (options) { + return new L.DivIcon(options); +}; + + +/* + * L.Popup is used for displaying popups on the map. + */ + +L.Map.mergeOptions({ + closePopupOnClick: true +}); + +L.Popup = L.Class.extend({ + includes: L.Mixin.Events, + + options: { + minWidth: 50, + maxWidth: 300, + // maxHeight: null, + autoPan: true, + closeButton: true, + offset: [0, 7], + autoPanPadding: [5, 5], + // autoPanPaddingTopLeft: null, + // autoPanPaddingBottomRight: null, + keepInView: false, + className: '', + zoomAnimation: true + }, + + initialize: function (options, source) { + L.setOptions(this, options); + + this._source = source; + this._animated = L.Browser.any3d && this.options.zoomAnimation; + this._isOpen = false; + }, + + onAdd: function (map) { + this._map = map; + + if (!this._container) { + this._initLayout(); + } + + var animFade = map.options.fadeAnimation; + + if (animFade) { + L.DomUtil.setOpacity(this._container, 0); + } + map._panes.popupPane.appendChild(this._container); + + map.on(this._getEvents(), this); + + this.update(); + + if (animFade) { + L.DomUtil.setOpacity(this._container, 1); + } + + this.fire('open'); + + map.fire('popupopen', {popup: this}); + + if (this._source) { + this._source.fire('popupopen', {popup: this}); + } + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + openOn: function (map) { + map.openPopup(this); + return this; + }, + + onRemove: function (map) { + map._panes.popupPane.removeChild(this._container); + + L.Util.falseFn(this._container.offsetWidth); // force reflow + + map.off(this._getEvents(), this); + + if (map.options.fadeAnimation) { + L.DomUtil.setOpacity(this._container, 0); + } + + this._map = null; + + this.fire('close'); + + map.fire('popupclose', {popup: this}); + + if (this._source) { + this._source.fire('popupclose', {popup: this}); + } + }, + + getLatLng: function () { + return this._latlng; + }, + + setLatLng: function (latlng) { + this._latlng = L.latLng(latlng); + if (this._map) { + this._updatePosition(); + this._adjustPan(); + } + return this; + }, + + getContent: function () { + return this._content; + }, + + setContent: function (content) { + this._content = content; + this.update(); + return this; + }, + + update: function () { + if (!this._map) { return; } + + this._container.style.visibility = 'hidden'; + + this._updateContent(); + this._updateLayout(); + this._updatePosition(); + + this._container.style.visibility = ''; + + this._adjustPan(); + }, + + _getEvents: function () { + var events = { + viewreset: this._updatePosition + }; + + if (this._animated) { + events.zoomanim = this._zoomAnimation; + } + if ('closeOnClick' in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) { + events.preclick = this._close; + } + if (this.options.keepInView) { + events.moveend = this._adjustPan; + } + + return events; + }, + + _close: function () { + if (this._map) { + this._map.closePopup(this); + } + }, + + _initLayout: function () { + var prefix = 'leaflet-popup', + containerClass = prefix + ' ' + this.options.className + ' leaflet-zoom-' + + (this._animated ? 'animated' : 'hide'), + container = this._container = L.DomUtil.create('div', containerClass), + closeButton; + + if (this.options.closeButton) { + closeButton = this._closeButton = + L.DomUtil.create('a', prefix + '-close-button', container); + closeButton.href = '#close'; + closeButton.innerHTML = '×'; + L.DomEvent.disableClickPropagation(closeButton); + + L.DomEvent.on(closeButton, 'click', this._onCloseButtonClick, this); + } + + var wrapper = this._wrapper = + L.DomUtil.create('div', prefix + '-content-wrapper', container); + L.DomEvent.disableClickPropagation(wrapper); + + this._contentNode = L.DomUtil.create('div', prefix + '-content', wrapper); + + L.DomEvent.disableScrollPropagation(this._contentNode); + L.DomEvent.on(wrapper, 'contextmenu', L.DomEvent.stopPropagation); + + this._tipContainer = L.DomUtil.create('div', prefix + '-tip-container', container); + this._tip = L.DomUtil.create('div', prefix + '-tip', this._tipContainer); + }, + + _updateContent: function () { + if (!this._content) { return; } + + if (typeof this._content === 'string') { + this._contentNode.innerHTML = this._content; + } else { + while (this._contentNode.hasChildNodes()) { + this._contentNode.removeChild(this._contentNode.firstChild); + } + this._contentNode.appendChild(this._content); + } + this.fire('contentupdate'); + }, + + _updateLayout: function () { + var container = this._contentNode, + style = container.style; + + style.width = ''; + style.whiteSpace = 'nowrap'; + + var width = container.offsetWidth; + width = Math.min(width, this.options.maxWidth); + width = Math.max(width, this.options.minWidth); + + style.width = (width + 1) + 'px'; + style.whiteSpace = ''; + + style.height = ''; + + var height = container.offsetHeight, + maxHeight = this.options.maxHeight, + scrolledClass = 'leaflet-popup-scrolled'; + + if (maxHeight && height > maxHeight) { + style.height = maxHeight + 'px'; + L.DomUtil.addClass(container, scrolledClass); + } else { + L.DomUtil.removeClass(container, scrolledClass); + } + + this._containerWidth = this._container.offsetWidth; + }, + + _updatePosition: function () { + if (!this._map) { return; } + + var pos = this._map.latLngToLayerPoint(this._latlng), + animated = this._animated, + offset = L.point(this.options.offset); + + if (animated) { + L.DomUtil.setPosition(this._container, pos); + } + + this._containerBottom = -offset.y - (animated ? 0 : pos.y); + this._containerLeft = -Math.round(this._containerWidth / 2) + offset.x + (animated ? 0 : pos.x); + + // bottom position the popup in case the height of the popup changes (images loading etc) + this._container.style.bottom = this._containerBottom + 'px'; + this._container.style.left = this._containerLeft + 'px'; + }, + + _zoomAnimation: function (opt) { + var pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center); + + L.DomUtil.setPosition(this._container, pos); + }, + + _adjustPan: function () { + if (!this.options.autoPan) { return; } + + var map = this._map, + containerHeight = this._container.offsetHeight, + containerWidth = this._containerWidth, + + layerPos = new L.Point(this._containerLeft, -containerHeight - this._containerBottom); + + if (this._animated) { + layerPos._add(L.DomUtil.getPosition(this._container)); + } + + var containerPos = map.layerPointToContainerPoint(layerPos), + padding = L.point(this.options.autoPanPadding), + paddingTL = L.point(this.options.autoPanPaddingTopLeft || padding), + paddingBR = L.point(this.options.autoPanPaddingBottomRight || padding), + size = map.getSize(), + dx = 0, + dy = 0; + + if (containerPos.x + containerWidth + paddingBR.x > size.x) { // right + dx = containerPos.x + containerWidth - size.x + paddingBR.x; + } + if (containerPos.x - dx - paddingTL.x < 0) { // left + dx = containerPos.x - paddingTL.x; + } + if (containerPos.y + containerHeight + paddingBR.y > size.y) { // bottom + dy = containerPos.y + containerHeight - size.y + paddingBR.y; + } + if (containerPos.y - dy - paddingTL.y < 0) { // top + dy = containerPos.y - paddingTL.y; + } + + if (dx || dy) { + map + .fire('autopanstart') + .panBy([dx, dy]); + } + }, + + _onCloseButtonClick: function (e) { + this._close(); + L.DomEvent.stop(e); + } +}); + +L.popup = function (options, source) { + return new L.Popup(options, source); +}; + + +L.Map.include({ + openPopup: function (popup, latlng, options) { // (Popup) or (String || HTMLElement, LatLng[, Object]) + this.closePopup(); + + if (!(popup instanceof L.Popup)) { + var content = popup; + + popup = new L.Popup(options) + .setLatLng(latlng) + .setContent(content); + } + popup._isOpen = true; + + this._popup = popup; + return this.addLayer(popup); + }, + + closePopup: function (popup) { + if (!popup || popup === this._popup) { + popup = this._popup; + this._popup = null; + } + if (popup) { + this.removeLayer(popup); + popup._isOpen = false; + } + return this; + } +}); + + +/* + * Popup extension to L.Marker, adding popup-related methods. + */ + +L.Marker.include({ + openPopup: function () { + if (this._popup && this._map && !this._map.hasLayer(this._popup)) { + this._popup.setLatLng(this._latlng); + this._map.openPopup(this._popup); + } + + return this; + }, + + closePopup: function () { + if (this._popup) { + this._popup._close(); + } + return this; + }, + + togglePopup: function () { + if (this._popup) { + if (this._popup._isOpen) { + this.closePopup(); + } else { + this.openPopup(); + } + } + return this; + }, + + bindPopup: function (content, options) { + var anchor = L.point(this.options.icon.options.popupAnchor || [0, 0]); + + anchor = anchor.add(L.Popup.prototype.options.offset); + + if (options && options.offset) { + anchor = anchor.add(options.offset); + } + + options = L.extend({offset: anchor}, options); + + if (!this._popupHandlersAdded) { + this + .on('click', this.togglePopup, this) + .on('remove', this.closePopup, this) + .on('move', this._movePopup, this); + this._popupHandlersAdded = true; + } + + if (content instanceof L.Popup) { + L.setOptions(content, options); + this._popup = content; + content._source = this; + } else { + this._popup = new L.Popup(options, this) + .setContent(content); + } + + return this; + }, + + setPopupContent: function (content) { + if (this._popup) { + this._popup.setContent(content); + } + return this; + }, + + unbindPopup: function () { + if (this._popup) { + this._popup = null; + this + .off('click', this.togglePopup, this) + .off('remove', this.closePopup, this) + .off('move', this._movePopup, this); + this._popupHandlersAdded = false; + } + return this; + }, + + getPopup: function () { + return this._popup; + }, + + _movePopup: function (e) { + this._popup.setLatLng(e.latlng); + } +}); + + +/* + * L.LayerGroup is a class to combine several layers into one so that + * you can manipulate the group (e.g. add/remove it) as one layer. + */ + +L.LayerGroup = L.Class.extend({ + initialize: function (layers) { + this._layers = {}; + + var i, len; + + if (layers) { + for (i = 0, len = layers.length; i < len; i++) { + this.addLayer(layers[i]); + } + } + }, + + addLayer: function (layer) { + var id = this.getLayerId(layer); + + this._layers[id] = layer; + + if (this._map) { + this._map.addLayer(layer); + } + + return this; + }, + + removeLayer: function (layer) { + var id = layer in this._layers ? layer : this.getLayerId(layer); + + if (this._map && this._layers[id]) { + this._map.removeLayer(this._layers[id]); + } + + delete this._layers[id]; + + return this; + }, + + hasLayer: function (layer) { + if (!layer) { return false; } + + return (layer in this._layers || this.getLayerId(layer) in this._layers); + }, + + clearLayers: function () { + this.eachLayer(this.removeLayer, this); + return this; + }, + + invoke: function (methodName) { + var args = Array.prototype.slice.call(arguments, 1), + i, layer; + + for (i in this._layers) { + layer = this._layers[i]; + + if (layer[methodName]) { + layer[methodName].apply(layer, args); + } + } + + return this; + }, + + onAdd: function (map) { + this._map = map; + this.eachLayer(map.addLayer, map); + }, + + onRemove: function (map) { + this.eachLayer(map.removeLayer, map); + this._map = null; + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + eachLayer: function (method, context) { + for (var i in this._layers) { + method.call(context, this._layers[i]); + } + return this; + }, + + getLayer: function (id) { + return this._layers[id]; + }, + + getLayers: function () { + var layers = []; + + for (var i in this._layers) { + layers.push(this._layers[i]); + } + return layers; + }, + + setZIndex: function (zIndex) { + return this.invoke('setZIndex', zIndex); + }, + + getLayerId: function (layer) { + return L.stamp(layer); + } +}); + +L.layerGroup = function (layers) { + return new L.LayerGroup(layers); +}; + + +/* + * L.FeatureGroup extends L.LayerGroup by introducing mouse events and additional methods + * shared between a group of interactive layers (like vectors or markers). + */ + +L.FeatureGroup = L.LayerGroup.extend({ + includes: L.Mixin.Events, + + statics: { + EVENTS: 'click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose' + }, + + addLayer: function (layer) { + if (this.hasLayer(layer)) { + return this; + } + + if ('on' in layer) { + layer.on(L.FeatureGroup.EVENTS, this._propagateEvent, this); + } + + L.LayerGroup.prototype.addLayer.call(this, layer); + + if (this._popupContent && layer.bindPopup) { + layer.bindPopup(this._popupContent, this._popupOptions); + } + + return this.fire('layeradd', {layer: layer}); + }, + + removeLayer: function (layer) { + if (!this.hasLayer(layer)) { + return this; + } + if (layer in this._layers) { + layer = this._layers[layer]; + } + + if ('off' in layer) { + layer.off(L.FeatureGroup.EVENTS, this._propagateEvent, this); + } + + L.LayerGroup.prototype.removeLayer.call(this, layer); + + if (this._popupContent) { + this.invoke('unbindPopup'); + } + + return this.fire('layerremove', {layer: layer}); + }, + + bindPopup: function (content, options) { + this._popupContent = content; + this._popupOptions = options; + return this.invoke('bindPopup', content, options); + }, + + openPopup: function (latlng) { + // open popup on the first layer + for (var id in this._layers) { + this._layers[id].openPopup(latlng); + break; + } + return this; + }, + + setStyle: function (style) { + return this.invoke('setStyle', style); + }, + + bringToFront: function () { + return this.invoke('bringToFront'); + }, + + bringToBack: function () { + return this.invoke('bringToBack'); + }, + + getBounds: function () { + var bounds = new L.LatLngBounds(); + + this.eachLayer(function (layer) { + bounds.extend(layer instanceof L.Marker ? layer.getLatLng() : layer.getBounds()); + }); + + return bounds; + }, + + _propagateEvent: function (e) { + e = L.extend({ + layer: e.target, + target: this + }, e); + this.fire(e.type, e); + } +}); + +L.featureGroup = function (layers) { + return new L.FeatureGroup(layers); +}; + + +/* + * L.Path is a base class for rendering vector paths on a map. Inherited by Polyline, Circle, etc. + */ + +L.Path = L.Class.extend({ + includes: [L.Mixin.Events], + + statics: { + // how much to extend the clip area around the map view + // (relative to its size, e.g. 0.5 is half the screen in each direction) + // set it so that SVG element doesn't exceed 1280px (vectors flicker on dragend if it is) + CLIP_PADDING: (function () { + var max = L.Browser.mobile ? 1280 : 2000, + target = (max / Math.max(window.outerWidth, window.outerHeight) - 1) / 2; + return Math.max(0, Math.min(0.5, target)); + })() + }, + + options: { + stroke: true, + color: '#0033ff', + dashArray: null, + lineCap: null, + lineJoin: null, + weight: 5, + opacity: 0.5, + + fill: false, + fillColor: null, //same as color by default + fillOpacity: 0.2, + + clickable: true + }, + + initialize: function (options) { + L.setOptions(this, options); + }, + + onAdd: function (map) { + this._map = map; + + if (!this._container) { + this._initElements(); + this._initEvents(); + } + + this.projectLatlngs(); + this._updatePath(); + + if (this._container) { + this._map._pathRoot.appendChild(this._container); + } + + this.fire('add'); + + map.on({ + 'viewreset': this.projectLatlngs, + 'moveend': this._updatePath + }, this); + }, + + addTo: function (map) { + map.addLayer(this); + return this; + }, + + onRemove: function (map) { + map._pathRoot.removeChild(this._container); + + // Need to fire remove event before we set _map to null as the event hooks might need the object + this.fire('remove'); + this._map = null; + + if (L.Browser.vml) { + this._container = null; + this._stroke = null; + this._fill = null; + } + + map.off({ + 'viewreset': this.projectLatlngs, + 'moveend': this._updatePath + }, this); + }, + + projectLatlngs: function () { + // do all projection stuff here + }, + + setStyle: function (style) { + L.setOptions(this, style); + + if (this._container) { + this._updateStyle(); + } + + return this; + }, + + redraw: function () { + if (this._map) { + this.projectLatlngs(); + this._updatePath(); + } + return this; + } +}); + +L.Map.include({ + _updatePathViewport: function () { + var p = L.Path.CLIP_PADDING, + size = this.getSize(), + panePos = L.DomUtil.getPosition(this._mapPane), + min = panePos.multiplyBy(-1)._subtract(size.multiplyBy(p)._round()), + max = min.add(size.multiplyBy(1 + p * 2)._round()); + + this._pathViewport = new L.Bounds(min, max); + } +}); + + +/* + * Extends L.Path with SVG-specific rendering code. + */ + +L.Path.SVG_NS = 'http://www.w3.org/2000/svg'; + +L.Browser.svg = !!(document.createElementNS && document.createElementNS(L.Path.SVG_NS, 'svg').createSVGRect); + +L.Path = L.Path.extend({ + statics: { + SVG: L.Browser.svg + }, + + bringToFront: function () { + var root = this._map._pathRoot, + path = this._container; + + if (path && root.lastChild !== path) { + root.appendChild(path); + } + return this; + }, + + bringToBack: function () { + var root = this._map._pathRoot, + path = this._container, + first = root.firstChild; + + if (path && first !== path) { + root.insertBefore(path, first); + } + return this; + }, + + getPathString: function () { + // form path string here + }, + + _createElement: function (name) { + return document.createElementNS(L.Path.SVG_NS, name); + }, + + _initElements: function () { + this._map._initPathRoot(); + this._initPath(); + this._initStyle(); + }, + + _initPath: function () { + this._container = this._createElement('g'); + + this._path = this._createElement('path'); + + if (this.options.className) { + L.DomUtil.addClass(this._path, this.options.className); + } + + this._container.appendChild(this._path); + }, + + _initStyle: function () { + if (this.options.stroke) { + this._path.setAttribute('stroke-linejoin', 'round'); + this._path.setAttribute('stroke-linecap', 'round'); + } + if (this.options.fill) { + this._path.setAttribute('fill-rule', 'evenodd'); + } + if (this.options.pointerEvents) { + this._path.setAttribute('pointer-events', this.options.pointerEvents); + } + if (!this.options.clickable && !this.options.pointerEvents) { + this._path.setAttribute('pointer-events', 'none'); + } + this._updateStyle(); + }, + + _updateStyle: function () { + if (this.options.stroke) { + this._path.setAttribute('stroke', this.options.color); + this._path.setAttribute('stroke-opacity', this.options.opacity); + this._path.setAttribute('stroke-width', this.options.weight); + if (this.options.dashArray) { + this._path.setAttribute('stroke-dasharray', this.options.dashArray); + } else { + this._path.removeAttribute('stroke-dasharray'); + } + if (this.options.lineCap) { + this._path.setAttribute('stroke-linecap', this.options.lineCap); + } + if (this.options.lineJoin) { + this._path.setAttribute('stroke-linejoin', this.options.lineJoin); + } + } else { + this._path.setAttribute('stroke', 'none'); + } + if (this.options.fill) { + this._path.setAttribute('fill', this.options.fillColor || this.options.color); + this._path.setAttribute('fill-opacity', this.options.fillOpacity); + } else { + this._path.setAttribute('fill', 'none'); + } + }, + + _updatePath: function () { + var str = this.getPathString(); + if (!str) { + // fix webkit empty string parsing bug + str = 'M0 0'; + } + this._path.setAttribute('d', str); + }, + + // TODO remove duplication with L.Map + _initEvents: function () { + if (this.options.clickable) { + if (L.Browser.svg || !L.Browser.vml) { + L.DomUtil.addClass(this._path, 'leaflet-clickable'); + } + + L.DomEvent.on(this._container, 'click', this._onMouseClick, this); + + var events = ['dblclick', 'mousedown', 'mouseover', + 'mouseout', 'mousemove', 'contextmenu']; + for (var i = 0; i < events.length; i++) { + L.DomEvent.on(this._container, events[i], this._fireMouseEvent, this); + } + } + }, + + _onMouseClick: function (e) { + if (this._map.dragging && this._map.dragging.moved()) { return; } + + this._fireMouseEvent(e); + }, + + _fireMouseEvent: function (e) { + if (!this._map || !this.hasEventListeners(e.type)) { return; } + + var map = this._map, + containerPoint = map.mouseEventToContainerPoint(e), + layerPoint = map.containerPointToLayerPoint(containerPoint), + latlng = map.layerPointToLatLng(layerPoint); + + this.fire(e.type, { + latlng: latlng, + layerPoint: layerPoint, + containerPoint: containerPoint, + originalEvent: e + }); + + if (e.type === 'contextmenu') { + L.DomEvent.preventDefault(e); + } + if (e.type !== 'mousemove') { + L.DomEvent.stopPropagation(e); + } + } +}); + +L.Map.include({ + _initPathRoot: function () { + if (!this._pathRoot) { + this._pathRoot = L.Path.prototype._createElement('svg'); + this._panes.overlayPane.appendChild(this._pathRoot); + + if (this.options.zoomAnimation && L.Browser.any3d) { + L.DomUtil.addClass(this._pathRoot, 'leaflet-zoom-animated'); + + this.on({ + 'zoomanim': this._animatePathZoom, + 'zoomend': this._endPathZoom + }); + } else { + L.DomUtil.addClass(this._pathRoot, 'leaflet-zoom-hide'); + } + + this.on('moveend', this._updateSvgViewport); + this._updateSvgViewport(); + } + }, + + _animatePathZoom: function (e) { + var scale = this.getZoomScale(e.zoom), + offset = this._getCenterOffset(e.center)._multiplyBy(-scale)._add(this._pathViewport.min); + + this._pathRoot.style[L.DomUtil.TRANSFORM] = + L.DomUtil.getTranslateString(offset) + ' scale(' + scale + ') '; + + this._pathZooming = true; + }, + + _endPathZoom: function () { + this._pathZooming = false; + }, + + _updateSvgViewport: function () { + + if (this._pathZooming) { + // Do not update SVGs while a zoom animation is going on otherwise the animation will break. + // When the zoom animation ends we will be updated again anyway + // This fixes the case where you do a momentum move and zoom while the move is still ongoing. + return; + } + + this._updatePathViewport(); + + var vp = this._pathViewport, + min = vp.min, + max = vp.max, + width = max.x - min.x, + height = max.y - min.y, + root = this._pathRoot, + pane = this._panes.overlayPane; + + // Hack to make flicker on drag end on mobile webkit less irritating + if (L.Browser.mobileWebkit) { + pane.removeChild(root); + } + + L.DomUtil.setPosition(root, min); + root.setAttribute('width', width); + root.setAttribute('height', height); + root.setAttribute('viewBox', [min.x, min.y, width, height].join(' ')); + + if (L.Browser.mobileWebkit) { + pane.appendChild(root); + } + } +}); + + +/* + * Popup extension to L.Path (polylines, polygons, circles), adding popup-related methods. + */ + +L.Path.include({ + + bindPopup: function (content, options) { + + if (content instanceof L.Popup) { + this._popup = content; + } else { + if (!this._popup || options) { + this._popup = new L.Popup(options, this); + } + this._popup.setContent(content); + } + + if (!this._popupHandlersAdded) { + this + .on('click', this._openPopup, this) + .on('remove', this.closePopup, this); + + this._popupHandlersAdded = true; + } + + return this; + }, + + unbindPopup: function () { + if (this._popup) { + this._popup = null; + this + .off('click', this._openPopup) + .off('remove', this.closePopup); + + this._popupHandlersAdded = false; + } + return this; + }, + + openPopup: function (latlng) { + + if (this._popup) { + // open the popup from one of the path's points if not specified + latlng = latlng || this._latlng || + this._latlngs[Math.floor(this._latlngs.length / 2)]; + + this._openPopup({latlng: latlng}); + } + + return this; + }, + + closePopup: function () { + if (this._popup) { + this._popup._close(); + } + return this; + }, + + _openPopup: function (e) { + this._popup.setLatLng(e.latlng); + this._map.openPopup(this._popup); + } +}); + + +/* + * Vector rendering for IE6-8 through VML. + * Thanks to Dmitry Baranovsky and his Raphael library for inspiration! + */ + +L.Browser.vml = !L.Browser.svg && (function () { + try { + var div = document.createElement('div'); + div.innerHTML = ''; + + var shape = div.firstChild; + shape.style.behavior = 'url(#default#VML)'; + + return shape && (typeof shape.adj === 'object'); + + } catch (e) { + return false; + } +}()); + +L.Path = L.Browser.svg || !L.Browser.vml ? L.Path : L.Path.extend({ + statics: { + VML: true, + CLIP_PADDING: 0.02 + }, + + _createElement: (function () { + try { + document.namespaces.add('lvml', 'urn:schemas-microsoft-com:vml'); + return function (name) { + return document.createElement(''); + }; + } catch (e) { + return function (name) { + return document.createElement( + '<' + name + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">'); + }; + } + }()), + + _initPath: function () { + var container = this._container = this._createElement('shape'); + + L.DomUtil.addClass(container, 'leaflet-vml-shape' + + (this.options.className ? ' ' + this.options.className : '')); + + if (this.options.clickable) { + L.DomUtil.addClass(container, 'leaflet-clickable'); + } + + container.coordsize = '1 1'; + + this._path = this._createElement('path'); + container.appendChild(this._path); + + this._map._pathRoot.appendChild(container); + }, + + _initStyle: function () { + this._updateStyle(); + }, + + _updateStyle: function () { + var stroke = this._stroke, + fill = this._fill, + options = this.options, + container = this._container; + + container.stroked = options.stroke; + container.filled = options.fill; + + if (options.stroke) { + if (!stroke) { + stroke = this._stroke = this._createElement('stroke'); + stroke.endcap = 'round'; + container.appendChild(stroke); + } + stroke.weight = options.weight + 'px'; + stroke.color = options.color; + stroke.opacity = options.opacity; + + if (options.dashArray) { + stroke.dashStyle = L.Util.isArray(options.dashArray) ? + options.dashArray.join(' ') : + options.dashArray.replace(/( *, *)/g, ' '); + } else { + stroke.dashStyle = ''; + } + if (options.lineCap) { + stroke.endcap = options.lineCap.replace('butt', 'flat'); + } + if (options.lineJoin) { + stroke.joinstyle = options.lineJoin; + } + + } else if (stroke) { + container.removeChild(stroke); + this._stroke = null; + } + + if (options.fill) { + if (!fill) { + fill = this._fill = this._createElement('fill'); + container.appendChild(fill); + } + fill.color = options.fillColor || options.color; + fill.opacity = options.fillOpacity; + + } else if (fill) { + container.removeChild(fill); + this._fill = null; + } + }, + + _updatePath: function () { + var style = this._container.style; + + style.display = 'none'; + this._path.v = this.getPathString() + ' '; // the space fixes IE empty path string bug + style.display = ''; + } +}); + +L.Map.include(L.Browser.svg || !L.Browser.vml ? {} : { + _initPathRoot: function () { + if (this._pathRoot) { return; } + + var root = this._pathRoot = document.createElement('div'); + root.className = 'leaflet-vml-container'; + this._panes.overlayPane.appendChild(root); + + this.on('moveend', this._updatePathViewport); + this._updatePathViewport(); + } +}); + + +/* + * Vector rendering for all browsers that support canvas. + */ + +L.Browser.canvas = (function () { + return !!document.createElement('canvas').getContext; +}()); + +L.Path = (L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? L.Path : L.Path.extend({ + statics: { + //CLIP_PADDING: 0.02, // not sure if there's a need to set it to a small value + CANVAS: true, + SVG: false + }, + + redraw: function () { + if (this._map) { + this.projectLatlngs(); + this._requestUpdate(); + } + return this; + }, + + setStyle: function (style) { + L.setOptions(this, style); + + if (this._map) { + this._updateStyle(); + this._requestUpdate(); + } + return this; + }, + + onRemove: function (map) { + map + .off('viewreset', this.projectLatlngs, this) + .off('moveend', this._updatePath, this); + + if (this.options.clickable) { + this._map.off('click', this._onClick, this); + this._map.off('mousemove', this._onMouseMove, this); + } + + this._requestUpdate(); + + this.fire('remove'); + this._map = null; + }, + + _requestUpdate: function () { + if (this._map && !L.Path._updateRequest) { + L.Path._updateRequest = L.Util.requestAnimFrame(this._fireMapMoveEnd, this._map); + } + }, + + _fireMapMoveEnd: function () { + L.Path._updateRequest = null; + this.fire('moveend'); + }, + + _initElements: function () { + this._map._initPathRoot(); + this._ctx = this._map._canvasCtx; + }, + + _updateStyle: function () { + var options = this.options; + + if (options.stroke) { + this._ctx.lineWidth = options.weight; + this._ctx.strokeStyle = options.color; + } + if (options.fill) { + this._ctx.fillStyle = options.fillColor || options.color; + } + + if (options.lineCap) { + this._ctx.lineCap = options.lineCap; + } + if (options.lineJoin) { + this._ctx.lineJoin = options.lineJoin; + } + }, + + _drawPath: function () { + var i, j, len, len2, point, drawMethod; + + this._ctx.beginPath(); + + for (i = 0, len = this._parts.length; i < len; i++) { + for (j = 0, len2 = this._parts[i].length; j < len2; j++) { + point = this._parts[i][j]; + drawMethod = (j === 0 ? 'move' : 'line') + 'To'; + + this._ctx[drawMethod](point.x, point.y); + } + // TODO refactor ugly hack + if (this instanceof L.Polygon) { + this._ctx.closePath(); + } + } + }, + + _checkIfEmpty: function () { + return !this._parts.length; + }, + + _updatePath: function () { + if (this._checkIfEmpty()) { return; } + + var ctx = this._ctx, + options = this.options; + + this._drawPath(); + ctx.save(); + this._updateStyle(); + + if (options.fill) { + ctx.globalAlpha = options.fillOpacity; + ctx.fill(options.fillRule || 'evenodd'); + } + + if (options.stroke) { + ctx.globalAlpha = options.opacity; + ctx.stroke(); + } + + ctx.restore(); + + // TODO optimization: 1 fill/stroke for all features with equal style instead of 1 for each feature + }, + + _initEvents: function () { + if (this.options.clickable) { + this._map.on('mousemove', this._onMouseMove, this); + this._map.on('click dblclick contextmenu', this._fireMouseEvent, this); + } + }, + + _fireMouseEvent: function (e) { + if (this._containsPoint(e.layerPoint)) { + this.fire(e.type, e); + } + }, + + _onMouseMove: function (e) { + if (!this._map || this._map._animatingZoom) { return; } + + // TODO don't do on each move + if (this._containsPoint(e.layerPoint)) { + this._ctx.canvas.style.cursor = 'pointer'; + this._mouseInside = true; + this.fire('mouseover', e); + + } else if (this._mouseInside) { + this._ctx.canvas.style.cursor = ''; + this._mouseInside = false; + this.fire('mouseout', e); + } + } +}); + +L.Map.include((L.Path.SVG && !window.L_PREFER_CANVAS) || !L.Browser.canvas ? {} : { + _initPathRoot: function () { + var root = this._pathRoot, + ctx; + + if (!root) { + root = this._pathRoot = document.createElement('canvas'); + root.style.position = 'absolute'; + ctx = this._canvasCtx = root.getContext('2d'); + + ctx.lineCap = 'round'; + ctx.lineJoin = 'round'; + + this._panes.overlayPane.appendChild(root); + + if (this.options.zoomAnimation) { + this._pathRoot.className = 'leaflet-zoom-animated'; + this.on('zoomanim', this._animatePathZoom); + this.on('zoomend', this._endPathZoom); + } + this.on('moveend', this._updateCanvasViewport); + this._updateCanvasViewport(); + } + }, + + _updateCanvasViewport: function () { + // don't redraw while zooming. See _updateSvgViewport for more details + if (this._pathZooming) { return; } + this._updatePathViewport(); + + var vp = this._pathViewport, + min = vp.min, + size = vp.max.subtract(min), + root = this._pathRoot; + + //TODO check if this works properly on mobile webkit + L.DomUtil.setPosition(root, min); + root.width = size.x; + root.height = size.y; + root.getContext('2d').translate(-min.x, -min.y); + } +}); + + +/* + * L.LineUtil contains different utility functions for line segments + * and polylines (clipping, simplification, distances, etc.) + */ + +/*jshint bitwise:false */ // allow bitwise operations for this file + +L.LineUtil = { + + // Simplify polyline with vertex reduction and Douglas-Peucker simplification. + // Improves rendering performance dramatically by lessening the number of points to draw. + + simplify: function (/*Point[]*/ points, /*Number*/ tolerance) { + if (!tolerance || !points.length) { + return points.slice(); + } + + var sqTolerance = tolerance * tolerance; + + // stage 1: vertex reduction + points = this._reducePoints(points, sqTolerance); + + // stage 2: Douglas-Peucker simplification + points = this._simplifyDP(points, sqTolerance); + + return points; + }, + + // distance from a point to a segment between two points + pointToSegmentDistance: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { + return Math.sqrt(this._sqClosestPointOnSegment(p, p1, p2, true)); + }, + + closestPointOnSegment: function (/*Point*/ p, /*Point*/ p1, /*Point*/ p2) { + return this._sqClosestPointOnSegment(p, p1, p2); + }, + + // Douglas-Peucker simplification, see http://en.wikipedia.org/wiki/Douglas-Peucker_algorithm + _simplifyDP: function (points, sqTolerance) { + + var len = points.length, + ArrayConstructor = typeof Uint8Array !== undefined + '' ? Uint8Array : Array, + markers = new ArrayConstructor(len); + + markers[0] = markers[len - 1] = 1; + + this._simplifyDPStep(points, markers, sqTolerance, 0, len - 1); + + var i, + newPoints = []; + + for (i = 0; i < len; i++) { + if (markers[i]) { + newPoints.push(points[i]); + } + } + + return newPoints; + }, + + _simplifyDPStep: function (points, markers, sqTolerance, first, last) { + + var maxSqDist = 0, + index, i, sqDist; + + for (i = first + 1; i <= last - 1; i++) { + sqDist = this._sqClosestPointOnSegment(points[i], points[first], points[last], true); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + markers[index] = 1; + + this._simplifyDPStep(points, markers, sqTolerance, first, index); + this._simplifyDPStep(points, markers, sqTolerance, index, last); + } + }, + + // reduce points that are too close to each other to a single point + _reducePoints: function (points, sqTolerance) { + var reducedPoints = [points[0]]; + + for (var i = 1, prev = 0, len = points.length; i < len; i++) { + if (this._sqDist(points[i], points[prev]) > sqTolerance) { + reducedPoints.push(points[i]); + prev = i; + } + } + if (prev < len - 1) { + reducedPoints.push(points[len - 1]); + } + return reducedPoints; + }, + + // Cohen-Sutherland line clipping algorithm. + // Used to avoid rendering parts of a polyline that are not currently visible. + + clipSegment: function (a, b, bounds, useLastCode) { + var codeA = useLastCode ? this._lastCode : this._getBitCode(a, bounds), + codeB = this._getBitCode(b, bounds), + + codeOut, p, newCode; + + // save 2nd code to avoid calculating it on the next segment + this._lastCode = codeB; + + while (true) { + // if a,b is inside the clip window (trivial accept) + if (!(codeA | codeB)) { + return [a, b]; + // if a,b is outside the clip window (trivial reject) + } else if (codeA & codeB) { + return false; + // other cases + } else { + codeOut = codeA || codeB; + p = this._getEdgeIntersection(a, b, codeOut, bounds); + newCode = this._getBitCode(p, bounds); + + if (codeOut === codeA) { + a = p; + codeA = newCode; + } else { + b = p; + codeB = newCode; + } + } + } + }, + + _getEdgeIntersection: function (a, b, code, bounds) { + var dx = b.x - a.x, + dy = b.y - a.y, + min = bounds.min, + max = bounds.max; + + if (code & 8) { // top + return new L.Point(a.x + dx * (max.y - a.y) / dy, max.y); + } else if (code & 4) { // bottom + return new L.Point(a.x + dx * (min.y - a.y) / dy, min.y); + } else if (code & 2) { // right + return new L.Point(max.x, a.y + dy * (max.x - a.x) / dx); + } else if (code & 1) { // left + return new L.Point(min.x, a.y + dy * (min.x - a.x) / dx); + } + }, + + _getBitCode: function (/*Point*/ p, bounds) { + var code = 0; + + if (p.x < bounds.min.x) { // left + code |= 1; + } else if (p.x > bounds.max.x) { // right + code |= 2; + } + if (p.y < bounds.min.y) { // bottom + code |= 4; + } else if (p.y > bounds.max.y) { // top + code |= 8; + } + + return code; + }, + + // square distance (to avoid unnecessary Math.sqrt calls) + _sqDist: function (p1, p2) { + var dx = p2.x - p1.x, + dy = p2.y - p1.y; + return dx * dx + dy * dy; + }, + + // return closest point on segment or distance to that point + _sqClosestPointOnSegment: function (p, p1, p2, sqDist) { + var x = p1.x, + y = p1.y, + dx = p2.x - x, + dy = p2.y - y, + dot = dx * dx + dy * dy, + t; + + if (dot > 0) { + t = ((p.x - x) * dx + (p.y - y) * dy) / dot; + + if (t > 1) { + x = p2.x; + y = p2.y; + } else if (t > 0) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return sqDist ? dx * dx + dy * dy : new L.Point(x, y); + } +}; + + +/* + * L.Polyline is used to display polylines on a map. + */ + +L.Polyline = L.Path.extend({ + initialize: function (latlngs, options) { + L.Path.prototype.initialize.call(this, options); + + this._latlngs = this._convertLatLngs(latlngs); + }, + + options: { + // how much to simplify the polyline on each zoom level + // more = better performance and smoother look, less = more accurate + smoothFactor: 1.0, + noClip: false + }, + + projectLatlngs: function () { + this._originalPoints = []; + + for (var i = 0, len = this._latlngs.length; i < len; i++) { + this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]); + } + }, + + getPathString: function () { + for (var i = 0, len = this._parts.length, str = ''; i < len; i++) { + str += this._getPathPartStr(this._parts[i]); + } + return str; + }, + + getLatLngs: function () { + return this._latlngs; + }, + + setLatLngs: function (latlngs) { + this._latlngs = this._convertLatLngs(latlngs); + return this.redraw(); + }, + + addLatLng: function (latlng) { + this._latlngs.push(L.latLng(latlng)); + return this.redraw(); + }, + + spliceLatLngs: function () { // (Number index, Number howMany) + var removed = [].splice.apply(this._latlngs, arguments); + this._convertLatLngs(this._latlngs, true); + this.redraw(); + return removed; + }, + + closestLayerPoint: function (p) { + var minDistance = Infinity, parts = this._parts, p1, p2, minPoint = null; + + for (var j = 0, jLen = parts.length; j < jLen; j++) { + var points = parts[j]; + for (var i = 1, len = points.length; i < len; i++) { + p1 = points[i - 1]; + p2 = points[i]; + var sqDist = L.LineUtil._sqClosestPointOnSegment(p, p1, p2, true); + if (sqDist < minDistance) { + minDistance = sqDist; + minPoint = L.LineUtil._sqClosestPointOnSegment(p, p1, p2); + } + } + } + if (minPoint) { + minPoint.distance = Math.sqrt(minDistance); + } + return minPoint; + }, + + getBounds: function () { + return new L.LatLngBounds(this.getLatLngs()); + }, + + _convertLatLngs: function (latlngs, overwrite) { + var i, len, target = overwrite ? latlngs : []; + + for (i = 0, len = latlngs.length; i < len; i++) { + if (L.Util.isArray(latlngs[i]) && typeof latlngs[i][0] !== 'number') { + return; + } + target[i] = L.latLng(latlngs[i]); + } + return target; + }, + + _initEvents: function () { + L.Path.prototype._initEvents.call(this); + }, + + _getPathPartStr: function (points) { + var round = L.Path.VML; + + for (var j = 0, len2 = points.length, str = '', p; j < len2; j++) { + p = points[j]; + if (round) { + p._round(); + } + str += (j ? 'L' : 'M') + p.x + ' ' + p.y; + } + return str; + }, + + _clipPoints: function () { + var points = this._originalPoints, + len = points.length, + i, k, segment; + + if (this.options.noClip) { + this._parts = [points]; + return; + } + + this._parts = []; + + var parts = this._parts, + vp = this._map._pathViewport, + lu = L.LineUtil; + + for (i = 0, k = 0; i < len - 1; i++) { + segment = lu.clipSegment(points[i], points[i + 1], vp, i); + if (!segment) { + continue; + } + + parts[k] = parts[k] || []; + parts[k].push(segment[0]); + + // if segment goes out of screen, or it's the last one, it's the end of the line part + if ((segment[1] !== points[i + 1]) || (i === len - 2)) { + parts[k].push(segment[1]); + k++; + } + } + }, + + // simplify each clipped part of the polyline + _simplifyPoints: function () { + var parts = this._parts, + lu = L.LineUtil; + + for (var i = 0, len = parts.length; i < len; i++) { + parts[i] = lu.simplify(parts[i], this.options.smoothFactor); + } + }, + + _updatePath: function () { + if (!this._map) { return; } + + this._clipPoints(); + this._simplifyPoints(); + + L.Path.prototype._updatePath.call(this); + } +}); + +L.polyline = function (latlngs, options) { + return new L.Polyline(latlngs, options); +}; + + +/* + * L.PolyUtil contains utility functions for polygons (clipping, etc.). + */ + +/*jshint bitwise:false */ // allow bitwise operations here + +L.PolyUtil = {}; + +/* + * Sutherland-Hodgeman polygon clipping algorithm. + * Used to avoid rendering parts of a polygon that are not currently visible. + */ +L.PolyUtil.clipPolygon = function (points, bounds) { + var clippedPoints, + edges = [1, 4, 2, 8], + i, j, k, + a, b, + len, edge, p, + lu = L.LineUtil; + + for (i = 0, len = points.length; i < len; i++) { + points[i]._code = lu._getBitCode(points[i], bounds); + } + + // for each edge (left, bottom, right, top) + for (k = 0; k < 4; k++) { + edge = edges[k]; + clippedPoints = []; + + for (i = 0, len = points.length, j = len - 1; i < len; j = i++) { + a = points[i]; + b = points[j]; + + // if a is inside the clip window + if (!(a._code & edge)) { + // if b is outside the clip window (a->b goes out of screen) + if (b._code & edge) { + p = lu._getEdgeIntersection(b, a, edge, bounds); + p._code = lu._getBitCode(p, bounds); + clippedPoints.push(p); + } + clippedPoints.push(a); + + // else if b is inside the clip window (a->b enters the screen) + } else if (!(b._code & edge)) { + p = lu._getEdgeIntersection(b, a, edge, bounds); + p._code = lu._getBitCode(p, bounds); + clippedPoints.push(p); + } + } + points = clippedPoints; + } + + return points; +}; + + +/* + * L.Polygon is used to display polygons on a map. + */ + +L.Polygon = L.Polyline.extend({ + options: { + fill: true + }, + + initialize: function (latlngs, options) { + L.Polyline.prototype.initialize.call(this, latlngs, options); + this._initWithHoles(latlngs); + }, + + _initWithHoles: function (latlngs) { + var i, len, hole; + if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) { + this._latlngs = this._convertLatLngs(latlngs[0]); + this._holes = latlngs.slice(1); + + for (i = 0, len = this._holes.length; i < len; i++) { + hole = this._holes[i] = this._convertLatLngs(this._holes[i]); + if (hole[0].equals(hole[hole.length - 1])) { + hole.pop(); + } + } + } + + // filter out last point if its equal to the first one + latlngs = this._latlngs; + + if (latlngs.length >= 2 && latlngs[0].equals(latlngs[latlngs.length - 1])) { + latlngs.pop(); + } + }, + + projectLatlngs: function () { + L.Polyline.prototype.projectLatlngs.call(this); + + // project polygon holes points + // TODO move this logic to Polyline to get rid of duplication + this._holePoints = []; + + if (!this._holes) { return; } + + var i, j, len, len2; + + for (i = 0, len = this._holes.length; i < len; i++) { + this._holePoints[i] = []; + + for (j = 0, len2 = this._holes[i].length; j < len2; j++) { + this._holePoints[i][j] = this._map.latLngToLayerPoint(this._holes[i][j]); + } + } + }, + + setLatLngs: function (latlngs) { + if (latlngs && L.Util.isArray(latlngs[0]) && (typeof latlngs[0][0] !== 'number')) { + this._initWithHoles(latlngs); + return this.redraw(); + } else { + return L.Polyline.prototype.setLatLngs.call(this, latlngs); + } + }, + + _clipPoints: function () { + var points = this._originalPoints, + newParts = []; + + this._parts = [points].concat(this._holePoints); + + if (this.options.noClip) { return; } + + for (var i = 0, len = this._parts.length; i < len; i++) { + var clipped = L.PolyUtil.clipPolygon(this._parts[i], this._map._pathViewport); + if (clipped.length) { + newParts.push(clipped); + } + } + + this._parts = newParts; + }, + + _getPathPartStr: function (points) { + var str = L.Polyline.prototype._getPathPartStr.call(this, points); + return str + (L.Browser.svg ? 'z' : 'x'); + } +}); + +L.polygon = function (latlngs, options) { + return new L.Polygon(latlngs, options); +}; + + +/* + * Contains L.MultiPolyline and L.MultiPolygon layers. + */ + +(function () { + function createMulti(Klass) { + + return L.FeatureGroup.extend({ + + initialize: function (latlngs, options) { + this._layers = {}; + this._options = options; + this.setLatLngs(latlngs); + }, + + setLatLngs: function (latlngs) { + var i = 0, + len = latlngs.length; + + this.eachLayer(function (layer) { + if (i < len) { + layer.setLatLngs(latlngs[i++]); + } else { + this.removeLayer(layer); + } + }, this); + + while (i < len) { + this.addLayer(new Klass(latlngs[i++], this._options)); + } + + return this; + }, + + getLatLngs: function () { + var latlngs = []; + + this.eachLayer(function (layer) { + latlngs.push(layer.getLatLngs()); + }); + + return latlngs; + } + }); + } + + L.MultiPolyline = createMulti(L.Polyline); + L.MultiPolygon = createMulti(L.Polygon); + + L.multiPolyline = function (latlngs, options) { + return new L.MultiPolyline(latlngs, options); + }; + + L.multiPolygon = function (latlngs, options) { + return new L.MultiPolygon(latlngs, options); + }; +}()); + + +/* + * L.Rectangle extends Polygon and creates a rectangle when passed a LatLngBounds object. + */ + +L.Rectangle = L.Polygon.extend({ + initialize: function (latLngBounds, options) { + L.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(latLngBounds), options); + }, + + setBounds: function (latLngBounds) { + this.setLatLngs(this._boundsToLatLngs(latLngBounds)); + }, + + _boundsToLatLngs: function (latLngBounds) { + latLngBounds = L.latLngBounds(latLngBounds); + return [ + latLngBounds.getSouthWest(), + latLngBounds.getNorthWest(), + latLngBounds.getNorthEast(), + latLngBounds.getSouthEast() + ]; + } +}); + +L.rectangle = function (latLngBounds, options) { + return new L.Rectangle(latLngBounds, options); +}; + + +/* + * L.Circle is a circle overlay (with a certain radius in meters). + */ + +L.Circle = L.Path.extend({ + initialize: function (latlng, radius, options) { + L.Path.prototype.initialize.call(this, options); + + this._latlng = L.latLng(latlng); + this._mRadius = radius; + }, + + options: { + fill: true + }, + + setLatLng: function (latlng) { + this._latlng = L.latLng(latlng); + return this.redraw(); + }, + + setRadius: function (radius) { + this._mRadius = radius; + return this.redraw(); + }, + + projectLatlngs: function () { + var lngRadius = this._getLngRadius(), + latlng = this._latlng, + pointLeft = this._map.latLngToLayerPoint([latlng.lat, latlng.lng - lngRadius]); + + this._point = this._map.latLngToLayerPoint(latlng); + this._radius = Math.max(this._point.x - pointLeft.x, 1); + }, + + getBounds: function () { + var lngRadius = this._getLngRadius(), + latRadius = (this._mRadius / 40075017) * 360, + latlng = this._latlng; + + return new L.LatLngBounds( + [latlng.lat - latRadius, latlng.lng - lngRadius], + [latlng.lat + latRadius, latlng.lng + lngRadius]); + }, + + getLatLng: function () { + return this._latlng; + }, + + getPathString: function () { + var p = this._point, + r = this._radius; + + if (this._checkIfEmpty()) { + return ''; + } + + if (L.Browser.svg) { + return 'M' + p.x + ',' + (p.y - r) + + 'A' + r + ',' + r + ',0,1,1,' + + (p.x - 0.1) + ',' + (p.y - r) + ' z'; + } else { + p._round(); + r = Math.round(r); + return 'AL ' + p.x + ',' + p.y + ' ' + r + ',' + r + ' 0,' + (65535 * 360); + } + }, + + getRadius: function () { + return this._mRadius; + }, + + // TODO Earth hardcoded, move into projection code! + + _getLatRadius: function () { + return (this._mRadius / 40075017) * 360; + }, + + _getLngRadius: function () { + return this._getLatRadius() / Math.cos(L.LatLng.DEG_TO_RAD * this._latlng.lat); + }, + + _checkIfEmpty: function () { + if (!this._map) { + return false; + } + var vp = this._map._pathViewport, + r = this._radius, + p = this._point; + + return p.x - r > vp.max.x || p.y - r > vp.max.y || + p.x + r < vp.min.x || p.y + r < vp.min.y; + } +}); + +L.circle = function (latlng, radius, options) { + return new L.Circle(latlng, radius, options); +}; + + +/* + * L.CircleMarker is a circle overlay with a permanent pixel radius. + */ + +L.CircleMarker = L.Circle.extend({ + options: { + radius: 10, + weight: 2 + }, + + initialize: function (latlng, options) { + L.Circle.prototype.initialize.call(this, latlng, null, options); + this._radius = this.options.radius; + }, + + projectLatlngs: function () { + this._point = this._map.latLngToLayerPoint(this._latlng); + }, + + _updateStyle : function () { + L.Circle.prototype._updateStyle.call(this); + this.setRadius(this.options.radius); + }, + + setLatLng: function (latlng) { + L.Circle.prototype.setLatLng.call(this, latlng); + if (this._popup && this._popup._isOpen) { + this._popup.setLatLng(latlng); + } + return this; + }, + + setRadius: function (radius) { + this.options.radius = this._radius = radius; + return this.redraw(); + }, + + getRadius: function () { + return this._radius; + } +}); + +L.circleMarker = function (latlng, options) { + return new L.CircleMarker(latlng, options); +}; + + +/* + * Extends L.Polyline to be able to manually detect clicks on Canvas-rendered polylines. + */ + +L.Polyline.include(!L.Path.CANVAS ? {} : { + _containsPoint: function (p, closed) { + var i, j, k, len, len2, dist, part, + w = this.options.weight / 2; + + if (L.Browser.touch) { + w += 10; // polyline click tolerance on touch devices + } + + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + if (!closed && (j === 0)) { + continue; + } + + dist = L.LineUtil.pointToSegmentDistance(p, part[k], part[j]); + + if (dist <= w) { + return true; + } + } + } + return false; + } +}); + + +/* + * Extends L.Polygon to be able to manually detect clicks on Canvas-rendered polygons. + */ + +L.Polygon.include(!L.Path.CANVAS ? {} : { + _containsPoint: function (p) { + var inside = false, + part, p1, p2, + i, j, k, + len, len2; + + // TODO optimization: check if within bounds first + + if (L.Polyline.prototype._containsPoint.call(this, p, true)) { + // click on polygon border + return true; + } + + // ray casting algorithm for detecting if point is in polygon + + for (i = 0, len = this._parts.length; i < len; i++) { + part = this._parts[i]; + + for (j = 0, len2 = part.length, k = len2 - 1; j < len2; k = j++) { + p1 = part[j]; + p2 = part[k]; + + if (((p1.y > p.y) !== (p2.y > p.y)) && + (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x)) { + inside = !inside; + } + } + } + + return inside; + } +}); + + +/* + * Extends L.Circle with Canvas-specific code. + */ + +L.Circle.include(!L.Path.CANVAS ? {} : { + _drawPath: function () { + var p = this._point; + this._ctx.beginPath(); + this._ctx.arc(p.x, p.y, this._radius, 0, Math.PI * 2, false); + }, + + _containsPoint: function (p) { + var center = this._point, + w2 = this.options.stroke ? this.options.weight / 2 : 0; + + return (p.distanceTo(center) <= this._radius + w2); + } +}); + + +/* + * CircleMarker canvas specific drawing parts. + */ + +L.CircleMarker.include(!L.Path.CANVAS ? {} : { + _updateStyle: function () { + L.Path.prototype._updateStyle.call(this); + } +}); + + +/* + * L.GeoJSON turns any GeoJSON data into a Leaflet layer. + */ + +L.GeoJSON = L.FeatureGroup.extend({ + + initialize: function (geojson, options) { + L.setOptions(this, options); + + this._layers = {}; + + if (geojson) { + this.addData(geojson); + } + }, + + addData: function (geojson) { + var features = L.Util.isArray(geojson) ? geojson : geojson.features, + i, len, feature; + + if (features) { + for (i = 0, len = features.length; i < len; i++) { + // Only add this if geometry or geometries are set and not null + feature = features[i]; + if (feature.geometries || feature.geometry || feature.features || feature.coordinates) { + this.addData(features[i]); + } + } + return this; + } + + var options = this.options; + + if (options.filter && !options.filter(geojson)) { return; } + + var layer = L.GeoJSON.geometryToLayer(geojson, options.pointToLayer, options.coordsToLatLng, options); + layer.feature = L.GeoJSON.asFeature(geojson); + + layer.defaultOptions = layer.options; + this.resetStyle(layer); + + if (options.onEachFeature) { + options.onEachFeature(geojson, layer); + } + + return this.addLayer(layer); + }, + + resetStyle: function (layer) { + var style = this.options.style; + if (style) { + // reset any custom styles + L.Util.extend(layer.options, layer.defaultOptions); + + this._setLayerStyle(layer, style); + } + }, + + setStyle: function (style) { + this.eachLayer(function (layer) { + this._setLayerStyle(layer, style); + }, this); + }, + + _setLayerStyle: function (layer, style) { + if (typeof style === 'function') { + style = style(layer.feature); + } + if (layer.setStyle) { + layer.setStyle(style); + } + } +}); + +L.extend(L.GeoJSON, { + geometryToLayer: function (geojson, pointToLayer, coordsToLatLng, vectorOptions) { + var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson, + coords = geometry.coordinates, + layers = [], + latlng, latlngs, i, len; + + coordsToLatLng = coordsToLatLng || this.coordsToLatLng; + + switch (geometry.type) { + case 'Point': + latlng = coordsToLatLng(coords); + return pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng); + + case 'MultiPoint': + for (i = 0, len = coords.length; i < len; i++) { + latlng = coordsToLatLng(coords[i]); + layers.push(pointToLayer ? pointToLayer(geojson, latlng) : new L.Marker(latlng)); + } + return new L.FeatureGroup(layers); + + case 'LineString': + latlngs = this.coordsToLatLngs(coords, 0, coordsToLatLng); + return new L.Polyline(latlngs, vectorOptions); + + case 'Polygon': + if (coords.length === 2 && !coords[1].length) { + throw new Error('Invalid GeoJSON object.'); + } + latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng); + return new L.Polygon(latlngs, vectorOptions); + + case 'MultiLineString': + latlngs = this.coordsToLatLngs(coords, 1, coordsToLatLng); + return new L.MultiPolyline(latlngs, vectorOptions); + + case 'MultiPolygon': + latlngs = this.coordsToLatLngs(coords, 2, coordsToLatLng); + return new L.MultiPolygon(latlngs, vectorOptions); + + case 'GeometryCollection': + for (i = 0, len = geometry.geometries.length; i < len; i++) { + + layers.push(this.geometryToLayer({ + geometry: geometry.geometries[i], + type: 'Feature', + properties: geojson.properties + }, pointToLayer, coordsToLatLng, vectorOptions)); + } + return new L.FeatureGroup(layers); + + default: + throw new Error('Invalid GeoJSON object.'); + } + }, + + coordsToLatLng: function (coords) { // (Array[, Boolean]) -> LatLng + return new L.LatLng(coords[1], coords[0], coords[2]); + }, + + coordsToLatLngs: function (coords, levelsDeep, coordsToLatLng) { // (Array[, Number, Function]) -> Array + var latlng, i, len, + latlngs = []; + + for (i = 0, len = coords.length; i < len; i++) { + latlng = levelsDeep ? + this.coordsToLatLngs(coords[i], levelsDeep - 1, coordsToLatLng) : + (coordsToLatLng || this.coordsToLatLng)(coords[i]); + + latlngs.push(latlng); + } + + return latlngs; + }, + + latLngToCoords: function (latlng) { + var coords = [latlng.lng, latlng.lat]; + + if (latlng.alt !== undefined) { + coords.push(latlng.alt); + } + return coords; + }, + + latLngsToCoords: function (latLngs) { + var coords = []; + + for (var i = 0, len = latLngs.length; i < len; i++) { + coords.push(L.GeoJSON.latLngToCoords(latLngs[i])); + } + + return coords; + }, + + getFeature: function (layer, newGeometry) { + return layer.feature ? L.extend({}, layer.feature, {geometry: newGeometry}) : L.GeoJSON.asFeature(newGeometry); + }, + + asFeature: function (geoJSON) { + if (geoJSON.type === 'Feature') { + return geoJSON; + } + + return { + type: 'Feature', + properties: {}, + geometry: geoJSON + }; + } +}); + +var PointToGeoJSON = { + toGeoJSON: function () { + return L.GeoJSON.getFeature(this, { + type: 'Point', + coordinates: L.GeoJSON.latLngToCoords(this.getLatLng()) + }); + } +}; + +L.Marker.include(PointToGeoJSON); +L.Circle.include(PointToGeoJSON); +L.CircleMarker.include(PointToGeoJSON); + +L.Polyline.include({ + toGeoJSON: function () { + return L.GeoJSON.getFeature(this, { + type: 'LineString', + coordinates: L.GeoJSON.latLngsToCoords(this.getLatLngs()) + }); + } +}); + +L.Polygon.include({ + toGeoJSON: function () { + var coords = [L.GeoJSON.latLngsToCoords(this.getLatLngs())], + i, len, hole; + + coords[0].push(coords[0][0]); + + if (this._holes) { + for (i = 0, len = this._holes.length; i < len; i++) { + hole = L.GeoJSON.latLngsToCoords(this._holes[i]); + hole.push(hole[0]); + coords.push(hole); + } + } + + return L.GeoJSON.getFeature(this, { + type: 'Polygon', + coordinates: coords + }); + } +}); + +(function () { + function multiToGeoJSON(type) { + return function () { + var coords = []; + + this.eachLayer(function (layer) { + coords.push(layer.toGeoJSON().geometry.coordinates); + }); + + return L.GeoJSON.getFeature(this, { + type: type, + coordinates: coords + }); + }; + } + + L.MultiPolyline.include({toGeoJSON: multiToGeoJSON('MultiLineString')}); + L.MultiPolygon.include({toGeoJSON: multiToGeoJSON('MultiPolygon')}); + + L.LayerGroup.include({ + toGeoJSON: function () { + + var geometry = this.feature && this.feature.geometry, + jsons = [], + json; + + if (geometry && geometry.type === 'MultiPoint') { + return multiToGeoJSON('MultiPoint').call(this); + } + + var isGeometryCollection = geometry && geometry.type === 'GeometryCollection'; + + this.eachLayer(function (layer) { + if (layer.toGeoJSON) { + json = layer.toGeoJSON(); + jsons.push(isGeometryCollection ? json.geometry : L.GeoJSON.asFeature(json)); + } + }); + + if (isGeometryCollection) { + return L.GeoJSON.getFeature(this, { + geometries: jsons, + type: 'GeometryCollection' + }); + } + + return { + type: 'FeatureCollection', + features: jsons + }; + } + }); +}()); + +L.geoJson = function (geojson, options) { + return new L.GeoJSON(geojson, options); +}; + + +/* + * L.DomEvent contains functions for working with DOM events. + */ + +L.DomEvent = { + /* inspired by John Resig, Dean Edwards and YUI addEvent implementations */ + addListener: function (obj, type, fn, context) { // (HTMLElement, String, Function[, Object]) + + var id = L.stamp(fn), + key = '_leaflet_' + type + id, + handler, originalHandler, newType; + + if (obj[key]) { return this; } + + handler = function (e) { + return fn.call(context || obj, e || L.DomEvent._getEvent()); + }; + + if (L.Browser.pointer && type.indexOf('touch') === 0) { + return this.addPointerListener(obj, type, handler, id); + } + if (L.Browser.touch && (type === 'dblclick') && this.addDoubleTapListener) { + this.addDoubleTapListener(obj, handler, id); + } + + if ('addEventListener' in obj) { + + if (type === 'mousewheel') { + obj.addEventListener('DOMMouseScroll', handler, false); + obj.addEventListener(type, handler, false); + + } else if ((type === 'mouseenter') || (type === 'mouseleave')) { + + originalHandler = handler; + newType = (type === 'mouseenter' ? 'mouseover' : 'mouseout'); + + handler = function (e) { + if (!L.DomEvent._checkMouse(obj, e)) { return; } + return originalHandler(e); + }; + + obj.addEventListener(newType, handler, false); + + } else if (type === 'click' && L.Browser.android) { + originalHandler = handler; + handler = function (e) { + return L.DomEvent._filterClick(e, originalHandler); + }; + + obj.addEventListener(type, handler, false); + } else { + obj.addEventListener(type, handler, false); + } + + } else if ('attachEvent' in obj) { + obj.attachEvent('on' + type, handler); + } + + obj[key] = handler; + + return this; + }, + + removeListener: function (obj, type, fn) { // (HTMLElement, String, Function) + + var id = L.stamp(fn), + key = '_leaflet_' + type + id, + handler = obj[key]; + + if (!handler) { return this; } + + if (L.Browser.pointer && type.indexOf('touch') === 0) { + this.removePointerListener(obj, type, id); + } else if (L.Browser.touch && (type === 'dblclick') && this.removeDoubleTapListener) { + this.removeDoubleTapListener(obj, id); + + } else if ('removeEventListener' in obj) { + + if (type === 'mousewheel') { + obj.removeEventListener('DOMMouseScroll', handler, false); + obj.removeEventListener(type, handler, false); + + } else if ((type === 'mouseenter') || (type === 'mouseleave')) { + obj.removeEventListener((type === 'mouseenter' ? 'mouseover' : 'mouseout'), handler, false); + } else { + obj.removeEventListener(type, handler, false); + } + } else if ('detachEvent' in obj) { + obj.detachEvent('on' + type, handler); + } + + obj[key] = null; + + return this; + }, + + stopPropagation: function (e) { + + if (e.stopPropagation) { + e.stopPropagation(); + } else { + e.cancelBubble = true; + } + L.DomEvent._skipped(e); + + return this; + }, + + disableScrollPropagation: function (el) { + var stop = L.DomEvent.stopPropagation; + + return L.DomEvent + .on(el, 'mousewheel', stop) + .on(el, 'MozMousePixelScroll', stop); + }, + + disableClickPropagation: function (el) { + var stop = L.DomEvent.stopPropagation; + + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.on(el, L.Draggable.START[i], stop); + } + + return L.DomEvent + .on(el, 'click', L.DomEvent._fakeStop) + .on(el, 'dblclick', stop); + }, + + preventDefault: function (e) { + + if (e.preventDefault) { + e.preventDefault(); + } else { + e.returnValue = false; + } + return this; + }, + + stop: function (e) { + return L.DomEvent + .preventDefault(e) + .stopPropagation(e); + }, + + getMousePosition: function (e, container) { + if (!container) { + return new L.Point(e.clientX, e.clientY); + } + + var rect = container.getBoundingClientRect(); + + return new L.Point( + e.clientX - rect.left - container.clientLeft, + e.clientY - rect.top - container.clientTop); + }, + + getWheelDelta: function (e) { + + var delta = 0; + + if (e.wheelDelta) { + delta = e.wheelDelta / 120; + } + if (e.detail) { + delta = -e.detail / 3; + } + return delta; + }, + + _skipEvents: {}, + + _fakeStop: function (e) { + // fakes stopPropagation by setting a special event flag, checked/reset with L.DomEvent._skipped(e) + L.DomEvent._skipEvents[e.type] = true; + }, + + _skipped: function (e) { + var skipped = this._skipEvents[e.type]; + // reset when checking, as it's only used in map container and propagates outside of the map + this._skipEvents[e.type] = false; + return skipped; + }, + + // check if element really left/entered the event target (for mouseenter/mouseleave) + _checkMouse: function (el, e) { + + var related = e.relatedTarget; + + if (!related) { return true; } + + try { + while (related && (related !== el)) { + related = related.parentNode; + } + } catch (err) { + return false; + } + return (related !== el); + }, + + _getEvent: function () { // evil magic for IE + /*jshint noarg:false */ + var e = window.event; + if (!e) { + var caller = arguments.callee.caller; + while (caller) { + e = caller['arguments'][0]; + if (e && window.Event === e.constructor) { + break; + } + caller = caller.caller; + } + } + return e; + }, + + // this is a horrible workaround for a bug in Android where a single touch triggers two click events + _filterClick: function (e, handler) { + var timeStamp = (e.timeStamp || e.originalEvent.timeStamp), + elapsed = L.DomEvent._lastClick && (timeStamp - L.DomEvent._lastClick); + + // are they closer together than 500ms yet more than 100ms? + // Android typically triggers them ~300ms apart while multiple listeners + // on the same event should be triggered far faster; + // or check if click is simulated on the element, and if it is, reject any non-simulated events + + if ((elapsed && elapsed > 100 && elapsed < 500) || (e.target._simulatedClick && !e._simulated)) { + L.DomEvent.stop(e); + return; + } + L.DomEvent._lastClick = timeStamp; + + return handler(e); + } +}; + +L.DomEvent.on = L.DomEvent.addListener; +L.DomEvent.off = L.DomEvent.removeListener; + + +/* + * L.Draggable allows you to add dragging capabilities to any element. Supports mobile devices too. + */ + +L.Draggable = L.Class.extend({ + includes: L.Mixin.Events, + + statics: { + START: L.Browser.touch ? ['touchstart', 'mousedown'] : ['mousedown'], + END: { + mousedown: 'mouseup', + touchstart: 'touchend', + pointerdown: 'touchend', + MSPointerDown: 'touchend' + }, + MOVE: { + mousedown: 'mousemove', + touchstart: 'touchmove', + pointerdown: 'touchmove', + MSPointerDown: 'touchmove' + } + }, + + initialize: function (element, dragStartTarget) { + this._element = element; + this._dragStartTarget = dragStartTarget || element; + }, + + enable: function () { + if (this._enabled) { return; } + + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.on(this._dragStartTarget, L.Draggable.START[i], this._onDown, this); + } + + this._enabled = true; + }, + + disable: function () { + if (!this._enabled) { return; } + + for (var i = L.Draggable.START.length - 1; i >= 0; i--) { + L.DomEvent.off(this._dragStartTarget, L.Draggable.START[i], this._onDown, this); + } + + this._enabled = false; + this._moved = false; + }, + + _onDown: function (e) { + this._moved = false; + + if (e.shiftKey || ((e.which !== 1) && (e.button !== 1) && !e.touches)) { return; } + + L.DomEvent.stopPropagation(e); + + if (L.Draggable._disabled) { return; } + + L.DomUtil.disableImageDrag(); + L.DomUtil.disableTextSelection(); + + if (this._moving) { return; } + + var first = e.touches ? e.touches[0] : e; + + this._startPoint = new L.Point(first.clientX, first.clientY); + this._startPos = this._newPos = L.DomUtil.getPosition(this._element); + + L.DomEvent + .on(document, L.Draggable.MOVE[e.type], this._onMove, this) + .on(document, L.Draggable.END[e.type], this._onUp, this); + }, + + _onMove: function (e) { + if (e.touches && e.touches.length > 1) { + this._moved = true; + return; + } + + var first = (e.touches && e.touches.length === 1 ? e.touches[0] : e), + newPoint = new L.Point(first.clientX, first.clientY), + offset = newPoint.subtract(this._startPoint); + + if (!offset.x && !offset.y) { return; } + if (L.Browser.touch && Math.abs(offset.x) + Math.abs(offset.y) < 3) { return; } + + L.DomEvent.preventDefault(e); + + if (!this._moved) { + this.fire('dragstart'); + + this._moved = true; + this._startPos = L.DomUtil.getPosition(this._element).subtract(offset); + + L.DomUtil.addClass(document.body, 'leaflet-dragging'); + this._lastTarget = e.target || e.srcElement; + L.DomUtil.addClass(this._lastTarget, 'leaflet-drag-target'); + } + + this._newPos = this._startPos.add(offset); + this._moving = true; + + L.Util.cancelAnimFrame(this._animRequest); + this._animRequest = L.Util.requestAnimFrame(this._updatePosition, this, true, this._dragStartTarget); + }, + + _updatePosition: function () { + this.fire('predrag'); + L.DomUtil.setPosition(this._element, this._newPos); + this.fire('drag'); + }, + + _onUp: function () { + L.DomUtil.removeClass(document.body, 'leaflet-dragging'); + + if (this._lastTarget) { + L.DomUtil.removeClass(this._lastTarget, 'leaflet-drag-target'); + this._lastTarget = null; + } + + for (var i in L.Draggable.MOVE) { + L.DomEvent + .off(document, L.Draggable.MOVE[i], this._onMove) + .off(document, L.Draggable.END[i], this._onUp); + } + + L.DomUtil.enableImageDrag(); + L.DomUtil.enableTextSelection(); + + if (this._moved && this._moving) { + // ensure drag is not fired after dragend + L.Util.cancelAnimFrame(this._animRequest); + + this.fire('dragend', { + distance: this._newPos.distanceTo(this._startPos) + }); + } + + this._moving = false; + } +}); + + +/* + L.Handler is a base class for handler classes that are used internally to inject + interaction features like dragging to classes like Map and Marker. +*/ + +L.Handler = L.Class.extend({ + initialize: function (map) { + this._map = map; + }, + + enable: function () { + if (this._enabled) { return; } + + this._enabled = true; + this.addHooks(); + }, + + disable: function () { + if (!this._enabled) { return; } + + this._enabled = false; + this.removeHooks(); + }, + + enabled: function () { + return !!this._enabled; + } +}); + + +/* + * L.Handler.MapDrag is used to make the map draggable (with panning inertia), enabled by default. + */ + +L.Map.mergeOptions({ + dragging: true, + + inertia: !L.Browser.android23, + inertiaDeceleration: 3400, // px/s^2 + inertiaMaxSpeed: Infinity, // px/s + inertiaThreshold: L.Browser.touch ? 32 : 18, // ms + easeLinearity: 0.25, + + // TODO refactor, move to CRS + worldCopyJump: false +}); + +L.Map.Drag = L.Handler.extend({ + addHooks: function () { + if (!this._draggable) { + var map = this._map; + + this._draggable = new L.Draggable(map._mapPane, map._container); + + this._draggable.on({ + 'dragstart': this._onDragStart, + 'drag': this._onDrag, + 'dragend': this._onDragEnd + }, this); + + if (map.options.worldCopyJump) { + this._draggable.on('predrag', this._onPreDrag, this); + map.on('viewreset', this._onViewReset, this); + + map.whenReady(this._onViewReset, this); + } + } + this._draggable.enable(); + }, + + removeHooks: function () { + this._draggable.disable(); + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _onDragStart: function () { + var map = this._map; + + if (map._panAnim) { + map._panAnim.stop(); + } + + map + .fire('movestart') + .fire('dragstart'); + + if (map.options.inertia) { + this._positions = []; + this._times = []; + } + }, + + _onDrag: function () { + if (this._map.options.inertia) { + var time = this._lastTime = +new Date(), + pos = this._lastPos = this._draggable._newPos; + + this._positions.push(pos); + this._times.push(time); + + if (time - this._times[0] > 200) { + this._positions.shift(); + this._times.shift(); + } + } + + this._map + .fire('move') + .fire('drag'); + }, + + _onViewReset: function () { + // TODO fix hardcoded Earth values + var pxCenter = this._map.getSize()._divideBy(2), + pxWorldCenter = this._map.latLngToLayerPoint([0, 0]); + + this._initialWorldOffset = pxWorldCenter.subtract(pxCenter).x; + this._worldWidth = this._map.project([0, 180]).x; + }, + + _onPreDrag: function () { + // TODO refactor to be able to adjust map pane position after zoom + var worldWidth = this._worldWidth, + halfWidth = Math.round(worldWidth / 2), + dx = this._initialWorldOffset, + x = this._draggable._newPos.x, + newX1 = (x - halfWidth + dx) % worldWidth + halfWidth - dx, + newX2 = (x + halfWidth + dx) % worldWidth - halfWidth - dx, + newX = Math.abs(newX1 + dx) < Math.abs(newX2 + dx) ? newX1 : newX2; + + this._draggable._newPos.x = newX; + }, + + _onDragEnd: function (e) { + var map = this._map, + options = map.options, + delay = +new Date() - this._lastTime, + + noInertia = !options.inertia || delay > options.inertiaThreshold || !this._positions[0]; + + map.fire('dragend', e); + + if (noInertia) { + map.fire('moveend'); + + } else { + + var direction = this._lastPos.subtract(this._positions[0]), + duration = (this._lastTime + delay - this._times[0]) / 1000, + ease = options.easeLinearity, + + speedVector = direction.multiplyBy(ease / duration), + speed = speedVector.distanceTo([0, 0]), + + limitedSpeed = Math.min(options.inertiaMaxSpeed, speed), + limitedSpeedVector = speedVector.multiplyBy(limitedSpeed / speed), + + decelerationDuration = limitedSpeed / (options.inertiaDeceleration * ease), + offset = limitedSpeedVector.multiplyBy(-decelerationDuration / 2).round(); + + if (!offset.x || !offset.y) { + map.fire('moveend'); + + } else { + offset = map._limitOffset(offset, map.options.maxBounds); + + L.Util.requestAnimFrame(function () { + map.panBy(offset, { + duration: decelerationDuration, + easeLinearity: ease, + noMoveStart: true + }); + }); + } + } + } +}); + +L.Map.addInitHook('addHandler', 'dragging', L.Map.Drag); + + +/* + * L.Handler.DoubleClickZoom is used to handle double-click zoom on the map, enabled by default. + */ + +L.Map.mergeOptions({ + doubleClickZoom: true +}); + +L.Map.DoubleClickZoom = L.Handler.extend({ + addHooks: function () { + this._map.on('dblclick', this._onDoubleClick, this); + }, + + removeHooks: function () { + this._map.off('dblclick', this._onDoubleClick, this); + }, + + _onDoubleClick: function (e) { + var map = this._map, + zoom = map.getZoom() + (e.originalEvent.shiftKey ? -1 : 1); + + if (map.options.doubleClickZoom === 'center') { + map.setZoom(zoom); + } else { + map.setZoomAround(e.containerPoint, zoom); + } + } +}); + +L.Map.addInitHook('addHandler', 'doubleClickZoom', L.Map.DoubleClickZoom); + + +/* + * L.Handler.ScrollWheelZoom is used by L.Map to enable mouse scroll wheel zoom on the map. + */ + +L.Map.mergeOptions({ + scrollWheelZoom: true +}); + +L.Map.ScrollWheelZoom = L.Handler.extend({ + addHooks: function () { + L.DomEvent.on(this._map._container, 'mousewheel', this._onWheelScroll, this); + L.DomEvent.on(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault); + this._delta = 0; + }, + + removeHooks: function () { + L.DomEvent.off(this._map._container, 'mousewheel', this._onWheelScroll); + L.DomEvent.off(this._map._container, 'MozMousePixelScroll', L.DomEvent.preventDefault); + }, + + _onWheelScroll: function (e) { + var delta = L.DomEvent.getWheelDelta(e); + + this._delta += delta; + this._lastMousePos = this._map.mouseEventToContainerPoint(e); + + if (!this._startTime) { + this._startTime = +new Date(); + } + + var left = Math.max(40 - (+new Date() - this._startTime), 0); + + clearTimeout(this._timer); + this._timer = setTimeout(L.bind(this._performZoom, this), left); + + L.DomEvent.preventDefault(e); + L.DomEvent.stopPropagation(e); + }, + + _performZoom: function () { + var map = this._map, + delta = this._delta, + zoom = map.getZoom(); + + delta = delta > 0 ? Math.ceil(delta) : Math.floor(delta); + delta = Math.max(Math.min(delta, 4), -4); + delta = map._limitZoom(zoom + delta) - zoom; + + this._delta = 0; + this._startTime = null; + + if (!delta) { return; } + + if (map.options.scrollWheelZoom === 'center') { + map.setZoom(zoom + delta); + } else { + map.setZoomAround(this._lastMousePos, zoom + delta); + } + } +}); + +L.Map.addInitHook('addHandler', 'scrollWheelZoom', L.Map.ScrollWheelZoom); + + +/* + * Extends the event handling code with double tap support for mobile browsers. + */ + +L.extend(L.DomEvent, { + + _touchstart: L.Browser.msPointer ? 'MSPointerDown' : L.Browser.pointer ? 'pointerdown' : 'touchstart', + _touchend: L.Browser.msPointer ? 'MSPointerUp' : L.Browser.pointer ? 'pointerup' : 'touchend', + + // inspired by Zepto touch code by Thomas Fuchs + addDoubleTapListener: function (obj, handler, id) { + var last, + doubleTap = false, + delay = 250, + touch, + pre = '_leaflet_', + touchstart = this._touchstart, + touchend = this._touchend, + trackedTouches = []; + + function onTouchStart(e) { + var count; + + if (L.Browser.pointer) { + trackedTouches.push(e.pointerId); + count = trackedTouches.length; + } else { + count = e.touches.length; + } + if (count > 1) { + return; + } + + var now = Date.now(), + delta = now - (last || now); + + touch = e.touches ? e.touches[0] : e; + doubleTap = (delta > 0 && delta <= delay); + last = now; + } + + function onTouchEnd(e) { + if (L.Browser.pointer) { + var idx = trackedTouches.indexOf(e.pointerId); + if (idx === -1) { + return; + } + trackedTouches.splice(idx, 1); + } + + if (doubleTap) { + if (L.Browser.pointer) { + // work around .type being readonly with MSPointer* events + var newTouch = { }, + prop; + + // jshint forin:false + for (var i in touch) { + prop = touch[i]; + if (typeof prop === 'function') { + newTouch[i] = prop.bind(touch); + } else { + newTouch[i] = prop; + } + } + touch = newTouch; + } + touch.type = 'dblclick'; + handler(touch); + last = null; + } + } + obj[pre + touchstart + id] = onTouchStart; + obj[pre + touchend + id] = onTouchEnd; + + // on pointer we need to listen on the document, otherwise a drag starting on the map and moving off screen + // will not come through to us, so we will lose track of how many touches are ongoing + var endElement = L.Browser.pointer ? document.documentElement : obj; + + obj.addEventListener(touchstart, onTouchStart, false); + endElement.addEventListener(touchend, onTouchEnd, false); + + if (L.Browser.pointer) { + endElement.addEventListener(L.DomEvent.POINTER_CANCEL, onTouchEnd, false); + } + + return this; + }, + + removeDoubleTapListener: function (obj, id) { + var pre = '_leaflet_'; + + obj.removeEventListener(this._touchstart, obj[pre + this._touchstart + id], false); + (L.Browser.pointer ? document.documentElement : obj).removeEventListener( + this._touchend, obj[pre + this._touchend + id], false); + + if (L.Browser.pointer) { + document.documentElement.removeEventListener(L.DomEvent.POINTER_CANCEL, obj[pre + this._touchend + id], + false); + } + + return this; + } +}); + + +/* + * Extends L.DomEvent to provide touch support for Internet Explorer and Windows-based devices. + */ + +L.extend(L.DomEvent, { + + //static + POINTER_DOWN: L.Browser.msPointer ? 'MSPointerDown' : 'pointerdown', + POINTER_MOVE: L.Browser.msPointer ? 'MSPointerMove' : 'pointermove', + POINTER_UP: L.Browser.msPointer ? 'MSPointerUp' : 'pointerup', + POINTER_CANCEL: L.Browser.msPointer ? 'MSPointerCancel' : 'pointercancel', + + _pointers: [], + _pointerDocumentListener: false, + + // Provides a touch events wrapper for (ms)pointer events. + // Based on changes by veproza https://github.com/CloudMade/Leaflet/pull/1019 + //ref http://www.w3.org/TR/pointerevents/ https://www.w3.org/Bugs/Public/show_bug.cgi?id=22890 + + addPointerListener: function (obj, type, handler, id) { + + switch (type) { + case 'touchstart': + return this.addPointerListenerStart(obj, type, handler, id); + case 'touchend': + return this.addPointerListenerEnd(obj, type, handler, id); + case 'touchmove': + return this.addPointerListenerMove(obj, type, handler, id); + default: + throw 'Unknown touch event type'; + } + }, + + addPointerListenerStart: function (obj, type, handler, id) { + var pre = '_leaflet_', + pointers = this._pointers; + + var cb = function (e) { + if (e.pointerType !== 'mouse' && e.pointerType !== e.MSPOINTER_TYPE_MOUSE) { + L.DomEvent.preventDefault(e); + } + + var alreadyInArray = false; + for (var i = 0; i < pointers.length; i++) { + if (pointers[i].pointerId === e.pointerId) { + alreadyInArray = true; + break; + } + } + if (!alreadyInArray) { + pointers.push(e); + } + + e.touches = pointers.slice(); + e.changedTouches = [e]; + + handler(e); + }; + + obj[pre + 'touchstart' + id] = cb; + obj.addEventListener(this.POINTER_DOWN, cb, false); + + // need to also listen for end events to keep the _pointers list accurate + // this needs to be on the body and never go away + if (!this._pointerDocumentListener) { + var internalCb = function (e) { + for (var i = 0; i < pointers.length; i++) { + if (pointers[i].pointerId === e.pointerId) { + pointers.splice(i, 1); + break; + } + } + }; + //We listen on the documentElement as any drags that end by moving the touch off the screen get fired there + document.documentElement.addEventListener(this.POINTER_UP, internalCb, false); + document.documentElement.addEventListener(this.POINTER_CANCEL, internalCb, false); + + this._pointerDocumentListener = true; + } + + return this; + }, + + addPointerListenerMove: function (obj, type, handler, id) { + var pre = '_leaflet_', + touches = this._pointers; + + function cb(e) { + + // don't fire touch moves when mouse isn't down + if ((e.pointerType === e.MSPOINTER_TYPE_MOUSE || e.pointerType === 'mouse') && e.buttons === 0) { return; } + + for (var i = 0; i < touches.length; i++) { + if (touches[i].pointerId === e.pointerId) { + touches[i] = e; + break; + } + } + + e.touches = touches.slice(); + e.changedTouches = [e]; + + handler(e); + } + + obj[pre + 'touchmove' + id] = cb; + obj.addEventListener(this.POINTER_MOVE, cb, false); + + return this; + }, + + addPointerListenerEnd: function (obj, type, handler, id) { + var pre = '_leaflet_', + touches = this._pointers; + + var cb = function (e) { + for (var i = 0; i < touches.length; i++) { + if (touches[i].pointerId === e.pointerId) { + touches.splice(i, 1); + break; + } + } + + e.touches = touches.slice(); + e.changedTouches = [e]; + + handler(e); + }; + + obj[pre + 'touchend' + id] = cb; + obj.addEventListener(this.POINTER_UP, cb, false); + obj.addEventListener(this.POINTER_CANCEL, cb, false); + + return this; + }, + + removePointerListener: function (obj, type, id) { + var pre = '_leaflet_', + cb = obj[pre + type + id]; + + switch (type) { + case 'touchstart': + obj.removeEventListener(this.POINTER_DOWN, cb, false); + break; + case 'touchmove': + obj.removeEventListener(this.POINTER_MOVE, cb, false); + break; + case 'touchend': + obj.removeEventListener(this.POINTER_UP, cb, false); + obj.removeEventListener(this.POINTER_CANCEL, cb, false); + break; + } + + return this; + } +}); + + +/* + * L.Handler.TouchZoom is used by L.Map to add pinch zoom on supported mobile browsers. + */ + +L.Map.mergeOptions({ + touchZoom: L.Browser.touch && !L.Browser.android23, + bounceAtZoomLimits: true +}); + +L.Map.TouchZoom = L.Handler.extend({ + addHooks: function () { + L.DomEvent.on(this._map._container, 'touchstart', this._onTouchStart, this); + }, + + removeHooks: function () { + L.DomEvent.off(this._map._container, 'touchstart', this._onTouchStart, this); + }, + + _onTouchStart: function (e) { + var map = this._map; + + if (!e.touches || e.touches.length !== 2 || map._animatingZoom || this._zooming) { return; } + + var p1 = map.mouseEventToLayerPoint(e.touches[0]), + p2 = map.mouseEventToLayerPoint(e.touches[1]), + viewCenter = map._getCenterLayerPoint(); + + this._startCenter = p1.add(p2)._divideBy(2); + this._startDist = p1.distanceTo(p2); + + this._moved = false; + this._zooming = true; + + this._centerOffset = viewCenter.subtract(this._startCenter); + + if (map._panAnim) { + map._panAnim.stop(); + } + + L.DomEvent + .on(document, 'touchmove', this._onTouchMove, this) + .on(document, 'touchend', this._onTouchEnd, this); + + L.DomEvent.preventDefault(e); + }, + + _onTouchMove: function (e) { + var map = this._map; + + if (!e.touches || e.touches.length !== 2 || !this._zooming) { return; } + + var p1 = map.mouseEventToLayerPoint(e.touches[0]), + p2 = map.mouseEventToLayerPoint(e.touches[1]); + + this._scale = p1.distanceTo(p2) / this._startDist; + this._delta = p1._add(p2)._divideBy(2)._subtract(this._startCenter); + + if (this._scale === 1) { return; } + + if (!map.options.bounceAtZoomLimits) { + if ((map.getZoom() === map.getMinZoom() && this._scale < 1) || + (map.getZoom() === map.getMaxZoom() && this._scale > 1)) { return; } + } + + if (!this._moved) { + L.DomUtil.addClass(map._mapPane, 'leaflet-touching'); + + map + .fire('movestart') + .fire('zoomstart'); + + this._moved = true; + } + + L.Util.cancelAnimFrame(this._animRequest); + this._animRequest = L.Util.requestAnimFrame( + this._updateOnMove, this, true, this._map._container); + + L.DomEvent.preventDefault(e); + }, + + _updateOnMove: function () { + var map = this._map, + origin = this._getScaleOrigin(), + center = map.layerPointToLatLng(origin), + zoom = map.getScaleZoom(this._scale); + + map._animateZoom(center, zoom, this._startCenter, this._scale, this._delta, false, true); + }, + + _onTouchEnd: function () { + if (!this._moved || !this._zooming) { + this._zooming = false; + return; + } + + var map = this._map; + + this._zooming = false; + L.DomUtil.removeClass(map._mapPane, 'leaflet-touching'); + L.Util.cancelAnimFrame(this._animRequest); + + L.DomEvent + .off(document, 'touchmove', this._onTouchMove) + .off(document, 'touchend', this._onTouchEnd); + + var origin = this._getScaleOrigin(), + center = map.layerPointToLatLng(origin), + + oldZoom = map.getZoom(), + floatZoomDelta = map.getScaleZoom(this._scale) - oldZoom, + roundZoomDelta = (floatZoomDelta > 0 ? + Math.ceil(floatZoomDelta) : Math.floor(floatZoomDelta)), + + zoom = map._limitZoom(oldZoom + roundZoomDelta), + scale = map.getZoomScale(zoom) / this._scale; + + map._animateZoom(center, zoom, origin, scale); + }, + + _getScaleOrigin: function () { + var centerOffset = this._centerOffset.subtract(this._delta).divideBy(this._scale); + return this._startCenter.add(centerOffset); + } +}); + +L.Map.addInitHook('addHandler', 'touchZoom', L.Map.TouchZoom); + + +/* + * L.Map.Tap is used to enable mobile hacks like quick taps and long hold. + */ + +L.Map.mergeOptions({ + tap: true, + tapTolerance: 15 +}); + +L.Map.Tap = L.Handler.extend({ + addHooks: function () { + L.DomEvent.on(this._map._container, 'touchstart', this._onDown, this); + }, + + removeHooks: function () { + L.DomEvent.off(this._map._container, 'touchstart', this._onDown, this); + }, + + _onDown: function (e) { + if (!e.touches) { return; } + + L.DomEvent.preventDefault(e); + + this._fireClick = true; + + // don't simulate click or track longpress if more than 1 touch + if (e.touches.length > 1) { + this._fireClick = false; + clearTimeout(this._holdTimeout); + return; + } + + var first = e.touches[0], + el = first.target; + + this._startPos = this._newPos = new L.Point(first.clientX, first.clientY); + + // if touching a link, highlight it + if (el.tagName && el.tagName.toLowerCase() === 'a') { + L.DomUtil.addClass(el, 'leaflet-active'); + } + + // simulate long hold but setting a timeout + this._holdTimeout = setTimeout(L.bind(function () { + if (this._isTapValid()) { + this._fireClick = false; + this._onUp(); + this._simulateEvent('contextmenu', first); + } + }, this), 1000); + + L.DomEvent + .on(document, 'touchmove', this._onMove, this) + .on(document, 'touchend', this._onUp, this); + }, + + _onUp: function (e) { + clearTimeout(this._holdTimeout); + + L.DomEvent + .off(document, 'touchmove', this._onMove, this) + .off(document, 'touchend', this._onUp, this); + + if (this._fireClick && e && e.changedTouches) { + + var first = e.changedTouches[0], + el = first.target; + + if (el && el.tagName && el.tagName.toLowerCase() === 'a') { + L.DomUtil.removeClass(el, 'leaflet-active'); + } + + // simulate click if the touch didn't move too much + if (this._isTapValid()) { + this._simulateEvent('click', first); + } + } + }, + + _isTapValid: function () { + return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance; + }, + + _onMove: function (e) { + var first = e.touches[0]; + this._newPos = new L.Point(first.clientX, first.clientY); + }, + + _simulateEvent: function (type, e) { + var simulatedEvent = document.createEvent('MouseEvents'); + + simulatedEvent._simulated = true; + e.target._simulatedClick = true; + + simulatedEvent.initMouseEvent( + type, true, true, window, 1, + e.screenX, e.screenY, + e.clientX, e.clientY, + false, false, false, false, 0, null); + + e.target.dispatchEvent(simulatedEvent); + } +}); + +if (L.Browser.touch && !L.Browser.pointer) { + L.Map.addInitHook('addHandler', 'tap', L.Map.Tap); +} + + +/* + * L.Handler.ShiftDragZoom is used to add shift-drag zoom interaction to the map + * (zoom to a selected bounding box), enabled by default. + */ + +L.Map.mergeOptions({ + boxZoom: true +}); + +L.Map.BoxZoom = L.Handler.extend({ + initialize: function (map) { + this._map = map; + this._container = map._container; + this._pane = map._panes.overlayPane; + this._moved = false; + }, + + addHooks: function () { + L.DomEvent.on(this._container, 'mousedown', this._onMouseDown, this); + }, + + removeHooks: function () { + L.DomEvent.off(this._container, 'mousedown', this._onMouseDown); + this._moved = false; + }, + + moved: function () { + return this._moved; + }, + + _onMouseDown: function (e) { + this._moved = false; + + if (!e.shiftKey || ((e.which !== 1) && (e.button !== 1))) { return false; } + + L.DomUtil.disableTextSelection(); + L.DomUtil.disableImageDrag(); + + this._startLayerPoint = this._map.mouseEventToLayerPoint(e); + + L.DomEvent + .on(document, 'mousemove', this._onMouseMove, this) + .on(document, 'mouseup', this._onMouseUp, this) + .on(document, 'keydown', this._onKeyDown, this); + }, + + _onMouseMove: function (e) { + if (!this._moved) { + this._box = L.DomUtil.create('div', 'leaflet-zoom-box', this._pane); + L.DomUtil.setPosition(this._box, this._startLayerPoint); + + //TODO refactor: move cursor to styles + this._container.style.cursor = 'crosshair'; + this._map.fire('boxzoomstart'); + } + + var startPoint = this._startLayerPoint, + box = this._box, + + layerPoint = this._map.mouseEventToLayerPoint(e), + offset = layerPoint.subtract(startPoint), + + newPos = new L.Point( + Math.min(layerPoint.x, startPoint.x), + Math.min(layerPoint.y, startPoint.y)); + + L.DomUtil.setPosition(box, newPos); + + this._moved = true; + + // TODO refactor: remove hardcoded 4 pixels + box.style.width = (Math.max(0, Math.abs(offset.x) - 4)) + 'px'; + box.style.height = (Math.max(0, Math.abs(offset.y) - 4)) + 'px'; + }, + + _finish: function () { + if (this._moved) { + this._pane.removeChild(this._box); + this._container.style.cursor = ''; + } + + L.DomUtil.enableTextSelection(); + L.DomUtil.enableImageDrag(); + + L.DomEvent + .off(document, 'mousemove', this._onMouseMove) + .off(document, 'mouseup', this._onMouseUp) + .off(document, 'keydown', this._onKeyDown); + }, + + _onMouseUp: function (e) { + + this._finish(); + + var map = this._map, + layerPoint = map.mouseEventToLayerPoint(e); + + if (this._startLayerPoint.equals(layerPoint)) { return; } + + var bounds = new L.LatLngBounds( + map.layerPointToLatLng(this._startLayerPoint), + map.layerPointToLatLng(layerPoint)); + + map.fitBounds(bounds); + + map.fire('boxzoomend', { + boxZoomBounds: bounds + }); + }, + + _onKeyDown: function (e) { + if (e.keyCode === 27) { + this._finish(); + } + } +}); + +L.Map.addInitHook('addHandler', 'boxZoom', L.Map.BoxZoom); + + +/* + * L.Map.Keyboard is handling keyboard interaction with the map, enabled by default. + */ + +L.Map.mergeOptions({ + keyboard: true, + keyboardPanOffset: 80, + keyboardZoomOffset: 1 +}); + +L.Map.Keyboard = L.Handler.extend({ + + keyCodes: { + left: [37], + right: [39], + down: [40], + up: [38], + zoomIn: [187, 107, 61, 171], + zoomOut: [189, 109, 173] + }, + + initialize: function (map) { + this._map = map; + + this._setPanOffset(map.options.keyboardPanOffset); + this._setZoomOffset(map.options.keyboardZoomOffset); + }, + + addHooks: function () { + var container = this._map._container; + + // make the container focusable by tabbing + if (container.tabIndex === -1) { + container.tabIndex = '0'; + } + + L.DomEvent + .on(container, 'focus', this._onFocus, this) + .on(container, 'blur', this._onBlur, this) + .on(container, 'mousedown', this._onMouseDown, this); + + this._map + .on('focus', this._addHooks, this) + .on('blur', this._removeHooks, this); + }, + + removeHooks: function () { + this._removeHooks(); + + var container = this._map._container; + + L.DomEvent + .off(container, 'focus', this._onFocus, this) + .off(container, 'blur', this._onBlur, this) + .off(container, 'mousedown', this._onMouseDown, this); + + this._map + .off('focus', this._addHooks, this) + .off('blur', this._removeHooks, this); + }, + + _onMouseDown: function () { + if (this._focused) { return; } + + var body = document.body, + docEl = document.documentElement, + top = body.scrollTop || docEl.scrollTop, + left = body.scrollLeft || docEl.scrollLeft; + + this._map._container.focus(); + + window.scrollTo(left, top); + }, + + _onFocus: function () { + this._focused = true; + this._map.fire('focus'); + }, + + _onBlur: function () { + this._focused = false; + this._map.fire('blur'); + }, + + _setPanOffset: function (pan) { + var keys = this._panKeys = {}, + codes = this.keyCodes, + i, len; + + for (i = 0, len = codes.left.length; i < len; i++) { + keys[codes.left[i]] = [-1 * pan, 0]; + } + for (i = 0, len = codes.right.length; i < len; i++) { + keys[codes.right[i]] = [pan, 0]; + } + for (i = 0, len = codes.down.length; i < len; i++) { + keys[codes.down[i]] = [0, pan]; + } + for (i = 0, len = codes.up.length; i < len; i++) { + keys[codes.up[i]] = [0, -1 * pan]; + } + }, + + _setZoomOffset: function (zoom) { + var keys = this._zoomKeys = {}, + codes = this.keyCodes, + i, len; + + for (i = 0, len = codes.zoomIn.length; i < len; i++) { + keys[codes.zoomIn[i]] = zoom; + } + for (i = 0, len = codes.zoomOut.length; i < len; i++) { + keys[codes.zoomOut[i]] = -zoom; + } + }, + + _addHooks: function () { + L.DomEvent.on(document, 'keydown', this._onKeyDown, this); + }, + + _removeHooks: function () { + L.DomEvent.off(document, 'keydown', this._onKeyDown, this); + }, + + _onKeyDown: function (e) { + var key = e.keyCode, + map = this._map; + + if (key in this._panKeys) { + + if (map._panAnim && map._panAnim._inProgress) { return; } + + map.panBy(this._panKeys[key]); + + if (map.options.maxBounds) { + map.panInsideBounds(map.options.maxBounds); + } + + } else if (key in this._zoomKeys) { + map.setZoom(map.getZoom() + this._zoomKeys[key]); + + } else { + return; + } + + L.DomEvent.stop(e); + } +}); + +L.Map.addInitHook('addHandler', 'keyboard', L.Map.Keyboard); + + +/* + * L.Handler.MarkerDrag is used internally by L.Marker to make the markers draggable. + */ + +L.Handler.MarkerDrag = L.Handler.extend({ + initialize: function (marker) { + this._marker = marker; + }, + + addHooks: function () { + var icon = this._marker._icon; + if (!this._draggable) { + this._draggable = new L.Draggable(icon, icon); + } + + this._draggable + .on('dragstart', this._onDragStart, this) + .on('drag', this._onDrag, this) + .on('dragend', this._onDragEnd, this); + this._draggable.enable(); + L.DomUtil.addClass(this._marker._icon, 'leaflet-marker-draggable'); + }, + + removeHooks: function () { + this._draggable + .off('dragstart', this._onDragStart, this) + .off('drag', this._onDrag, this) + .off('dragend', this._onDragEnd, this); + + this._draggable.disable(); + L.DomUtil.removeClass(this._marker._icon, 'leaflet-marker-draggable'); + }, + + moved: function () { + return this._draggable && this._draggable._moved; + }, + + _onDragStart: function () { + this._marker + .closePopup() + .fire('movestart') + .fire('dragstart'); + }, + + _onDrag: function () { + var marker = this._marker, + shadow = marker._shadow, + iconPos = L.DomUtil.getPosition(marker._icon), + latlng = marker._map.layerPointToLatLng(iconPos); + + // update shadow position + if (shadow) { + L.DomUtil.setPosition(shadow, iconPos); + } + + marker._latlng = latlng; + + marker + .fire('move', {latlng: latlng}) + .fire('drag'); + }, + + _onDragEnd: function (e) { + this._marker + .fire('moveend') + .fire('dragend', e); + } +}); + + +/* + * L.Control is a base class for implementing map controls. Handles positioning. + * All other controls extend from this class. + */ + +L.Control = L.Class.extend({ + options: { + position: 'topright' + }, + + initialize: function (options) { + L.setOptions(this, options); + }, + + getPosition: function () { + return this.options.position; + }, + + setPosition: function (position) { + var map = this._map; + + if (map) { + map.removeControl(this); + } + + this.options.position = position; + + if (map) { + map.addControl(this); + } + + return this; + }, + + getContainer: function () { + return this._container; + }, + + addTo: function (map) { + this._map = map; + + var container = this._container = this.onAdd(map), + pos = this.getPosition(), + corner = map._controlCorners[pos]; + + L.DomUtil.addClass(container, 'leaflet-control'); + + if (pos.indexOf('bottom') !== -1) { + corner.insertBefore(container, corner.firstChild); + } else { + corner.appendChild(container); + } + + return this; + }, + + removeFrom: function (map) { + var pos = this.getPosition(), + corner = map._controlCorners[pos]; + + corner.removeChild(this._container); + this._map = null; + + if (this.onRemove) { + this.onRemove(map); + } + + return this; + }, + + _refocusOnMap: function () { + if (this._map) { + this._map.getContainer().focus(); + } + } +}); + +L.control = function (options) { + return new L.Control(options); +}; + + +// adds control-related methods to L.Map + +L.Map.include({ + addControl: function (control) { + control.addTo(this); + return this; + }, + + removeControl: function (control) { + control.removeFrom(this); + return this; + }, + + _initControlPos: function () { + var corners = this._controlCorners = {}, + l = 'leaflet-', + container = this._controlContainer = + L.DomUtil.create('div', l + 'control-container', this._container); + + function createCorner(vSide, hSide) { + var className = l + vSide + ' ' + l + hSide; + + corners[vSide + hSide] = L.DomUtil.create('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'right'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + }, + + _clearControlPos: function () { + this._container.removeChild(this._controlContainer); + } +}); + + +/* + * L.Control.Zoom is used for the default zoom buttons on the map. + */ + +L.Control.Zoom = L.Control.extend({ + options: { + position: 'topleft', + zoomInText: '+', + zoomInTitle: 'Zoom in', + zoomOutText: '-', + zoomOutTitle: 'Zoom out' + }, + + onAdd: function (map) { + var zoomName = 'leaflet-control-zoom', + container = L.DomUtil.create('div', zoomName + ' leaflet-bar'); + + this._map = map; + + this._zoomInButton = this._createButton( + this.options.zoomInText, this.options.zoomInTitle, + zoomName + '-in', container, this._zoomIn, this); + this._zoomOutButton = this._createButton( + this.options.zoomOutText, this.options.zoomOutTitle, + zoomName + '-out', container, this._zoomOut, this); + + this._updateDisabled(); + map.on('zoomend zoomlevelschange', this._updateDisabled, this); + + return container; + }, + + onRemove: function (map) { + map.off('zoomend zoomlevelschange', this._updateDisabled, this); + }, + + _zoomIn: function (e) { + this._map.zoomIn(e.shiftKey ? 3 : 1); + }, + + _zoomOut: function (e) { + this._map.zoomOut(e.shiftKey ? 3 : 1); + }, + + _createButton: function (html, title, className, container, fn, context) { + var link = L.DomUtil.create('a', className, container); + link.innerHTML = html; + link.href = '#'; + link.title = title; + + var stop = L.DomEvent.stopPropagation; + + L.DomEvent + .on(link, 'click', stop) + .on(link, 'mousedown', stop) + .on(link, 'dblclick', stop) + .on(link, 'click', L.DomEvent.preventDefault) + .on(link, 'click', fn, context) + .on(link, 'click', this._refocusOnMap, context); + + return link; + }, + + _updateDisabled: function () { + var map = this._map, + className = 'leaflet-disabled'; + + L.DomUtil.removeClass(this._zoomInButton, className); + L.DomUtil.removeClass(this._zoomOutButton, className); + + if (map._zoom === map.getMinZoom()) { + L.DomUtil.addClass(this._zoomOutButton, className); + } + if (map._zoom === map.getMaxZoom()) { + L.DomUtil.addClass(this._zoomInButton, className); + } + } +}); + +L.Map.mergeOptions({ + zoomControl: true +}); + +L.Map.addInitHook(function () { + if (this.options.zoomControl) { + this.zoomControl = new L.Control.Zoom(); + this.addControl(this.zoomControl); + } +}); + +L.control.zoom = function (options) { + return new L.Control.Zoom(options); +}; + + + +/* + * L.Control.Attribution is used for displaying attribution on the map (added by default). + */ + +L.Control.Attribution = L.Control.extend({ + options: { + position: 'bottomright', + prefix: 'Leaflet' + }, + + initialize: function (options) { + L.setOptions(this, options); + + this._attributions = {}; + }, + + onAdd: function (map) { + this._container = L.DomUtil.create('div', 'leaflet-control-attribution'); + L.DomEvent.disableClickPropagation(this._container); + + for (var i in map._layers) { + if (map._layers[i].getAttribution) { + this.addAttribution(map._layers[i].getAttribution()); + } + } + + map + .on('layeradd', this._onLayerAdd, this) + .on('layerremove', this._onLayerRemove, this); + + this._update(); + + return this._container; + }, + + onRemove: function (map) { + map + .off('layeradd', this._onLayerAdd) + .off('layerremove', this._onLayerRemove); + + }, + + setPrefix: function (prefix) { + this.options.prefix = prefix; + this._update(); + return this; + }, + + addAttribution: function (text) { + if (!text) { return; } + + if (!this._attributions[text]) { + this._attributions[text] = 0; + } + this._attributions[text]++; + + this._update(); + + return this; + }, + + removeAttribution: function (text) { + if (!text) { return; } + + if (this._attributions[text]) { + this._attributions[text]--; + this._update(); + } + + return this; + }, + + _update: function () { + if (!this._map) { return; } + + var attribs = []; + + for (var i in this._attributions) { + if (this._attributions[i]) { + attribs.push(i); + } + } + + var prefixAndAttribs = []; + + if (this.options.prefix) { + prefixAndAttribs.push(this.options.prefix); + } + if (attribs.length) { + prefixAndAttribs.push(attribs.join(', ')); + } + + this._container.innerHTML = prefixAndAttribs.join(' | '); + }, + + _onLayerAdd: function (e) { + if (e.layer.getAttribution) { + this.addAttribution(e.layer.getAttribution()); + } + }, + + _onLayerRemove: function (e) { + if (e.layer.getAttribution) { + this.removeAttribution(e.layer.getAttribution()); + } + } +}); + +L.Map.mergeOptions({ + attributionControl: true +}); + +L.Map.addInitHook(function () { + if (this.options.attributionControl) { + this.attributionControl = (new L.Control.Attribution()).addTo(this); + } +}); + +L.control.attribution = function (options) { + return new L.Control.Attribution(options); +}; + + +/* + * L.Control.Scale is used for displaying metric/imperial scale on the map. + */ + +L.Control.Scale = L.Control.extend({ + options: { + position: 'bottomleft', + maxWidth: 100, + metric: true, + imperial: true, + updateWhenIdle: false + }, + + onAdd: function (map) { + this._map = map; + + var className = 'leaflet-control-scale', + container = L.DomUtil.create('div', className), + options = this.options; + + this._addScales(options, className, container); + + map.on(options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + map.whenReady(this._update, this); + + return container; + }, + + onRemove: function (map) { + map.off(this.options.updateWhenIdle ? 'moveend' : 'move', this._update, this); + }, + + _addScales: function (options, className, container) { + if (options.metric) { + this._mScale = L.DomUtil.create('div', className + '-line', container); + } + if (options.imperial) { + this._iScale = L.DomUtil.create('div', className + '-line', container); + } + }, + + _update: function () { + var bounds = this._map.getBounds(), + centerLat = bounds.getCenter().lat, + halfWorldMeters = 6378137 * Math.PI * Math.cos(centerLat * Math.PI / 180), + dist = halfWorldMeters * (bounds.getNorthEast().lng - bounds.getSouthWest().lng) / 180, + + size = this._map.getSize(), + options = this.options, + maxMeters = 0; + + if (size.x > 0) { + maxMeters = dist * (options.maxWidth / size.x); + } + + this._updateScales(options, maxMeters); + }, + + _updateScales: function (options, maxMeters) { + if (options.metric && maxMeters) { + this._updateMetric(maxMeters); + } + + if (options.imperial && maxMeters) { + this._updateImperial(maxMeters); + } + }, + + _updateMetric: function (maxMeters) { + var meters = this._getRoundNum(maxMeters); + + this._mScale.style.width = this._getScaleWidth(meters / maxMeters) + 'px'; + this._mScale.innerHTML = meters < 1000 ? meters + ' m' : (meters / 1000) + ' km'; + }, + + _updateImperial: function (maxMeters) { + var maxFeet = maxMeters * 3.2808399, + scale = this._iScale, + maxMiles, miles, feet; + + if (maxFeet > 5280) { + maxMiles = maxFeet / 5280; + miles = this._getRoundNum(maxMiles); + + scale.style.width = this._getScaleWidth(miles / maxMiles) + 'px'; + scale.innerHTML = miles + ' mi'; + + } else { + feet = this._getRoundNum(maxFeet); + + scale.style.width = this._getScaleWidth(feet / maxFeet) + 'px'; + scale.innerHTML = feet + ' ft'; + } + }, + + _getScaleWidth: function (ratio) { + return Math.round(this.options.maxWidth * ratio) - 10; + }, + + _getRoundNum: function (num) { + var pow10 = Math.pow(10, (Math.floor(num) + '').length - 1), + d = num / pow10; + + d = d >= 10 ? 10 : d >= 5 ? 5 : d >= 3 ? 3 : d >= 2 ? 2 : 1; + + return pow10 * d; + } +}); + +L.control.scale = function (options) { + return new L.Control.Scale(options); +}; + + +/* + * L.Control.Layers is a control to allow users to switch between different layers on the map. + */ + +L.Control.Layers = L.Control.extend({ + options: { + collapsed: true, + position: 'topright', + autoZIndex: true + }, + + initialize: function (baseLayers, overlays, options) { + L.setOptions(this, options); + + this._layers = {}; + this._lastZIndex = 0; + this._handlingClick = false; + + for (var i in baseLayers) { + this._addLayer(baseLayers[i], i); + } + + for (i in overlays) { + this._addLayer(overlays[i], i, true); + } + }, + + onAdd: function (map) { + this._initLayout(); + this._update(); + + map + .on('layeradd', this._onLayerChange, this) + .on('layerremove', this._onLayerChange, this); + + return this._container; + }, + + onRemove: function (map) { + map + .off('layeradd', this._onLayerChange, this) + .off('layerremove', this._onLayerChange, this); + }, + + addBaseLayer: function (layer, name) { + this._addLayer(layer, name); + this._update(); + return this; + }, + + addOverlay: function (layer, name) { + this._addLayer(layer, name, true); + this._update(); + return this; + }, + + removeLayer: function (layer) { + var id = L.stamp(layer); + delete this._layers[id]; + this._update(); + return this; + }, + + _initLayout: function () { + var className = 'leaflet-control-layers', + container = this._container = L.DomUtil.create('div', className); + + //Makes this work on IE10 Touch devices by stopping it from firing a mouseout event when the touch is released + container.setAttribute('aria-haspopup', true); + + if (!L.Browser.touch) { + L.DomEvent + .disableClickPropagation(container) + .disableScrollPropagation(container); + } else { + L.DomEvent.on(container, 'click', L.DomEvent.stopPropagation); + } + + var form = this._form = L.DomUtil.create('form', className + '-list'); + + if (this.options.collapsed) { + if (!L.Browser.android) { + L.DomEvent + .on(container, 'mouseover', this._expand, this) + .on(container, 'mouseout', this._collapse, this); + } + var link = this._layersLink = L.DomUtil.create('a', className + '-toggle', container); + link.href = '#'; + link.title = 'Layers'; + + if (L.Browser.touch) { + L.DomEvent + .on(link, 'click', L.DomEvent.stop) + .on(link, 'click', this._expand, this); + } + else { + L.DomEvent.on(link, 'focus', this._expand, this); + } + //Work around for Firefox android issue https://github.com/Leaflet/Leaflet/issues/2033 + L.DomEvent.on(form, 'click', function () { + setTimeout(L.bind(this._onInputClick, this), 0); + }, this); + + this._map.on('click', this._collapse, this); + // TODO keyboard accessibility + } else { + this._expand(); + } + + this._baseLayersList = L.DomUtil.create('div', className + '-base', form); + this._separator = L.DomUtil.create('div', className + '-separator', form); + this._overlaysList = L.DomUtil.create('div', className + '-overlays', form); + + container.appendChild(form); + }, + + _addLayer: function (layer, name, overlay) { + var id = L.stamp(layer); + + this._layers[id] = { + layer: layer, + name: name, + overlay: overlay + }; + + if (this.options.autoZIndex && layer.setZIndex) { + this._lastZIndex++; + layer.setZIndex(this._lastZIndex); + } + }, + + _update: function () { + if (!this._container) { + return; + } + + this._baseLayersList.innerHTML = ''; + this._overlaysList.innerHTML = ''; + + var baseLayersPresent = false, + overlaysPresent = false, + i, obj; + + for (i in this._layers) { + obj = this._layers[i]; + this._addItem(obj); + overlaysPresent = overlaysPresent || obj.overlay; + baseLayersPresent = baseLayersPresent || !obj.overlay; + } + + this._separator.style.display = overlaysPresent && baseLayersPresent ? '' : 'none'; + }, + + _onLayerChange: function (e) { + var obj = this._layers[L.stamp(e.layer)]; + + if (!obj) { return; } + + if (!this._handlingClick) { + this._update(); + } + + var type = obj.overlay ? + (e.type === 'layeradd' ? 'overlayadd' : 'overlayremove') : + (e.type === 'layeradd' ? 'baselayerchange' : null); + + if (type) { + this._map.fire(type, obj); + } + }, + + // IE7 bugs out if you create a radio dynamically, so you have to do it this hacky way (see http://bit.ly/PqYLBe) + _createRadioElement: function (name, checked) { + + var radioHtml = '= 0) { + this._onZoomTransitionEnd(); + } + }, + + _nothingToAnimate: function () { + return !this._container.getElementsByClassName('leaflet-zoom-animated').length; + }, + + _tryAnimatedZoom: function (center, zoom, options) { + + if (this._animatingZoom) { return true; } + + options = options || {}; + + // don't animate if disabled, not supported or zoom difference is too large + if (!this._zoomAnimated || options.animate === false || this._nothingToAnimate() || + Math.abs(zoom - this._zoom) > this.options.zoomAnimationThreshold) { return false; } + + // offset is the pixel coords of the zoom origin relative to the current center + var scale = this.getZoomScale(zoom), + offset = this._getCenterOffset(center)._divideBy(1 - 1 / scale), + origin = this._getCenterLayerPoint()._add(offset); + + // don't animate if the zoom origin isn't within one screen from the current center, unless forced + if (options.animate !== true && !this.getSize().contains(offset)) { return false; } + + this + .fire('movestart') + .fire('zoomstart'); + + this._animateZoom(center, zoom, origin, scale, null, true); + + return true; + }, + + _animateZoom: function (center, zoom, origin, scale, delta, backwards, forTouchZoom) { + + if (!forTouchZoom) { + this._animatingZoom = true; + } + + // put transform transition on all layers with leaflet-zoom-animated class + L.DomUtil.addClass(this._mapPane, 'leaflet-zoom-anim'); + + // remember what center/zoom to set after animation + this._animateToCenter = center; + this._animateToZoom = zoom; + + // disable any dragging during animation + if (L.Draggable) { + L.Draggable._disabled = true; + } + + L.Util.requestAnimFrame(function () { + this.fire('zoomanim', { + center: center, + zoom: zoom, + origin: origin, + scale: scale, + delta: delta, + backwards: backwards + }); + // horrible hack to work around a Chrome bug https://github.com/Leaflet/Leaflet/issues/3689 + setTimeout(L.bind(this._onZoomTransitionEnd, this), 250); + }, this); + }, + + _onZoomTransitionEnd: function () { + if (!this._animatingZoom) { return; } + + this._animatingZoom = false; + + L.DomUtil.removeClass(this._mapPane, 'leaflet-zoom-anim'); + + L.Util.requestAnimFrame(function () { + this._resetView(this._animateToCenter, this._animateToZoom, true, true); + + if (L.Draggable) { + L.Draggable._disabled = false; + } + }, this); + } +}); + + +/* + Zoom animation logic for L.TileLayer. +*/ + +L.TileLayer.include({ + _animateZoom: function (e) { + if (!this._animating) { + this._animating = true; + this._prepareBgBuffer(); + } + + var bg = this._bgBuffer, + transform = L.DomUtil.TRANSFORM, + initialTransform = e.delta ? L.DomUtil.getTranslateString(e.delta) : bg.style[transform], + scaleStr = L.DomUtil.getScaleString(e.scale, e.origin); + + bg.style[transform] = e.backwards ? + scaleStr + ' ' + initialTransform : + initialTransform + ' ' + scaleStr; + }, + + _endZoomAnim: function () { + var front = this._tileContainer, + bg = this._bgBuffer; + + front.style.visibility = ''; + front.parentNode.appendChild(front); // Bring to fore + + // force reflow + L.Util.falseFn(bg.offsetWidth); + + var zoom = this._map.getZoom(); + if (zoom > this.options.maxZoom || zoom < this.options.minZoom) { + this._clearBgBuffer(); + } + + this._animating = false; + }, + + _clearBgBuffer: function () { + var map = this._map; + + if (map && !map._animatingZoom && !map.touchZoom._zooming) { + this._bgBuffer.innerHTML = ''; + this._bgBuffer.style[L.DomUtil.TRANSFORM] = ''; + } + }, + + _prepareBgBuffer: function () { + + var front = this._tileContainer, + bg = this._bgBuffer; + + // if foreground layer doesn't have many tiles but bg layer does, + // keep the existing bg layer and just zoom it some more + + var bgLoaded = this._getLoadedTilesPercentage(bg), + frontLoaded = this._getLoadedTilesPercentage(front); + + if (bg && bgLoaded > 0.5 && frontLoaded < 0.5) { + + front.style.visibility = 'hidden'; + this._stopLoadingImages(front); + return; + } + + // prepare the buffer to become the front tile pane + bg.style.visibility = 'hidden'; + bg.style[L.DomUtil.TRANSFORM] = ''; + + // switch out the current layer to be the new bg layer (and vice-versa) + this._tileContainer = bg; + bg = this._bgBuffer = front; + + this._stopLoadingImages(bg); + + //prevent bg buffer from clearing right after zoom + clearTimeout(this._clearBgBufferTimer); + }, + + _getLoadedTilesPercentage: function (container) { + var tiles = container.getElementsByTagName('img'), + i, len, count = 0; + + for (i = 0, len = tiles.length; i < len; i++) { + if (tiles[i].complete) { + count++; + } + } + return count / len; + }, + + // stops loading all tiles in the background layer + _stopLoadingImages: function (container) { + var tiles = Array.prototype.slice.call(container.getElementsByTagName('img')), + i, len, tile; + + for (i = 0, len = tiles.length; i < len; i++) { + tile = tiles[i]; + + if (!tile.complete) { + tile.onload = L.Util.falseFn; + tile.onerror = L.Util.falseFn; + tile.src = L.Util.emptyImageUrl; + + tile.parentNode.removeChild(tile); + } + } + } +}); + + +/* + * Provides L.Map with convenient shortcuts for using browser geolocation features. + */ + +L.Map.include({ + _defaultLocateOptions: { + watch: false, + setView: false, + maxZoom: Infinity, + timeout: 10000, + maximumAge: 0, + enableHighAccuracy: false + }, + + locate: function (/*Object*/ options) { + + options = this._locateOptions = L.extend(this._defaultLocateOptions, options); + + if (!navigator.geolocation) { + this._handleGeolocationError({ + code: 0, + message: 'Geolocation not supported.' + }); + return this; + } + + var onResponse = L.bind(this._handleGeolocationResponse, this), + onError = L.bind(this._handleGeolocationError, this); + + if (options.watch) { + this._locationWatchId = + navigator.geolocation.watchPosition(onResponse, onError, options); + } else { + navigator.geolocation.getCurrentPosition(onResponse, onError, options); + } + return this; + }, + + stopLocate: function () { + if (navigator.geolocation) { + navigator.geolocation.clearWatch(this._locationWatchId); + } + if (this._locateOptions) { + this._locateOptions.setView = false; + } + return this; + }, + + _handleGeolocationError: function (error) { + var c = error.code, + message = error.message || + (c === 1 ? 'permission denied' : + (c === 2 ? 'position unavailable' : 'timeout')); + + if (this._locateOptions.setView && !this._loaded) { + this.fitWorld(); + } + + this.fire('locationerror', { + code: c, + message: 'Geolocation error: ' + message + '.' + }); + }, + + _handleGeolocationResponse: function (pos) { + var lat = pos.coords.latitude, + lng = pos.coords.longitude, + latlng = new L.LatLng(lat, lng), + + latAccuracy = 180 * pos.coords.accuracy / 40075017, + lngAccuracy = latAccuracy / Math.cos(L.LatLng.DEG_TO_RAD * lat), + + bounds = L.latLngBounds( + [lat - latAccuracy, lng - lngAccuracy], + [lat + latAccuracy, lng + lngAccuracy]), + + options = this._locateOptions; + + if (options.setView) { + var zoom = Math.min(this.getBoundsZoom(bounds), options.maxZoom); + this.setView(latlng, zoom); + } + + var data = { + latlng: latlng, + bounds: bounds, + timestamp: pos.timestamp + }; + + for (var i in pos.coords) { + if (typeof pos.coords[i] === 'number') { + data[i] = pos.coords[i]; + } + } + + this.fire('locationfound', data); + } +}); + + +}(window, document)); +/* + Leaflet.markercluster, Provides Beautiful Animated Marker Clustering functionality for Leaflet, a JS library for interactive maps. + https://github.com/Leaflet/Leaflet.markercluster + (c) 2012-2013, Dave Leaver, smartrak +*/ + +!function(t,e){L.MarkerClusterGroup=L.FeatureGroup.extend({options:{maxClusterRadius:80,iconCreateFunction:null,spiderfyOnMaxZoom:!0,showCoverageOnHover:!0,zoomToBoundsOnClick:!0,singleMarkerMode:!1,disableClusteringAtZoom:null,removeOutsideVisibleBounds:!0,animateAddingMarkers:!1,spiderfyDistanceMultiplier:1,chunkedLoading:!1,chunkInterval:200,chunkDelay:50,chunkProgress:null,polygonOptions:{}},initialize:function(t){L.Util.setOptions(this,t),this.options.iconCreateFunction||(this.options.iconCreateFunction=this._defaultIconCreateFunction),this._featureGroup=L.featureGroup(),this._featureGroup.on(L.FeatureGroup.EVENTS,this._propagateEvent,this),this._nonPointGroup=L.featureGroup(),this._nonPointGroup.on(L.FeatureGroup.EVENTS,this._propagateEvent,this),this._inZoomAnimation=0,this._needsClustering=[],this._needsRemoving=[],this._currentShownBounds=null,this._queue=[]},addLayer:function(t){if(t instanceof L.LayerGroup){var e=[];for(var i in t._layers)e.push(t._layers[i]);return this.addLayers(e)}if(!t.getLatLng)return this._nonPointGroup.addLayer(t),this;if(!this._map)return this._needsClustering.push(t),this;if(this.hasLayer(t))return this;this._unspiderfy&&this._unspiderfy(),this._addLayer(t,this._maxZoom);var n=t,s=this._map.getZoom();if(t.__parent)for(;n.__parent._zoom>=s;)n=n.__parent;return this._currentShownBounds.contains(n.getLatLng())&&(this.options.animateAddingMarkers?this._animationAddLayer(t,n):this._animationAddLayerNonAnimated(t,n)),this},removeLayer:function(t){if(t instanceof L.LayerGroup){var e=[];for(var i in t._layers)e.push(t._layers[i]);return this.removeLayers(e)}return t.getLatLng?this._map?t.__parent?(this._unspiderfy&&(this._unspiderfy(),this._unspiderfyLayer(t)),this._removeLayer(t,!0),this._featureGroup.hasLayer(t)&&(this._featureGroup.removeLayer(t),t.setOpacity&&t.setOpacity(1)),this):this:(!this._arraySplice(this._needsClustering,t)&&this.hasLayer(t)&&this._needsRemoving.push(t),this):(this._nonPointGroup.removeLayer(t),this)},addLayers:function(t){var e,i,n,s,r=this._featureGroup,o=this._nonPointGroup,a=this.options.chunkedLoading,h=this.options.chunkInterval,_=this.options.chunkProgress;if(this._map){var u=0,l=(new Date).getTime(),d=L.bind(function(){for(var e=(new Date).getTime();uh)break}if(s=t[u],s.getLatLng){if(!this.hasLayer(s)&&(this._addLayer(s,this._maxZoom),s.__parent&&2===s.__parent.getChildCount())){var n=s.__parent.getAllChildMarkers(),p=n[0]===s?n[1]:n[0];r.removeLayer(p)}}else o.addLayer(s)}_&&_(u,t.length,(new Date).getTime()-l),u===t.length?(this._featureGroup.eachLayer(function(t){t instanceof L.MarkerCluster&&t._iconNeedsUpdate&&t._updateIcon()}),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds)):setTimeout(d,this.options.chunkDelay)},this);d()}else{for(e=[],i=0,n=t.length;n>i;i++)s=t[i],s.getLatLng?this.hasLayer(s)||e.push(s):o.addLayer(s);this._needsClustering=this._needsClustering.concat(e)}return this},removeLayers:function(t){var e,i,n,s=this._featureGroup,r=this._nonPointGroup;if(!this._map){for(e=0,i=t.length;i>e;e++)n=t[e],this._arraySplice(this._needsClustering,n),r.removeLayer(n);return this}for(e=0,i=t.length;i>e;e++)n=t[e],n.__parent?(this._removeLayer(n,!0,!0),s.hasLayer(n)&&(s.removeLayer(n),n.setOpacity&&n.setOpacity(1))):r.removeLayer(n);return this._topClusterLevel._recursivelyAddChildrenToMap(null,this._zoom,this._currentShownBounds),s.eachLayer(function(t){t instanceof L.MarkerCluster&&t._updateIcon()}),this},clearLayers:function(){return this._map||(this._needsClustering=[],delete this._gridClusters,delete this._gridUnclustered),this._noanimationUnspiderfy&&this._noanimationUnspiderfy(),this._featureGroup.clearLayers(),this._nonPointGroup.clearLayers(),this.eachLayer(function(t){delete t.__parent}),this._map&&this._generateInitialClusters(),this},getBounds:function(){var t=new L.LatLngBounds;if(this._topClusterLevel)t.extend(this._topClusterLevel._bounds);else for(var e=this._needsClustering.length-1;e>=0;e--)t.extend(this._needsClustering[e].getLatLng());return t.extend(this._nonPointGroup.getBounds()),t},eachLayer:function(t,e){var i,n=this._needsClustering.slice();for(this._topClusterLevel&&this._topClusterLevel.getAllChildMarkers(n),i=n.length-1;i>=0;i--)t.call(e,n[i]);this._nonPointGroup.eachLayer(t,e)},getLayers:function(){var t=[];return this.eachLayer(function(e){t.push(e)}),t},getLayer:function(t){var e=null;return this.eachLayer(function(i){L.stamp(i)===t&&(e=i)}),e},hasLayer:function(t){if(!t)return!1;var e,i=this._needsClustering;for(e=i.length-1;e>=0;e--)if(i[e]===t)return!0;for(i=this._needsRemoving,e=i.length-1;e>=0;e--)if(i[e]===t)return!1;return!(!t.__parent||t.__parent._group!==this)||this._nonPointGroup.hasLayer(t)},zoomToShowLayer:function(t,e){var i=function(){if((t._icon||t.__parent._icon)&&!this._inZoomAnimation)if(this._map.off("moveend",i,this),this.off("animationend",i,this),t._icon)e();else if(t.__parent._icon){var n=function(){this.off("spiderfied",n,this),e()};this.on("spiderfied",n,this),t.__parent.spiderfy()}};t._icon&&this._map.getBounds().contains(t.getLatLng())?e():t.__parent._zoome;e++)n=this._needsRemoving[e],this._removeLayer(n,!0);this._needsRemoving=[],this._zoom=this._map.getZoom(),this._currentShownBounds=this._getExpandedVisibleBounds(),this._map.on("zoomend",this._zoomEnd,this),this._map.on("moveend",this._moveEnd,this),this._spiderfierOnAdd&&this._spiderfierOnAdd(),this._bindEvents(),i=this._needsClustering,this._needsClustering=[],this.addLayers(i)},onRemove:function(t){t.off("zoomend",this._zoomEnd,this),t.off("moveend",this._moveEnd,this),this._unbindEvents(),this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim",""),this._spiderfierOnRemove&&this._spiderfierOnRemove(),this._hideCoverage(),this._featureGroup.onRemove(t),this._nonPointGroup.onRemove(t),this._featureGroup.clearLayers(),this._map=null},getVisibleParent:function(t){for(var e=t;e&&!e._icon;)e=e.__parent;return e||null},_arraySplice:function(t,e){for(var i=t.length-1;i>=0;i--)if(t[i]===e)return t.splice(i,1),!0},_removeLayer:function(t,e,i){var n=this._gridClusters,s=this._gridUnclustered,r=this._featureGroup,o=this._map;if(e)for(var a=this._maxZoom;a>=0&&s[a].removeObject(t,o.project(t.getLatLng(),a));a--);var h,_=t.__parent,u=_._markers;for(this._arraySplice(u,t);_&&(_._childCount--,!(_._zoom<0));)e&&_._childCount<=1?(h=_._markers[0]===t?_._markers[1]:_._markers[0],n[_._zoom].removeObject(_,o.project(_._cLatLng,_._zoom)),s[_._zoom].addObject(h,o.project(h.getLatLng(),_._zoom)),this._arraySplice(_.__parent._childClusters,_),_.__parent._markers.push(h),h.__parent=_.__parent,_._icon&&(r.removeLayer(_),i||r.addLayer(h))):(_._recalculateBounds(),i&&_._icon||_._updateIcon()),_=_.__parent;delete t.__parent},_isOrIsParent:function(t,e){for(;e;){if(t===e)return!0;e=e.parentNode}return!1},_propagateEvent:function(t){if(t.layer instanceof L.MarkerCluster){if(t.originalEvent&&this._isOrIsParent(t.layer._icon,t.originalEvent.relatedTarget))return;t.type="cluster"+t.type}this.fire(t.type,t)},_defaultIconCreateFunction:function(t){var e=t.getChildCount(),i=" marker-cluster-";return i+=10>e?"small":100>e?"medium":"large",new L.DivIcon({html:"
    "+e+"
    ",className:"marker-cluster"+i,iconSize:new L.Point(40,40)})},_bindEvents:function(){var t=this._map,e=this.options.spiderfyOnMaxZoom,i=this.options.showCoverageOnHover,n=this.options.zoomToBoundsOnClick;(e||n)&&this.on("clusterclick",this._zoomOrSpiderfy,this),i&&(this.on("clustermouseover",this._showCoverage,this),this.on("clustermouseout",this._hideCoverage,this),t.on("zoomend",this._hideCoverage,this))},_zoomOrSpiderfy:function(t){var e=this._map;e.getMaxZoom()===e.getZoom()?this.options.spiderfyOnMaxZoom&&t.layer.spiderfy():this.options.zoomToBoundsOnClick&&t.layer.zoomToBounds(),t.originalEvent&&13===t.originalEvent.keyCode&&e._container.focus()},_showCoverage:function(t){var e=this._map;this._inZoomAnimation||(this._shownPolygon&&e.removeLayer(this._shownPolygon),t.layer.getChildCount()>2&&t.layer!==this._spiderfied&&(this._shownPolygon=new L.Polygon(t.layer.getConvexHull(),this.options.polygonOptions),e.addLayer(this._shownPolygon)))},_hideCoverage:function(){this._shownPolygon&&(this._map.removeLayer(this._shownPolygon),this._shownPolygon=null)},_unbindEvents:function(){var t=this.options.spiderfyOnMaxZoom,e=this.options.showCoverageOnHover,i=this.options.zoomToBoundsOnClick,n=this._map;(t||i)&&this.off("clusterclick",this._zoomOrSpiderfy,this),e&&(this.off("clustermouseover",this._showCoverage,this),this.off("clustermouseout",this._hideCoverage,this),n.off("zoomend",this._hideCoverage,this))},_zoomEnd:function(){this._map&&(this._mergeSplitClusters(),this._zoom=this._map._zoom,this._currentShownBounds=this._getExpandedVisibleBounds())},_moveEnd:function(){if(!this._inZoomAnimation){var t=this._getExpandedVisibleBounds();this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,this._zoom,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,this._map._zoom,t),this._currentShownBounds=t}},_generateInitialClusters:function(){var t=this._map.getMaxZoom(),e=this.options.maxClusterRadius,i=e;"function"!=typeof e&&(i=function(){return e}),this.options.disableClusteringAtZoom&&(t=this.options.disableClusteringAtZoom-1),this._maxZoom=t,this._gridClusters={},this._gridUnclustered={};for(var n=t;n>=0;n--)this._gridClusters[n]=new L.DistanceGrid(i(n)),this._gridUnclustered[n]=new L.DistanceGrid(i(n));this._topClusterLevel=new L.MarkerCluster(this,-1)},_addLayer:function(t,e){var i,n,s=this._gridClusters,r=this._gridUnclustered;for(this.options.singleMarkerMode&&(t.options.icon=this.options.iconCreateFunction({getChildCount:function(){return 1},getAllChildMarkers:function(){return[t]}}));e>=0;e--){i=this._map.project(t.getLatLng(),e);var o=s[e].getNearObject(i);if(o)return o._addChild(t),t.__parent=o,void 0;if(o=r[e].getNearObject(i)){var a=o.__parent;a&&this._removeLayer(o,!1);var h=new L.MarkerCluster(this,e,o,t);s[e].addObject(h,this._map.project(h._cLatLng,e)),o.__parent=h,t.__parent=h;var _=h;for(n=e-1;n>a._zoom;n--)_=new L.MarkerCluster(this,n,_),s[n].addObject(_,this._map.project(o.getLatLng(),n));for(a._addChild(_),n=e;n>=0&&r[n].removeObject(o,this._map.project(o.getLatLng(),n));n--);return}r[e].addObject(t,i)}this._topClusterLevel._addChild(t),t.__parent=this._topClusterLevel},_enqueue:function(t){this._queue.push(t),this._queueTimeout||(this._queueTimeout=setTimeout(L.bind(this._processQueue,this),300))},_processQueue:function(){for(var t=0;tthis._map._zoom?(this._animationStart(),this._animationZoomOut(this._zoom,this._map._zoom)):this._moveEnd()},_getExpandedVisibleBounds:function(){if(!this.options.removeOutsideVisibleBounds)return this.getBounds();var t=this._map,e=t.getBounds(),i=e._southWest,n=e._northEast,s=L.Browser.mobile?0:Math.abs(i.lat-n.lat),r=L.Browser.mobile?0:Math.abs(i.lng-n.lng);return new L.LatLngBounds(new L.LatLng(i.lat-s,i.lng-r,!0),new L.LatLng(n.lat+s,n.lng+r,!0))},_animationAddLayerNonAnimated:function(t,e){if(e===t)this._featureGroup.addLayer(t);else if(2===e._childCount){e._addToMap();var i=e.getAllChildMarkers();this._featureGroup.removeLayer(i[0]),this._featureGroup.removeLayer(i[1])}else e._updateIcon()}}),L.MarkerClusterGroup.include(L.DomUtil.TRANSITION?{_animationStart:function(){this._map._mapPane.className+=" leaflet-cluster-anim",this._inZoomAnimation++},_animationEnd:function(){this._map&&(this._map._mapPane.className=this._map._mapPane.className.replace(" leaflet-cluster-anim","")),this._inZoomAnimation--,this.fire("animationend")},_animationZoomIn:function(t,e){var i,n=this._getExpandedVisibleBounds(),s=this._featureGroup;this._topClusterLevel._recursively(n,t,0,function(r){var o,a=r._latlng,h=r._markers;for(n.contains(a)||(a=null),r._isSingleParent()&&t+1===e?(s.removeLayer(r),r._recursivelyAddChildrenToMap(null,e,n)):(r.setOpacity(0),r._recursivelyAddChildrenToMap(a,e,n)),i=h.length-1;i>=0;i--)o=h[i],n.contains(o._latlng)||s.removeLayer(o)}),this._forceLayout(),this._topClusterLevel._recursivelyBecomeVisible(n,e),s.eachLayer(function(t){t instanceof L.MarkerCluster||!t._icon||t.setOpacity(1)}),this._topClusterLevel._recursively(n,t,e,function(t){t._recursivelyRestoreChildPositions(e)}),this._enqueue(function(){this._topClusterLevel._recursively(n,t,0,function(t){s.removeLayer(t),t.setOpacity(1)}),this._animationEnd()})},_animationZoomOut:function(t,e){this._animationZoomOutSingle(this._topClusterLevel,t-1,e),this._topClusterLevel._recursivelyAddChildrenToMap(null,e,this._getExpandedVisibleBounds()),this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,t,this._getExpandedVisibleBounds())},_animationZoomOutSingle:function(t,e,i){var n=this._getExpandedVisibleBounds();t._recursivelyAnimateChildrenInAndAddSelfToMap(n,e+1,i);var s=this;this._forceLayout(),t._recursivelyBecomeVisible(n,i),this._enqueue(function(){if(1===t._childCount){var r=t._markers[0];r.setLatLng(r.getLatLng()),r.setOpacity&&r.setOpacity(1)}else t._recursively(n,i,0,function(t){t._recursivelyRemoveChildrenFromMap(n,e+1)});s._animationEnd()})},_animationAddLayer:function(t,e){var i=this,n=this._featureGroup;n.addLayer(t),e!==t&&(e._childCount>2?(e._updateIcon(),this._forceLayout(),this._animationStart(),t._setPos(this._map.latLngToLayerPoint(e.getLatLng())),t.setOpacity(0),this._enqueue(function(){n.removeLayer(t),t.setOpacity(1),i._animationEnd()})):(this._forceLayout(),i._animationStart(),i._animationZoomOutSingle(e,this._map.getMaxZoom(),this._map.getZoom())))},_forceLayout:function(){L.Util.falseFn(e.body.offsetWidth)}}:{_animationStart:function(){},_animationZoomIn:function(t,e){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,e,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationZoomOut:function(t,e){this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds,t),this._topClusterLevel._recursivelyAddChildrenToMap(null,e,this._getExpandedVisibleBounds()),this.fire("animationend")},_animationAddLayer:function(t,e){this._animationAddLayerNonAnimated(t,e)}}),L.markerClusterGroup=function(t){return new L.MarkerClusterGroup(t)},L.MarkerCluster=L.Marker.extend({initialize:function(t,e,i,n){L.Marker.prototype.initialize.call(this,i?i._cLatLng||i.getLatLng():new L.LatLng(0,0),{icon:this}),this._group=t,this._zoom=e,this._markers=[],this._childClusters=[],this._childCount=0,this._iconNeedsUpdate=!0,this._bounds=new L.LatLngBounds,i&&this._addChild(i),n&&this._addChild(n)},getAllChildMarkers:function(t){t=t||[];for(var e=this._childClusters.length-1;e>=0;e--)this._childClusters[e].getAllChildMarkers(t);for(var i=this._markers.length-1;i>=0;i--)t.push(this._markers[i]);return t},getChildCount:function(){return this._childCount},zoomToBounds:function(){for(var t,e=this._childClusters.slice(),i=this._group._map,n=i.getBoundsZoom(this._bounds),s=this._zoom+1,r=i.getZoom();e.length>0&&n>s;){s++;var o=[];for(t=0;ts?this._group._map.setView(this._latlng,s):r>=n?this._group._map.setView(this._latlng,r+1):this._group._map.fitBounds(this._bounds)},getBounds:function(){var t=new L.LatLngBounds;return t.extend(this._bounds),t},_updateIcon:function(){this._iconNeedsUpdate=!0,this._icon&&this.setIcon(this)},createIcon:function(){return this._iconNeedsUpdate&&(this._iconObj=this._group.options.iconCreateFunction(this),this._iconNeedsUpdate=!1),this._iconObj.createIcon()},createShadow:function(){return this._iconObj.createShadow()},_addChild:function(t,e){this._iconNeedsUpdate=!0,this._expandBounds(t),t instanceof L.MarkerCluster?(e||(this._childClusters.push(t),t.__parent=this),this._childCount+=t._childCount):(e||this._markers.push(t),this._childCount++),this.__parent&&this.__parent._addChild(t,!0)},_expandBounds:function(t){var e,i=t._wLatLng||t._latlng;t instanceof L.MarkerCluster?(this._bounds.extend(t._bounds),e=t._childCount):(this._bounds.extend(i),e=1),this._cLatLng||(this._cLatLng=t._cLatLng||i);var n=this._childCount+e;this._wLatLng?(this._wLatLng.lat=(i.lat*e+this._wLatLng.lat*this._childCount)/n,this._wLatLng.lng=(i.lng*e+this._wLatLng.lng*this._childCount)/n):this._latlng=this._wLatLng=new L.LatLng(i.lat,i.lng)},_addToMap:function(t){t&&(this._backupLatlng=this._latlng,this.setLatLng(t)),this._group._featureGroup.addLayer(this)},_recursivelyAnimateChildrenIn:function(t,e,i){this._recursively(t,0,i-1,function(t){var i,n,s=t._markers;for(i=s.length-1;i>=0;i--)n=s[i],n._icon&&(n._setPos(e),n.setOpacity(0))},function(t){var i,n,s=t._childClusters;for(i=s.length-1;i>=0;i--)n=s[i],n._icon&&(n._setPos(e),n.setOpacity(0))})},_recursivelyAnimateChildrenInAndAddSelfToMap:function(t,e,i){this._recursively(t,i,0,function(n){n._recursivelyAnimateChildrenIn(t,n._group._map.latLngToLayerPoint(n.getLatLng()).round(),e),n._isSingleParent()&&e-1===i?(n.setOpacity(1),n._recursivelyRemoveChildrenFromMap(t,e)):n.setOpacity(0),n._addToMap()})},_recursivelyBecomeVisible:function(t,e){this._recursively(t,0,e,null,function(t){t.setOpacity(1)})},_recursivelyAddChildrenToMap:function(t,e,i){this._recursively(i,-1,e,function(n){if(e!==n._zoom)for(var s=n._markers.length-1;s>=0;s--){var r=n._markers[s];i.contains(r._latlng)&&(t&&(r._backupLatlng=r.getLatLng(),r.setLatLng(t),r.setOpacity&&r.setOpacity(0)),n._group._featureGroup.addLayer(r))}},function(e){e._addToMap(t)})},_recursivelyRestoreChildPositions:function(t){for(var e=this._markers.length-1;e>=0;e--){var i=this._markers[e];i._backupLatlng&&(i.setLatLng(i._backupLatlng),delete i._backupLatlng)}if(t-1===this._zoom)for(var n=this._childClusters.length-1;n>=0;n--)this._childClusters[n]._restorePosition();else for(var s=this._childClusters.length-1;s>=0;s--)this._childClusters[s]._recursivelyRestoreChildPositions(t)},_restorePosition:function(){this._backupLatlng&&(this.setLatLng(this._backupLatlng),delete this._backupLatlng)},_recursivelyRemoveChildrenFromMap:function(t,e,i){var n,s;this._recursively(t,-1,e-1,function(t){for(s=t._markers.length-1;s>=0;s--)n=t._markers[s],i&&i.contains(n._latlng)||(t._group._featureGroup.removeLayer(n),n.setOpacity&&n.setOpacity(1))},function(t){for(s=t._childClusters.length-1;s>=0;s--)n=t._childClusters[s],i&&i.contains(n._latlng)||(t._group._featureGroup.removeLayer(n),n.setOpacity&&n.setOpacity(1))})},_recursively:function(t,e,i,n,s){var r,o,a=this._childClusters,h=this._zoom;if(e>h)for(r=a.length-1;r>=0;r--)o=a[r],t.intersects(o._bounds)&&o._recursively(t,e,i,n,s);else if(n&&n(this),s&&this._zoom===i&&s(this),i>h)for(r=a.length-1;r>=0;r--)o=a[r],t.intersects(o._bounds)&&o._recursively(t,e,i,n,s)},_recalculateBounds:function(){var t,e=this._markers,i=this._childClusters;for(this._bounds=new L.LatLngBounds,delete this._wLatLng,t=e.length-1;t>=0;t--)this._expandBounds(e[t]);for(t=i.length-1;t>=0;t--)this._expandBounds(i[t])},_isSingleParent:function(){return this._childClusters.length>0&&this._childClusters[0]._childCount===this._childCount}}),L.DistanceGrid=function(t){this._cellSize=t,this._sqCellSize=t*t,this._grid={},this._objectPoint={}},L.DistanceGrid.prototype={addObject:function(t,e){var i=this._getCoord(e.x),n=this._getCoord(e.y),s=this._grid,r=s[n]=s[n]||{},o=r[i]=r[i]||[],a=L.Util.stamp(t);this._objectPoint[a]=e,o.push(t)},updateObject:function(t,e){this.removeObject(t),this.addObject(t,e)},removeObject:function(t,e){var i,n,s=this._getCoord(e.x),r=this._getCoord(e.y),o=this._grid,a=o[r]=o[r]||{},h=a[s]=a[s]||[];for(delete this._objectPoint[L.Util.stamp(t)],i=0,n=h.length;n>i;i++)if(h[i]===t)return h.splice(i,1),1===n&&delete a[s],!0},eachObject:function(t,e){var i,n,s,r,o,a,h,_=this._grid;for(i in _){o=_[i];for(n in o)for(a=o[n],s=0,r=a.length;r>s;s++)h=t.call(e,a[s]),h&&(s--,r--)}},getNearObject:function(t){var e,i,n,s,r,o,a,h,_=this._getCoord(t.x),u=this._getCoord(t.y),l=this._objectPoint,d=this._sqCellSize,p=null;for(e=u-1;u+1>=e;e++)if(s=this._grid[e])for(i=_-1;_+1>=i;i++)if(r=s[i])for(n=0,o=r.length;o>n;n++)a=r[n],h=this._sqDist(l[L.Util.stamp(a)],t),d>h&&(d=h,p=a);return p},_getCoord:function(t){return Math.floor(t/this._cellSize)},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n}},function(){L.QuickHull={getDistant:function(t,e){var i=e[1].lat-e[0].lat,n=e[0].lng-e[1].lng;return n*(t.lat-e[0].lat)+i*(t.lng-e[0].lng)},findMostDistantPointFromBaseLine:function(t,e){var i,n,s,r=0,o=null,a=[];for(i=e.length-1;i>=0;i--)n=e[i],s=this.getDistant(n,t),s>0&&(a.push(n),s>r&&(r=s,o=n));return{maxPoint:o,newPoints:a}},buildConvexHull:function(t,e){var i=[],n=this.findMostDistantPointFromBaseLine(t,e);return n.maxPoint?(i=i.concat(this.buildConvexHull([t[0],n.maxPoint],n.newPoints)),i=i.concat(this.buildConvexHull([n.maxPoint,t[1]],n.newPoints))):[t[0]]},getConvexHull:function(t){var e,i=!1,n=!1,s=null,r=null;for(e=t.length-1;e>=0;e--){var o=t[e];(i===!1||o.lat>i)&&(s=o,i=o.lat),(n===!1||o.lat=0;e--)t=i[e].getLatLng(),n.push(t);return L.QuickHull.getConvexHull(n)}}),L.MarkerCluster.include({_2PI:2*Math.PI,_circleFootSeparation:25,_circleStartAngle:Math.PI/6,_spiralFootSeparation:28,_spiralLengthStart:11,_spiralLengthFactor:5,_circleSpiralSwitchover:9,spiderfy:function(){if(this._group._spiderfied!==this&&!this._group._inZoomAnimation){var t,e=this.getAllChildMarkers(),i=this._group,n=i._map,s=n.latLngToLayerPoint(this._latlng);this._group._unspiderfy(),this._group._spiderfied=this,e.length>=this._circleSpiralSwitchover?t=this._generatePointsSpiral(e.length,s):(s.y+=10,t=this._generatePointsCircle(e.length,s)),this._animationSpiderfy(e,t)}},unspiderfy:function(t){this._group._inZoomAnimation||(this._animationUnspiderfy(t),this._group._spiderfied=null)},_generatePointsCircle:function(t,e){var i,n,s=this._group.options.spiderfyDistanceMultiplier*this._circleFootSeparation*(2+t),r=s/this._2PI,o=this._2PI/t,a=[];for(a.length=t,i=t-1;i>=0;i--)n=this._circleStartAngle+i*o,a[i]=new L.Point(e.x+r*Math.cos(n),e.y+r*Math.sin(n))._round();return a},_generatePointsSpiral:function(t,e){var i,n=this._group.options.spiderfyDistanceMultiplier*this._spiralLengthStart,s=this._group.options.spiderfyDistanceMultiplier*this._spiralFootSeparation,r=this._group.options.spiderfyDistanceMultiplier*this._spiralLengthFactor,o=0,a=[];for(a.length=t,i=t-1;i>=0;i--)o+=s/n+5e-4*i,a[i]=new L.Point(e.x+n*Math.cos(o),e.y+n*Math.sin(o))._round(),n+=this._2PI*r/o;return a},_noanimationUnspiderfy:function(){var t,e,i=this._group,n=i._map,s=i._featureGroup,r=this.getAllChildMarkers();for(this.setOpacity(1),e=r.length-1;e>=0;e--)t=r[e],s.removeLayer(t),t._preSpiderfyLatlng&&(t.setLatLng(t._preSpiderfyLatlng),delete t._preSpiderfyLatlng),t.setZIndexOffset&&t.setZIndexOffset(0),t._spiderLeg&&(n.removeLayer(t._spiderLeg),delete t._spiderLeg);i._spiderfied=null}}),L.MarkerCluster.include(L.DomUtil.TRANSITION?{SVG_ANIMATION:function(){return e.createElementNS("http://www.w3.org/2000/svg","animate").toString().indexOf("SVGAnimate")>-1}(),_animationSpiderfy:function(t,i){var n,s,r,o,a=this,h=this._group,_=h._map,u=h._featureGroup,l=_.latLngToLayerPoint(this._latlng);for(n=t.length-1;n>=0;n--)s=t[n],s.setOpacity?(s.setZIndexOffset(1e6),s.setOpacity(0),u.addLayer(s),s._setPos(l)):u.addLayer(s);h._forceLayout(),h._animationStart();var d=L.Path.SVG?0:.3,p=L.Path.SVG_NS;for(n=t.length-1;n>=0;n--)if(o=_.layerPointToLatLng(i[n]),s=t[n],s._preSpiderfyLatlng=s._latlng,s.setLatLng(o),s.setOpacity&&s.setOpacity(1),r=new L.Polyline([a._latlng,o],{weight:1.5,color:"#222",opacity:d}),_.addLayer(r),s._spiderLeg=r,L.Path.SVG&&this.SVG_ANIMATION){var c=r._path.getTotalLength();r._path.setAttribute("stroke-dasharray",c+","+c);var m=e.createElementNS(p,"animate");m.setAttribute("attributeName","stroke-dashoffset"),m.setAttribute("begin","indefinite"),m.setAttribute("from",c),m.setAttribute("to",0),m.setAttribute("dur",.25),r._path.appendChild(m),m.beginElement(),m=e.createElementNS(p,"animate"),m.setAttribute("attributeName","stroke-opacity"),m.setAttribute("attributeName","stroke-opacity"),m.setAttribute("begin","indefinite"),m.setAttribute("from",0),m.setAttribute("to",.5),m.setAttribute("dur",.25),r._path.appendChild(m),m.beginElement()}if(a.setOpacity(.3),L.Path.SVG)for(this._group._forceLayout(),n=t.length-1;n>=0;n--)s=t[n]._spiderLeg,s.options.opacity=.5,s._path.setAttribute("stroke-opacity",.5);setTimeout(function(){h._animationEnd(),h.fire("spiderfied")},200)},_animationUnspiderfy:function(t){var e,i,n,s=this._group,r=s._map,o=s._featureGroup,a=t?r._latLngToNewLayerPoint(this._latlng,t.zoom,t.center):r.latLngToLayerPoint(this._latlng),h=this.getAllChildMarkers(),_=L.Path.SVG&&this.SVG_ANIMATION;for(s._animationStart(),this.setOpacity(1),i=h.length-1;i>=0;i--)e=h[i],e._preSpiderfyLatlng&&(e.setLatLng(e._preSpiderfyLatlng),delete e._preSpiderfyLatlng,e.setOpacity?(e._setPos(a),e.setOpacity(0)):o.removeLayer(e),_&&(n=e._spiderLeg._path.childNodes[0],n.setAttribute("to",n.getAttribute("from")),n.setAttribute("from",0),n.beginElement(),n=e._spiderLeg._path.childNodes[1],n.setAttribute("from",.5),n.setAttribute("to",0),n.setAttribute("stroke-opacity",0),n.beginElement(),e._spiderLeg._path.setAttribute("stroke-opacity",0)));setTimeout(function(){var t=0;for(i=h.length-1;i>=0;i--)e=h[i],e._spiderLeg&&t++;for(i=h.length-1;i>=0;i--)e=h[i],e._spiderLeg&&(e.setOpacity&&(e.setOpacity(1),e.setZIndexOffset(0)),t>1&&o.removeLayer(e),r.removeLayer(e._spiderLeg),delete e._spiderLeg);s._animationEnd()},200)}}:{_animationSpiderfy:function(t,e){var i,n,s,r,o=this._group,a=o._map,h=o._featureGroup;for(i=t.length-1;i>=0;i--)r=a.layerPointToLatLng(e[i]),n=t[i],n._preSpiderfyLatlng=n._latlng,n.setLatLng(r),n.setZIndexOffset&&n.setZIndexOffset(1e6),h.addLayer(n),s=new L.Polyline([this._latlng,r],{weight:1.5,color:"#222"}),a.addLayer(s),n._spiderLeg=s;this.setOpacity(.3),o.fire("spiderfied")},_animationUnspiderfy:function(){this._noanimationUnspiderfy()}}),L.MarkerClusterGroup.include({_spiderfied:null,_spiderfierOnAdd:function(){this._map.on("click",this._unspiderfyWrapper,this),this._map.options.zoomAnimation&&this._map.on("zoomstart",this._unspiderfyZoomStart,this),this._map.on("zoomend",this._noanimationUnspiderfy,this),L.Path.SVG&&!L.Browser.touch&&this._map._initPathRoot()},_spiderfierOnRemove:function(){this._map.off("click",this._unspiderfyWrapper,this),this._map.off("zoomstart",this._unspiderfyZoomStart,this),this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy()},_unspiderfyZoomStart:function(){this._map&&this._map.on("zoomanim",this._unspiderfyZoomAnim,this)},_unspiderfyZoomAnim:function(t){L.DomUtil.hasClass(this._map._mapPane,"leaflet-touching")||(this._map.off("zoomanim",this._unspiderfyZoomAnim,this),this._unspiderfy(t))},_unspiderfyWrapper:function(){this._unspiderfy()},_unspiderfy:function(t){this._spiderfied&&this._spiderfied.unspiderfy(t)},_noanimationUnspiderfy:function(){this._spiderfied&&this._spiderfied._noanimationUnspiderfy()},_unspiderfyLayer:function(t){t._spiderLeg&&(this._featureGroup.removeLayer(t),t.setOpacity(1),t.setZIndexOffset(0),this._map.removeLayer(t._spiderLeg),delete t._spiderLeg)}})}(window,document); +/* + Leaflet.AwesomeMarkers, a plugin that adds colorful iconic markers for Leaflet, based on the Font Awesome icons + (c) 2012-2013, Lennard Voogdt + + http://leafletjs.com + https://github.com/lvoogdt +*/ + +/*global L*/ + + +(function (window, document, undefined) { + "use strict"; + /* + * Leaflet.AwesomeMarkers assumes that you have already included the Leaflet library. + */ + + L.AwesomeMarkers = {}; + + L.AwesomeMarkers.version = '2.0.1'; + + L.AwesomeMarkers.Icon = L.Icon.extend({ + options: { + iconSize: [35, 45], + iconAnchor: [17, 42], + popupAnchor: [1, -32], + shadowAnchor: [10, 12], + shadowSize: [36, 16], + className: 'awesome-marker', + prefix: 'glyphicon', + spinClass: 'fa-spin', + extraClasses: '', + icon: 'home', + markerColor: 'blue', + iconColor: 'white' + }, + + initialize: function (options) { + options = L.Util.setOptions(this, options); + }, + + createIcon: function () { + var div = document.createElement('div'), + options = this.options; + + if (options.icon) { + div.innerHTML = this._createInner(); + } + + if (options.bgPos) { + div.style.backgroundPosition = + (-options.bgPos.x) + 'px ' + (-options.bgPos.y) + 'px'; + } + + this._setIconStyles(div, 'icon-' + options.markerColor); + return div; + }, + + _createInner: function() { + var iconClass, iconSpinClass = "", iconColorClass = "", iconColorStyle = "", options = this.options; + + if(options.icon.slice(0,options.prefix.length+1) === options.prefix + "-") { + iconClass = options.icon; + } else { + iconClass = options.prefix + "-" + options.icon; + } + + if(options.spin && typeof options.spinClass === "string") { + iconSpinClass = options.spinClass; + } + + if(options.iconColor) { + if(options.iconColor === 'white' || options.iconColor === 'black') { + iconColorClass = "icon-" + options.iconColor; + } else { + iconColorStyle = "style='color: " + options.iconColor + "' "; + } + } + + return ""; + }, + + _setIconStyles: function (img, name) { + var options = this.options, + size = L.point(options[name === 'shadow' ? 'shadowSize' : 'iconSize']), + anchor; + + if (name === 'shadow') { + anchor = L.point(options.shadowAnchor || options.iconAnchor); + } else { + anchor = L.point(options.iconAnchor); + } + + if (!anchor && size) { + anchor = size.divideBy(2, true); + } + + img.className = 'awesome-marker-' + name + ' ' + options.className; + + if (anchor) { + img.style.marginLeft = (-anchor.x) + 'px'; + img.style.marginTop = (-anchor.y) + 'px'; + } + + if (size) { + img.style.width = size.x + 'px'; + img.style.height = size.y + 'px'; + } + }, + + createShadow: function () { + var div = document.createElement('div'); + + this._setIconStyles(div, 'shadow'); + return div; + } + }); + + L.AwesomeMarkers.icon = function (options) { + return new L.AwesomeMarkers.Icon(options); + }; + +}(this, document)); + + + +(function() { + $(document).on('turbolinks:load', function() { + $('#event_start_time').change(function() { + if ($('#event_start_time').val() >= $('#event_end_time').val()) { + return $('#event_end_time').val($('#event_start_time').val()); + } + }); + $('#event_end_time').change(function() { + if ($('#event_start_time').val() >= $('#event_end_time').val()) { + return $('#event_start_time').val($('#event_end_time').val()); + } + }); + $('#event_repeat').each(function() { + if ($(this).val() === '0') { + $('.field.rule').hide(); + } + return $(this).change(function() { + if ($(this).val() > 0) { + return $('.field.rule').show(); + } else { + return $('.field.rule').hide(); + } + }); + }); + return $('#event_tags').each(function() { + var elt; + elt = $(this); + return $.ajax({ + url: '/tags.json' + }).done(function(data) { + var tags; + return tags = jQuery.map(data, function(n) { + return n[0]; + }); + }); + }); + }); + +}).call(this); +(function() { + $(document).on('turbolinks:load', function() { + $('body.pages form :input').prop('disabled', false); + return $('body.pages form').submit(function() { + $('input[name=utf8]').prop('disabled', true); + return $(':input', this).filter(function() { + return this.value.length === 0; + }).prop('disabled', true); + }); + }); + +}).call(this); +(function() { + $(document).on('turbolinks:load', function() {}); + +}).call(this); +(function() { + $(document).on('turbolinks:load', function() {}); + +}).call(this); +(function() { + var modulo = function(a, b) { return (+a % (b = +b) + b) % b; }; + + $(document).on('turbolinks:load', function() { + var idx, markerColors; + markerColors = ['blue', 'red', 'darkred', 'orange', 'green', 'darkgreen', 'purple', 'darkpuple', 'cadetblue']; + idx = 0; + $('#map.list').each(function() { + var controls, map; + map = L.map('map'); + L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap' + }).addTo(map); + controls = L.control.layers(null, null, { + collapsed: false + }).addTo(map); + return $('li a', this).each(function() { + var markerColor, text, url; + url = $(this).attr('href'); + text = $(this).html(); + markerColor = markerColors[modulo(idx++, markerColors.length)]; + if (location.search && url.indexOf('?') >= 0) { + url += '&' + location.search.substr(1); + } else { + url += location.search; + } + return $.getJSON(url, function(json) { + var layer; + if (json) { + layer = L.markerClusterGroup({ + maxClusterRadius: 30 + }).addLayer(L.geoJson(json, { + pointToLayer: function(feature, latlng) { + var marker; + marker = L.AwesomeMarkers.icon({ + prefix: 'fa', + icon: feature.properties.icon || 'calendar', + markerColor: markerColor + }); + return L.marker(latlng, { + icon: marker + }); + }, + onEachFeature: function(feature, layer) { + if (feature.properties && feature.properties.popupContent) { + return layer.bindPopup(feature.properties.popupContent); + } + } + })); + map.addLayer(layer); + controls.addOverlay(layer, text + ' - ' + json.length); + if ((/maps\//.test(location.href) || /maps.json/.test(url)) && layer.getBounds()._northEast && layer.getBounds()._southWest) { + return map.fitBounds(layer.getBounds()); + } + } + }); + }); + }); + return $('#map.event, #map.orga').each(function() { + var coord, map, marker, markerColor, url; + coord = [$(this).data('latitude'), $(this).data('longitude')]; + map = L.map('map').setView([coord[0], coord[1]], 16); + L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap' + }).addTo(map); + url = $(this).data('url'); + markerColor = markerColors[modulo(idx++, markerColors.length)]; + if (location.search && url.indexOf('?') >= 0) { + url += '&' + location.search.substr(1); + } else { + url += location.search; + } + marker = L.AwesomeMarkers.icon({ + prefix: 'fa', + icon: 'calendar' + }); + L.marker([coord[0], coord[1]], { + icon: marker + }).addTo(map); + return $.getJSON(url, function(json) { + var layer; + layer = L.markerClusterGroup({ + maxClusterRadius: 30 + }).addLayer(L.geoJson(json, { + pointToLayer: function(feature, latlng) { + marker = L.AwesomeMarkers.icon({ + prefix: 'fa', + icon: feature.properties.icon || 'calendar', + markerColor: markerColor + }); + return L.marker(latlng, { + icon: marker + }); + }, + onEachFeature: function(feature, layer) { + if (feature.properties && feature.properties.popupContent) { + return layer.bindPopup(feature.properties.popupContent); + } + } + })); + return map.addLayer(layer); + }); + }); + }); + +}).call(this); +(function() { + $(document).on('turbolinks:load', function() { + $('body.moderations .field.closer input[type=radio]').click(function() { + return $('body.moderations #event_reason').parent().slideUp(); + }); + return $('body.moderations .field.opener input[type=radio]').click(function() { + return $('body.moderations #event_reason').parent().slideDown(); + }); + }); + +}).call(this); +(function() { + + +}).call(this); +(function() { + + +}).call(this); +(function() { + var pager; + + pager = true; + + $(document).on('turbolinks:load', function() { + $('.pagination .next a').attr('data-remote', true).click(function() { + return $('#loading').fadeIn(); + }); + if (pager) { + pager = false; + $(document).on('ajax:success', '.pagination .next a', function(event, data, status, xhr) { + var elts, next; + $('#loading').fadeOut(); + elts = $('tbody tr', data); + $(this).parents('tfoot').prev().append(elts); + next = $('.pagination .next a', data).attr('href'); + if (next != null) { + return $(this).show().data('remote', true).attr('href', next); + } else { + return $(this).parents('.pagination').remove(); + } + }); + } + if ($('.pagination .next a').size() > 0) { + return $(document).scroll(function() { + if ($(window).scrollTop() === $(document).height() - $(window).height() && $('.pagination .next a').is(':visible')) { + return $('.pagination .next a').hide().click(); + } + }); + } + }); + +}).call(this); +(function() { + $(document).on('turbolinks:load', function() { + $('table.list.dates tbody tr').each(function() { + var vals; + vals = $(this).find('td.quantity').map(function() { + var val; + val = $(this).find('a').html().replace(' ', '').trim(); + if (val && val !== '') { + return parseInt(val); + } else { + return 0; + } + }); + return $(this).find('.sparkline').sparkline(vals, { + width: '5em' + }); + }); + return $('table.list.dates tfoot').each(function() { + var vals; + vals = $(this).find('th.quantity').map(function() { + return parseInt($(this).html().replace(' ', '')); + }); + return $(this).find('.sparkline').sparkline(vals, { + type: 'bar', + height: '3em', + barWidth: '100%', + barColor: '#9CC5EE', + barSpacing: 2 + }); + }); + }); + +}).call(this); +(function() { + $(document).on('turbolinks:load', function() { + return tinyMCE.init({ + schema: 'html5', + menubar: false, + language: 'fr_FR', + selector: 'textarea.description', + content_css: '/assets/application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css', + entity_encoding: 'raw', + add_unload_trigger: true, + browser_spellcheck: true, + toolbar: [' bold italic strikethrough | bullist numlist outdent indent | alignleft aligncenter alignright alignjustify | link image media insertdatetime charmap table | undo redo | searchreplace | code visualblocks preview fullscreen'], + plugins: 'lists, advlist, autolink, link, image, charmap, paste, print, preview, table, fullscreen, searchreplace, media, insertdatetime, visualblocks, visualchars, wordcount, contextmenu, code' + }); + }); + + $(document).on('turbolinks:before-cache', function() { + return tinymce.remove(); + }); + +}).call(this); +(function() { + + +}).call(this); +(function() { + $(document).on('turbolinks:load', function() { + if (!Modernizr.testAllProps('forceBrokenImageIcon')) { + $('img.favicon').one('error', function() { + return $(this).css({ + visibility: 'hidden' + }); + }); + } + return $('.field.tags input').tagsInput({ + delimiter: [' '], + defaultText: '', + autocomplete_url: '/tags.json' + }); + }); + +}).call(this); diff --git a/public/assets/application-ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd.js.gz b/public/assets/application-ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd.js.gz new file mode 100644 index 00000000..d9b6ccf6 Binary files /dev/null and b/public/assets/application-ec97ffd43bcc565dc5aabd8a6e1c3c75b2437f6b27265f56c2fa98eb72866bcd.js.gz differ diff --git a/public/assets/tinymce-7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa.js b/public/assets/tinymce-7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa.js new file mode 100644 index 00000000..c384bd93 --- /dev/null +++ b/public/assets/tinymce-7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa.js @@ -0,0 +1,21 @@ +(function() { + $(document).on('turbolinks:load', function() { + return tinyMCE.init({ + schema: 'html5', + menubar: false, + language: 'fr_FR', + selector: 'textarea.description', + content_css: '/assets/application-1ea0f88318a587e6ca20994d21c9db4627195434bcc8a52f59e75be21b50f937.css', + entity_encoding: 'raw', + add_unload_trigger: true, + browser_spellcheck: true, + toolbar: [' bold italic strikethrough | bullist numlist outdent indent | alignleft aligncenter alignright alignjustify | link image media insertdatetime charmap table | undo redo | searchreplace | code visualblocks preview fullscreen'], + plugins: 'lists, advlist, autolink, link, image, charmap, paste, print, preview, table, fullscreen, searchreplace, media, insertdatetime, visualblocks, visualchars, wordcount, contextmenu, code' + }); + }); + + $(document).on('turbolinks:before-cache', function() { + return tinymce.remove(); + }); + +}).call(this); diff --git a/public/assets/tinymce-7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa.js.gz b/public/assets/tinymce-7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa.js.gz new file mode 100644 index 00000000..6e9e6c0c Binary files /dev/null and b/public/assets/tinymce-7243549c535fe401a7b69bb597a277ca520491fe82e3f6efb6b800f605ef56fa.js.gz differ
    text|text2

    text|text2

    ' + padd + '