diff --git a/README.creole b/README.creole
index 67e99e75..eab2e6c2 100644
--- a/README.creole
+++ b/README.creole
@@ -1,4 +1,4 @@
-== Agenda du libre
+= Agenda du libre
Ceci est une application permettant de lister des évènements sur un calendier,
évènements proposés par tout un chacun mais nécessitant une modération avant
@@ -16,7 +16,7 @@ https://git.framasoft.org/agenda-libre/agenda-libre-php
https://bitbucket.org/mlhamel/agendadulibre
-=== Prérequis
+== Prérequis
Exemple d'installation pour Debian:
{{{
@@ -32,7 +32,7 @@ Il faut de plus installer les paquets {{{zlib1g-dev}}} et {{{libsqlite3-dev}}}
ou {{{libmysqlclient-dev}}}.
-=== Code source
+== Code source
Pour installer l'Agenda du libre, vous devrez récupérer le code source à l'aide
de "git", puis installer les dépendances avec "bundler" (bundler est packagé
@@ -50,7 +50,7 @@ Dans le cas d'une installation en mode production, on utilisera plutôt
`--without test developement`.
-=== Base de données
+== Base de données
La base de données peut être "sqlite3", "postgresql" ou "mysql2". Par défaut
sqlite3 est configurée, mais vous pouvez la modifier dans le fichier
@@ -59,7 +59,7 @@ sqlite3 est configurée, mais vous pouvez la modifier dans le fichier
/!\ En pratique, en 2017-03, PostgreSQL server ne parvenait pas à interprêter
le fichier schema.rb généré automatiquement depuis une base fonctionnelle.
-==== Dans le cas d'une installation MySQL
+=== Dans le cas d'une installation MySQL
Il convient d'installer d'abord MySQL server sur sa machine :
{{{
@@ -85,8 +85,9 @@ Mettez en place le schéma comme ceci:
}}}
-=== Lancement
-==== En développement
+== Lancement
+
+=== En développement
Puis lancez rails (avec tests et vérifications de sécurité):
{{{
@@ -100,7 +101,7 @@ Votre premier modérateur admin est identifié par "admin@example.com", son mot
passe étant "password".
-==== En production
+=== En production
{{{
~ # apt install libapache2-mod-passenger
@@ -121,7 +122,7 @@ passe étant "password".
}}}
-=== Aide mémoire
+== Aide mémoire
Commande à utiliser en console rails pour créer plusieurs événements:
{{{
@@ -134,7 +135,7 @@ Commande à utiliser pour précompiler les assets avant leur déploiement dans u
bundle exec rake assets:precompile RAILS_ENV=assets
}}}
-=== Mise à jour
+== Mise à jour
Pour mettre à jour l'agenda du libre, il faut d'abord aller chercher la version de master la plus récente sur le git
upstream (https://git.framasoft.org/agenda-libre/agenda-libre-ruby). Une fois cela fait, il faut y ré-appliquer les
@@ -160,7 +161,7 @@ Puis redémarrer avec:
~ $ bundle exec rails s
}}}
-=== Discussions
+== Discussions
https://kiwiirc.com/client/irc.freenode.net/agendadulibre
diff --git a/app/assets/stylesheets/all.sass b/app/assets/stylesheets/all.sass
index 68cd2c8d..1464459b 100644
--- a/app/assets/stylesheets/all.sass
+++ b/app/assets/stylesheets/all.sass
@@ -82,10 +82,12 @@ header.top
ul.countries
float: right
+ width: 91px
font-size: smaller
- margin-top: -1em
+ margin-top: -0.8em
line-height: 1em
- margin-left: 2em
+ margin-left: 1em
+ margin-right: 1em
list-style-type: none
li
text-align: left
diff --git a/app/assets/stylesheets/events.sass b/app/assets/stylesheets/events.sass
index bc36ca5d..49e55d78 100644
--- a/app/assets/stylesheets/events.sass
+++ b/app/assets/stylesheets/events.sass
@@ -23,7 +23,6 @@ p.full_address
text-transform: capitalize
body.events.index table
- width: 90%
max-width: 100%
// So we can ellipse past events
table-layout: fixed
diff --git a/app/assets/stylesheets/orgas.sass b/app/assets/stylesheets/orgas.sass
index b3edd3c1..e0890c61 100644
--- a/app/assets/stylesheets/orgas.sass
+++ b/app/assets/stylesheets/orgas.sass
@@ -9,9 +9,7 @@ form#orga_search
position: absolute
font-size: larger
text-align: right
- margin-top: -1.2em
white-space: nowrap
- padding-right: 2px
label
display: none
em.fa
diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml
index dc246544..7f9805ff 100644
--- a/app/views/layouts/application.html.haml
+++ b/app/views/layouts/application.html.haml
@@ -28,13 +28,13 @@
%li.map_france
= link_to t('.france'), '//www.agendadulibre.org'
%li.map_quebec
- = link_to t('.quebec'), '//www.agendadulibre.qc.ca'
+ = link_to t('.quebec'), 'http://www.agendadulibre.qc.ca'
%li.map_belgique
= link_to t('.belgique'), '//www.agendadulibre.be'
%li.map_suisse
= link_to t('.suisse'), '//www.agendadulibre.ch'
%li.map_brasil
- = link_to t('.brasil'), '//agenda.softwarelivre.org'
+ = link_to t('.brasil'), 'http://agenda.softwarelivre.org'
%h1= link_to t('.title'), root_path
%h2= t '.subtitle'
diff --git a/public/assets/.sprockets-manifest-15ceda6913c644f60925b0cd3079d44e.json b/public/assets/.sprockets-manifest-15ceda6913c644f60925b0cd3079d44e.json
index 26616d78..fe26ad95 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":"2017-01-19T14:15:27+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":"2017-01-19T14:15:27+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":"2017-01-19T14:15:27+01:00","size":1535,"digest":"d9c2bb73769af777c8a71720d29741f3a499aebd5a043e9a119bd0d9597aed47","integrity":"sha256-2cK7c3aa93fIpxcg0pdB86SZrr1aBD6aEZvQ2Vl67Uc="},"active_admin/orderable-29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617.png":{"logical_path":"active_admin/orderable.png","mtime":"2017-01-19T14:15:27+01:00","size":220,"digest":"29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617","integrity":"sha256-KTdNu1WwAS14o3xhTVc7s0dPB3mEm0eKFH0PGEXKZhc="},"active_admin/print-87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68.css":{"logical_path":"active_admin/print.css","mtime":"2017-01-19T14:15:27+01:00","size":5494,"digest":"87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68","integrity":"sha256-h8X/wdhpqRkSO8wdxexRsgvHn9muq57td+NDjGrNT2g="},"active_admin-5d8dda85fd2e9296c4f9682a0c4441e4dfc6c2779454e8144be78befe3ff05bb.js":{"logical_path":"active_admin.js","mtime":"2017-01-02T09:58:37+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="},"layers-2x-066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf.png":{"logical_path":"layers-2x.png","mtime":"2016-12-23T18:42:51+01:00","size":1259,"digest":"066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf","integrity":"sha256-Bm2sqFDY/77wB68AsG6sABVyje4nnFHzy2xxbffELt8="},"layers-1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6.png":{"logical_path":"layers.png","mtime":"2016-12-23T18:42:51+01:00","size":696,"digest":"1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6","integrity":"sha256-Hbvp0CjikvNvy6j4s6KNXokydU/CIVuaxp5M3s9RB8Y="},"marker-icon-2x-2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d.png":{"logical_path":"marker-icon-2x.png","mtime":"2016-12-23T18:42:51+01:00","size":2586,"digest":"2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d","integrity":"sha256-LXei5MLwi7rEGAgyTvlGuaL+YbYVBIDQEbcrN5w7I40="},"marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png":{"logical_path":"marker-icon.png","mtime":"2016-12-23T18:42:51+01:00","size":1466,"digest":"574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437","integrity":"sha256-V0w6XMqF9BFAhbaEFZbWLwDXyJLHsD8oy/owHesdxDc="},"marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png":{"logical_path":"marker-shadow.png","mtime":"2016-12-23T18:42:51+01:00","size":618,"digest":"264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da","integrity":"sha256-Jk9cZAM58ELdcpBiz8BMF/jqDymIK1OOOEjtjxDttNo="},"tinymce-61f3d9ea006866183d7b56bbc9ecf39545adcc8a1a3b58566ea14ea52db8b902.js":{"logical_path":"tinymce.js","mtime":"2017-01-19T14:51:27+01:00","size":971,"digest":"61f3d9ea006866183d7b56bbc9ecf39545adcc8a1a3b58566ea14ea52db8b902","integrity":"sha256-YfPZ6gBoZhg9e1a7yezzlUWtzIoaO1hWbqFOpS24uQI="},"application-bd35f325ad91254feab551b82972c18f9d928b5016ff20ee040c3380f9c95a7c.css":{"logical_path":"application.css","mtime":"2017-01-19T14:51:27+01:00","size":173942,"digest":"bd35f325ad91254feab551b82972c18f9d928b5016ff20ee040c3380f9c95a7c","integrity":"sha256-vTXzJa2RJU/qtVG4KXLBj52Si1AW/yDuBAwzgPnJWnw="},"leaflet/dist/images/layers-1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6.png":{"logical_path":"leaflet/dist/images/layers.png","mtime":"2016-12-23T22:30:26+01:00","size":696,"digest":"1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6","integrity":"sha256-Hbvp0CjikvNvy6j4s6KNXokydU/CIVuaxp5M3s9RB8Y="},"leaflet/dist/images/layers-2x-066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf.png":{"logical_path":"leaflet/dist/images/layers-2x.png","mtime":"2016-12-23T22:30:26+01:00","size":1259,"digest":"066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf","integrity":"sha256-Bm2sqFDY/77wB68AsG6sABVyje4nnFHzy2xxbffELt8="},"leaflet/dist/images/marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png":{"logical_path":"leaflet/dist/images/marker-icon.png","mtime":"2016-12-23T22:30:26+01:00","size":1466,"digest":"574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437","integrity":"sha256-V0w6XMqF9BFAhbaEFZbWLwDXyJLHsD8oy/owHesdxDc="},"application-e76d31e280284497ba66d10672ca0b022ab11b512f1ed51be1b4db85d47330eb.js":{"logical_path":"application.js","mtime":"2017-01-02T09:58:37+01:00","size":2358436,"digest":"e76d31e280284497ba66d10672ca0b022ab11b512f1ed51be1b4db85d47330eb","integrity":"sha256-520x4oAoRJe6ZtEGcsoLAiqxG1EvHtUb4bTbhdRzMOs="},"leaflet/dist/images/marker-icon-2x-2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d.png":{"logical_path":"leaflet/dist/images/marker-icon-2x.png","mtime":"2016-12-23T22:30:26+01:00","size":2586,"digest":"2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d","integrity":"sha256-LXei5MLwi7rEGAgyTvlGuaL+YbYVBIDQEbcrN5w7I40="},"leaflet/dist/images/marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png":{"logical_path":"leaflet/dist/images/marker-shadow.png","mtime":"2016-12-23T22:30:26+01:00","size":618,"digest":"264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da","integrity":"sha256-Jk9cZAM58ELdcpBiz8BMF/jqDymIK1OOOEjtjxDttNo="},"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},"active_admin-5493c4a52f825b68a247f214b88addaf61bada07aefa1441589c72d8d81f8b12.css":{"logical_path":"active_admin.css","mtime":"2017-01-19T14:15:27+01:00","size":78899,"digest":"5493c4a52f825b68a247f214b88addaf61bada07aefa1441589c72d8d81f8b12","integrity":"sha256-VJPEpS+CW2iiR/IUuIrdr2G62geu+hRBWJxy2NgfixI="},"active_admin-9238692e361f8ed7317fd8414cfadeff93c57b9a6ed3fd35cfe9a3fe4ddc3829.js":{"logical_path":"active_admin.js","mtime":"2017-01-19T14:51:27+01:00","size":692387,"digest":"9238692e361f8ed7317fd8414cfadeff93c57b9a6ed3fd35cfe9a3fe4ddc3829","integrity":"sha256-kjhpLjYfjtcxf9hBTPre/5PFe5pu0/01z+mj/k3cOCk="},"lef-9fcdd7ddd4d40de29c3809b59688c668b85f5628e219d4cd8a8810b72a64533b.png":{"logical_path":"lef.png","mtime":"2017-01-07T19:07:36+01:00","size":8332,"digest":"9fcdd7ddd4d40de29c3809b59688c668b85f5628e219d4cd8a8810b72a64533b","integrity":"sha256-n83X3dTUDeKcOAm1lojGaLhfVijiGdTNiogQtypkUzs="},"application-a9c6770a672e537abdfdbea5f842747b5d0b0d402d9216a5f1bb789f051ba10e.js":{"logical_path":"application.js","mtime":"2017-01-19T14:51:27+01:00","size":2365648,"digest":"a9c6770a672e537abdfdbea5f842747b5d0b0d402d9216a5f1bb789f051ba10e","integrity":"sha256-qcZ3CmcuU3q9/b6l+EJ0e10LDUAtkhal8bt4nwUboQ4="},"tinymce/langs/ar-e47a394dbc461d20aa547fa0c2027a9dad45fe55e18adb0da309045be6ed4109.js":{"logical_path":"tinymce/langs/ar.js","mtime":"2016-10-29T16:47:26+02:00","size":17776,"digest":"e47a394dbc461d20aa547fa0c2027a9dad45fe55e18adb0da309045be6ed4109","integrity":"sha256-5Ho5TbxGHSCqVH+gwgJ6na1F/lXhitsNowkEW+btQQk="},"tinymce/langs/ar_SA-24b62c76e99e114ae44480a67edbacf74f9fed0ff7afbeacce4966f1bc80333b.js":{"logical_path":"tinymce/langs/ar_SA.js","mtime":"2016-10-29T16:47:26+02:00","size":10003,"digest":"24b62c76e99e114ae44480a67edbacf74f9fed0ff7afbeacce4966f1bc80333b","integrity":"sha256-JLYsdumeEUrkRICmftus90+f7Q/3r76szklm8byAMzs="},"tinymce/langs/az-d40cecd6eb423f910f528ae98dcdaba4c2449802621759437174cf8c8c0d5b25.js":{"logical_path":"tinymce/langs/az.js","mtime":"2016-10-29T16:47:26+02:00","size":9680,"digest":"d40cecd6eb423f910f528ae98dcdaba4c2449802621759437174cf8c8c0d5b25","integrity":"sha256-1Azs1utCP5EPUorpjc2rpMJEmAJiF1lDcXTPjIwNWyU="},"tinymce/langs/be-42e2884c0f2394afa1a17e31e702660eeea0d84618bbd18155cb0313c6eb5f69.js":{"logical_path":"tinymce/langs/be.js","mtime":"2016-10-29T16:47:26+02:00","size":21064,"digest":"42e2884c0f2394afa1a17e31e702660eeea0d84618bbd18155cb0313c6eb5f69","integrity":"sha256-QuKITA8jlK+hoX4x5wJmDu6g2EYYu9GBVcsDE8brX2k="},"tinymce/langs/bg_BG-2fac9d785519d2791d42881fcc08c6a82e841575785690259d7f64e1b88d7db3.js":{"logical_path":"tinymce/langs/bg_BG.js","mtime":"2016-10-29T16:47:26+02:00","size":23945,"digest":"2fac9d785519d2791d42881fcc08c6a82e841575785690259d7f64e1b88d7db3","integrity":"sha256-L6ydeFUZ0nkdQogfzAjGqC6EFXV4VpAlnX9k4biNfbM="},"tinymce/langs/bn_BD-ef48a9094445dfa624af0c35902f60b7949b30585b5e9bc760a225fe2c07cbd3.js":{"logical_path":"tinymce/langs/bn_BD.js","mtime":"2016-10-29T16:47:26+02:00","size":6846,"digest":"ef48a9094445dfa624af0c35902f60b7949b30585b5e9bc760a225fe2c07cbd3","integrity":"sha256-70ipCURF36Ykrww1kC9gt5SbMFhbXpvHYKIl/iwHy9M="},"tinymce/langs/bs-eb66c6d6910a30b950d7a0e784027ed288e6e5a2c6db7741da3359f2067e2e0e.js":{"logical_path":"tinymce/langs/bs.js","mtime":"2016-10-29T16:47:26+02:00","size":6970,"digest":"eb66c6d6910a30b950d7a0e784027ed288e6e5a2c6db7741da3359f2067e2e0e","integrity":"sha256-62bG1pEKMLlQ16DnhAJ+0ojm5aLG23dB2jNZ8gZ+Lg4="},"tinymce/langs/ca-77ab49d6420318ed7f5fd51b6856bc396c393095bd78ea91dad83e38e5859637.js":{"logical_path":"tinymce/langs/ca.js","mtime":"2016-10-29T16:47:26+02:00","size":7787,"digest":"77ab49d6420318ed7f5fd51b6856bc396c393095bd78ea91dad83e38e5859637","integrity":"sha256-d6tJ1kIDGO1/X9UbaFa8OWw5MJW9eOqR2tg+OOWFljc="},"tinymce/langs/cs-3d7ecd619895e3e4c7e1db06f6526682ef88f22f6e8b50d7b45ed0ae7c206714.js":{"logical_path":"tinymce/langs/cs.js","mtime":"2016-10-29T16:47:26+02:00","size":8751,"digest":"3d7ecd619895e3e4c7e1db06f6526682ef88f22f6e8b50d7b45ed0ae7c206714","integrity":"sha256-PX7NYZiV4+TH4dsG9lJmgu+I8i9ui1DXtF7QrnwgZxQ="},"tinymce/langs/cs_CZ-4b0778d233e200dea350f8361129da2844e23d1c0f2d524aeffa6d34e581368d.js":{"logical_path":"tinymce/langs/cs_CZ.js","mtime":"2016-10-29T16:47:26+02:00","size":8399,"digest":"4b0778d233e200dea350f8361129da2844e23d1c0f2d524aeffa6d34e581368d","integrity":"sha256-Swd40jPiAN6jUPg2ESnaKETiPRwPLVJK7/ptNOWBNo0="},"tinymce/langs/cy-a847ff54657ccf76eec362cc14fbc63adb4c67f9b1de023b29d817aba4fec062.js":{"logical_path":"tinymce/langs/cy.js","mtime":"2016-10-29T16:47:26+02:00","size":6142,"digest":"a847ff54657ccf76eec362cc14fbc63adb4c67f9b1de023b29d817aba4fec062","integrity":"sha256-qEf/VGV8z3buw2LMFPvGOttMZ/mx3gI7KdgXq6T+wGI="},"tinymce/langs/da-a87f3ae1c442c3bb26b81569687e98072d038269360361ec9728f00d6b17c282.js":{"logical_path":"tinymce/langs/da.js","mtime":"2016-10-29T16:47:26+02:00","size":7513,"digest":"a87f3ae1c442c3bb26b81569687e98072d038269360361ec9728f00d6b17c282","integrity":"sha256-qH864cRCw7smuBVpaH6YBy0Dgmk2A2HslyjwDWsXwoI="},"tinymce/langs/de-a10c8f6f1e53da776e7026dffd54ea8ba527492b767e5b54ae5c3974f6953311.js":{"logical_path":"tinymce/langs/de.js","mtime":"2016-10-29T16:47:26+02:00","size":8280,"digest":"a10c8f6f1e53da776e7026dffd54ea8ba527492b767e5b54ae5c3974f6953311","integrity":"sha256-oQyPbx5T2nducCbf/VTqi6UnSSt2fltUrlw5dPaVMxE="},"tinymce/langs/de_AT-a32eec12ce12b06fc2c8919d2372242e9596712c96dc9bcfff1fc8b7dc458627.js":{"logical_path":"tinymce/langs/de_AT.js","mtime":"2016-10-29T16:47:26+02:00","size":8308,"digest":"a32eec12ce12b06fc2c8919d2372242e9596712c96dc9bcfff1fc8b7dc458627","integrity":"sha256-oy7sEs4SsG/CyJGdI3IkLpWWcSyW3JvP/x/It9xFhic="},"tinymce/langs/dv-182756bf33409a8b76bc684d0a2ab86881718c360d1f1fb3fbc609ce3f5e3f49.js":{"logical_path":"tinymce/langs/dv.js","mtime":"2016-10-29T16:47:26+02:00","size":18100,"digest":"182756bf33409a8b76bc684d0a2ab86881718c360d1f1fb3fbc609ce3f5e3f49","integrity":"sha256-GCdWvzNAmot2vGhNCiq4aIFxjDYNHx+z+8YJzj9eP0k="},"tinymce/langs/el-6bbdbf3ccb2b2f22f961eb6749b43fe401c84fd6d89cd3f76a538dc2d06bb071.js":{"logical_path":"tinymce/langs/el.js","mtime":"2016-10-29T16:47:26+02:00","size":22973,"digest":"6bbdbf3ccb2b2f22f961eb6749b43fe401c84fd6d89cd3f76a538dc2d06bb071","integrity":"sha256-a72/PMsrLyL5YetnSbQ/5AHIT9bYnNP3alONwtBrsHE="},"tinymce/langs/en_CA-1a9457da814b0a1f15687a00f3ae4b38da0301445b52c29a423a38c943d9a431.js":{"logical_path":"tinymce/langs/en_CA.js","mtime":"2016-10-29T16:47:26+02:00","size":6916,"digest":"1a9457da814b0a1f15687a00f3ae4b38da0301445b52c29a423a38c943d9a431","integrity":"sha256-GpRX2oFLCh8VaHoA865LONoDAURbUsKaQjo4yUPZpDE="},"tinymce/langs/en_GB-a0f7752ff9ddf3ed3d2cd24fee04fe585bca0babc4ce655560c923ad386c0c07.js":{"logical_path":"tinymce/langs/en_GB.js","mtime":"2016-10-29T16:47:26+02:00","size":5907,"digest":"a0f7752ff9ddf3ed3d2cd24fee04fe585bca0babc4ce655560c923ad386c0c07","integrity":"sha256-oPd1L/nd8+09LNJP7gT+WFvKC6vEzmVVYMkjrThsDAc="},"tinymce/langs/eo-cbac4d7ebcb0679845b852d9354e74586e78cf13eef82de1400cf6b465e203cd.js":{"logical_path":"tinymce/langs/eo.js","mtime":"2016-10-29T16:47:26+02:00","size":7466,"digest":"cbac4d7ebcb0679845b852d9354e74586e78cf13eef82de1400cf6b465e203cd","integrity":"sha256-y6xNfrywZ5hFuFLZNU50WG54zxPu+C3hQAz2tGXiA80="},"tinymce/langs/es-133cb5bd627c2a299544586bf365859b5a1c96c57d18c90e7853048434a809b6.js":{"logical_path":"tinymce/langs/es.js","mtime":"2016-10-29T16:47:26+02:00","size":7809,"digest":"133cb5bd627c2a299544586bf365859b5a1c96c57d18c90e7853048434a809b6","integrity":"sha256-Ezy1vWJ8KimVRFhr82WFm1oclsV9GMkOeFMEhDSoCbY="},"tinymce/langs/es_MX-352fcdff567919ff09a2e19c56d8059d339bffdbd999bcc82aa6d8340c19bbb9.js":{"logical_path":"tinymce/langs/es_MX.js","mtime":"2016-10-29T16:47:26+02:00","size":7894,"digest":"352fcdff567919ff09a2e19c56d8059d339bffdbd999bcc82aa6d8340c19bbb9","integrity":"sha256-NS/N/1Z5Gf8JouGcVtgFnTOb/9vZmbzIKqbYNAwZu7k="},"tinymce/langs/et-2cfc3da0ff39a37f0bd3c896d49604028f231ff0e7d8ebbc30dbfd4b87125f06.js":{"logical_path":"tinymce/langs/et.js","mtime":"2016-10-29T16:47:26+02:00","size":7784,"digest":"2cfc3da0ff39a37f0bd3c896d49604028f231ff0e7d8ebbc30dbfd4b87125f06","integrity":"sha256-LPw9oP85o38L08iW1JYEAo8jH/Dn2Ou8MNv9S4cSXwY="},"tinymce/langs/eu-8698ae4de236fcd490d0f45d485dbd33d8e9de6eb3db51191e1b4f8ea96a4d06.js":{"logical_path":"tinymce/langs/eu.js","mtime":"2016-10-29T16:47:26+02:00","size":7011,"digest":"8698ae4de236fcd490d0f45d485dbd33d8e9de6eb3db51191e1b4f8ea96a4d06","integrity":"sha256-hpiuTeI2/NSQ0PRdSF29M9jp3m6z21EZHhtPjqlqTQY="},"tinymce/langs/fa-7eff96595e93d4b0df484dd90c14b7b412966157ed8293f54429e9418882bdb2.js":{"logical_path":"tinymce/langs/fa.js","mtime":"2016-10-29T16:47:26+02:00","size":16462,"digest":"7eff96595e93d4b0df484dd90c14b7b412966157ed8293f54429e9418882bdb2","integrity":"sha256-fv+WWV6T1LDfSE3ZDBS3tBKWYVftgpP1RCnpQYiCvbI="},"tinymce/langs/fa_IR-2f9d33f16718ae9c1f3642905b58434ed2662fb66448c4d115b8491aa68e7904.js":{"logical_path":"tinymce/langs/fa_IR.js","mtime":"2016-10-29T16:47:26+02:00","size":17725,"digest":"2f9d33f16718ae9c1f3642905b58434ed2662fb66448c4d115b8491aa68e7904","integrity":"sha256-L50z8WcYrpwfNkKQW1hDTtJmL7ZkSMTRFbhJGqaOeQQ="},"tinymce/langs/fi-6ca2a93d045067a86e8ea973d0b89368054b15eb9b9625bf1ca871608a5cb77c.js":{"logical_path":"tinymce/langs/fi.js","mtime":"2016-10-29T16:47:26+02:00","size":8143,"digest":"6ca2a93d045067a86e8ea973d0b89368054b15eb9b9625bf1ca871608a5cb77c","integrity":"sha256-bKKpPQRQZ6hujqlz0LiTaAVLFeubliW/HKhxYIpct3w="},"tinymce/langs/fo-340609cecd5571e4eacb8fe7bd1343c8553d96d12610fb77d9a812dc6d3635fd.js":{"logical_path":"tinymce/langs/fo.js","mtime":"2016-10-29T16:47:26+02:00","size":8044,"digest":"340609cecd5571e4eacb8fe7bd1343c8553d96d12610fb77d9a812dc6d3635fd","integrity":"sha256-NAYJzs1VceTqy4/nvRNDyFU9ltEmEPt32agS3G02Nf0="},"tinymce/langs/fr_CH-e0f43c0f20727368ff0767f3bf099bc23eed9fe3e81f8bbe514381e786526928.js":{"logical_path":"tinymce/langs/fr_CH.js","mtime":"2016-10-29T16:47:26+02:00","size":8074,"digest":"e0f43c0f20727368ff0767f3bf099bc23eed9fe3e81f8bbe514381e786526928","integrity":"sha256-4PQ8DyByc2j/B2fzvwmbwj7tn+PoH4u+UUOB54ZSaSg="},"tinymce/langs/fr_FR-2702de7be93bd1e0d7120ae3c9e637061565186c66886f155ffca0663df25b4c.js":{"logical_path":"tinymce/langs/fr_FR.js","mtime":"2016-10-29T16:47:26+02:00","size":8009,"digest":"2702de7be93bd1e0d7120ae3c9e637061565186c66886f155ffca0663df25b4c","integrity":"sha256-JwLee+k70eDXEgrjyeY3BhVlGGxmiG8VX/ygZj3yW0w="},"tinymce/langs/ga-d2a3de6f28723d75b03f7f42fd3aedfd045d473425ee38f02350b56035383af1.js":{"logical_path":"tinymce/langs/ga.js","mtime":"2016-10-29T16:47:26+02:00","size":8591,"digest":"d2a3de6f28723d75b03f7f42fd3aedfd045d473425ee38f02350b56035383af1","integrity":"sha256-0qPebyhyPXWwP39C/Trt/QRdRzQl7jjwI1C1YDU4OvE="},"tinymce/langs/gd-0453e8b97bf3b6cca4065712ec59f20343ad6131735b38547e865177a1c1c490.js":{"logical_path":"tinymce/langs/gd.js","mtime":"2016-10-29T16:47:26+02:00","size":8808,"digest":"0453e8b97bf3b6cca4065712ec59f20343ad6131735b38547e865177a1c1c490","integrity":"sha256-BFPouXvztsykBlcS7FnyA0OtYTFzWzhUfoZRd6HBxJA="},"tinymce/langs/gl-1ff612222934f558870111f10934110c5656df3bb293c917e4eeceeb6605fcc5.js":{"logical_path":"tinymce/langs/gl.js","mtime":"2016-10-29T16:47:26+02:00","size":7066,"digest":"1ff612222934f558870111f10934110c5656df3bb293c917e4eeceeb6605fcc5","integrity":"sha256-H/YSIik09ViHARHxCTQRDFZW3zuyk8kX5O7O62YF/MU="},"tinymce/langs/he_IL-981f5250a4b8d404b37040ca5a35c30498cbeeb36e298d81bf1592b43fff7656.js":{"logical_path":"tinymce/langs/he_IL.js","mtime":"2016-10-29T16:47:26+02:00","size":16081,"digest":"981f5250a4b8d404b37040ca5a35c30498cbeeb36e298d81bf1592b43fff7656","integrity":"sha256-mB9SUKS41ASzcEDKWjXDBJjL7rNuKY2BvxWStD//dlY="},"tinymce/langs/hi_IN-695e0cdc22974fe45e0f35289f0249ddab87245165c9df765ec1b885ec55f0be.js":{"logical_path":"tinymce/langs/hi_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":18458,"digest":"695e0cdc22974fe45e0f35289f0249ddab87245165c9df765ec1b885ec55f0be","integrity":"sha256-aV4M3CKXT+ReDzUonwJJ3auHJFFlyd92XsG4hexV8L4="},"tinymce/langs/hr-f77f1da2736d13f9a9a86ebf596f592fcc748f2975a0b9904b512d630f2c1a17.js":{"logical_path":"tinymce/langs/hr.js","mtime":"2016-10-29T16:47:26+02:00","size":7577,"digest":"f77f1da2736d13f9a9a86ebf596f592fcc748f2975a0b9904b512d630f2c1a17","integrity":"sha256-938donNtE/mpqG6/WW9ZL8x0jyl1oLmQS1EtYw8sGhc="},"tinymce/langs/hu_HU-1e22021a4f9c61919aa024041af555eac277bfc08ccb8c07fd329b87090a15e5.js":{"logical_path":"tinymce/langs/hu_HU.js","mtime":"2016-10-29T16:47:26+02:00","size":9410,"digest":"1e22021a4f9c61919aa024041af555eac277bfc08ccb8c07fd329b87090a15e5","integrity":"sha256-HiICGk+cYZGaoCQEGvVV6sJ3v8CMy4wH/TKbhwkKFeU="},"tinymce/langs/hy-0d384f3f82cd76793d3e7428a2140ea97f40a943f25ef99a2e3709a3e02b0930.js":{"logical_path":"tinymce/langs/hy.js","mtime":"2016-10-29T16:47:26+02:00","size":20375,"digest":"0d384f3f82cd76793d3e7428a2140ea97f40a943f25ef99a2e3709a3e02b0930","integrity":"sha256-DThPP4LNdnk9PnQoohQOqX9AqUPyXvmaLjcJo+ArCTA="},"tinymce/langs/id-e9fd018be745b5f14f4af47887420f98c590c607e01d57aca77ef5affe188523.js":{"logical_path":"tinymce/langs/id.js","mtime":"2016-10-29T16:47:26+02:00","size":7141,"digest":"e9fd018be745b5f14f4af47887420f98c590c607e01d57aca77ef5affe188523","integrity":"sha256-6f0Bi+dFtfFPSvR4h0IPmMWQxgfgHVesp371r/4YhSM="},"tinymce/langs/is_IS-c6e8c3e7b0e6b447faec3d8d258928f97c84558b29882c056513fb71cf237bfa.js":{"logical_path":"tinymce/langs/is_IS.js","mtime":"2016-10-29T16:47:26+02:00","size":7928,"digest":"c6e8c3e7b0e6b447faec3d8d258928f97c84558b29882c056513fb71cf237bfa","integrity":"sha256-xujD57DmtEf67D2NJYko+XyEVYspiCwFZRP7cc8je/o="},"tinymce/langs/it-25b5546d48c80ad666b600e5be3a05718b80645721b191785c1fafe1853f4a46.js":{"logical_path":"tinymce/langs/it.js","mtime":"2016-10-29T16:47:26+02:00","size":7598,"digest":"25b5546d48c80ad666b600e5be3a05718b80645721b191785c1fafe1853f4a46","integrity":"sha256-JbVUbUjICtZmtgDlvjoFcYuAZFchsZF4XB+v4YU/SkY="},"tinymce/langs/ja-80f0e7414030c32617ff651da1affa0bc85ee514fc9bc81f46edfd8ce0053ac0.js":{"logical_path":"tinymce/langs/ja.js","mtime":"2016-10-29T16:47:26+02:00","size":12290,"digest":"80f0e7414030c32617ff651da1affa0bc85ee514fc9bc81f46edfd8ce0053ac0","integrity":"sha256-gPDnQUAwwyYX/2Udoa/6C8he5RT8m8gfRu39jOAFOsA="},"tinymce/langs/ka_GE-3c6b82346a7070a8b6a15ae6e8faeecc5bfe63ad7e616b7de2e8ab8a75ec39c2.js":{"logical_path":"tinymce/langs/ka_GE.js","mtime":"2016-10-29T16:47:26+02:00","size":21284,"digest":"3c6b82346a7070a8b6a15ae6e8faeecc5bfe63ad7e616b7de2e8ab8a75ec39c2","integrity":"sha256-PGuCNGpwcKi2oVrm6PruzFv+Y61+YWt94uirinXsOcI="},"tinymce/langs/kab-3651d08aacd5bda15a04698f202bf616efbf13d3789aadd9ce4f93e430818c13.js":{"logical_path":"tinymce/langs/kab.js","mtime":"2016-10-29T16:47:26+02:00","size":7391,"digest":"3651d08aacd5bda15a04698f202bf616efbf13d3789aadd9ce4f93e430818c13","integrity":"sha256-NlHQiqzVvaFaBGmPICv2Fu+/E9N4mq3Zzk+T5DCBjBM="},"tinymce/langs/kk-0c1ba792ed9445c6512f310228f64d1cde7b5a98e0b212cca749dcb2d728fe86.js":{"logical_path":"tinymce/langs/kk.js","mtime":"2016-10-29T16:47:26+02:00","size":16681,"digest":"0c1ba792ed9445c6512f310228f64d1cde7b5a98e0b212cca749dcb2d728fe86","integrity":"sha256-DBunku2URcZRLzECKPZNHN57WpjgshLMp0ncstco/oY="},"tinymce/langs/km_KH-8bec84e4078db8c2a680260c073dfb486c85b61df62e14ca330d66d2ca2a85b5.js":{"logical_path":"tinymce/langs/km_KH.js","mtime":"2016-10-29T16:47:26+02:00","size":21396,"digest":"8bec84e4078db8c2a680260c073dfb486c85b61df62e14ca330d66d2ca2a85b5","integrity":"sha256-i+yE5AeNuMKmgCYMBz37SGyFth32LhTKMw1m0soqhbU="},"tinymce/langs/ko-ca535bfa388701fb020d24bdfb3f1e3aab127005b54e943cb1bd60b723c8194d.js":{"logical_path":"tinymce/langs/ko.js","mtime":"2016-10-29T16:47:26+02:00","size":10105,"digest":"ca535bfa388701fb020d24bdfb3f1e3aab127005b54e943cb1bd60b723c8194d","integrity":"sha256-ylNb+jiHAfsCDSS9+z8eOqsScAW1TpQ8sb1gtyPIGU0="},"tinymce/langs/ko_KR-daa3ae950d70260bb0901a5349034952247905523bbc2bcbe527701b6aad8e28.js":{"logical_path":"tinymce/langs/ko_KR.js","mtime":"2016-10-29T16:47:26+02:00","size":10005,"digest":"daa3ae950d70260bb0901a5349034952247905523bbc2bcbe527701b6aad8e28","integrity":"sha256-2qOulQ1wJguwkBpTSQNJUiR5BVI7vCvL5SdwG2qtjig="},"tinymce/langs/ku-95e2ffe4f0112df658f10327c26f2cdddedc2e2ed769767366ae22a465c14725.js":{"logical_path":"tinymce/langs/ku.js","mtime":"2016-10-29T16:47:26+02:00","size":18274,"digest":"95e2ffe4f0112df658f10327c26f2cdddedc2e2ed769767366ae22a465c14725","integrity":"sha256-leL/5PARLfZY8QMnwm8s3d7cLi7XaXZzZq4ipGXBRyU="},"tinymce/langs/ku_IQ-a212e8d41695989e56834facf72474e1934ef166c3fcaf7e255ef573a3c6e76c.js":{"logical_path":"tinymce/langs/ku_IQ.js","mtime":"2016-10-29T16:47:26+02:00","size":18364,"digest":"a212e8d41695989e56834facf72474e1934ef166c3fcaf7e255ef573a3c6e76c","integrity":"sha256-ohLo1BaVmJ5Wg0+s9yR04ZNO8WbD/K9+JV71c6PG52w="},"tinymce/langs/lb-94992279091f1a0be927d5d858d3724037d8d81ee6b63cc9ddde84af6124cc04.js":{"logical_path":"tinymce/langs/lb.js","mtime":"2016-10-29T16:47:26+02:00","size":7467,"digest":"94992279091f1a0be927d5d858d3724037d8d81ee6b63cc9ddde84af6124cc04","integrity":"sha256-lJkieQkfGgvpJ9XYWNNyQDfY2B7mtjzJ3d6Er2EkzAQ="},"tinymce/langs/lt-ae7cefbe2cb5420770b31eabf92605728135ab5d6a2d2cfca2d19809be67e6a8.js":{"logical_path":"tinymce/langs/lt.js","mtime":"2016-10-29T16:47:26+02:00","size":8899,"digest":"ae7cefbe2cb5420770b31eabf92605728135ab5d6a2d2cfca2d19809be67e6a8","integrity":"sha256-rnzvviy1Qgdwsx6r+SYFcoE1q11qLSz8otGYCb5n5qg="},"tinymce/langs/lv-e468ed0c78466d1fff9f4957645606d8aa683cb5dc35931e047df97b8e4f5433.js":{"logical_path":"tinymce/langs/lv.js","mtime":"2016-10-29T16:47:26+02:00","size":8282,"digest":"e468ed0c78466d1fff9f4957645606d8aa683cb5dc35931e047df97b8e4f5433","integrity":"sha256-5GjtDHhGbR//n0lXZFYG2KpoPLXcNZMeBH35e45PVDM="},"tinymce/langs/mk_MK-55aadb5d7c82c940cfcf42f24423ef0471294c9b02994a6b22e1b4173baa7ec6.js":{"logical_path":"tinymce/langs/mk_MK.js","mtime":"2016-10-29T16:47:26+02:00","size":18733,"digest":"55aadb5d7c82c940cfcf42f24423ef0471294c9b02994a6b22e1b4173baa7ec6","integrity":"sha256-VarbXXyCyUDPz0LyRCPvBHEpTJsCmUprIuG0FzuqfsY="},"tinymce/langs/ml-685de0808683c02274dcbe9f8de023a4d72a49b040e7526a99e3d5c786ff71ae.js":{"logical_path":"tinymce/langs/ml.js","mtime":"2016-10-29T16:47:26+02:00","size":7339,"digest":"685de0808683c02274dcbe9f8de023a4d72a49b040e7526a99e3d5c786ff71ae","integrity":"sha256-aF3ggIaDwCJ03L6fjeAjpNcqSbBA51JqmePVx4b/ca4="},"tinymce/langs/ml_IN-1ab433621d6257c7632718048bacc5ed98a302d33cf0438bbbb4200914e01aac.js":{"logical_path":"tinymce/langs/ml_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":20019,"digest":"1ab433621d6257c7632718048bacc5ed98a302d33cf0438bbbb4200914e01aac","integrity":"sha256-GrQzYh1iV8djJxgEi6zF7ZijAtM88EOLu7QgCRTgGqw="},"tinymce/langs/mn_MN-b89f88cb9b0dd7c879e27532b2979c421c1e69648f65a4108a56060f1e1cb009.js":{"logical_path":"tinymce/langs/mn_MN.js","mtime":"2016-10-29T16:47:26+02:00","size":6877,"digest":"b89f88cb9b0dd7c879e27532b2979c421c1e69648f65a4108a56060f1e1cb009","integrity":"sha256-uJ+Iy5sN18h54nUyspecQhweaWSPZaQQilYGDx4csAk="},"tinymce/langs/nb_NO-602ee249e98a26ae24c1ee1311090c68a782050086a835f1e7bd46ebfe7879ef.js":{"logical_path":"tinymce/langs/nb_NO.js","mtime":"2016-10-29T16:47:26+02:00","size":7528,"digest":"602ee249e98a26ae24c1ee1311090c68a782050086a835f1e7bd46ebfe7879ef","integrity":"sha256-YC7iSemKJq4kwe4TEQkMaKeCBQCGqDXx571G6/54ee8="},"tinymce/langs/nl-9c77e9404a27fd7e4bd162ad44538b72d2725b7fee7be7ca3ff801cf217c4968.js":{"logical_path":"tinymce/langs/nl.js","mtime":"2016-10-29T16:47:26+02:00","size":7235,"digest":"9c77e9404a27fd7e4bd162ad44538b72d2725b7fee7be7ca3ff801cf217c4968","integrity":"sha256-nHfpQEon/X5L0WKtRFOLctJyW3/ue+fKP/gBzyF8SWg="},"tinymce/langs/pl-5cdd1586ec8fa67293f2175cd92edbd0392c4c77b24ed64cfdceccba8d99f152.js":{"logical_path":"tinymce/langs/pl.js","mtime":"2016-10-29T16:47:26+02:00","size":8129,"digest":"5cdd1586ec8fa67293f2175cd92edbd0392c4c77b24ed64cfdceccba8d99f152","integrity":"sha256-XN0VhuyPpnKT8hdc2S7b0DksTHeyTtZM/c7Muo2Z8VI="},"tinymce/langs/pt_BR-e3955318582d58bd241a62c3a81afe74e0ae7f31bdc92a562b75243701f114df.js":{"logical_path":"tinymce/langs/pt_BR.js","mtime":"2016-10-29T16:47:26+02:00","size":8002,"digest":"e3955318582d58bd241a62c3a81afe74e0ae7f31bdc92a562b75243701f114df","integrity":"sha256-45VTGFgtWL0kGmLDqBr+dOCufzG9ySpWK3UkNwHxFN8="},"tinymce/langs/pt_PT-1101d080ecd5da091c7ef70b23200adbd5136c5c32f0c79dae665a443d76c35d.js":{"logical_path":"tinymce/langs/pt_PT.js","mtime":"2016-10-29T16:47:26+02:00","size":8145,"digest":"1101d080ecd5da091c7ef70b23200adbd5136c5c32f0c79dae665a443d76c35d","integrity":"sha256-EQHQgOzV2gkcfvcLIyAK29UTbFwy8MedrmZaRD12w10="},"tinymce/langs/ro-484799d182031efab087d749b024014fd06ed06c1e7712b8bf87f3a4a965c2e4.js":{"logical_path":"tinymce/langs/ro.js","mtime":"2016-10-29T16:47:26+02:00","size":8275,"digest":"484799d182031efab087d749b024014fd06ed06c1e7712b8bf87f3a4a965c2e4","integrity":"sha256-SEeZ0YIDHvqwh9dJsCQBT9Bu0GwedxK4v4fzpKllwuQ="},"tinymce/langs/ru-bdd59cb662a7b4d61e770b07014d1791051312c87513ecc1891e80beba617171.js":{"logical_path":"tinymce/langs/ru.js","mtime":"2016-10-29T16:47:26+02:00","size":22290,"digest":"bdd59cb662a7b4d61e770b07014d1791051312c87513ecc1891e80beba617171","integrity":"sha256-vdWctmKntNYedwsHAU0XkQUTEsh1E+zBiR6AvrphcXE="},"tinymce/langs/ru_RU-9f18ef14f3493cc586cc0b4827fd9e1c2ed3d5790ce536d94f87e2d6aac2a688.js":{"logical_path":"tinymce/langs/ru_RU.js","mtime":"2016-10-29T16:47:26+02:00","size":4955,"digest":"9f18ef14f3493cc586cc0b4827fd9e1c2ed3d5790ce536d94f87e2d6aac2a688","integrity":"sha256-nxjvFPNJPMWGzAtIJ/2eHC7T1XkM5TbZT4fi1qrCpog="},"tinymce/langs/si_LK-5bfefe9b2fe82574d8ed839df18fd8ff65f1e1cd458b0a843cf1aadd1be704ea.js":{"logical_path":"tinymce/langs/si_LK.js","mtime":"2016-10-29T16:47:26+02:00","size":15856,"digest":"5bfefe9b2fe82574d8ed839df18fd8ff65f1e1cd458b0a843cf1aadd1be704ea","integrity":"sha256-W/7+my/oJXTY7YOd8Y/Y/2Xx4c1FiwqEPPGq3RvnBOo="},"tinymce/langs/sk-412fa7f982ad2733341203daa035c8ba10035262ca8f5f65e0a41507512209bd.js":{"logical_path":"tinymce/langs/sk.js","mtime":"2016-10-29T16:47:26+02:00","size":8958,"digest":"412fa7f982ad2733341203daa035c8ba10035262ca8f5f65e0a41507512209bd","integrity":"sha256-QS+n+YKtJzM0EgPaoDXIuhADUmLKj19l4KQVB1EiCb0="},"tinymce/langs/sl_SI-2bea4f3854a992ea840808019dc99467dc173b12454ea6016135234a741671d8.js":{"logical_path":"tinymce/langs/sl_SI.js","mtime":"2016-10-29T16:47:26+02:00","size":7039,"digest":"2bea4f3854a992ea840808019dc99467dc173b12454ea6016135234a741671d8","integrity":"sha256-K+pPOFSpkuqECAgBncmUZ9wXOxJFTqYBYTUjSnQWcdg="},"tinymce/langs/sr-e0b97453a0c1000b19f7ce37fcf86bcacbf9125d81076f60a77686820c7925c6.js":{"logical_path":"tinymce/langs/sr.js","mtime":"2016-10-29T16:47:26+02:00","size":6336,"digest":"e0b97453a0c1000b19f7ce37fcf86bcacbf9125d81076f60a77686820c7925c6","integrity":"sha256-4Ll0U6DBAAsZ9843/Phrysv5El2BB29gp3aGggx5JcY="},"tinymce/langs/sv_SE-fa561a7e49fdb42d5913337176fed6bc8a9997eb3ca8f20ecd9520d5391e17be.js":{"logical_path":"tinymce/langs/sv_SE.js","mtime":"2016-10-29T16:47:26+02:00","size":7496,"digest":"fa561a7e49fdb42d5913337176fed6bc8a9997eb3ca8f20ecd9520d5391e17be","integrity":"sha256-+lYafkn9tC1ZEzNxdv7WvIqZl+s8qPIOzZUg1TkeF74="},"tinymce/langs/ta-0fd0e62250fc1a3626ac2111c5adb4194a6a898888bb224414f6dc8adf53d385.js":{"logical_path":"tinymce/langs/ta.js","mtime":"2016-10-29T16:47:26+02:00","size":22617,"digest":"0fd0e62250fc1a3626ac2111c5adb4194a6a898888bb224414f6dc8adf53d385","integrity":"sha256-D9DmIlD8GjYmrCERxa20GUpqiYiIuyJEFPbcit9T04U="},"tinymce/langs/ta_IN-32303f65d2378e8202b17b0920b79277a2580872e3a6bd9409aeceef90867dd0.js":{"logical_path":"tinymce/langs/ta_IN.js","mtime":"2016-10-29T16:47:26+02:00","size":22620,"digest":"32303f65d2378e8202b17b0920b79277a2580872e3a6bd9409aeceef90867dd0","integrity":"sha256-MjA/ZdI3joICsXsJILeSd6JYCHLjpr2UCa7O75CGfdA="},"tinymce/langs/tg-1618248bf0aeda614a37ced9fd28b6623ae24f8453368bfb8c483ce820a3cb34.js":{"logical_path":"tinymce/langs/tg.js","mtime":"2016-10-29T16:47:26+02:00","size":18183,"digest":"1618248bf0aeda614a37ced9fd28b6623ae24f8453368bfb8c483ce820a3cb34","integrity":"sha256-Fhgki/Cu2mFKN87Z/Si2YjriT4RTNov7jEg86CCjyzQ="},"tinymce/langs/th_TH-5279a379afc886c2a31e22aba9d7ee7a8e3edf4c0785f39aaa20ccd03d46b19a.js":{"logical_path":"tinymce/langs/th_TH.js","mtime":"2016-10-29T16:47:26+02:00","size":17297,"digest":"5279a379afc886c2a31e22aba9d7ee7a8e3edf4c0785f39aaa20ccd03d46b19a","integrity":"sha256-Unmjea/IhsKjHiKrqdfueo4+30wHhfOaqiDM0D1GsZo="},"tinymce/langs/tr-3765d4a8923ef22864747d9c825c3e0af401e1356e75a5458bed837d486b7673.js":{"logical_path":"tinymce/langs/tr.js","mtime":"2016-10-29T16:47:26+02:00","size":8705,"digest":"3765d4a8923ef22864747d9c825c3e0af401e1356e75a5458bed837d486b7673","integrity":"sha256-N2XUqJI+8ihkdH2cglw+CvQB4TVudaVFi+2DfUhrdnM="},"tinymce/langs/tr_TR-5c93f80b89e5dd2eb6972f37bf50b76d7705c61f28120b836704a91fa5c14399.js":{"logical_path":"tinymce/langs/tr_TR.js","mtime":"2016-10-29T16:47:26+02:00","size":8141,"digest":"5c93f80b89e5dd2eb6972f37bf50b76d7705c61f28120b836704a91fa5c14399","integrity":"sha256-XJP4C4nl3S62ly83v1C3bXcFxh8oEguDZwSpH6XBQ5k="},"tinymce/langs/tt-22e302672df0a77de14688f58dbedf82de7a26e9089c9535a2a3cd1c0ccd7903.js":{"logical_path":"tinymce/langs/tt.js","mtime":"2016-10-29T16:47:26+02:00","size":19035,"digest":"22e302672df0a77de14688f58dbedf82de7a26e9089c9535a2a3cd1c0ccd7903","integrity":"sha256-IuMCZy3wp33hRoj1jb7fgt56JukInJU1oqPNHAzNeQM="},"tinymce/langs/ug-50232038bf7262c90be4f2919c74ff93792cdb82e6064d0f5c86f885b93271c5.js":{"logical_path":"tinymce/langs/ug.js","mtime":"2016-10-29T16:47:26+02:00","size":16888,"digest":"50232038bf7262c90be4f2919c74ff93792cdb82e6064d0f5c86f885b93271c5","integrity":"sha256-UCMgOL9yYskL5PKRnHT/k3ks24LmBk0PXIb4hbkyccU="},"tinymce/langs/uk-f6d7911714c422b178c0249df323cf7ec415a83195d751c7e4024590b1738c42.js":{"logical_path":"tinymce/langs/uk.js","mtime":"2016-10-29T16:47:26+02:00","size":22264,"digest":"f6d7911714c422b178c0249df323cf7ec415a83195d751c7e4024590b1738c42","integrity":"sha256-9teRFxTEIrF4wCSd8yPPfsQVqDGV11HH5AJFkLFzjEI="},"tinymce/langs/uk_UA-bd8cd7ca66228d85f8a4cadb651c49981f26e007fa2a952856c8c3d3c1baa66a.js":{"logical_path":"tinymce/langs/uk_UA.js","mtime":"2016-10-29T16:47:26+02:00","size":21691,"digest":"bd8cd7ca66228d85f8a4cadb651c49981f26e007fa2a952856c8c3d3c1baa66a","integrity":"sha256-vYzXymYijYX4pMrbZRxJmB8m4Af6KpUoVsjD08G6pmo="},"tinymce/langs/vi-6aa451047e4df911c92e10178e6a70147534e438c64df7a88eee37b4a440726e.js":{"logical_path":"tinymce/langs/vi.js","mtime":"2016-10-29T16:47:26+02:00","size":9901,"digest":"6aa451047e4df911c92e10178e6a70147534e438c64df7a88eee37b4a440726e","integrity":"sha256-aqRRBH5N+RHJLhAXjmpwFHU05DjGTfeoju43tKRAcm4="},"tinymce/langs/vi_VN-aea52c2e9397f8a657d54d50a42b3c5be0ca79480555a4fa1e0f5d785c5f6561.js":{"logical_path":"tinymce/langs/vi_VN.js","mtime":"2016-10-29T16:47:26+02:00","size":9899,"digest":"aea52c2e9397f8a657d54d50a42b3c5be0ca79480555a4fa1e0f5d785c5f6561","integrity":"sha256-rqUsLpOX+KZX1U1QpCs8W+DKeUgFVaT6Hg9deFxfZWE="},"tinymce/langs/zh_CN-8622ec46e2980b6f5baf3b745c6b0187dd2a54ddecbbf69a21f0b3e72f84f6dc.js":{"logical_path":"tinymce/langs/zh_CN.js","mtime":"2016-10-29T16:47:26+02:00","size":8483,"digest":"8622ec46e2980b6f5baf3b745c6b0187dd2a54ddecbbf69a21f0b3e72f84f6dc","integrity":"sha256-hiLsRuKYC29brzt0XGsBh90qVN3su/aaIfCz5y+E9tw="},"tinymce/langs/zh_TW-90723da3b889f2a4477d4aaf00ca3e75439998269b36b359ea4caa37e1defb4a.js":{"logical_path":"tinymce/langs/zh_TW.js","mtime":"2016-10-29T16:47:26+02:00","size":9379,"digest":"90723da3b889f2a4477d4aaf00ca3e75439998269b36b359ea4caa37e1defb4a","integrity":"sha256-kHI9o7iJ8qRHfUqvAMo+dUOZmCabNrNZ6kyqN+He+0o="},"tinymce/preinit-84328a53e798df12f891eb49871773fd9f925439c8630e9e22423a82ef9e6f89.js":{"logical_path":"tinymce/preinit.js","mtime":"2017-01-05T19:24:14+01:00","size":82,"digest":"84328a53e798df12f891eb49871773fd9f925439c8630e9e22423a82ef9e6f89","integrity":"sha256-hDKKU+eY3xL4ketJhxdz/Z+SVDnIYw6eIkI6gu+eb4k="},"tinymce/tinymce-6c5174912bf31a8ee031a9acb5d2b7d05f7bcd42e3ed0d26d7d217a5343eb2bf.js":{"logical_path":"tinymce/tinymce.js","mtime":"2017-01-05T19:24:14+01:00","size":1294513,"digest":"6c5174912bf31a8ee031a9acb5d2b7d05f7bcd42e3ed0d26d7d217a5343eb2bf","integrity":"sha256-bFF0kSvzGo7gMamstdK30F97zULj7Q0m19IXpTQ+sr8="},"tinymce/jquery.tinymce-275e24af4bf53bfb60f7fef218163106ad0648b8ad384ecfab9b4fd52f48603d.js":{"logical_path":"tinymce/jquery.tinymce.js","mtime":"2017-01-05T19:24:14+01:00","size":3592,"digest":"275e24af4bf53bfb60f7fef218163106ad0648b8ad384ecfab9b4fd52f48603d","integrity":"sha256-J14kr0v1O/tg9/7yGBYxBq0GSLitOE7Pq5tP1S9IYD0="},"tinymce/langs/readme-5a8b6a04d57b5c88e3fb7f2a870b8e2d3a48ec03ce6474206c41df78c155b2de.md":{"logical_path":"tinymce/langs/readme.md","mtime":"2017-01-05T19:24:14+01:00","size":151,"digest":"5a8b6a04d57b5c88e3fb7f2a870b8e2d3a48ec03ce6474206c41df78c155b2de","integrity":"sha256-WotqBNV7XIjj+38qhwuOLTpI7APOZHQgbEHfeMFVst4="},"tinymce/license-5fda611b8191f00121f161a93e7399cdb71789dba923e8ed09b50a1f78d32c5e.txt":{"logical_path":"tinymce/license.txt","mtime":"2017-01-05T19:24:14+01:00","size":26427,"digest":"5fda611b8191f00121f161a93e7399cdb71789dba923e8ed09b50a1f78d32c5e","integrity":"sha256-X9phG4GR8AEh8WGpPnOZzbcXidupI+jtCbUKH3jTLF4="},"tinymce/plugins/advlist/plugin-d0bd2b90c1aaf60ddbb048d955f7fbf76e303b5f3227f8e4dc83207548c3fc46.js":{"logical_path":"tinymce/plugins/advlist/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":2092,"digest":"d0bd2b90c1aaf60ddbb048d955f7fbf76e303b5f3227f8e4dc83207548c3fc46","integrity":"sha256-0L0rkMGq9g3bsEjZVff7924wO18yJ/jk3IMgdUjD/EY="},"tinymce/plugins/anchor/plugin-48c211e97cdf2fba4e8456ddc0464c301c61ff2d2751aa178d41c5eca38fbbe9.js":{"logical_path":"tinymce/plugins/anchor/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":958,"digest":"48c211e97cdf2fba4e8456ddc0464c301c61ff2d2751aa178d41c5eca38fbbe9","integrity":"sha256-SMIR6XzfL7pOhFbdwEZMMBxh/y0nUaoXjUHF7KOPu+k="},"tinymce/plugins/autolink/plugin-9d514baa6f3816a43008241db075d724fcb274632fad3db792e87e5feb6c5d0f.js":{"logical_path":"tinymce/plugins/autolink/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":2061,"digest":"9d514baa6f3816a43008241db075d724fcb274632fad3db792e87e5feb6c5d0f","integrity":"sha256-nVFLqm84FqQwCCQdsHXXJPyydGMvrT23kuh+X+tsXQ8="},"tinymce/plugins/autoresize/plugin-7cd05d5431f8713c948291ad40ef63eabe44908bd831d5675afacaca296a4d7b.js":{"logical_path":"tinymce/plugins/autoresize/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1904,"digest":"7cd05d5431f8713c948291ad40ef63eabe44908bd831d5675afacaca296a4d7b","integrity":"sha256-fNBdVDH4cTyUgpGtQO9j6r5EkIvYMdVnWvrKyilqTXs="},"tinymce/plugins/autosave/plugin-43448ce025b165ed1b509554cadf02cda4ed104ccfae6d0708379f02e981220f.js":{"logical_path":"tinymce/plugins/autosave/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":2188,"digest":"43448ce025b165ed1b509554cadf02cda4ed104ccfae6d0708379f02e981220f","integrity":"sha256-Q0SM4CWxZe0bUJVUyt8CzaTtEEzPrm0HCDefAumBIg8="},"tinymce/plugins/bbcode/plugin-dae2432b6477ca1acc3bd6ac7af33c379797c7f2dcf2c82513475d4c234d851d.js":{"logical_path":"tinymce/plugins/bbcode/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":3137,"digest":"dae2432b6477ca1acc3bd6ac7af33c379797c7f2dcf2c82513475d4c234d851d","integrity":"sha256-2uJDK2R3yhrMO9asevM8N5eXx/Lc8sglE0ddTCNNhR0="},"tinymce/plugins/charmap/plugin-ee3f9ed5d0135f19975a4609701b4b4546f00ab1afee9b74a38bb3d76ab94eca.js":{"logical_path":"tinymce/plugins/charmap/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":8200,"digest":"ee3f9ed5d0135f19975a4609701b4b4546f00ab1afee9b74a38bb3d76ab94eca","integrity":"sha256-7j+e1dATXxmXWkYJcBtLRUbwCrGv7pt0o4uz12q5Tso="},"tinymce/plugins/code/plugin-093b2519070297197c89cabd2b7bf7b7920786fa3ae5b055322fb16d51d113d4.js":{"logical_path":"tinymce/plugins/code/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":722,"digest":"093b2519070297197c89cabd2b7bf7b7920786fa3ae5b055322fb16d51d113d4","integrity":"sha256-CTslGQcClxl8icq9K3v3t5IHhvo65bBVMi+xbVHRE9Q="},"tinymce/plugins/codesample/css/prism-1988b66704b4d23e78c6c20c38a6856cbc1f0be96d6d60a3a0b12f4408f1057e.css":{"logical_path":"tinymce/plugins/codesample/css/prism.css","mtime":"2017-01-05T19:24:14+01:00","size":1776,"digest":"1988b66704b4d23e78c6c20c38a6856cbc1f0be96d6d60a3a0b12f4408f1057e","integrity":"sha256-GYi2ZwS00j54xsIMOKaFbLwfC+ltbWCjoLEvRAjxBX4="},"tinymce/plugins/codesample/plugin-edd0d2e7f821002843d7726182d9a8b1520fcabb753b95ee7956180337be23da.js":{"logical_path":"tinymce/plugins/codesample/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":20333,"digest":"edd0d2e7f821002843d7726182d9a8b1520fcabb753b95ee7956180337be23da","integrity":"sha256-7dDS5/ghAChD13JhgtmosVIPyrt1O5XueVYYAze+I9o="},"tinymce/plugins/colorpicker/plugin-526e08119d96ecdeeb21c1031cba4b471aba860fbc6ed0f8a010275088c00531.js":{"logical_path":"tinymce/plugins/colorpicker/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1223,"digest":"526e08119d96ecdeeb21c1031cba4b471aba860fbc6ed0f8a010275088c00531","integrity":"sha256-Um4IEZ2W7N7rIcEDHLpLRxq6hg+8btD4oBAnUIjABTE="},"tinymce/plugins/contextmenu/plugin-11ad7a2ff14cc47d12a3dd0170741590335e606b0efaa8dc77837409e54afa0a.js":{"logical_path":"tinymce/plugins/contextmenu/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1132,"digest":"11ad7a2ff14cc47d12a3dd0170741590335e606b0efaa8dc77837409e54afa0a","integrity":"sha256-Ea16L/FMxH0So90BcHQVkDNeYGsO+qjcd4N0CeVK+go="},"tinymce/plugins/directionality/plugin-6381fb028625726e50977d00f342fa1f60e39dc794e0ea1c7d9feff8846ab9fc.js":{"logical_path":"tinymce/plugins/directionality/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":727,"digest":"6381fb028625726e50977d00f342fa1f60e39dc794e0ea1c7d9feff8846ab9fc","integrity":"sha256-Y4H7AoYlcm5Ql30A80L6H2DjnceU4OocfZ/v+IRqufw="},"tinymce/plugins/emoticons/img/smiley-cool-bb0e93a050a32df7913e4026b3c88a176998e0e3e073ba06e9b73f6c24227c9c.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-cool.gif","mtime":"2017-01-05T19:24:14+01:00","size":354,"digest":"bb0e93a050a32df7913e4026b3c88a176998e0e3e073ba06e9b73f6c24227c9c","integrity":"sha256-uw6ToFCjLfeRPkAms8iKF2mY4OPgc7oG6bc/bCQifJw="},"tinymce/plugins/emoticons/img/smiley-cry-a0c5f3e7a682449c973c9d9f7c46342081c46920686d2353f57aff91ab907f68.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-cry.gif","mtime":"2017-01-05T19:24:14+01:00","size":329,"digest":"a0c5f3e7a682449c973c9d9f7c46342081c46920686d2353f57aff91ab907f68","integrity":"sha256-oMXz56aCRJyXPJ2ffEY0IIHEaSBobSNT9Xr/kauQf2g="},"tinymce/plugins/emoticons/img/smiley-embarassed-d3cafcb50b335672cb5e9f4600ea9ea261dac7828dd28844d4927c393a25618f.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-embarassed.gif","mtime":"2017-01-05T19:24:14+01:00","size":331,"digest":"d3cafcb50b335672cb5e9f4600ea9ea261dac7828dd28844d4927c393a25618f","integrity":"sha256-08r8tQszVnLLXp9GAOqeomHax4KN0ohE1JJ8OTolYY8="},"tinymce/plugins/emoticons/img/smiley-foot-in-mouth-03fe04d3ed533423ac81f05146584b0c451be3d4a30e76687ceef283ed07071f.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif","mtime":"2017-01-05T19:24:14+01:00","size":342,"digest":"03fe04d3ed533423ac81f05146584b0c451be3d4a30e76687ceef283ed07071f","integrity":"sha256-A/4E0+1TNCOsgfBRRlhLDEUb49SjDnZofO7yg+0HBx8="},"tinymce/plugins/emoticons/img/smiley-frown-1b984bf98931dd1debb54461eb9d83e985f2b2999fe14bcb556d6c0921bc83b0.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-frown.gif","mtime":"2017-01-05T19:24:14+01:00","size":340,"digest":"1b984bf98931dd1debb54461eb9d83e985f2b2999fe14bcb556d6c0921bc83b0","integrity":"sha256-G5hL+Ykx3R3rtURh652D6YXyspmf4UvLVW1sCSG8g7A="},"tinymce/plugins/emoticons/img/smiley-innocent-8db353ef102196f2c6ddf5c4666446de955d7b14fc0957c806c9dbfb48fb0c29.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-innocent.gif","mtime":"2017-01-05T19:24:14+01:00","size":336,"digest":"8db353ef102196f2c6ddf5c4666446de955d7b14fc0957c806c9dbfb48fb0c29","integrity":"sha256-jbNT7xAhlvLG3fXEZmRG3pVdexT8CVfIBsnb+0j7DCk="},"tinymce/plugins/emoticons/img/smiley-kiss-3154c3665356c13ab10fefdbac1fe187fff978a0052037c99cdc4a97103413f2.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-kiss.gif","mtime":"2017-01-05T19:24:14+01:00","size":338,"digest":"3154c3665356c13ab10fefdbac1fe187fff978a0052037c99cdc4a97103413f2","integrity":"sha256-MVTDZlNWwTqxD+/brB/hh//5eKAFIDfJnNxKlxA0E/I="},"tinymce/plugins/emoticons/img/smiley-laughing-8f6adedcd091975ffead171867a6304d908bb6541a6ccb4919286ec6b7d4551e.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-laughing.gif","mtime":"2017-01-05T19:24:14+01:00","size":343,"digest":"8f6adedcd091975ffead171867a6304d908bb6541a6ccb4919286ec6b7d4551e","integrity":"sha256-j2re3NCRl1/+rRcYZ6YwTZCLtlQabMtJGShuxrfUVR4="},"tinymce/plugins/emoticons/img/smiley-money-mouth-f0b9f4f22e237f5dbc851f900fed8d7eca4c954ae6fbc606c0cd8be431d0ac80.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-money-mouth.gif","mtime":"2017-01-05T19:24:14+01:00","size":321,"digest":"f0b9f4f22e237f5dbc851f900fed8d7eca4c954ae6fbc606c0cd8be431d0ac80","integrity":"sha256-8Ln08i4jf128hR+QD+2NfspMlUrm+8YGwM2L5DHQrIA="},"tinymce/plugins/emoticons/img/smiley-sealed-9933b442636b6e537df7b564e2c3f7a2873526eea6b022a98eb1e468e5204c32.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-sealed.gif","mtime":"2017-01-05T19:24:14+01:00","size":323,"digest":"9933b442636b6e537df7b564e2c3f7a2873526eea6b022a98eb1e468e5204c32","integrity":"sha256-mTO0QmNrblN997Vk4sP3ooc1Ju6msCKpjrHkaOUgTDI="},"tinymce/plugins/emoticons/img/smiley-smile-fd89cd460ffcacb7e725e00c0275ef5b3924ce468248e5ff4fb43545571cfa65.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-smile.gif","mtime":"2017-01-05T19:24:14+01:00","size":344,"digest":"fd89cd460ffcacb7e725e00c0275ef5b3924ce468248e5ff4fb43545571cfa65","integrity":"sha256-/YnNRg/8rLfnJeAMAnXvWzkkzkaCSOX/T7Q1RVcc+mU="},"tinymce/plugins/emoticons/img/smiley-surprised-3871f356cb41976d7ae8a5f005e8739e4d014352a8adef9b33f773d81b6e6c01.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-surprised.gif","mtime":"2017-01-05T19:24:14+01:00","size":338,"digest":"3871f356cb41976d7ae8a5f005e8739e4d014352a8adef9b33f773d81b6e6c01","integrity":"sha256-OHHzVstBl2166KXwBehznk0BQ1Kore+bM/dz2BtubAE="},"tinymce/plugins/emoticons/img/smiley-tongue-out-5843c85667a8226dc43be83749fd9fbbc5d20b1577de2b763915d99815d37d47.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-tongue-out.gif","mtime":"2017-01-05T19:24:14+01:00","size":328,"digest":"5843c85667a8226dc43be83749fd9fbbc5d20b1577de2b763915d99815d37d47","integrity":"sha256-WEPIVmeoIm3EO+g3Sf2fu8XSCxV33it2ORXZmBXTfUc="},"tinymce/plugins/emoticons/img/smiley-undecided-d8b9bcbb433951ff3c4ca8dd959ac3844239b98e6d52218833e1485a91f67347.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-undecided.gif","mtime":"2017-01-05T19:24:14+01:00","size":337,"digest":"d8b9bcbb433951ff3c4ca8dd959ac3844239b98e6d52218833e1485a91f67347","integrity":"sha256-2Lm8u0M5Uf88TKjdlZrDhEI5uY5tUiGIM+FIWpH2c0c="},"tinymce/plugins/emoticons/img/smiley-wink-2af75ad7b1c08488505513503e34b15f40005e04a2a9568f698f0945d2d8ba1f.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-wink.gif","mtime":"2017-01-05T19:24:14+01:00","size":350,"digest":"2af75ad7b1c08488505513503e34b15f40005e04a2a9568f698f0945d2d8ba1f","integrity":"sha256-Kvda17HAhIhQVRNQPjSxX0AAXgSiqVaPaY8JRdLYuh8="},"tinymce/plugins/emoticons/img/smiley-yell-bba903fbcb46fce8c68b9e01863fd095b3b1d0e6aa72161f3a88d762a5f90a79.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-yell.gif","mtime":"2017-01-05T19:24:14+01:00","size":336,"digest":"bba903fbcb46fce8c68b9e01863fd095b3b1d0e6aa72161f3a88d762a5f90a79","integrity":"sha256-u6kD+8tG/OjGi54Bhj/QlbOx0OaqchYfOojXYqX5Cnk="},"tinymce/plugins/emoticons/plugin-75971da62ade77af7e79b3a560c5ad5ac20ee5f88c7ba236e03902777fdec715.js":{"logical_path":"tinymce/plugins/emoticons/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":912,"digest":"75971da62ade77af7e79b3a560c5ad5ac20ee5f88c7ba236e03902777fdec715","integrity":"sha256-dZcdpired69+ebOlYMWtWsIO5fiMe6I24DkCd3/exxU="},"tinymce/plugins/example/dialog-5c61ad829a494e75c2234019c7941f6dd096ec693cf5a5538c0bf30485097841.html":{"logical_path":"tinymce/plugins/example/dialog.html","mtime":"2017-01-05T19:24:14+01:00","size":213,"digest":"5c61ad829a494e75c2234019c7941f6dd096ec693cf5a5538c0bf30485097841","integrity":"sha256-XGGtgppJTnXCI0AZx5QfbdCW7Gk89aVTjAvzBIUJeEE="},"tinymce/plugins/example/plugin-505b7ee82929e7338700605a4ca63d94dbe6ef2ce2d21104cc01c2adba829868.js":{"logical_path":"tinymce/plugins/example/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":659,"digest":"505b7ee82929e7338700605a4ca63d94dbe6ef2ce2d21104cc01c2adba829868","integrity":"sha256-UFt+6Ckp5zOHAGBaTKY9lNvm7yzi0hEEzAHCrbqCmGg="},"tinymce/plugins/example_dependency/plugin-9bb52c9d45c0f7e4d67c12f546c68095b2d5284aa84ab3a2c45f1d986f464653.js":{"logical_path":"tinymce/plugins/example_dependency/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":74,"digest":"9bb52c9d45c0f7e4d67c12f546c68095b2d5284aa84ab3a2c45f1d986f464653","integrity":"sha256-m7UsnUXA9+TWfBL1RsaAlbLVKEqoSrOixF8dmG9GRlM="},"tinymce/plugins/fullpage/plugin-6cd62d14e4225c1db1d9008a485143d8e8b7a7c699e2ceec16a8012b44875ef5.js":{"logical_path":"tinymce/plugins/fullpage/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":6309,"digest":"6cd62d14e4225c1db1d9008a485143d8e8b7a7c699e2ceec16a8012b44875ef5","integrity":"sha256-bNYtFOQiXB2x2QCKSFFD2Oi3p8aZ4s7sFqgBK0SHXvU="},"tinymce/plugins/fullscreen/plugin-972d5e04901e74c8c657ee88fb3cc245b94aeb005d54adeac00c07a0c5700f7a.js":{"logical_path":"tinymce/plugins/fullscreen/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1676,"digest":"972d5e04901e74c8c657ee88fb3cc245b94aeb005d54adeac00c07a0c5700f7a","integrity":"sha256-ly1eBJAedMjGV+6I+zzCRblK6wBdVK3qwAwHoMVwD3o="},"tinymce/plugins/hr/plugin-92558948637f4349e0f327350ddebdd225313e582f08aacf6cb5549d03fc9a8b.js":{"logical_path":"tinymce/plugins/hr/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":323,"digest":"92558948637f4349e0f327350ddebdd225313e582f08aacf6cb5549d03fc9a8b","integrity":"sha256-klWJSGN/Q0ng8yc1Dd690iUxPlgvCKrPbLVUnQP8mos="},"tinymce/plugins/image/plugin-1c01f4fb87c7523d1ae9bfd7018d883601b59c40edc79c552ef39e8374cb9313.js":{"logical_path":"tinymce/plugins/image/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":8205,"digest":"1c01f4fb87c7523d1ae9bfd7018d883601b59c40edc79c552ef39e8374cb9313","integrity":"sha256-HAH0+4fHUj0a6b/XAY2INgG1nEDtx5xVLvOeg3TLkxM="},"tinymce/plugins/imagetools/plugin-78f93669c67ad2579caa99f1069d5ac7f5dc97af9be40d7b55c036dbe650574f.js":{"logical_path":"tinymce/plugins/imagetools/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":30907,"digest":"78f93669c67ad2579caa99f1069d5ac7f5dc97af9be40d7b55c036dbe650574f","integrity":"sha256-ePk2acZ60lecqpnxBp1ax/Xcl6+b5A17VcA22+ZQV08="},"tinymce/plugins/importcss/plugin-31acbbaaa49b20377e4f5d5a830d9e09babddb9a1c5b7978b843098176e318b5.js":{"logical_path":"tinymce/plugins/importcss/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":2749,"digest":"31acbbaaa49b20377e4f5d5a830d9e09babddb9a1c5b7978b843098176e318b5","integrity":"sha256-May7qqSbIDd+T11agw2eCbq925ocW3l4uEMJgXbjGLU="},"tinymce/plugins/insertdatetime/plugin-923510af4de6343c5ea6b8e679dbaa87d27a7abdaf84ac1f0000ad5722787577.js":{"logical_path":"tinymce/plugins/insertdatetime/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1972,"digest":"923510af4de6343c5ea6b8e679dbaa87d27a7abdaf84ac1f0000ad5722787577","integrity":"sha256-kjUQr03mNDxeprjmeduqh9J6er2vhKwfAACtVyJ4dXc="},"tinymce/plugins/legacyoutput/plugin-ac8239571ea6699f14a76058ea9e53a2a5d9b7e84bed3446d7bd4137b8cdac9f.js":{"logical_path":"tinymce/plugins/legacyoutput/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":3264,"digest":"ac8239571ea6699f14a76058ea9e53a2a5d9b7e84bed3446d7bd4137b8cdac9f","integrity":"sha256-rII5Vx6maZ8Up2BY6p5ToqXZt+hL7TRG171BN7jNrJ8="},"tinymce/plugins/link/plugin-e38346a4005bc814b0117a2dae6eb04ea9a7a8865d658afce406a8394fec0577.js":{"logical_path":"tinymce/plugins/link/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":6995,"digest":"e38346a4005bc814b0117a2dae6eb04ea9a7a8865d658afce406a8394fec0577","integrity":"sha256-44NGpABbyBSwEXotrm6wTqmnqIZdZYr85AaoOU/sBXc="},"tinymce/plugins/lists/plugin-69b25b69974c739637d6459489642fc95bddc8dc564f2b482d7ec94f3082f935.js":{"logical_path":"tinymce/plugins/lists/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":10332,"digest":"69b25b69974c739637d6459489642fc95bddc8dc564f2b482d7ec94f3082f935","integrity":"sha256-abJbaZdMc5Y31kWUiWQvyVvdyNxWTytILX7JTzCC+TU="},"tinymce/plugins/media/plugin-deedd7c7d99c7401ab698f65a1e299819b487e52e662c7866f8700ba8d67a351.js":{"logical_path":"tinymce/plugins/media/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":16223,"digest":"deedd7c7d99c7401ab698f65a1e299819b487e52e662c7866f8700ba8d67a351","integrity":"sha256-3u3Xx9mcdAGraY9loeKZgZtIflLmYseGb4cAuo1no1E="},"tinymce/plugins/nonbreaking/plugin-4b15567cb04f551924ca87994514a09fef17c2c6ea3bb6d50e8ca65dc3243b93.js":{"logical_path":"tinymce/plugins/nonbreaking/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":656,"digest":"4b15567cb04f551924ca87994514a09fef17c2c6ea3bb6d50e8ca65dc3243b93","integrity":"sha256-SxVWfLBPVRkkyoeZRRSgn+8XwsbqO7bVDoymXcMkO5M="},"tinymce/plugins/noneditable/plugin-245b5d236dfea414326bdbcd601333f54d5c2b30a0133cbcbbbae329306b0df3.js":{"logical_path":"tinymce/plugins/noneditable/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1271,"digest":"245b5d236dfea414326bdbcd601333f54d5c2b30a0133cbcbbbae329306b0df3","integrity":"sha256-JFtdI23+pBQya9vNYBMz9U1cKzCgEzy8u7rjKTBrDfM="},"tinymce/plugins/pagebreak/plugin-4c8f35adfd6fb570639318219a4d2ad0e8850619f37ff322ad101683ec00829a.js":{"logical_path":"tinymce/plugins/pagebreak/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1231,"digest":"4c8f35adfd6fb570639318219a4d2ad0e8850619f37ff322ad101683ec00829a","integrity":"sha256-TI81rf1vtXBjkxghmk0q0OiFBhnzf/MirRAWg+wAgpo="},"tinymce/plugins/paste/plugin-f7d8cf6dd7bd24908d5c58c664a54cbe4838c542ec26e35f3c4b198f53f98291.js":{"logical_path":"tinymce/plugins/paste/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":17685,"digest":"f7d8cf6dd7bd24908d5c58c664a54cbe4838c542ec26e35f3c4b198f53f98291","integrity":"sha256-99jPbde9JJCNXFjGZKVMvkg4xULsJuNfPEsZj1P5gpE="},"tinymce/plugins/preview/plugin-d0b7e78697d7a21e237e170bbb0a9c95c40b8dc0118a5ccb6a8347eba1800ade.js":{"logical_path":"tinymce/plugins/preview/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1603,"digest":"d0b7e78697d7a21e237e170bbb0a9c95c40b8dc0118a5ccb6a8347eba1800ade","integrity":"sha256-0LfnhpfXoh4jfhcLuwqclcQLjcARilzLaoNH66GACt4="},"tinymce/plugins/print/plugin-07acb7cbdb293f598245755b4a5a142b9d7b78f6b7a6268e3a447b8941f64472.js":{"logical_path":"tinymce/plugins/print/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":294,"digest":"07acb7cbdb293f598245755b4a5a142b9d7b78f6b7a6268e3a447b8941f64472","integrity":"sha256-B6y3y9spP1mCRXVbSloUK517ePa3piaOOkR7iUH2RHI="},"tinymce/plugins/save/plugin-ef4b15573e862995e82aadd8ede1117ee36a05b335a74640804e24dbca5ebac1.js":{"logical_path":"tinymce/plugins/save/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1151,"digest":"ef4b15573e862995e82aadd8ede1117ee36a05b335a74640804e24dbca5ebac1","integrity":"sha256-70sVVz6GKZXoKq3Y7eERfuNqBbM1p0ZAgE4k28peusE="},"tinymce/plugins/searchreplace/plugin-e1f614dbe20b45955a82405a9aa38c415ab49288d74d0daf881db666eb5549c6.js":{"logical_path":"tinymce/plugins/searchreplace/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":6494,"digest":"e1f614dbe20b45955a82405a9aa38c415ab49288d74d0daf881db666eb5549c6","integrity":"sha256-4fYU2+ILRZVagkBamqOMQVq0kojXTQ2viB22ZutVScY="},"tinymce/plugins/spellchecker/plugin-8c198efb4ebcd3ae3f53b2906bed0265f883bd3fca5f8a30880aac630f4eb787.js":{"logical_path":"tinymce/plugins/spellchecker/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":10049,"digest":"8c198efb4ebcd3ae3f53b2906bed0265f883bd3fca5f8a30880aac630f4eb787","integrity":"sha256-jBmO+068064/U7KQa+0CZfiDvT/KX4owiAqsYw9Ot4c="},"tinymce/plugins/tabfocus/plugin-4b947a94b9cc31d699fed7bb19d2efd9b62fbbd75f0858040bca4ba6402113a0.js":{"logical_path":"tinymce/plugins/tabfocus/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1265,"digest":"4b947a94b9cc31d699fed7bb19d2efd9b62fbbd75f0858040bca4ba6402113a0","integrity":"sha256-S5R6lLnMMdaZ/te7GdLv2bYvu9dfCFgEC8pLpkAhE6A="},"tinymce/plugins/table/plugin-5f3b35988457fcc9d2c2c35601acb041b7b59246dc16d061433cf741cba9ca26.js":{"logical_path":"tinymce/plugins/table/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":46153,"digest":"5f3b35988457fcc9d2c2c35601acb041b7b59246dc16d061433cf741cba9ca26","integrity":"sha256-Xzs1mIRX/MnSwsNWAaywQbe1kkbcFtBhQzz3QcupyiY="},"tinymce/plugins/template/plugin-3a7a1a203ced83abdb7f36897c974eed07a876be4f54809e06ad5b17ec957222.js":{"logical_path":"tinymce/plugins/template/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":4521,"digest":"3a7a1a203ced83abdb7f36897c974eed07a876be4f54809e06ad5b17ec957222","integrity":"sha256-OnoaIDztg6vbfzaJfJdO7Qeodr5PVICeBq1bF+yVciI="},"tinymce/plugins/textcolor/plugin-d05a9d9a6fbdcca3f07c45e6ef1ba8913f48c68a68a871343cdce2075c0ef0b6.js":{"logical_path":"tinymce/plugins/textcolor/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":4146,"digest":"d05a9d9a6fbdcca3f07c45e6ef1ba8913f48c68a68a871343cdce2075c0ef0b6","integrity":"sha256-0Fqdmm+9zKPwfEXm7xuokT9IxopoqHE0PNziB1wO8LY="},"tinymce/plugins/textpattern/plugin-d630f2bc3205e400af41041eb224fe793ae950de5d5f4732f63e36f6819d3f35.js":{"logical_path":"tinymce/plugins/textpattern/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":2746,"digest":"d630f2bc3205e400af41041eb224fe793ae950de5d5f4732f63e36f6819d3f35","integrity":"sha256-1jDyvDIF5ACvQQQesiT+eTrpUN5dX0cy9j429oGdPzU="},"tinymce/plugins/toc/plugin-c20dc2987d4fcd8f859871ec0f2988f3c819c373d2fadad8c5b923b9b749d0ef.js":{"logical_path":"tinymce/plugins/toc/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":2772,"digest":"c20dc2987d4fcd8f859871ec0f2988f3c819c373d2fadad8c5b923b9b749d0ef","integrity":"sha256-wg3CmH1PzY+FmHHsDymI88gZw3PS+trYxbkjubdJ0O8="},"tinymce/plugins/visualblocks/css/visualblocks-e44e30ebe30e4d3763425a9ea38b558ff13106b8e16a30a74ae21b4c174b3417.css":{"logical_path":"tinymce/plugins/visualblocks/css/visualblocks.css","mtime":"2017-01-05T19:24:14+01:00","size":4767,"digest":"e44e30ebe30e4d3763425a9ea38b558ff13106b8e16a30a74ae21b4c174b3417","integrity":"sha256-5E4w6+MOTTdjQlqeo4tVj/ExBrjhajCnSuIbTBdLNBc="},"tinymce/plugins/visualblocks/plugin-6cdc811487cbbee106512812da68f1999fcb3db14f99c5cb4a070be578c9bb0a.js":{"logical_path":"tinymce/plugins/visualblocks/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1154,"digest":"6cdc811487cbbee106512812da68f1999fcb3db14f99c5cb4a070be578c9bb0a","integrity":"sha256-bNyBFIfLvuEGUSgS2mjxmZ/LPbFPmcXLSgcL5XjJuwo="},"tinymce/plugins/visualchars/plugin-0e8c1d7cba0b1471014fe3359a04ff4d4d981070777cdf31f4d95a732b83581f.js":{"logical_path":"tinymce/plugins/visualchars/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":1179,"digest":"0e8c1d7cba0b1471014fe3359a04ff4d4d981070777cdf31f4d95a732b83581f","integrity":"sha256-DowdfLoLFHEBT+M1mgT/TU2YEHB3fN8x9NlacyuDWB8="},"tinymce/plugins/wordcount/plugin-d9ec660425815b227f5c3fbdc8b5d066a0530ec824ba37b47169aabd3f864ea7.js":{"logical_path":"tinymce/plugins/wordcount/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":12048,"digest":"d9ec660425815b227f5c3fbdc8b5d066a0530ec824ba37b47169aabd3f864ea7","integrity":"sha256-2exmBCWBWyJ/XD+9yLXQZqBTDsgkuje0cWmqvT+GTqc="},"tinymce/skins/lightgray/content.inline.min-5bb5c69d072a2d17d11e0c54bc81b6f863e5b5a126060d862970f0c22f5d49ec.css":{"logical_path":"tinymce/skins/lightgray/content.inline.min.css","mtime":"2017-01-05T19:24:14+01:00","size":2770,"digest":"5bb5c69d072a2d17d11e0c54bc81b6f863e5b5a126060d862970f0c22f5d49ec","integrity":"sha256-W7XGnQcqLRfRHgxUvIG2+GPltaEmBg2GKXDwwi9dSew="},"tinymce/skins/lightgray/content.min-92e6da84fb5c2dfcc35f34d311ca1914b19064ada45fa9a08ff2a68c0fc0e657.css":{"logical_path":"tinymce/skins/lightgray/content.min.css","mtime":"2017-01-05T19:24:14+01:00","size":3194,"digest":"92e6da84fb5c2dfcc35f34d311ca1914b19064ada45fa9a08ff2a68c0fc0e657","integrity":"sha256-kubahPtcLfzDXzTTEcoZFLGQZK2kX6mgj/KmjA/A5lc="},"tinymce/skins/lightgray/fonts/tinymce-small-a10fc4343d95b716c16d77463d475be5c079599ea67e1cd2bd3a94d5e7f508f9.eot":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.eot","mtime":"2017-01-05T19:24:14+01:00","size":9492,"digest":"a10fc4343d95b716c16d77463d475be5c079599ea67e1cd2bd3a94d5e7f508f9","integrity":"sha256-oQ/END2VtxbBbXdGPUdb5cB5WZ6mfhzSvTqU1ef1CPk="},"tinymce/skins/lightgray/fonts/tinymce-small-e7773001446ab937e1d8d4bd5e8dbd9b31d112037353a14b319e36dd010ed8ee.svg":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.svg","mtime":"2017-01-05T19:24:14+01:00","size":24727,"digest":"e7773001446ab937e1d8d4bd5e8dbd9b31d112037353a14b319e36dd010ed8ee","integrity":"sha256-53cwAURquTfh2NS9Xo29mzHREgNzU6FLMZ423QEO2O4="},"tinymce/skins/lightgray/fonts/tinymce-small-2f657502906d6f5c3fc8df3a82969114ebe030addfdc061c60c974b0f515fd09.ttf":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.ttf","mtime":"2017-01-05T19:24:14+01:00","size":9304,"digest":"2f657502906d6f5c3fc8df3a82969114ebe030addfdc061c60c974b0f515fd09","integrity":"sha256-L2V1ApBtb1w/yN86gpaRFOvgMK3f3AYcYMl0sPUV/Qk="},"tinymce/skins/lightgray/fonts/tinymce-small-d3efbb678ca6de5632902bd93772746ba2f8e4e2322b953936e12694a183aa31.woff":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.woff","mtime":"2017-01-05T19:24:14+01:00","size":9380,"digest":"d3efbb678ca6de5632902bd93772746ba2f8e4e2322b953936e12694a183aa31","integrity":"sha256-0++7Z4ym3lYykCvZN3J0a6L45OIyK5U5NuEmlKGDqjE="},"tinymce/skins/lightgray/fonts/tinymce-2e9c4a68fde992476e0db9e44128cb1f2e898f0de0b80f552a8acb52bb7ca0db.eot":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.eot","mtime":"2017-01-05T19:24:14+01:00","size":17572,"digest":"2e9c4a68fde992476e0db9e44128cb1f2e898f0de0b80f552a8acb52bb7ca0db","integrity":"sha256-LpxKaP3pkkduDbnkQSjLHy6Jjw3guA9VKorLUrt8oNs="},"tinymce/skins/lightgray/fonts/tinymce-2094ddadc265c7f33570475fc78ef7adcdcb814e49060d17f5b4c4f8d1cb7ec6.svg":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.svg","mtime":"2017-01-05T19:24:14+01:00","size":45991,"digest":"2094ddadc265c7f33570475fc78ef7adcdcb814e49060d17f5b4c4f8d1cb7ec6","integrity":"sha256-IJTdrcJlx/M1cEdfx473rc3LgU5JBg0X9bTE+NHLfsY="},"tinymce/skins/lightgray/fonts/tinymce-477ea2d46c1a975dd492af4c10235fabfd09069595779cce00ea0381ca9b4a20.ttf":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.ttf","mtime":"2017-01-05T19:24:14+01:00","size":17408,"digest":"477ea2d46c1a975dd492af4c10235fabfd09069595779cce00ea0381ca9b4a20","integrity":"sha256-R36i1Gwal13Ukq9MECNfq/0JBpWVd5zOAOoDgcqbSiA="},"tinymce/skins/lightgray/fonts/tinymce-1ebc636bb24cbea637946ba8c22cbf4f35d8343ba9763045d2aee59e3714ae78.woff":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.woff","mtime":"2017-01-05T19:24:14+01:00","size":17484,"digest":"1ebc636bb24cbea637946ba8c22cbf4f35d8343ba9763045d2aee59e3714ae78","integrity":"sha256-Hrxja7JMvqY3lGuowiy/TzXYNDupdjBF0q7lnjcUrng="},"tinymce/skins/lightgray/img/anchor-2861666fd107d278d4449970615136d06d7f746be9bb19072cf9c8f30e565e1e.gif":{"logical_path":"tinymce/skins/lightgray/img/anchor.gif","mtime":"2017-01-05T19:24:14+01:00","size":53,"digest":"2861666fd107d278d4449970615136d06d7f746be9bb19072cf9c8f30e565e1e","integrity":"sha256-KGFmb9EH0njURJlwYVE20G1/dGvpuxkHLPnI8w5WXh4="},"tinymce/skins/lightgray/img/loader-eb7cfd3d959b2e09c170f532e29f8b825f9bc770b2279fde58e595617753e244.gif":{"logical_path":"tinymce/skins/lightgray/img/loader.gif","mtime":"2017-01-05T19:24:14+01:00","size":2608,"digest":"eb7cfd3d959b2e09c170f532e29f8b825f9bc770b2279fde58e595617753e244","integrity":"sha256-63z9PZWbLgnBcPUy4p+Lgl+bx3CyJ5/eWOWVYXdT4kQ="},"tinymce/skins/lightgray/img/object-e6a15e52bc4a17b085073ba8debd4708ead6ae3d4cbeb3880c65cb7afc489777.gif":{"logical_path":"tinymce/skins/lightgray/img/object.gif","mtime":"2017-01-05T19:24:14+01:00","size":152,"digest":"e6a15e52bc4a17b085073ba8debd4708ead6ae3d4cbeb3880c65cb7afc489777","integrity":"sha256-5qFeUrxKF7CFBzuo3r1HCOrWrj1MvrOIDGXLevxIl3c="},"tinymce/skins/lightgray/img/trans-9cf020d7c3bba7f5ab10cda54aabef934f906d4f9a3acf99e9e7dc6c98579635.gif":{"logical_path":"tinymce/skins/lightgray/img/trans.gif","mtime":"2017-01-05T19:24:14+01:00","size":43,"digest":"9cf020d7c3bba7f5ab10cda54aabef934f906d4f9a3acf99e9e7dc6c98579635","integrity":"sha256-nPAg18O7p/WrEM2lSqvvk0+QbU+aOs+Z6efcbJhXljU="},"tinymce/skins/lightgray/skin.ie7.min-f7e68049f5f1ba278870818429ef905d07846781701c40657d9d24ca6638cced.css":{"logical_path":"tinymce/skins/lightgray/skin.ie7.min.css","mtime":"2017-01-05T19:24:14+01:00","size":34904,"digest":"f7e68049f5f1ba278870818429ef905d07846781701c40657d9d24ca6638cced","integrity":"sha256-9+aASfXxuieIcIGEKe+QXQeEZ4FwHEBlfZ0kymY4zO0="},"tinymce/skins/lightgray/skin.min-6d81e56f6d40695f114abe4db834ccb7bf7d89cfa07c10c2cbaebb8066ace27b.css":{"logical_path":"tinymce/skins/lightgray/skin.min.css","mtime":"2017-01-05T19:24:14+01:00","size":38232,"digest":"6d81e56f6d40695f114abe4db834ccb7bf7d89cfa07c10c2cbaebb8066ace27b","integrity":"sha256-bYHlb21AaV8RSr5NuDTMt799ic+gfBDCy667gGas4ns="},"tinymce/themes/inlite/theme-95446aa7daedd3754400947ecc50a607971ef2068d093e7c9a889c5908ff6f40.js":{"logical_path":"tinymce/themes/inlite/theme.js","mtime":"2017-01-05T19:24:14+01:00","size":16382,"digest":"95446aa7daedd3754400947ecc50a607971ef2068d093e7c9a889c5908ff6f40","integrity":"sha256-lURqp9rt03VEAJR+zFCmB5ce8gaNCT58moicWQj/b0A="},"tinymce/themes/modern/theme-f0a3cccc241fa5d46bde8e19160e3f351368881a7c3cdb0c4ce84da11901064c.js":{"logical_path":"tinymce/themes/modern/theme.js","mtime":"2017-01-05T19:24:14+01:00","size":13155,"digest":"f0a3cccc241fa5d46bde8e19160e3f351368881a7c3cdb0c4ce84da11901064c","integrity":"sha256-8KPMzCQfpdRr3o4ZFg4/NRNoiBp8PNsMTOhNoRkBBkw="}},"assets":{"active_admin.css":"active_admin-5493c4a52f825b68a247f214b88addaf61bada07aefa1441589c72d8d81f8b12.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-9238692e361f8ed7317fd8414cfadeff93c57b9a6ed3fd35cfe9a3fe4ddc3829.js","layers-2x.png":"layers-2x-066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf.png","layers.png":"layers-1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6.png","marker-icon-2x.png":"marker-icon-2x-2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d.png","marker-icon.png":"marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png","marker-shadow.png":"marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png","tinymce.js":"tinymce-61f3d9ea006866183d7b56bbc9ecf39545adcc8a1a3b58566ea14ea52db8b902.js","application.css":"application-bd35f325ad91254feab551b82972c18f9d928b5016ff20ee040c3380f9c95a7c.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-9fcdd7ddd4d40de29c3809b59688c668b85f5628e219d4cd8a8810b72a64533b.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-a9c6770a672e537abdfdbea5f842747b5d0b0d402d9216a5f1bb789f051ba10e.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-bb0e93a050a32df7913e4026b3c88a176998e0e3e073ba06e9b73f6c24227c9c.gif","tinymce/plugins/emoticons/img/smiley-cry.gif":"tinymce/plugins/emoticons/img/smiley-cry-a0c5f3e7a682449c973c9d9f7c46342081c46920686d2353f57aff91ab907f68.gif","tinymce/plugins/emoticons/img/smiley-embarassed.gif":"tinymce/plugins/emoticons/img/smiley-embarassed-d3cafcb50b335672cb5e9f4600ea9ea261dac7828dd28844d4927c393a25618f.gif","tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif":"tinymce/plugins/emoticons/img/smiley-foot-in-mouth-03fe04d3ed533423ac81f05146584b0c451be3d4a30e76687ceef283ed07071f.gif","tinymce/plugins/emoticons/img/smiley-frown.gif":"tinymce/plugins/emoticons/img/smiley-frown-1b984bf98931dd1debb54461eb9d83e985f2b2999fe14bcb556d6c0921bc83b0.gif","tinymce/plugins/emoticons/img/smiley-innocent.gif":"tinymce/plugins/emoticons/img/smiley-innocent-8db353ef102196f2c6ddf5c4666446de955d7b14fc0957c806c9dbfb48fb0c29.gif","tinymce/plugins/emoticons/img/smiley-kiss.gif":"tinymce/plugins/emoticons/img/smiley-kiss-3154c3665356c13ab10fefdbac1fe187fff978a0052037c99cdc4a97103413f2.gif","tinymce/plugins/emoticons/img/smiley-laughing.gif":"tinymce/plugins/emoticons/img/smiley-laughing-8f6adedcd091975ffead171867a6304d908bb6541a6ccb4919286ec6b7d4551e.gif","tinymce/plugins/emoticons/img/smiley-money-mouth.gif":"tinymce/plugins/emoticons/img/smiley-money-mouth-f0b9f4f22e237f5dbc851f900fed8d7eca4c954ae6fbc606c0cd8be431d0ac80.gif","tinymce/plugins/emoticons/img/smiley-sealed.gif":"tinymce/plugins/emoticons/img/smiley-sealed-9933b442636b6e537df7b564e2c3f7a2873526eea6b022a98eb1e468e5204c32.gif","tinymce/plugins/emoticons/img/smiley-smile.gif":"tinymce/plugins/emoticons/img/smiley-smile-fd89cd460ffcacb7e725e00c0275ef5b3924ce468248e5ff4fb43545571cfa65.gif","tinymce/plugins/emoticons/img/smiley-surprised.gif":"tinymce/plugins/emoticons/img/smiley-surprised-3871f356cb41976d7ae8a5f005e8739e4d014352a8adef9b33f773d81b6e6c01.gif","tinymce/plugins/emoticons/img/smiley-tongue-out.gif":"tinymce/plugins/emoticons/img/smiley-tongue-out-5843c85667a8226dc43be83749fd9fbbc5d20b1577de2b763915d99815d37d47.gif","tinymce/plugins/emoticons/img/smiley-undecided.gif":"tinymce/plugins/emoticons/img/smiley-undecided-d8b9bcbb433951ff3c4ca8dd959ac3844239b98e6d52218833e1485a91f67347.gif","tinymce/plugins/emoticons/img/smiley-wink.gif":"tinymce/plugins/emoticons/img/smiley-wink-2af75ad7b1c08488505513503e34b15f40005e04a2a9568f698f0945d2d8ba1f.gif","tinymce/plugins/emoticons/img/smiley-yell.gif":"tinymce/plugins/emoticons/img/smiley-yell-bba903fbcb46fce8c68b9e01863fd095b3b1d0e6aa72161f3a88d762a5f90a79.gif","tinymce/skins/lightgray/img/anchor.gif":"tinymce/skins/lightgray/img/anchor-2861666fd107d278d4449970615136d06d7f746be9bb19072cf9c8f30e565e1e.gif","tinymce/skins/lightgray/img/loader.gif":"tinymce/skins/lightgray/img/loader-eb7cfd3d959b2e09c170f532e29f8b825f9bc770b2279fde58e595617753e244.gif","tinymce/skins/lightgray/img/object.gif":"tinymce/skins/lightgray/img/object-e6a15e52bc4a17b085073ba8debd4708ead6ae3d4cbeb3880c65cb7afc489777.gif","tinymce/skins/lightgray/img/trans.gif":"tinymce/skins/lightgray/img/trans-9cf020d7c3bba7f5ab10cda54aabef934f906d4f9a3acf99e9e7dc6c98579635.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-275e24af4bf53bfb60f7fef218163106ad0648b8ad384ecfab9b4fd52f48603d.js","tinymce/langs/readme.md":"tinymce/langs/readme-5a8b6a04d57b5c88e3fb7f2a870b8e2d3a48ec03ce6474206c41df78c155b2de.md","tinymce/license.txt":"tinymce/license-5fda611b8191f00121f161a93e7399cdb71789dba923e8ed09b50a1f78d32c5e.txt","tinymce/plugins/advlist/plugin.js":"tinymce/plugins/advlist/plugin-d0bd2b90c1aaf60ddbb048d955f7fbf76e303b5f3227f8e4dc83207548c3fc46.js","tinymce/plugins/anchor/plugin.js":"tinymce/plugins/anchor/plugin-48c211e97cdf2fba4e8456ddc0464c301c61ff2d2751aa178d41c5eca38fbbe9.js","tinymce/plugins/autolink/plugin.js":"tinymce/plugins/autolink/plugin-9d514baa6f3816a43008241db075d724fcb274632fad3db792e87e5feb6c5d0f.js","tinymce/plugins/autoresize/plugin.js":"tinymce/plugins/autoresize/plugin-7cd05d5431f8713c948291ad40ef63eabe44908bd831d5675afacaca296a4d7b.js","tinymce/plugins/autosave/plugin.js":"tinymce/plugins/autosave/plugin-43448ce025b165ed1b509554cadf02cda4ed104ccfae6d0708379f02e981220f.js","tinymce/plugins/bbcode/plugin.js":"tinymce/plugins/bbcode/plugin-dae2432b6477ca1acc3bd6ac7af33c379797c7f2dcf2c82513475d4c234d851d.js","tinymce/plugins/charmap/plugin.js":"tinymce/plugins/charmap/plugin-ee3f9ed5d0135f19975a4609701b4b4546f00ab1afee9b74a38bb3d76ab94eca.js","tinymce/plugins/code/plugin.js":"tinymce/plugins/code/plugin-093b2519070297197c89cabd2b7bf7b7920786fa3ae5b055322fb16d51d113d4.js","tinymce/plugins/codesample/css/prism.css":"tinymce/plugins/codesample/css/prism-1988b66704b4d23e78c6c20c38a6856cbc1f0be96d6d60a3a0b12f4408f1057e.css","tinymce/plugins/codesample/plugin.dev.js":"tinymce/plugins/codesample/plugin.dev.js","tinymce/plugins/codesample/plugin.js":"tinymce/plugins/codesample/plugin-edd0d2e7f821002843d7726182d9a8b1520fcabb753b95ee7956180337be23da.js","tinymce/plugins/colorpicker/plugin.js":"tinymce/plugins/colorpicker/plugin-526e08119d96ecdeeb21c1031cba4b471aba860fbc6ed0f8a010275088c00531.js","tinymce/plugins/contextmenu/plugin.js":"tinymce/plugins/contextmenu/plugin-11ad7a2ff14cc47d12a3dd0170741590335e606b0efaa8dc77837409e54afa0a.js","tinymce/plugins/directionality/plugin.js":"tinymce/plugins/directionality/plugin-6381fb028625726e50977d00f342fa1f60e39dc794e0ea1c7d9feff8846ab9fc.js","tinymce/plugins/emoticons/plugin.js":"tinymce/plugins/emoticons/plugin-75971da62ade77af7e79b3a560c5ad5ac20ee5f88c7ba236e03902777fdec715.js","tinymce/plugins/example/dialog.html":"tinymce/plugins/example/dialog-5c61ad829a494e75c2234019c7941f6dd096ec693cf5a5538c0bf30485097841.html","tinymce/plugins/example/plugin.js":"tinymce/plugins/example/plugin-505b7ee82929e7338700605a4ca63d94dbe6ef2ce2d21104cc01c2adba829868.js","tinymce/plugins/example_dependency/plugin.js":"tinymce/plugins/example_dependency/plugin-9bb52c9d45c0f7e4d67c12f546c68095b2d5284aa84ab3a2c45f1d986f464653.js","tinymce/plugins/fullpage/plugin.js":"tinymce/plugins/fullpage/plugin-6cd62d14e4225c1db1d9008a485143d8e8b7a7c699e2ceec16a8012b44875ef5.js","tinymce/plugins/fullscreen/plugin.js":"tinymce/plugins/fullscreen/plugin-972d5e04901e74c8c657ee88fb3cc245b94aeb005d54adeac00c07a0c5700f7a.js","tinymce/plugins/hr/plugin.js":"tinymce/plugins/hr/plugin-92558948637f4349e0f327350ddebdd225313e582f08aacf6cb5549d03fc9a8b.js","tinymce/plugins/image/plugin.js":"tinymce/plugins/image/plugin-1c01f4fb87c7523d1ae9bfd7018d883601b59c40edc79c552ef39e8374cb9313.js","tinymce/plugins/imagetools/plugin.js":"tinymce/plugins/imagetools/plugin-78f93669c67ad2579caa99f1069d5ac7f5dc97af9be40d7b55c036dbe650574f.js","tinymce/plugins/importcss/plugin.js":"tinymce/plugins/importcss/plugin-31acbbaaa49b20377e4f5d5a830d9e09babddb9a1c5b7978b843098176e318b5.js","tinymce/plugins/insertdatetime/plugin.js":"tinymce/plugins/insertdatetime/plugin-923510af4de6343c5ea6b8e679dbaa87d27a7abdaf84ac1f0000ad5722787577.js","tinymce/plugins/layer/plugin.js":"tinymce/plugins/layer/plugin.js","tinymce/plugins/legacyoutput/plugin.js":"tinymce/plugins/legacyoutput/plugin-ac8239571ea6699f14a76058ea9e53a2a5d9b7e84bed3446d7bd4137b8cdac9f.js","tinymce/plugins/link/plugin.js":"tinymce/plugins/link/plugin-e38346a4005bc814b0117a2dae6eb04ea9a7a8865d658afce406a8394fec0577.js","tinymce/plugins/lists/plugin.js":"tinymce/plugins/lists/plugin-69b25b69974c739637d6459489642fc95bddc8dc564f2b482d7ec94f3082f935.js","tinymce/plugins/media/plugin.js":"tinymce/plugins/media/plugin-deedd7c7d99c7401ab698f65a1e299819b487e52e662c7866f8700ba8d67a351.js","tinymce/plugins/nonbreaking/plugin.js":"tinymce/plugins/nonbreaking/plugin-4b15567cb04f551924ca87994514a09fef17c2c6ea3bb6d50e8ca65dc3243b93.js","tinymce/plugins/noneditable/plugin.js":"tinymce/plugins/noneditable/plugin-245b5d236dfea414326bdbcd601333f54d5c2b30a0133cbcbbbae329306b0df3.js","tinymce/plugins/pagebreak/plugin.js":"tinymce/plugins/pagebreak/plugin-4c8f35adfd6fb570639318219a4d2ad0e8850619f37ff322ad101683ec00829a.js","tinymce/plugins/paste/plugin.dev.js":"tinymce/plugins/paste/plugin.dev.js","tinymce/plugins/paste/plugin.js":"tinymce/plugins/paste/plugin-f7d8cf6dd7bd24908d5c58c664a54cbe4838c542ec26e35f3c4b198f53f98291.js","tinymce/plugins/preview/plugin.js":"tinymce/plugins/preview/plugin-d0b7e78697d7a21e237e170bbb0a9c95c40b8dc0118a5ccb6a8347eba1800ade.js","tinymce/plugins/print/plugin.js":"tinymce/plugins/print/plugin-07acb7cbdb293f598245755b4a5a142b9d7b78f6b7a6268e3a447b8941f64472.js","tinymce/plugins/save/plugin.js":"tinymce/plugins/save/plugin-ef4b15573e862995e82aadd8ede1117ee36a05b335a74640804e24dbca5ebac1.js","tinymce/plugins/searchreplace/plugin.js":"tinymce/plugins/searchreplace/plugin-e1f614dbe20b45955a82405a9aa38c415ab49288d74d0daf881db666eb5549c6.js","tinymce/plugins/spellchecker/plugin.dev.js":"tinymce/plugins/spellchecker/plugin.dev.js","tinymce/plugins/spellchecker/plugin.js":"tinymce/plugins/spellchecker/plugin-8c198efb4ebcd3ae3f53b2906bed0265f883bd3fca5f8a30880aac630f4eb787.js","tinymce/plugins/tabfocus/plugin.js":"tinymce/plugins/tabfocus/plugin-4b947a94b9cc31d699fed7bb19d2efd9b62fbbd75f0858040bca4ba6402113a0.js","tinymce/plugins/table/plugin.dev.js":"tinymce/plugins/table/plugin.dev.js","tinymce/plugins/table/plugin.js":"tinymce/plugins/table/plugin-5f3b35988457fcc9d2c2c35601acb041b7b59246dc16d061433cf741cba9ca26.js","tinymce/plugins/template/plugin.js":"tinymce/plugins/template/plugin-3a7a1a203ced83abdb7f36897c974eed07a876be4f54809e06ad5b17ec957222.js","tinymce/plugins/textcolor/plugin.js":"tinymce/plugins/textcolor/plugin-d05a9d9a6fbdcca3f07c45e6ef1ba8913f48c68a68a871343cdce2075c0ef0b6.js","tinymce/plugins/textpattern/plugin.js":"tinymce/plugins/textpattern/plugin-d630f2bc3205e400af41041eb224fe793ae950de5d5f4732f63e36f6819d3f35.js","tinymce/plugins/toc/plugin.js":"tinymce/plugins/toc/plugin-c20dc2987d4fcd8f859871ec0f2988f3c819c373d2fadad8c5b923b9b749d0ef.js","tinymce/plugins/visualblocks/css/visualblocks.css":"tinymce/plugins/visualblocks/css/visualblocks-e44e30ebe30e4d3763425a9ea38b558ff13106b8e16a30a74ae21b4c174b3417.css","tinymce/plugins/visualblocks/plugin.js":"tinymce/plugins/visualblocks/plugin-6cdc811487cbbee106512812da68f1999fcb3db14f99c5cb4a070be578c9bb0a.js","tinymce/plugins/visualchars/plugin.js":"tinymce/plugins/visualchars/plugin-0e8c1d7cba0b1471014fe3359a04ff4d4d981070777cdf31f4d95a732b83581f.js","tinymce/plugins/wordcount/plugin.js":"tinymce/plugins/wordcount/plugin-d9ec660425815b227f5c3fbdc8b5d066a0530ec824ba37b47169aabd3f864ea7.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-5bb5c69d072a2d17d11e0c54bc81b6f863e5b5a126060d862970f0c22f5d49ec.css","tinymce/skins/lightgray/content.min.css":"tinymce/skins/lightgray/content.min-92e6da84fb5c2dfcc35f34d311ca1914b19064ada45fa9a08ff2a68c0fc0e657.css","tinymce/skins/lightgray/fonts/tinymce-small.eot":"tinymce/skins/lightgray/fonts/tinymce-small-a10fc4343d95b716c16d77463d475be5c079599ea67e1cd2bd3a94d5e7f508f9.eot","tinymce/skins/lightgray/fonts/tinymce-small.svg":"tinymce/skins/lightgray/fonts/tinymce-small-e7773001446ab937e1d8d4bd5e8dbd9b31d112037353a14b319e36dd010ed8ee.svg","tinymce/skins/lightgray/fonts/tinymce-small.ttf":"tinymce/skins/lightgray/fonts/tinymce-small-2f657502906d6f5c3fc8df3a82969114ebe030addfdc061c60c974b0f515fd09.ttf","tinymce/skins/lightgray/fonts/tinymce-small.woff":"tinymce/skins/lightgray/fonts/tinymce-small-d3efbb678ca6de5632902bd93772746ba2f8e4e2322b953936e12694a183aa31.woff","tinymce/skins/lightgray/fonts/tinymce.eot":"tinymce/skins/lightgray/fonts/tinymce-2e9c4a68fde992476e0db9e44128cb1f2e898f0de0b80f552a8acb52bb7ca0db.eot","tinymce/skins/lightgray/fonts/tinymce.svg":"tinymce/skins/lightgray/fonts/tinymce-2094ddadc265c7f33570475fc78ef7adcdcb814e49060d17f5b4c4f8d1cb7ec6.svg","tinymce/skins/lightgray/fonts/tinymce.ttf":"tinymce/skins/lightgray/fonts/tinymce-477ea2d46c1a975dd492af4c10235fabfd09069595779cce00ea0381ca9b4a20.ttf","tinymce/skins/lightgray/fonts/tinymce.woff":"tinymce/skins/lightgray/fonts/tinymce-1ebc636bb24cbea637946ba8c22cbf4f35d8343ba9763045d2aee59e3714ae78.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-f7e68049f5f1ba278870818429ef905d07846781701c40657d9d24ca6638cced.css","tinymce/skins/lightgray/skin.less":"tinymce/skins/lightgray/skin.less","tinymce/skins/lightgray/skin.min.css":"tinymce/skins/lightgray/skin.min-6d81e56f6d40695f114abe4db834ccb7bf7d89cfa07c10c2cbaebb8066ace27b.css","tinymce/themes/inlite/theme.js":"tinymce/themes/inlite/theme-95446aa7daedd3754400947ecc50a607971ef2068d093e7c9a889c5908ff6f40.js","tinymce/themes/modern/theme.js":"tinymce/themes/modern/theme-f0a3cccc241fa5d46bde8e19160e3f351368881a7c3cdb0c4ce84da11901064c.js","tinymce/tinymce.js":"tinymce/tinymce-6c5174912bf31a8ee031a9acb5d2b7d05f7bcd42e3ed0d26d7d217a5343eb2bf.js","tinymce/langs/ar.js":"tinymce/langs/ar-e47a394dbc461d20aa547fa0c2027a9dad45fe55e18adb0da309045be6ed4109.js","tinymce/langs/ar_SA.js":"tinymce/langs/ar_SA-24b62c76e99e114ae44480a67edbacf74f9fed0ff7afbeacce4966f1bc80333b.js","tinymce/langs/az.js":"tinymce/langs/az-d40cecd6eb423f910f528ae98dcdaba4c2449802621759437174cf8c8c0d5b25.js","tinymce/langs/be.js":"tinymce/langs/be-42e2884c0f2394afa1a17e31e702660eeea0d84618bbd18155cb0313c6eb5f69.js","tinymce/langs/bg_BG.js":"tinymce/langs/bg_BG-2fac9d785519d2791d42881fcc08c6a82e841575785690259d7f64e1b88d7db3.js","tinymce/langs/bn_BD.js":"tinymce/langs/bn_BD-ef48a9094445dfa624af0c35902f60b7949b30585b5e9bc760a225fe2c07cbd3.js","tinymce/langs/bs.js":"tinymce/langs/bs-eb66c6d6910a30b950d7a0e784027ed288e6e5a2c6db7741da3359f2067e2e0e.js","tinymce/langs/ca.js":"tinymce/langs/ca-77ab49d6420318ed7f5fd51b6856bc396c393095bd78ea91dad83e38e5859637.js","tinymce/langs/cs.js":"tinymce/langs/cs-3d7ecd619895e3e4c7e1db06f6526682ef88f22f6e8b50d7b45ed0ae7c206714.js","tinymce/langs/cs_CZ.js":"tinymce/langs/cs_CZ-4b0778d233e200dea350f8361129da2844e23d1c0f2d524aeffa6d34e581368d.js","tinymce/langs/cy.js":"tinymce/langs/cy-a847ff54657ccf76eec362cc14fbc63adb4c67f9b1de023b29d817aba4fec062.js","tinymce/langs/da.js":"tinymce/langs/da-a87f3ae1c442c3bb26b81569687e98072d038269360361ec9728f00d6b17c282.js","tinymce/langs/de.js":"tinymce/langs/de-a10c8f6f1e53da776e7026dffd54ea8ba527492b767e5b54ae5c3974f6953311.js","tinymce/langs/de_AT.js":"tinymce/langs/de_AT-a32eec12ce12b06fc2c8919d2372242e9596712c96dc9bcfff1fc8b7dc458627.js","tinymce/langs/dv.js":"tinymce/langs/dv-182756bf33409a8b76bc684d0a2ab86881718c360d1f1fb3fbc609ce3f5e3f49.js","tinymce/langs/el.js":"tinymce/langs/el-6bbdbf3ccb2b2f22f961eb6749b43fe401c84fd6d89cd3f76a538dc2d06bb071.js","tinymce/langs/en_CA.js":"tinymce/langs/en_CA-1a9457da814b0a1f15687a00f3ae4b38da0301445b52c29a423a38c943d9a431.js","tinymce/langs/en_GB.js":"tinymce/langs/en_GB-a0f7752ff9ddf3ed3d2cd24fee04fe585bca0babc4ce655560c923ad386c0c07.js","tinymce/langs/eo.js":"tinymce/langs/eo-cbac4d7ebcb0679845b852d9354e74586e78cf13eef82de1400cf6b465e203cd.js","tinymce/langs/es.js":"tinymce/langs/es-133cb5bd627c2a299544586bf365859b5a1c96c57d18c90e7853048434a809b6.js","tinymce/langs/es_MX.js":"tinymce/langs/es_MX-352fcdff567919ff09a2e19c56d8059d339bffdbd999bcc82aa6d8340c19bbb9.js","tinymce/langs/et.js":"tinymce/langs/et-2cfc3da0ff39a37f0bd3c896d49604028f231ff0e7d8ebbc30dbfd4b87125f06.js","tinymce/langs/eu.js":"tinymce/langs/eu-8698ae4de236fcd490d0f45d485dbd33d8e9de6eb3db51191e1b4f8ea96a4d06.js","tinymce/langs/fa.js":"tinymce/langs/fa-7eff96595e93d4b0df484dd90c14b7b412966157ed8293f54429e9418882bdb2.js","tinymce/langs/fa_IR.js":"tinymce/langs/fa_IR-2f9d33f16718ae9c1f3642905b58434ed2662fb66448c4d115b8491aa68e7904.js","tinymce/langs/fi.js":"tinymce/langs/fi-6ca2a93d045067a86e8ea973d0b89368054b15eb9b9625bf1ca871608a5cb77c.js","tinymce/langs/fo.js":"tinymce/langs/fo-340609cecd5571e4eacb8fe7bd1343c8553d96d12610fb77d9a812dc6d3635fd.js","tinymce/langs/fr_CH.js":"tinymce/langs/fr_CH-e0f43c0f20727368ff0767f3bf099bc23eed9fe3e81f8bbe514381e786526928.js","tinymce/langs/fr_FR.js":"tinymce/langs/fr_FR-2702de7be93bd1e0d7120ae3c9e637061565186c66886f155ffca0663df25b4c.js","tinymce/langs/ga.js":"tinymce/langs/ga-d2a3de6f28723d75b03f7f42fd3aedfd045d473425ee38f02350b56035383af1.js","tinymce/langs/gd.js":"tinymce/langs/gd-0453e8b97bf3b6cca4065712ec59f20343ad6131735b38547e865177a1c1c490.js","tinymce/langs/gl.js":"tinymce/langs/gl-1ff612222934f558870111f10934110c5656df3bb293c917e4eeceeb6605fcc5.js","tinymce/langs/he_IL.js":"tinymce/langs/he_IL-981f5250a4b8d404b37040ca5a35c30498cbeeb36e298d81bf1592b43fff7656.js","tinymce/langs/hi_IN.js":"tinymce/langs/hi_IN-695e0cdc22974fe45e0f35289f0249ddab87245165c9df765ec1b885ec55f0be.js","tinymce/langs/hr.js":"tinymce/langs/hr-f77f1da2736d13f9a9a86ebf596f592fcc748f2975a0b9904b512d630f2c1a17.js","tinymce/langs/hu_HU.js":"tinymce/langs/hu_HU-1e22021a4f9c61919aa024041af555eac277bfc08ccb8c07fd329b87090a15e5.js","tinymce/langs/hy.js":"tinymce/langs/hy-0d384f3f82cd76793d3e7428a2140ea97f40a943f25ef99a2e3709a3e02b0930.js","tinymce/langs/id.js":"tinymce/langs/id-e9fd018be745b5f14f4af47887420f98c590c607e01d57aca77ef5affe188523.js","tinymce/langs/is_IS.js":"tinymce/langs/is_IS-c6e8c3e7b0e6b447faec3d8d258928f97c84558b29882c056513fb71cf237bfa.js","tinymce/langs/it.js":"tinymce/langs/it-25b5546d48c80ad666b600e5be3a05718b80645721b191785c1fafe1853f4a46.js","tinymce/langs/ja.js":"tinymce/langs/ja-80f0e7414030c32617ff651da1affa0bc85ee514fc9bc81f46edfd8ce0053ac0.js","tinymce/langs/ka_GE.js":"tinymce/langs/ka_GE-3c6b82346a7070a8b6a15ae6e8faeecc5bfe63ad7e616b7de2e8ab8a75ec39c2.js","tinymce/langs/kab.js":"tinymce/langs/kab-3651d08aacd5bda15a04698f202bf616efbf13d3789aadd9ce4f93e430818c13.js","tinymce/langs/kk.js":"tinymce/langs/kk-0c1ba792ed9445c6512f310228f64d1cde7b5a98e0b212cca749dcb2d728fe86.js","tinymce/langs/km_KH.js":"tinymce/langs/km_KH-8bec84e4078db8c2a680260c073dfb486c85b61df62e14ca330d66d2ca2a85b5.js","tinymce/langs/ko.js":"tinymce/langs/ko-ca535bfa388701fb020d24bdfb3f1e3aab127005b54e943cb1bd60b723c8194d.js","tinymce/langs/ko_KR.js":"tinymce/langs/ko_KR-daa3ae950d70260bb0901a5349034952247905523bbc2bcbe527701b6aad8e28.js","tinymce/langs/ku.js":"tinymce/langs/ku-95e2ffe4f0112df658f10327c26f2cdddedc2e2ed769767366ae22a465c14725.js","tinymce/langs/ku_IQ.js":"tinymce/langs/ku_IQ-a212e8d41695989e56834facf72474e1934ef166c3fcaf7e255ef573a3c6e76c.js","tinymce/langs/lb.js":"tinymce/langs/lb-94992279091f1a0be927d5d858d3724037d8d81ee6b63cc9ddde84af6124cc04.js","tinymce/langs/lt.js":"tinymce/langs/lt-ae7cefbe2cb5420770b31eabf92605728135ab5d6a2d2cfca2d19809be67e6a8.js","tinymce/langs/lv.js":"tinymce/langs/lv-e468ed0c78466d1fff9f4957645606d8aa683cb5dc35931e047df97b8e4f5433.js","tinymce/langs/mk_MK.js":"tinymce/langs/mk_MK-55aadb5d7c82c940cfcf42f24423ef0471294c9b02994a6b22e1b4173baa7ec6.js","tinymce/langs/ml.js":"tinymce/langs/ml-685de0808683c02274dcbe9f8de023a4d72a49b040e7526a99e3d5c786ff71ae.js","tinymce/langs/ml_IN.js":"tinymce/langs/ml_IN-1ab433621d6257c7632718048bacc5ed98a302d33cf0438bbbb4200914e01aac.js","tinymce/langs/mn_MN.js":"tinymce/langs/mn_MN-b89f88cb9b0dd7c879e27532b2979c421c1e69648f65a4108a56060f1e1cb009.js","tinymce/langs/nb_NO.js":"tinymce/langs/nb_NO-602ee249e98a26ae24c1ee1311090c68a782050086a835f1e7bd46ebfe7879ef.js","tinymce/langs/nl.js":"tinymce/langs/nl-9c77e9404a27fd7e4bd162ad44538b72d2725b7fee7be7ca3ff801cf217c4968.js","tinymce/langs/pl.js":"tinymce/langs/pl-5cdd1586ec8fa67293f2175cd92edbd0392c4c77b24ed64cfdceccba8d99f152.js","tinymce/langs/pt_BR.js":"tinymce/langs/pt_BR-e3955318582d58bd241a62c3a81afe74e0ae7f31bdc92a562b75243701f114df.js","tinymce/langs/pt_PT.js":"tinymce/langs/pt_PT-1101d080ecd5da091c7ef70b23200adbd5136c5c32f0c79dae665a443d76c35d.js","tinymce/langs/ro.js":"tinymce/langs/ro-484799d182031efab087d749b024014fd06ed06c1e7712b8bf87f3a4a965c2e4.js","tinymce/langs/ru.js":"tinymce/langs/ru-bdd59cb662a7b4d61e770b07014d1791051312c87513ecc1891e80beba617171.js","tinymce/langs/ru_RU.js":"tinymce/langs/ru_RU-9f18ef14f3493cc586cc0b4827fd9e1c2ed3d5790ce536d94f87e2d6aac2a688.js","tinymce/langs/si_LK.js":"tinymce/langs/si_LK-5bfefe9b2fe82574d8ed839df18fd8ff65f1e1cd458b0a843cf1aadd1be704ea.js","tinymce/langs/sk.js":"tinymce/langs/sk-412fa7f982ad2733341203daa035c8ba10035262ca8f5f65e0a41507512209bd.js","tinymce/langs/sl_SI.js":"tinymce/langs/sl_SI-2bea4f3854a992ea840808019dc99467dc173b12454ea6016135234a741671d8.js","tinymce/langs/sr.js":"tinymce/langs/sr-e0b97453a0c1000b19f7ce37fcf86bcacbf9125d81076f60a77686820c7925c6.js","tinymce/langs/sv_SE.js":"tinymce/langs/sv_SE-fa561a7e49fdb42d5913337176fed6bc8a9997eb3ca8f20ecd9520d5391e17be.js","tinymce/langs/ta.js":"tinymce/langs/ta-0fd0e62250fc1a3626ac2111c5adb4194a6a898888bb224414f6dc8adf53d385.js","tinymce/langs/ta_IN.js":"tinymce/langs/ta_IN-32303f65d2378e8202b17b0920b79277a2580872e3a6bd9409aeceef90867dd0.js","tinymce/langs/tg.js":"tinymce/langs/tg-1618248bf0aeda614a37ced9fd28b6623ae24f8453368bfb8c483ce820a3cb34.js","tinymce/langs/th_TH.js":"tinymce/langs/th_TH-5279a379afc886c2a31e22aba9d7ee7a8e3edf4c0785f39aaa20ccd03d46b19a.js","tinymce/langs/tr.js":"tinymce/langs/tr-3765d4a8923ef22864747d9c825c3e0af401e1356e75a5458bed837d486b7673.js","tinymce/langs/tr_TR.js":"tinymce/langs/tr_TR-5c93f80b89e5dd2eb6972f37bf50b76d7705c61f28120b836704a91fa5c14399.js","tinymce/langs/tt.js":"tinymce/langs/tt-22e302672df0a77de14688f58dbedf82de7a26e9089c9535a2a3cd1c0ccd7903.js","tinymce/langs/ug.js":"tinymce/langs/ug-50232038bf7262c90be4f2919c74ff93792cdb82e6064d0f5c86f885b93271c5.js","tinymce/langs/uk.js":"tinymce/langs/uk-f6d7911714c422b178c0249df323cf7ec415a83195d751c7e4024590b1738c42.js","tinymce/langs/uk_UA.js":"tinymce/langs/uk_UA-bd8cd7ca66228d85f8a4cadb651c49981f26e007fa2a952856c8c3d3c1baa66a.js","tinymce/langs/vi.js":"tinymce/langs/vi-6aa451047e4df911c92e10178e6a70147534e438c64df7a88eee37b4a440726e.js","tinymce/langs/vi_VN.js":"tinymce/langs/vi_VN-aea52c2e9397f8a657d54d50a42b3c5be0ca79480555a4fa1e0f5d785c5f6561.js","tinymce/langs/zh_CN.js":"tinymce/langs/zh_CN-8622ec46e2980b6f5baf3b745c6b0187dd2a54ddecbbf69a21f0b3e72f84f6dc.js","tinymce/langs/zh_TW.js":"tinymce/langs/zh_TW-90723da3b889f2a4477d4aaf00ca3e75439998269b36b359ea4caa37e1defb4a.js","leaflet/dist/images/layers.png":"leaflet/dist/images/layers-1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6.png","leaflet/dist/images/layers-2x.png":"leaflet/dist/images/layers-2x-066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf.png","leaflet/dist/images/marker-icon.png":"leaflet/dist/images/marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png","leaflet/dist/images/marker-icon-2x.png":"leaflet/dist/images/marker-icon-2x-2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d.png","leaflet/dist/images/marker-shadow.png":"leaflet/dist/images/marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png","tinymce/preinit.js":"tinymce/preinit-84328a53e798df12f891eb49871773fd9f925439c8630e9e22423a82ef9e6f89.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":"2017-05-04T21:49:46+02: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":"2017-05-04T21:49:46+02: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":"2017-05-04T21:49:46+02:00","size":1535,"digest":"d9c2bb73769af777c8a71720d29741f3a499aebd5a043e9a119bd0d9597aed47","integrity":"sha256-2cK7c3aa93fIpxcg0pdB86SZrr1aBD6aEZvQ2Vl67Uc="},"active_admin/orderable-29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617.png":{"logical_path":"active_admin/orderable.png","mtime":"2017-05-04T21:49:46+02:00","size":220,"digest":"29374dbb55b0012d78a37c614d573bb3474f0779849b478a147d0f1845ca6617","integrity":"sha256-KTdNu1WwAS14o3xhTVc7s0dPB3mEm0eKFH0PGEXKZhc="},"active_admin/print-87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68.css":{"logical_path":"active_admin/print.css","mtime":"2017-05-04T21:49:46+02:00","size":5494,"digest":"87c5ffc1d869a919123bcc1dc5ec51b20bc79fd9aeab9eed77e3438c6acd4f68","integrity":"sha256-h8X/wdhpqRkSO8wdxexRsgvHn9muq57td+NDjGrNT2g="},"active_admin-5d8dda85fd2e9296c4f9682a0c4441e4dfc6c2779454e8144be78befe3ff05bb.js":{"logical_path":"active_admin.js","mtime":"2017-01-02T09:58:37+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":"2017-04-24T19:17:54+02: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":"2017-04-24T19:17:54+02: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":"2017-04-24T19:17:54+02: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":"2017-04-24T19:17:54+02: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":"2017-04-24T19:17:54+02: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":"2017-04-24T19:17:54+02:00","size":3756,"digest":"faf32007ae120c302213557626e660dd10e711c5dd4f1113d35f26dc05b78d2f","integrity":"sha256-+vMgB64SDDAiE1V2JuZg3RDnEcXdTxET018m3AW3jS8="},"font-awesome/fontawesome-webfont-7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979.eot":{"logical_path":"font-awesome/fontawesome-webfont.eot","mtime":"2017-04-24T19:17:40+02:00","size":165742,"digest":"7bfcab6db99d5cfbf1705ca0536ddc78585432cc5fa41bbd7ad0f009033b2979","integrity":"sha256-e/yrbbmdXPvxcFygU23ceFhUMsxfpBu9etDwCQM7KXk="},"font-awesome/fontawesome-webfont-2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe.woff2":{"logical_path":"font-awesome/fontawesome-webfont.woff2","mtime":"2017-04-24T19:17:40+02:00","size":77160,"digest":"2adefcbc041e7d18fcf2d417879dc5a09997aa64d675b7a3c4b6ce33da13f3fe","integrity":"sha256-Kt78vAQefRj88tQXh53FoJmXqmTWdbejxLbOM9oT8/4="},"font-awesome/fontawesome-webfont-ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07.woff":{"logical_path":"font-awesome/fontawesome-webfont.woff","mtime":"2017-04-24T19:17:40+02:00","size":98024,"digest":"ba0c59deb5450f5cb41b3f93609ee2d0d995415877ddfa223e8a8a7533474f07","integrity":"sha256-ugxZ3rVFD1y0Gz+TYJ7i0NmVQVh33foiPoqKdTNHTwc="},"font-awesome/fontawesome-webfont-aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8.ttf":{"logical_path":"font-awesome/fontawesome-webfont.ttf","mtime":"2017-04-24T19:17:40+02:00","size":165548,"digest":"aa58f33f239a0fb02f5c7a6c45c043d7a9ac9a093335806694ecd6d4edc0d6a8","integrity":"sha256-qljzPyOaD7AvXHpsRcBD16msmgkzNYBmlOzW1O3A1qg="},"font-awesome/fontawesome-webfont-ad6157926c1622ba4e1d03d478f1541368524bfc46f51e42fe0d945f7ef323e4.svg":{"logical_path":"font-awesome/fontawesome-webfont.svg","mtime":"2017-04-24T19:17:40+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":"2017-04-24T19:16:49+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":"2017-04-24T19:17:54+02: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="},"layers-2x-066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf.png":{"logical_path":"layers-2x.png","mtime":"2017-04-24T19:17:55+02:00","size":1259,"digest":"066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf","integrity":"sha256-Bm2sqFDY/77wB68AsG6sABVyje4nnFHzy2xxbffELt8="},"layers-1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6.png":{"logical_path":"layers.png","mtime":"2017-04-24T19:17:55+02:00","size":696,"digest":"1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6","integrity":"sha256-Hbvp0CjikvNvy6j4s6KNXokydU/CIVuaxp5M3s9RB8Y="},"marker-icon-2x-2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d.png":{"logical_path":"marker-icon-2x.png","mtime":"2017-04-24T19:17:55+02:00","size":2586,"digest":"2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d","integrity":"sha256-LXei5MLwi7rEGAgyTvlGuaL+YbYVBIDQEbcrN5w7I40="},"marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png":{"logical_path":"marker-icon.png","mtime":"2017-04-24T19:17:55+02:00","size":1466,"digest":"574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437","integrity":"sha256-V0w6XMqF9BFAhbaEFZbWLwDXyJLHsD8oy/owHesdxDc="},"marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png":{"logical_path":"marker-shadow.png","mtime":"2017-04-24T19:17:55+02:00","size":618,"digest":"264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da","integrity":"sha256-Jk9cZAM58ELdcpBiz8BMF/jqDymIK1OOOEjtjxDttNo="},"tinymce-61f3d9ea006866183d7b56bbc9ecf39545adcc8a1a3b58566ea14ea52db8b902.js":{"logical_path":"tinymce.js","mtime":"2017-01-19T14:51:27+01:00","size":971,"digest":"61f3d9ea006866183d7b56bbc9ecf39545adcc8a1a3b58566ea14ea52db8b902","integrity":"sha256-YfPZ6gBoZhg9e1a7yezzlUWtzIoaO1hWbqFOpS24uQI="},"application-bd35f325ad91254feab551b82972c18f9d928b5016ff20ee040c3380f9c95a7c.css":{"logical_path":"application.css","mtime":"2017-01-19T14:51:27+01:00","size":173942,"digest":"bd35f325ad91254feab551b82972c18f9d928b5016ff20ee040c3380f9c95a7c","integrity":"sha256-vTXzJa2RJU/qtVG4KXLBj52Si1AW/yDuBAwzgPnJWnw="},"leaflet/dist/images/layers-1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6.png":{"logical_path":"leaflet/dist/images/layers.png","mtime":"2017-04-24T19:16:54+02:00","size":696,"digest":"1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6","integrity":"sha256-Hbvp0CjikvNvy6j4s6KNXokydU/CIVuaxp5M3s9RB8Y="},"leaflet/dist/images/layers-2x-066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf.png":{"logical_path":"leaflet/dist/images/layers-2x.png","mtime":"2017-04-24T19:16:54+02:00","size":1259,"digest":"066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf","integrity":"sha256-Bm2sqFDY/77wB68AsG6sABVyje4nnFHzy2xxbffELt8="},"leaflet/dist/images/marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png":{"logical_path":"leaflet/dist/images/marker-icon.png","mtime":"2017-04-24T19:16:54+02:00","size":1466,"digest":"574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437","integrity":"sha256-V0w6XMqF9BFAhbaEFZbWLwDXyJLHsD8oy/owHesdxDc="},"application-e76d31e280284497ba66d10672ca0b022ab11b512f1ed51be1b4db85d47330eb.js":{"logical_path":"application.js","mtime":"2017-01-02T09:58:37+01:00","size":2358436,"digest":"e76d31e280284497ba66d10672ca0b022ab11b512f1ed51be1b4db85d47330eb","integrity":"sha256-520x4oAoRJe6ZtEGcsoLAiqxG1EvHtUb4bTbhdRzMOs="},"leaflet/dist/images/marker-icon-2x-2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d.png":{"logical_path":"leaflet/dist/images/marker-icon-2x.png","mtime":"2017-04-24T19:16:54+02:00","size":2586,"digest":"2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d","integrity":"sha256-LXei5MLwi7rEGAgyTvlGuaL+YbYVBIDQEbcrN5w7I40="},"leaflet/dist/images/marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png":{"logical_path":"leaflet/dist/images/marker-shadow.png","mtime":"2017-04-24T19:16:54+02:00","size":618,"digest":"264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da","integrity":"sha256-Jk9cZAM58ELdcpBiz8BMF/jqDymIK1OOOEjtjxDttNo="},"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},"active_admin-5493c4a52f825b68a247f214b88addaf61bada07aefa1441589c72d8d81f8b12.css":{"logical_path":"active_admin.css","mtime":"2017-01-19T14:15:27+01:00","size":78899,"digest":"5493c4a52f825b68a247f214b88addaf61bada07aefa1441589c72d8d81f8b12","integrity":"sha256-VJPEpS+CW2iiR/IUuIrdr2G62geu+hRBWJxy2NgfixI="},"active_admin-9238692e361f8ed7317fd8414cfadeff93c57b9a6ed3fd35cfe9a3fe4ddc3829.js":{"logical_path":"active_admin.js","mtime":"2017-01-19T14:51:27+01:00","size":692387,"digest":"9238692e361f8ed7317fd8414cfadeff93c57b9a6ed3fd35cfe9a3fe4ddc3829","integrity":"sha256-kjhpLjYfjtcxf9hBTPre/5PFe5pu0/01z+mj/k3cOCk="},"lef-9fcdd7ddd4d40de29c3809b59688c668b85f5628e219d4cd8a8810b72a64533b.png":{"logical_path":"lef.png","mtime":"2017-01-07T19:07:36+01:00","size":8332,"digest":"9fcdd7ddd4d40de29c3809b59688c668b85f5628e219d4cd8a8810b72a64533b","integrity":"sha256-n83X3dTUDeKcOAm1lojGaLhfVijiGdTNiogQtypkUzs="},"application-a9c6770a672e537abdfdbea5f842747b5d0b0d402d9216a5f1bb789f051ba10e.js":{"logical_path":"application.js","mtime":"2017-01-19T14:51:27+01:00","size":2365648,"digest":"a9c6770a672e537abdfdbea5f842747b5d0b0d402d9216a5f1bb789f051ba10e","integrity":"sha256-qcZ3CmcuU3q9/b6l+EJ0e10LDUAtkhal8bt4nwUboQ4="},"tinymce/langs/ar-e47a394dbc461d20aa547fa0c2027a9dad45fe55e18adb0da309045be6ed4109.js":{"logical_path":"tinymce/langs/ar.js","mtime":"2017-04-24T19:17:55+02:00","size":17776,"digest":"e47a394dbc461d20aa547fa0c2027a9dad45fe55e18adb0da309045be6ed4109","integrity":"sha256-5Ho5TbxGHSCqVH+gwgJ6na1F/lXhitsNowkEW+btQQk="},"tinymce/langs/ar_SA-24b62c76e99e114ae44480a67edbacf74f9fed0ff7afbeacce4966f1bc80333b.js":{"logical_path":"tinymce/langs/ar_SA.js","mtime":"2017-04-24T19:17:55+02:00","size":10003,"digest":"24b62c76e99e114ae44480a67edbacf74f9fed0ff7afbeacce4966f1bc80333b","integrity":"sha256-JLYsdumeEUrkRICmftus90+f7Q/3r76szklm8byAMzs="},"tinymce/langs/az-d40cecd6eb423f910f528ae98dcdaba4c2449802621759437174cf8c8c0d5b25.js":{"logical_path":"tinymce/langs/az.js","mtime":"2017-04-24T19:17:55+02:00","size":9680,"digest":"d40cecd6eb423f910f528ae98dcdaba4c2449802621759437174cf8c8c0d5b25","integrity":"sha256-1Azs1utCP5EPUorpjc2rpMJEmAJiF1lDcXTPjIwNWyU="},"tinymce/langs/be-42e2884c0f2394afa1a17e31e702660eeea0d84618bbd18155cb0313c6eb5f69.js":{"logical_path":"tinymce/langs/be.js","mtime":"2017-04-24T19:17:55+02:00","size":21064,"digest":"42e2884c0f2394afa1a17e31e702660eeea0d84618bbd18155cb0313c6eb5f69","integrity":"sha256-QuKITA8jlK+hoX4x5wJmDu6g2EYYu9GBVcsDE8brX2k="},"tinymce/langs/bg_BG-2fac9d785519d2791d42881fcc08c6a82e841575785690259d7f64e1b88d7db3.js":{"logical_path":"tinymce/langs/bg_BG.js","mtime":"2017-04-24T19:17:55+02:00","size":23945,"digest":"2fac9d785519d2791d42881fcc08c6a82e841575785690259d7f64e1b88d7db3","integrity":"sha256-L6ydeFUZ0nkdQogfzAjGqC6EFXV4VpAlnX9k4biNfbM="},"tinymce/langs/bn_BD-ef48a9094445dfa624af0c35902f60b7949b30585b5e9bc760a225fe2c07cbd3.js":{"logical_path":"tinymce/langs/bn_BD.js","mtime":"2017-04-24T19:17:55+02:00","size":6846,"digest":"ef48a9094445dfa624af0c35902f60b7949b30585b5e9bc760a225fe2c07cbd3","integrity":"sha256-70ipCURF36Ykrww1kC9gt5SbMFhbXpvHYKIl/iwHy9M="},"tinymce/langs/bs-eb66c6d6910a30b950d7a0e784027ed288e6e5a2c6db7741da3359f2067e2e0e.js":{"logical_path":"tinymce/langs/bs.js","mtime":"2017-04-24T19:17:55+02:00","size":6970,"digest":"eb66c6d6910a30b950d7a0e784027ed288e6e5a2c6db7741da3359f2067e2e0e","integrity":"sha256-62bG1pEKMLlQ16DnhAJ+0ojm5aLG23dB2jNZ8gZ+Lg4="},"tinymce/langs/ca-77ab49d6420318ed7f5fd51b6856bc396c393095bd78ea91dad83e38e5859637.js":{"logical_path":"tinymce/langs/ca.js","mtime":"2017-04-24T19:17:55+02:00","size":7787,"digest":"77ab49d6420318ed7f5fd51b6856bc396c393095bd78ea91dad83e38e5859637","integrity":"sha256-d6tJ1kIDGO1/X9UbaFa8OWw5MJW9eOqR2tg+OOWFljc="},"tinymce/langs/cs-3d7ecd619895e3e4c7e1db06f6526682ef88f22f6e8b50d7b45ed0ae7c206714.js":{"logical_path":"tinymce/langs/cs.js","mtime":"2017-04-24T19:17:55+02:00","size":8751,"digest":"3d7ecd619895e3e4c7e1db06f6526682ef88f22f6e8b50d7b45ed0ae7c206714","integrity":"sha256-PX7NYZiV4+TH4dsG9lJmgu+I8i9ui1DXtF7QrnwgZxQ="},"tinymce/langs/cs_CZ-4b0778d233e200dea350f8361129da2844e23d1c0f2d524aeffa6d34e581368d.js":{"logical_path":"tinymce/langs/cs_CZ.js","mtime":"2017-04-24T19:17:55+02:00","size":8399,"digest":"4b0778d233e200dea350f8361129da2844e23d1c0f2d524aeffa6d34e581368d","integrity":"sha256-Swd40jPiAN6jUPg2ESnaKETiPRwPLVJK7/ptNOWBNo0="},"tinymce/langs/cy-a847ff54657ccf76eec362cc14fbc63adb4c67f9b1de023b29d817aba4fec062.js":{"logical_path":"tinymce/langs/cy.js","mtime":"2017-04-24T19:17:55+02:00","size":6142,"digest":"a847ff54657ccf76eec362cc14fbc63adb4c67f9b1de023b29d817aba4fec062","integrity":"sha256-qEf/VGV8z3buw2LMFPvGOttMZ/mx3gI7KdgXq6T+wGI="},"tinymce/langs/da-a87f3ae1c442c3bb26b81569687e98072d038269360361ec9728f00d6b17c282.js":{"logical_path":"tinymce/langs/da.js","mtime":"2017-04-24T19:17:55+02:00","size":7513,"digest":"a87f3ae1c442c3bb26b81569687e98072d038269360361ec9728f00d6b17c282","integrity":"sha256-qH864cRCw7smuBVpaH6YBy0Dgmk2A2HslyjwDWsXwoI="},"tinymce/langs/de-a10c8f6f1e53da776e7026dffd54ea8ba527492b767e5b54ae5c3974f6953311.js":{"logical_path":"tinymce/langs/de.js","mtime":"2017-04-24T19:17:55+02:00","size":8280,"digest":"a10c8f6f1e53da776e7026dffd54ea8ba527492b767e5b54ae5c3974f6953311","integrity":"sha256-oQyPbx5T2nducCbf/VTqi6UnSSt2fltUrlw5dPaVMxE="},"tinymce/langs/de_AT-a32eec12ce12b06fc2c8919d2372242e9596712c96dc9bcfff1fc8b7dc458627.js":{"logical_path":"tinymce/langs/de_AT.js","mtime":"2017-04-24T19:17:55+02:00","size":8308,"digest":"a32eec12ce12b06fc2c8919d2372242e9596712c96dc9bcfff1fc8b7dc458627","integrity":"sha256-oy7sEs4SsG/CyJGdI3IkLpWWcSyW3JvP/x/It9xFhic="},"tinymce/langs/dv-182756bf33409a8b76bc684d0a2ab86881718c360d1f1fb3fbc609ce3f5e3f49.js":{"logical_path":"tinymce/langs/dv.js","mtime":"2017-04-24T19:17:55+02:00","size":18100,"digest":"182756bf33409a8b76bc684d0a2ab86881718c360d1f1fb3fbc609ce3f5e3f49","integrity":"sha256-GCdWvzNAmot2vGhNCiq4aIFxjDYNHx+z+8YJzj9eP0k="},"tinymce/langs/el-6bbdbf3ccb2b2f22f961eb6749b43fe401c84fd6d89cd3f76a538dc2d06bb071.js":{"logical_path":"tinymce/langs/el.js","mtime":"2017-04-24T19:17:55+02:00","size":22973,"digest":"6bbdbf3ccb2b2f22f961eb6749b43fe401c84fd6d89cd3f76a538dc2d06bb071","integrity":"sha256-a72/PMsrLyL5YetnSbQ/5AHIT9bYnNP3alONwtBrsHE="},"tinymce/langs/en_CA-1a9457da814b0a1f15687a00f3ae4b38da0301445b52c29a423a38c943d9a431.js":{"logical_path":"tinymce/langs/en_CA.js","mtime":"2017-04-24T19:17:55+02:00","size":6916,"digest":"1a9457da814b0a1f15687a00f3ae4b38da0301445b52c29a423a38c943d9a431","integrity":"sha256-GpRX2oFLCh8VaHoA865LONoDAURbUsKaQjo4yUPZpDE="},"tinymce/langs/en_GB-a0f7752ff9ddf3ed3d2cd24fee04fe585bca0babc4ce655560c923ad386c0c07.js":{"logical_path":"tinymce/langs/en_GB.js","mtime":"2017-04-24T19:17:55+02:00","size":5907,"digest":"a0f7752ff9ddf3ed3d2cd24fee04fe585bca0babc4ce655560c923ad386c0c07","integrity":"sha256-oPd1L/nd8+09LNJP7gT+WFvKC6vEzmVVYMkjrThsDAc="},"tinymce/langs/eo-cbac4d7ebcb0679845b852d9354e74586e78cf13eef82de1400cf6b465e203cd.js":{"logical_path":"tinymce/langs/eo.js","mtime":"2017-04-24T19:17:55+02:00","size":7466,"digest":"cbac4d7ebcb0679845b852d9354e74586e78cf13eef82de1400cf6b465e203cd","integrity":"sha256-y6xNfrywZ5hFuFLZNU50WG54zxPu+C3hQAz2tGXiA80="},"tinymce/langs/es-133cb5bd627c2a299544586bf365859b5a1c96c57d18c90e7853048434a809b6.js":{"logical_path":"tinymce/langs/es.js","mtime":"2017-04-24T19:17:55+02:00","size":7809,"digest":"133cb5bd627c2a299544586bf365859b5a1c96c57d18c90e7853048434a809b6","integrity":"sha256-Ezy1vWJ8KimVRFhr82WFm1oclsV9GMkOeFMEhDSoCbY="},"tinymce/langs/es_MX-352fcdff567919ff09a2e19c56d8059d339bffdbd999bcc82aa6d8340c19bbb9.js":{"logical_path":"tinymce/langs/es_MX.js","mtime":"2017-04-24T19:17:55+02:00","size":7894,"digest":"352fcdff567919ff09a2e19c56d8059d339bffdbd999bcc82aa6d8340c19bbb9","integrity":"sha256-NS/N/1Z5Gf8JouGcVtgFnTOb/9vZmbzIKqbYNAwZu7k="},"tinymce/langs/et-2cfc3da0ff39a37f0bd3c896d49604028f231ff0e7d8ebbc30dbfd4b87125f06.js":{"logical_path":"tinymce/langs/et.js","mtime":"2017-04-24T19:17:55+02:00","size":7784,"digest":"2cfc3da0ff39a37f0bd3c896d49604028f231ff0e7d8ebbc30dbfd4b87125f06","integrity":"sha256-LPw9oP85o38L08iW1JYEAo8jH/Dn2Ou8MNv9S4cSXwY="},"tinymce/langs/eu-8698ae4de236fcd490d0f45d485dbd33d8e9de6eb3db51191e1b4f8ea96a4d06.js":{"logical_path":"tinymce/langs/eu.js","mtime":"2017-04-24T19:17:55+02:00","size":7011,"digest":"8698ae4de236fcd490d0f45d485dbd33d8e9de6eb3db51191e1b4f8ea96a4d06","integrity":"sha256-hpiuTeI2/NSQ0PRdSF29M9jp3m6z21EZHhtPjqlqTQY="},"tinymce/langs/fa-7eff96595e93d4b0df484dd90c14b7b412966157ed8293f54429e9418882bdb2.js":{"logical_path":"tinymce/langs/fa.js","mtime":"2017-04-24T19:17:55+02:00","size":16462,"digest":"7eff96595e93d4b0df484dd90c14b7b412966157ed8293f54429e9418882bdb2","integrity":"sha256-fv+WWV6T1LDfSE3ZDBS3tBKWYVftgpP1RCnpQYiCvbI="},"tinymce/langs/fa_IR-2f9d33f16718ae9c1f3642905b58434ed2662fb66448c4d115b8491aa68e7904.js":{"logical_path":"tinymce/langs/fa_IR.js","mtime":"2017-04-24T19:17:55+02:00","size":17725,"digest":"2f9d33f16718ae9c1f3642905b58434ed2662fb66448c4d115b8491aa68e7904","integrity":"sha256-L50z8WcYrpwfNkKQW1hDTtJmL7ZkSMTRFbhJGqaOeQQ="},"tinymce/langs/fi-6ca2a93d045067a86e8ea973d0b89368054b15eb9b9625bf1ca871608a5cb77c.js":{"logical_path":"tinymce/langs/fi.js","mtime":"2017-04-24T19:17:55+02:00","size":8143,"digest":"6ca2a93d045067a86e8ea973d0b89368054b15eb9b9625bf1ca871608a5cb77c","integrity":"sha256-bKKpPQRQZ6hujqlz0LiTaAVLFeubliW/HKhxYIpct3w="},"tinymce/langs/fo-340609cecd5571e4eacb8fe7bd1343c8553d96d12610fb77d9a812dc6d3635fd.js":{"logical_path":"tinymce/langs/fo.js","mtime":"2017-04-24T19:17:55+02:00","size":8044,"digest":"340609cecd5571e4eacb8fe7bd1343c8553d96d12610fb77d9a812dc6d3635fd","integrity":"sha256-NAYJzs1VceTqy4/nvRNDyFU9ltEmEPt32agS3G02Nf0="},"tinymce/langs/fr_CH-e0f43c0f20727368ff0767f3bf099bc23eed9fe3e81f8bbe514381e786526928.js":{"logical_path":"tinymce/langs/fr_CH.js","mtime":"2017-04-24T19:17:55+02:00","size":8074,"digest":"e0f43c0f20727368ff0767f3bf099bc23eed9fe3e81f8bbe514381e786526928","integrity":"sha256-4PQ8DyByc2j/B2fzvwmbwj7tn+PoH4u+UUOB54ZSaSg="},"tinymce/langs/fr_FR-2702de7be93bd1e0d7120ae3c9e637061565186c66886f155ffca0663df25b4c.js":{"logical_path":"tinymce/langs/fr_FR.js","mtime":"2017-04-24T19:17:55+02:00","size":8009,"digest":"2702de7be93bd1e0d7120ae3c9e637061565186c66886f155ffca0663df25b4c","integrity":"sha256-JwLee+k70eDXEgrjyeY3BhVlGGxmiG8VX/ygZj3yW0w="},"tinymce/langs/ga-d2a3de6f28723d75b03f7f42fd3aedfd045d473425ee38f02350b56035383af1.js":{"logical_path":"tinymce/langs/ga.js","mtime":"2017-04-24T19:17:55+02:00","size":8591,"digest":"d2a3de6f28723d75b03f7f42fd3aedfd045d473425ee38f02350b56035383af1","integrity":"sha256-0qPebyhyPXWwP39C/Trt/QRdRzQl7jjwI1C1YDU4OvE="},"tinymce/langs/gd-0453e8b97bf3b6cca4065712ec59f20343ad6131735b38547e865177a1c1c490.js":{"logical_path":"tinymce/langs/gd.js","mtime":"2017-04-24T19:17:55+02:00","size":8808,"digest":"0453e8b97bf3b6cca4065712ec59f20343ad6131735b38547e865177a1c1c490","integrity":"sha256-BFPouXvztsykBlcS7FnyA0OtYTFzWzhUfoZRd6HBxJA="},"tinymce/langs/gl-1ff612222934f558870111f10934110c5656df3bb293c917e4eeceeb6605fcc5.js":{"logical_path":"tinymce/langs/gl.js","mtime":"2017-04-24T19:17:55+02:00","size":7066,"digest":"1ff612222934f558870111f10934110c5656df3bb293c917e4eeceeb6605fcc5","integrity":"sha256-H/YSIik09ViHARHxCTQRDFZW3zuyk8kX5O7O62YF/MU="},"tinymce/langs/he_IL-981f5250a4b8d404b37040ca5a35c30498cbeeb36e298d81bf1592b43fff7656.js":{"logical_path":"tinymce/langs/he_IL.js","mtime":"2017-04-24T19:17:55+02:00","size":16081,"digest":"981f5250a4b8d404b37040ca5a35c30498cbeeb36e298d81bf1592b43fff7656","integrity":"sha256-mB9SUKS41ASzcEDKWjXDBJjL7rNuKY2BvxWStD//dlY="},"tinymce/langs/hi_IN-695e0cdc22974fe45e0f35289f0249ddab87245165c9df765ec1b885ec55f0be.js":{"logical_path":"tinymce/langs/hi_IN.js","mtime":"2017-04-24T19:17:55+02:00","size":18458,"digest":"695e0cdc22974fe45e0f35289f0249ddab87245165c9df765ec1b885ec55f0be","integrity":"sha256-aV4M3CKXT+ReDzUonwJJ3auHJFFlyd92XsG4hexV8L4="},"tinymce/langs/hr-f77f1da2736d13f9a9a86ebf596f592fcc748f2975a0b9904b512d630f2c1a17.js":{"logical_path":"tinymce/langs/hr.js","mtime":"2017-04-24T19:17:55+02:00","size":7577,"digest":"f77f1da2736d13f9a9a86ebf596f592fcc748f2975a0b9904b512d630f2c1a17","integrity":"sha256-938donNtE/mpqG6/WW9ZL8x0jyl1oLmQS1EtYw8sGhc="},"tinymce/langs/hu_HU-1e22021a4f9c61919aa024041af555eac277bfc08ccb8c07fd329b87090a15e5.js":{"logical_path":"tinymce/langs/hu_HU.js","mtime":"2017-04-24T19:17:55+02:00","size":9410,"digest":"1e22021a4f9c61919aa024041af555eac277bfc08ccb8c07fd329b87090a15e5","integrity":"sha256-HiICGk+cYZGaoCQEGvVV6sJ3v8CMy4wH/TKbhwkKFeU="},"tinymce/langs/hy-0d384f3f82cd76793d3e7428a2140ea97f40a943f25ef99a2e3709a3e02b0930.js":{"logical_path":"tinymce/langs/hy.js","mtime":"2017-04-24T19:17:55+02:00","size":20375,"digest":"0d384f3f82cd76793d3e7428a2140ea97f40a943f25ef99a2e3709a3e02b0930","integrity":"sha256-DThPP4LNdnk9PnQoohQOqX9AqUPyXvmaLjcJo+ArCTA="},"tinymce/langs/id-e9fd018be745b5f14f4af47887420f98c590c607e01d57aca77ef5affe188523.js":{"logical_path":"tinymce/langs/id.js","mtime":"2017-04-24T19:17:55+02:00","size":7141,"digest":"e9fd018be745b5f14f4af47887420f98c590c607e01d57aca77ef5affe188523","integrity":"sha256-6f0Bi+dFtfFPSvR4h0IPmMWQxgfgHVesp371r/4YhSM="},"tinymce/langs/is_IS-c6e8c3e7b0e6b447faec3d8d258928f97c84558b29882c056513fb71cf237bfa.js":{"logical_path":"tinymce/langs/is_IS.js","mtime":"2017-04-24T19:17:55+02:00","size":7928,"digest":"c6e8c3e7b0e6b447faec3d8d258928f97c84558b29882c056513fb71cf237bfa","integrity":"sha256-xujD57DmtEf67D2NJYko+XyEVYspiCwFZRP7cc8je/o="},"tinymce/langs/it-25b5546d48c80ad666b600e5be3a05718b80645721b191785c1fafe1853f4a46.js":{"logical_path":"tinymce/langs/it.js","mtime":"2017-04-24T19:17:55+02:00","size":7598,"digest":"25b5546d48c80ad666b600e5be3a05718b80645721b191785c1fafe1853f4a46","integrity":"sha256-JbVUbUjICtZmtgDlvjoFcYuAZFchsZF4XB+v4YU/SkY="},"tinymce/langs/ja-80f0e7414030c32617ff651da1affa0bc85ee514fc9bc81f46edfd8ce0053ac0.js":{"logical_path":"tinymce/langs/ja.js","mtime":"2017-04-24T19:17:55+02:00","size":12290,"digest":"80f0e7414030c32617ff651da1affa0bc85ee514fc9bc81f46edfd8ce0053ac0","integrity":"sha256-gPDnQUAwwyYX/2Udoa/6C8he5RT8m8gfRu39jOAFOsA="},"tinymce/langs/ka_GE-3c6b82346a7070a8b6a15ae6e8faeecc5bfe63ad7e616b7de2e8ab8a75ec39c2.js":{"logical_path":"tinymce/langs/ka_GE.js","mtime":"2017-04-24T19:17:55+02:00","size":21284,"digest":"3c6b82346a7070a8b6a15ae6e8faeecc5bfe63ad7e616b7de2e8ab8a75ec39c2","integrity":"sha256-PGuCNGpwcKi2oVrm6PruzFv+Y61+YWt94uirinXsOcI="},"tinymce/langs/kab-3651d08aacd5bda15a04698f202bf616efbf13d3789aadd9ce4f93e430818c13.js":{"logical_path":"tinymce/langs/kab.js","mtime":"2017-04-24T19:17:55+02:00","size":7391,"digest":"3651d08aacd5bda15a04698f202bf616efbf13d3789aadd9ce4f93e430818c13","integrity":"sha256-NlHQiqzVvaFaBGmPICv2Fu+/E9N4mq3Zzk+T5DCBjBM="},"tinymce/langs/kk-0c1ba792ed9445c6512f310228f64d1cde7b5a98e0b212cca749dcb2d728fe86.js":{"logical_path":"tinymce/langs/kk.js","mtime":"2017-04-24T19:17:55+02:00","size":16681,"digest":"0c1ba792ed9445c6512f310228f64d1cde7b5a98e0b212cca749dcb2d728fe86","integrity":"sha256-DBunku2URcZRLzECKPZNHN57WpjgshLMp0ncstco/oY="},"tinymce/langs/km_KH-8bec84e4078db8c2a680260c073dfb486c85b61df62e14ca330d66d2ca2a85b5.js":{"logical_path":"tinymce/langs/km_KH.js","mtime":"2017-04-24T19:17:55+02:00","size":21396,"digest":"8bec84e4078db8c2a680260c073dfb486c85b61df62e14ca330d66d2ca2a85b5","integrity":"sha256-i+yE5AeNuMKmgCYMBz37SGyFth32LhTKMw1m0soqhbU="},"tinymce/langs/ko-ca535bfa388701fb020d24bdfb3f1e3aab127005b54e943cb1bd60b723c8194d.js":{"logical_path":"tinymce/langs/ko.js","mtime":"2017-04-24T19:17:55+02:00","size":10105,"digest":"ca535bfa388701fb020d24bdfb3f1e3aab127005b54e943cb1bd60b723c8194d","integrity":"sha256-ylNb+jiHAfsCDSS9+z8eOqsScAW1TpQ8sb1gtyPIGU0="},"tinymce/langs/ko_KR-daa3ae950d70260bb0901a5349034952247905523bbc2bcbe527701b6aad8e28.js":{"logical_path":"tinymce/langs/ko_KR.js","mtime":"2017-04-24T19:17:55+02:00","size":10005,"digest":"daa3ae950d70260bb0901a5349034952247905523bbc2bcbe527701b6aad8e28","integrity":"sha256-2qOulQ1wJguwkBpTSQNJUiR5BVI7vCvL5SdwG2qtjig="},"tinymce/langs/ku-95e2ffe4f0112df658f10327c26f2cdddedc2e2ed769767366ae22a465c14725.js":{"logical_path":"tinymce/langs/ku.js","mtime":"2017-04-24T19:17:55+02:00","size":18274,"digest":"95e2ffe4f0112df658f10327c26f2cdddedc2e2ed769767366ae22a465c14725","integrity":"sha256-leL/5PARLfZY8QMnwm8s3d7cLi7XaXZzZq4ipGXBRyU="},"tinymce/langs/ku_IQ-a212e8d41695989e56834facf72474e1934ef166c3fcaf7e255ef573a3c6e76c.js":{"logical_path":"tinymce/langs/ku_IQ.js","mtime":"2017-04-24T19:17:55+02:00","size":18364,"digest":"a212e8d41695989e56834facf72474e1934ef166c3fcaf7e255ef573a3c6e76c","integrity":"sha256-ohLo1BaVmJ5Wg0+s9yR04ZNO8WbD/K9+JV71c6PG52w="},"tinymce/langs/lb-94992279091f1a0be927d5d858d3724037d8d81ee6b63cc9ddde84af6124cc04.js":{"logical_path":"tinymce/langs/lb.js","mtime":"2017-04-24T19:17:55+02:00","size":7467,"digest":"94992279091f1a0be927d5d858d3724037d8d81ee6b63cc9ddde84af6124cc04","integrity":"sha256-lJkieQkfGgvpJ9XYWNNyQDfY2B7mtjzJ3d6Er2EkzAQ="},"tinymce/langs/lt-ae7cefbe2cb5420770b31eabf92605728135ab5d6a2d2cfca2d19809be67e6a8.js":{"logical_path":"tinymce/langs/lt.js","mtime":"2017-04-24T19:17:55+02:00","size":8899,"digest":"ae7cefbe2cb5420770b31eabf92605728135ab5d6a2d2cfca2d19809be67e6a8","integrity":"sha256-rnzvviy1Qgdwsx6r+SYFcoE1q11qLSz8otGYCb5n5qg="},"tinymce/langs/lv-e468ed0c78466d1fff9f4957645606d8aa683cb5dc35931e047df97b8e4f5433.js":{"logical_path":"tinymce/langs/lv.js","mtime":"2017-04-24T19:17:55+02:00","size":8282,"digest":"e468ed0c78466d1fff9f4957645606d8aa683cb5dc35931e047df97b8e4f5433","integrity":"sha256-5GjtDHhGbR//n0lXZFYG2KpoPLXcNZMeBH35e45PVDM="},"tinymce/langs/mk_MK-55aadb5d7c82c940cfcf42f24423ef0471294c9b02994a6b22e1b4173baa7ec6.js":{"logical_path":"tinymce/langs/mk_MK.js","mtime":"2017-04-24T19:17:55+02:00","size":18733,"digest":"55aadb5d7c82c940cfcf42f24423ef0471294c9b02994a6b22e1b4173baa7ec6","integrity":"sha256-VarbXXyCyUDPz0LyRCPvBHEpTJsCmUprIuG0FzuqfsY="},"tinymce/langs/ml-685de0808683c02274dcbe9f8de023a4d72a49b040e7526a99e3d5c786ff71ae.js":{"logical_path":"tinymce/langs/ml.js","mtime":"2017-04-24T19:17:55+02:00","size":7339,"digest":"685de0808683c02274dcbe9f8de023a4d72a49b040e7526a99e3d5c786ff71ae","integrity":"sha256-aF3ggIaDwCJ03L6fjeAjpNcqSbBA51JqmePVx4b/ca4="},"tinymce/langs/ml_IN-1ab433621d6257c7632718048bacc5ed98a302d33cf0438bbbb4200914e01aac.js":{"logical_path":"tinymce/langs/ml_IN.js","mtime":"2017-04-24T19:17:55+02:00","size":20019,"digest":"1ab433621d6257c7632718048bacc5ed98a302d33cf0438bbbb4200914e01aac","integrity":"sha256-GrQzYh1iV8djJxgEi6zF7ZijAtM88EOLu7QgCRTgGqw="},"tinymce/langs/mn_MN-b89f88cb9b0dd7c879e27532b2979c421c1e69648f65a4108a56060f1e1cb009.js":{"logical_path":"tinymce/langs/mn_MN.js","mtime":"2017-04-24T19:17:55+02:00","size":6877,"digest":"b89f88cb9b0dd7c879e27532b2979c421c1e69648f65a4108a56060f1e1cb009","integrity":"sha256-uJ+Iy5sN18h54nUyspecQhweaWSPZaQQilYGDx4csAk="},"tinymce/langs/nb_NO-602ee249e98a26ae24c1ee1311090c68a782050086a835f1e7bd46ebfe7879ef.js":{"logical_path":"tinymce/langs/nb_NO.js","mtime":"2017-04-24T19:17:55+02:00","size":7528,"digest":"602ee249e98a26ae24c1ee1311090c68a782050086a835f1e7bd46ebfe7879ef","integrity":"sha256-YC7iSemKJq4kwe4TEQkMaKeCBQCGqDXx571G6/54ee8="},"tinymce/langs/nl-9c77e9404a27fd7e4bd162ad44538b72d2725b7fee7be7ca3ff801cf217c4968.js":{"logical_path":"tinymce/langs/nl.js","mtime":"2017-04-24T19:17:55+02:00","size":7235,"digest":"9c77e9404a27fd7e4bd162ad44538b72d2725b7fee7be7ca3ff801cf217c4968","integrity":"sha256-nHfpQEon/X5L0WKtRFOLctJyW3/ue+fKP/gBzyF8SWg="},"tinymce/langs/pl-5cdd1586ec8fa67293f2175cd92edbd0392c4c77b24ed64cfdceccba8d99f152.js":{"logical_path":"tinymce/langs/pl.js","mtime":"2017-04-24T19:17:55+02:00","size":8129,"digest":"5cdd1586ec8fa67293f2175cd92edbd0392c4c77b24ed64cfdceccba8d99f152","integrity":"sha256-XN0VhuyPpnKT8hdc2S7b0DksTHeyTtZM/c7Muo2Z8VI="},"tinymce/langs/pt_BR-e3955318582d58bd241a62c3a81afe74e0ae7f31bdc92a562b75243701f114df.js":{"logical_path":"tinymce/langs/pt_BR.js","mtime":"2017-04-24T19:17:55+02:00","size":8002,"digest":"e3955318582d58bd241a62c3a81afe74e0ae7f31bdc92a562b75243701f114df","integrity":"sha256-45VTGFgtWL0kGmLDqBr+dOCufzG9ySpWK3UkNwHxFN8="},"tinymce/langs/pt_PT-1101d080ecd5da091c7ef70b23200adbd5136c5c32f0c79dae665a443d76c35d.js":{"logical_path":"tinymce/langs/pt_PT.js","mtime":"2017-04-24T19:17:55+02:00","size":8145,"digest":"1101d080ecd5da091c7ef70b23200adbd5136c5c32f0c79dae665a443d76c35d","integrity":"sha256-EQHQgOzV2gkcfvcLIyAK29UTbFwy8MedrmZaRD12w10="},"tinymce/langs/ro-484799d182031efab087d749b024014fd06ed06c1e7712b8bf87f3a4a965c2e4.js":{"logical_path":"tinymce/langs/ro.js","mtime":"2017-04-24T19:17:55+02:00","size":8275,"digest":"484799d182031efab087d749b024014fd06ed06c1e7712b8bf87f3a4a965c2e4","integrity":"sha256-SEeZ0YIDHvqwh9dJsCQBT9Bu0GwedxK4v4fzpKllwuQ="},"tinymce/langs/ru-bdd59cb662a7b4d61e770b07014d1791051312c87513ecc1891e80beba617171.js":{"logical_path":"tinymce/langs/ru.js","mtime":"2017-04-24T19:17:55+02:00","size":22290,"digest":"bdd59cb662a7b4d61e770b07014d1791051312c87513ecc1891e80beba617171","integrity":"sha256-vdWctmKntNYedwsHAU0XkQUTEsh1E+zBiR6AvrphcXE="},"tinymce/langs/ru_RU-9f18ef14f3493cc586cc0b4827fd9e1c2ed3d5790ce536d94f87e2d6aac2a688.js":{"logical_path":"tinymce/langs/ru_RU.js","mtime":"2017-04-24T19:17:55+02:00","size":4955,"digest":"9f18ef14f3493cc586cc0b4827fd9e1c2ed3d5790ce536d94f87e2d6aac2a688","integrity":"sha256-nxjvFPNJPMWGzAtIJ/2eHC7T1XkM5TbZT4fi1qrCpog="},"tinymce/langs/si_LK-5bfefe9b2fe82574d8ed839df18fd8ff65f1e1cd458b0a843cf1aadd1be704ea.js":{"logical_path":"tinymce/langs/si_LK.js","mtime":"2017-04-24T19:17:55+02:00","size":15856,"digest":"5bfefe9b2fe82574d8ed839df18fd8ff65f1e1cd458b0a843cf1aadd1be704ea","integrity":"sha256-W/7+my/oJXTY7YOd8Y/Y/2Xx4c1FiwqEPPGq3RvnBOo="},"tinymce/langs/sk-412fa7f982ad2733341203daa035c8ba10035262ca8f5f65e0a41507512209bd.js":{"logical_path":"tinymce/langs/sk.js","mtime":"2017-04-24T19:17:55+02:00","size":8958,"digest":"412fa7f982ad2733341203daa035c8ba10035262ca8f5f65e0a41507512209bd","integrity":"sha256-QS+n+YKtJzM0EgPaoDXIuhADUmLKj19l4KQVB1EiCb0="},"tinymce/langs/sl_SI-2bea4f3854a992ea840808019dc99467dc173b12454ea6016135234a741671d8.js":{"logical_path":"tinymce/langs/sl_SI.js","mtime":"2017-04-24T19:17:55+02:00","size":7039,"digest":"2bea4f3854a992ea840808019dc99467dc173b12454ea6016135234a741671d8","integrity":"sha256-K+pPOFSpkuqECAgBncmUZ9wXOxJFTqYBYTUjSnQWcdg="},"tinymce/langs/sr-e0b97453a0c1000b19f7ce37fcf86bcacbf9125d81076f60a77686820c7925c6.js":{"logical_path":"tinymce/langs/sr.js","mtime":"2017-04-24T19:17:55+02:00","size":6336,"digest":"e0b97453a0c1000b19f7ce37fcf86bcacbf9125d81076f60a77686820c7925c6","integrity":"sha256-4Ll0U6DBAAsZ9843/Phrysv5El2BB29gp3aGggx5JcY="},"tinymce/langs/sv_SE-fa561a7e49fdb42d5913337176fed6bc8a9997eb3ca8f20ecd9520d5391e17be.js":{"logical_path":"tinymce/langs/sv_SE.js","mtime":"2017-04-24T19:17:55+02:00","size":7496,"digest":"fa561a7e49fdb42d5913337176fed6bc8a9997eb3ca8f20ecd9520d5391e17be","integrity":"sha256-+lYafkn9tC1ZEzNxdv7WvIqZl+s8qPIOzZUg1TkeF74="},"tinymce/langs/ta-0fd0e62250fc1a3626ac2111c5adb4194a6a898888bb224414f6dc8adf53d385.js":{"logical_path":"tinymce/langs/ta.js","mtime":"2017-04-24T19:17:55+02:00","size":22617,"digest":"0fd0e62250fc1a3626ac2111c5adb4194a6a898888bb224414f6dc8adf53d385","integrity":"sha256-D9DmIlD8GjYmrCERxa20GUpqiYiIuyJEFPbcit9T04U="},"tinymce/langs/ta_IN-32303f65d2378e8202b17b0920b79277a2580872e3a6bd9409aeceef90867dd0.js":{"logical_path":"tinymce/langs/ta_IN.js","mtime":"2017-04-24T19:17:55+02:00","size":22620,"digest":"32303f65d2378e8202b17b0920b79277a2580872e3a6bd9409aeceef90867dd0","integrity":"sha256-MjA/ZdI3joICsXsJILeSd6JYCHLjpr2UCa7O75CGfdA="},"tinymce/langs/tg-1618248bf0aeda614a37ced9fd28b6623ae24f8453368bfb8c483ce820a3cb34.js":{"logical_path":"tinymce/langs/tg.js","mtime":"2017-04-24T19:17:55+02:00","size":18183,"digest":"1618248bf0aeda614a37ced9fd28b6623ae24f8453368bfb8c483ce820a3cb34","integrity":"sha256-Fhgki/Cu2mFKN87Z/Si2YjriT4RTNov7jEg86CCjyzQ="},"tinymce/langs/th_TH-5279a379afc886c2a31e22aba9d7ee7a8e3edf4c0785f39aaa20ccd03d46b19a.js":{"logical_path":"tinymce/langs/th_TH.js","mtime":"2017-04-24T19:17:55+02:00","size":17297,"digest":"5279a379afc886c2a31e22aba9d7ee7a8e3edf4c0785f39aaa20ccd03d46b19a","integrity":"sha256-Unmjea/IhsKjHiKrqdfueo4+30wHhfOaqiDM0D1GsZo="},"tinymce/langs/tr-3765d4a8923ef22864747d9c825c3e0af401e1356e75a5458bed837d486b7673.js":{"logical_path":"tinymce/langs/tr.js","mtime":"2017-04-24T19:17:55+02:00","size":8705,"digest":"3765d4a8923ef22864747d9c825c3e0af401e1356e75a5458bed837d486b7673","integrity":"sha256-N2XUqJI+8ihkdH2cglw+CvQB4TVudaVFi+2DfUhrdnM="},"tinymce/langs/tr_TR-5c93f80b89e5dd2eb6972f37bf50b76d7705c61f28120b836704a91fa5c14399.js":{"logical_path":"tinymce/langs/tr_TR.js","mtime":"2017-04-24T19:17:55+02:00","size":8141,"digest":"5c93f80b89e5dd2eb6972f37bf50b76d7705c61f28120b836704a91fa5c14399","integrity":"sha256-XJP4C4nl3S62ly83v1C3bXcFxh8oEguDZwSpH6XBQ5k="},"tinymce/langs/tt-22e302672df0a77de14688f58dbedf82de7a26e9089c9535a2a3cd1c0ccd7903.js":{"logical_path":"tinymce/langs/tt.js","mtime":"2017-04-24T19:17:55+02:00","size":19035,"digest":"22e302672df0a77de14688f58dbedf82de7a26e9089c9535a2a3cd1c0ccd7903","integrity":"sha256-IuMCZy3wp33hRoj1jb7fgt56JukInJU1oqPNHAzNeQM="},"tinymce/langs/ug-50232038bf7262c90be4f2919c74ff93792cdb82e6064d0f5c86f885b93271c5.js":{"logical_path":"tinymce/langs/ug.js","mtime":"2017-04-24T19:17:55+02:00","size":16888,"digest":"50232038bf7262c90be4f2919c74ff93792cdb82e6064d0f5c86f885b93271c5","integrity":"sha256-UCMgOL9yYskL5PKRnHT/k3ks24LmBk0PXIb4hbkyccU="},"tinymce/langs/uk-f6d7911714c422b178c0249df323cf7ec415a83195d751c7e4024590b1738c42.js":{"logical_path":"tinymce/langs/uk.js","mtime":"2017-04-24T19:17:55+02:00","size":22264,"digest":"f6d7911714c422b178c0249df323cf7ec415a83195d751c7e4024590b1738c42","integrity":"sha256-9teRFxTEIrF4wCSd8yPPfsQVqDGV11HH5AJFkLFzjEI="},"tinymce/langs/uk_UA-bd8cd7ca66228d85f8a4cadb651c49981f26e007fa2a952856c8c3d3c1baa66a.js":{"logical_path":"tinymce/langs/uk_UA.js","mtime":"2017-04-24T19:17:55+02:00","size":21691,"digest":"bd8cd7ca66228d85f8a4cadb651c49981f26e007fa2a952856c8c3d3c1baa66a","integrity":"sha256-vYzXymYijYX4pMrbZRxJmB8m4Af6KpUoVsjD08G6pmo="},"tinymce/langs/vi-6aa451047e4df911c92e10178e6a70147534e438c64df7a88eee37b4a440726e.js":{"logical_path":"tinymce/langs/vi.js","mtime":"2017-04-24T19:17:55+02:00","size":9901,"digest":"6aa451047e4df911c92e10178e6a70147534e438c64df7a88eee37b4a440726e","integrity":"sha256-aqRRBH5N+RHJLhAXjmpwFHU05DjGTfeoju43tKRAcm4="},"tinymce/langs/vi_VN-aea52c2e9397f8a657d54d50a42b3c5be0ca79480555a4fa1e0f5d785c5f6561.js":{"logical_path":"tinymce/langs/vi_VN.js","mtime":"2017-04-24T19:17:55+02:00","size":9899,"digest":"aea52c2e9397f8a657d54d50a42b3c5be0ca79480555a4fa1e0f5d785c5f6561","integrity":"sha256-rqUsLpOX+KZX1U1QpCs8W+DKeUgFVaT6Hg9deFxfZWE="},"tinymce/langs/zh_CN-8622ec46e2980b6f5baf3b745c6b0187dd2a54ddecbbf69a21f0b3e72f84f6dc.js":{"logical_path":"tinymce/langs/zh_CN.js","mtime":"2017-04-24T19:17:55+02:00","size":8483,"digest":"8622ec46e2980b6f5baf3b745c6b0187dd2a54ddecbbf69a21f0b3e72f84f6dc","integrity":"sha256-hiLsRuKYC29brzt0XGsBh90qVN3su/aaIfCz5y+E9tw="},"tinymce/langs/zh_TW-90723da3b889f2a4477d4aaf00ca3e75439998269b36b359ea4caa37e1defb4a.js":{"logical_path":"tinymce/langs/zh_TW.js","mtime":"2017-04-24T19:17:55+02:00","size":9379,"digest":"90723da3b889f2a4477d4aaf00ca3e75439998269b36b359ea4caa37e1defb4a","integrity":"sha256-kHI9o7iJ8qRHfUqvAMo+dUOZmCabNrNZ6kyqN+He+0o="},"tinymce/preinit-84328a53e798df12f891eb49871773fd9f925439c8630e9e22423a82ef9e6f89.js":{"logical_path":"tinymce/preinit.js","mtime":"2017-01-05T19:24:14+01:00","size":82,"digest":"84328a53e798df12f891eb49871773fd9f925439c8630e9e22423a82ef9e6f89","integrity":"sha256-hDKKU+eY3xL4ketJhxdz/Z+SVDnIYw6eIkI6gu+eb4k="},"tinymce/tinymce-6c5174912bf31a8ee031a9acb5d2b7d05f7bcd42e3ed0d26d7d217a5343eb2bf.js":{"logical_path":"tinymce/tinymce.js","mtime":"2017-01-05T19:24:14+01:00","size":1294513,"digest":"6c5174912bf31a8ee031a9acb5d2b7d05f7bcd42e3ed0d26d7d217a5343eb2bf","integrity":"sha256-bFF0kSvzGo7gMamstdK30F97zULj7Q0m19IXpTQ+sr8="},"tinymce/jquery.tinymce-275e24af4bf53bfb60f7fef218163106ad0648b8ad384ecfab9b4fd52f48603d.js":{"logical_path":"tinymce/jquery.tinymce.js","mtime":"2017-05-02T18:30:07+02:00","size":3592,"digest":"275e24af4bf53bfb60f7fef218163106ad0648b8ad384ecfab9b4fd52f48603d","integrity":"sha256-J14kr0v1O/tg9/7yGBYxBq0GSLitOE7Pq5tP1S9IYD0="},"tinymce/langs/readme-5a8b6a04d57b5c88e3fb7f2a870b8e2d3a48ec03ce6474206c41df78c155b2de.md":{"logical_path":"tinymce/langs/readme.md","mtime":"2017-05-02T18:30:07+02:00","size":151,"digest":"5a8b6a04d57b5c88e3fb7f2a870b8e2d3a48ec03ce6474206c41df78c155b2de","integrity":"sha256-WotqBNV7XIjj+38qhwuOLTpI7APOZHQgbEHfeMFVst4="},"tinymce/license-5fda611b8191f00121f161a93e7399cdb71789dba923e8ed09b50a1f78d32c5e.txt":{"logical_path":"tinymce/license.txt","mtime":"2017-05-02T18:30:07+02:00","size":26427,"digest":"5fda611b8191f00121f161a93e7399cdb71789dba923e8ed09b50a1f78d32c5e","integrity":"sha256-X9phG4GR8AEh8WGpPnOZzbcXidupI+jtCbUKH3jTLF4="},"tinymce/plugins/advlist/plugin-d0bd2b90c1aaf60ddbb048d955f7fbf76e303b5f3227f8e4dc83207548c3fc46.js":{"logical_path":"tinymce/plugins/advlist/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":2092,"digest":"d0bd2b90c1aaf60ddbb048d955f7fbf76e303b5f3227f8e4dc83207548c3fc46","integrity":"sha256-0L0rkMGq9g3bsEjZVff7924wO18yJ/jk3IMgdUjD/EY="},"tinymce/plugins/anchor/plugin-48c211e97cdf2fba4e8456ddc0464c301c61ff2d2751aa178d41c5eca38fbbe9.js":{"logical_path":"tinymce/plugins/anchor/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":958,"digest":"48c211e97cdf2fba4e8456ddc0464c301c61ff2d2751aa178d41c5eca38fbbe9","integrity":"sha256-SMIR6XzfL7pOhFbdwEZMMBxh/y0nUaoXjUHF7KOPu+k="},"tinymce/plugins/autolink/plugin-9d514baa6f3816a43008241db075d724fcb274632fad3db792e87e5feb6c5d0f.js":{"logical_path":"tinymce/plugins/autolink/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":2061,"digest":"9d514baa6f3816a43008241db075d724fcb274632fad3db792e87e5feb6c5d0f","integrity":"sha256-nVFLqm84FqQwCCQdsHXXJPyydGMvrT23kuh+X+tsXQ8="},"tinymce/plugins/autoresize/plugin-7cd05d5431f8713c948291ad40ef63eabe44908bd831d5675afacaca296a4d7b.js":{"logical_path":"tinymce/plugins/autoresize/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1904,"digest":"7cd05d5431f8713c948291ad40ef63eabe44908bd831d5675afacaca296a4d7b","integrity":"sha256-fNBdVDH4cTyUgpGtQO9j6r5EkIvYMdVnWvrKyilqTXs="},"tinymce/plugins/autosave/plugin-43448ce025b165ed1b509554cadf02cda4ed104ccfae6d0708379f02e981220f.js":{"logical_path":"tinymce/plugins/autosave/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":2188,"digest":"43448ce025b165ed1b509554cadf02cda4ed104ccfae6d0708379f02e981220f","integrity":"sha256-Q0SM4CWxZe0bUJVUyt8CzaTtEEzPrm0HCDefAumBIg8="},"tinymce/plugins/bbcode/plugin-dae2432b6477ca1acc3bd6ac7af33c379797c7f2dcf2c82513475d4c234d851d.js":{"logical_path":"tinymce/plugins/bbcode/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":3137,"digest":"dae2432b6477ca1acc3bd6ac7af33c379797c7f2dcf2c82513475d4c234d851d","integrity":"sha256-2uJDK2R3yhrMO9asevM8N5eXx/Lc8sglE0ddTCNNhR0="},"tinymce/plugins/charmap/plugin-ee3f9ed5d0135f19975a4609701b4b4546f00ab1afee9b74a38bb3d76ab94eca.js":{"logical_path":"tinymce/plugins/charmap/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":8200,"digest":"ee3f9ed5d0135f19975a4609701b4b4546f00ab1afee9b74a38bb3d76ab94eca","integrity":"sha256-7j+e1dATXxmXWkYJcBtLRUbwCrGv7pt0o4uz12q5Tso="},"tinymce/plugins/code/plugin-093b2519070297197c89cabd2b7bf7b7920786fa3ae5b055322fb16d51d113d4.js":{"logical_path":"tinymce/plugins/code/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":722,"digest":"093b2519070297197c89cabd2b7bf7b7920786fa3ae5b055322fb16d51d113d4","integrity":"sha256-CTslGQcClxl8icq9K3v3t5IHhvo65bBVMi+xbVHRE9Q="},"tinymce/plugins/codesample/css/prism-1988b66704b4d23e78c6c20c38a6856cbc1f0be96d6d60a3a0b12f4408f1057e.css":{"logical_path":"tinymce/plugins/codesample/css/prism.css","mtime":"2017-05-02T18:30:07+02:00","size":1776,"digest":"1988b66704b4d23e78c6c20c38a6856cbc1f0be96d6d60a3a0b12f4408f1057e","integrity":"sha256-GYi2ZwS00j54xsIMOKaFbLwfC+ltbWCjoLEvRAjxBX4="},"tinymce/plugins/codesample/plugin-edd0d2e7f821002843d7726182d9a8b1520fcabb753b95ee7956180337be23da.js":{"logical_path":"tinymce/plugins/codesample/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":20333,"digest":"edd0d2e7f821002843d7726182d9a8b1520fcabb753b95ee7956180337be23da","integrity":"sha256-7dDS5/ghAChD13JhgtmosVIPyrt1O5XueVYYAze+I9o="},"tinymce/plugins/colorpicker/plugin-526e08119d96ecdeeb21c1031cba4b471aba860fbc6ed0f8a010275088c00531.js":{"logical_path":"tinymce/plugins/colorpicker/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1223,"digest":"526e08119d96ecdeeb21c1031cba4b471aba860fbc6ed0f8a010275088c00531","integrity":"sha256-Um4IEZ2W7N7rIcEDHLpLRxq6hg+8btD4oBAnUIjABTE="},"tinymce/plugins/contextmenu/plugin-11ad7a2ff14cc47d12a3dd0170741590335e606b0efaa8dc77837409e54afa0a.js":{"logical_path":"tinymce/plugins/contextmenu/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1132,"digest":"11ad7a2ff14cc47d12a3dd0170741590335e606b0efaa8dc77837409e54afa0a","integrity":"sha256-Ea16L/FMxH0So90BcHQVkDNeYGsO+qjcd4N0CeVK+go="},"tinymce/plugins/directionality/plugin-6381fb028625726e50977d00f342fa1f60e39dc794e0ea1c7d9feff8846ab9fc.js":{"logical_path":"tinymce/plugins/directionality/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":727,"digest":"6381fb028625726e50977d00f342fa1f60e39dc794e0ea1c7d9feff8846ab9fc","integrity":"sha256-Y4H7AoYlcm5Ql30A80L6H2DjnceU4OocfZ/v+IRqufw="},"tinymce/plugins/emoticons/img/smiley-cool-bb0e93a050a32df7913e4026b3c88a176998e0e3e073ba06e9b73f6c24227c9c.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-cool.gif","mtime":"2017-05-02T18:30:07+02:00","size":354,"digest":"bb0e93a050a32df7913e4026b3c88a176998e0e3e073ba06e9b73f6c24227c9c","integrity":"sha256-uw6ToFCjLfeRPkAms8iKF2mY4OPgc7oG6bc/bCQifJw="},"tinymce/plugins/emoticons/img/smiley-cry-a0c5f3e7a682449c973c9d9f7c46342081c46920686d2353f57aff91ab907f68.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-cry.gif","mtime":"2017-05-02T18:30:07+02:00","size":329,"digest":"a0c5f3e7a682449c973c9d9f7c46342081c46920686d2353f57aff91ab907f68","integrity":"sha256-oMXz56aCRJyXPJ2ffEY0IIHEaSBobSNT9Xr/kauQf2g="},"tinymce/plugins/emoticons/img/smiley-embarassed-d3cafcb50b335672cb5e9f4600ea9ea261dac7828dd28844d4927c393a25618f.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-embarassed.gif","mtime":"2017-05-02T18:30:07+02:00","size":331,"digest":"d3cafcb50b335672cb5e9f4600ea9ea261dac7828dd28844d4927c393a25618f","integrity":"sha256-08r8tQszVnLLXp9GAOqeomHax4KN0ohE1JJ8OTolYY8="},"tinymce/plugins/emoticons/img/smiley-foot-in-mouth-03fe04d3ed533423ac81f05146584b0c451be3d4a30e76687ceef283ed07071f.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif","mtime":"2017-05-02T18:30:07+02:00","size":342,"digest":"03fe04d3ed533423ac81f05146584b0c451be3d4a30e76687ceef283ed07071f","integrity":"sha256-A/4E0+1TNCOsgfBRRlhLDEUb49SjDnZofO7yg+0HBx8="},"tinymce/plugins/emoticons/img/smiley-frown-1b984bf98931dd1debb54461eb9d83e985f2b2999fe14bcb556d6c0921bc83b0.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-frown.gif","mtime":"2017-05-02T18:30:07+02:00","size":340,"digest":"1b984bf98931dd1debb54461eb9d83e985f2b2999fe14bcb556d6c0921bc83b0","integrity":"sha256-G5hL+Ykx3R3rtURh652D6YXyspmf4UvLVW1sCSG8g7A="},"tinymce/plugins/emoticons/img/smiley-innocent-8db353ef102196f2c6ddf5c4666446de955d7b14fc0957c806c9dbfb48fb0c29.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-innocent.gif","mtime":"2017-05-02T18:30:07+02:00","size":336,"digest":"8db353ef102196f2c6ddf5c4666446de955d7b14fc0957c806c9dbfb48fb0c29","integrity":"sha256-jbNT7xAhlvLG3fXEZmRG3pVdexT8CVfIBsnb+0j7DCk="},"tinymce/plugins/emoticons/img/smiley-kiss-3154c3665356c13ab10fefdbac1fe187fff978a0052037c99cdc4a97103413f2.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-kiss.gif","mtime":"2017-05-02T18:30:07+02:00","size":338,"digest":"3154c3665356c13ab10fefdbac1fe187fff978a0052037c99cdc4a97103413f2","integrity":"sha256-MVTDZlNWwTqxD+/brB/hh//5eKAFIDfJnNxKlxA0E/I="},"tinymce/plugins/emoticons/img/smiley-laughing-8f6adedcd091975ffead171867a6304d908bb6541a6ccb4919286ec6b7d4551e.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-laughing.gif","mtime":"2017-05-02T18:30:07+02:00","size":343,"digest":"8f6adedcd091975ffead171867a6304d908bb6541a6ccb4919286ec6b7d4551e","integrity":"sha256-j2re3NCRl1/+rRcYZ6YwTZCLtlQabMtJGShuxrfUVR4="},"tinymce/plugins/emoticons/img/smiley-money-mouth-f0b9f4f22e237f5dbc851f900fed8d7eca4c954ae6fbc606c0cd8be431d0ac80.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-money-mouth.gif","mtime":"2017-05-02T18:30:07+02:00","size":321,"digest":"f0b9f4f22e237f5dbc851f900fed8d7eca4c954ae6fbc606c0cd8be431d0ac80","integrity":"sha256-8Ln08i4jf128hR+QD+2NfspMlUrm+8YGwM2L5DHQrIA="},"tinymce/plugins/emoticons/img/smiley-sealed-9933b442636b6e537df7b564e2c3f7a2873526eea6b022a98eb1e468e5204c32.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-sealed.gif","mtime":"2017-05-02T18:30:07+02:00","size":323,"digest":"9933b442636b6e537df7b564e2c3f7a2873526eea6b022a98eb1e468e5204c32","integrity":"sha256-mTO0QmNrblN997Vk4sP3ooc1Ju6msCKpjrHkaOUgTDI="},"tinymce/plugins/emoticons/img/smiley-smile-fd89cd460ffcacb7e725e00c0275ef5b3924ce468248e5ff4fb43545571cfa65.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-smile.gif","mtime":"2017-05-02T18:30:07+02:00","size":344,"digest":"fd89cd460ffcacb7e725e00c0275ef5b3924ce468248e5ff4fb43545571cfa65","integrity":"sha256-/YnNRg/8rLfnJeAMAnXvWzkkzkaCSOX/T7Q1RVcc+mU="},"tinymce/plugins/emoticons/img/smiley-surprised-3871f356cb41976d7ae8a5f005e8739e4d014352a8adef9b33f773d81b6e6c01.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-surprised.gif","mtime":"2017-05-02T18:30:07+02:00","size":338,"digest":"3871f356cb41976d7ae8a5f005e8739e4d014352a8adef9b33f773d81b6e6c01","integrity":"sha256-OHHzVstBl2166KXwBehznk0BQ1Kore+bM/dz2BtubAE="},"tinymce/plugins/emoticons/img/smiley-tongue-out-5843c85667a8226dc43be83749fd9fbbc5d20b1577de2b763915d99815d37d47.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-tongue-out.gif","mtime":"2017-05-02T18:30:07+02:00","size":328,"digest":"5843c85667a8226dc43be83749fd9fbbc5d20b1577de2b763915d99815d37d47","integrity":"sha256-WEPIVmeoIm3EO+g3Sf2fu8XSCxV33it2ORXZmBXTfUc="},"tinymce/plugins/emoticons/img/smiley-undecided-d8b9bcbb433951ff3c4ca8dd959ac3844239b98e6d52218833e1485a91f67347.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-undecided.gif","mtime":"2017-05-02T18:30:07+02:00","size":337,"digest":"d8b9bcbb433951ff3c4ca8dd959ac3844239b98e6d52218833e1485a91f67347","integrity":"sha256-2Lm8u0M5Uf88TKjdlZrDhEI5uY5tUiGIM+FIWpH2c0c="},"tinymce/plugins/emoticons/img/smiley-wink-2af75ad7b1c08488505513503e34b15f40005e04a2a9568f698f0945d2d8ba1f.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-wink.gif","mtime":"2017-05-02T18:30:07+02:00","size":350,"digest":"2af75ad7b1c08488505513503e34b15f40005e04a2a9568f698f0945d2d8ba1f","integrity":"sha256-Kvda17HAhIhQVRNQPjSxX0AAXgSiqVaPaY8JRdLYuh8="},"tinymce/plugins/emoticons/img/smiley-yell-bba903fbcb46fce8c68b9e01863fd095b3b1d0e6aa72161f3a88d762a5f90a79.gif":{"logical_path":"tinymce/plugins/emoticons/img/smiley-yell.gif","mtime":"2017-05-02T18:30:07+02:00","size":336,"digest":"bba903fbcb46fce8c68b9e01863fd095b3b1d0e6aa72161f3a88d762a5f90a79","integrity":"sha256-u6kD+8tG/OjGi54Bhj/QlbOx0OaqchYfOojXYqX5Cnk="},"tinymce/plugins/emoticons/plugin-75971da62ade77af7e79b3a560c5ad5ac20ee5f88c7ba236e03902777fdec715.js":{"logical_path":"tinymce/plugins/emoticons/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":912,"digest":"75971da62ade77af7e79b3a560c5ad5ac20ee5f88c7ba236e03902777fdec715","integrity":"sha256-dZcdpired69+ebOlYMWtWsIO5fiMe6I24DkCd3/exxU="},"tinymce/plugins/example/dialog-5c61ad829a494e75c2234019c7941f6dd096ec693cf5a5538c0bf30485097841.html":{"logical_path":"tinymce/plugins/example/dialog.html","mtime":"2017-05-02T18:30:07+02:00","size":213,"digest":"5c61ad829a494e75c2234019c7941f6dd096ec693cf5a5538c0bf30485097841","integrity":"sha256-XGGtgppJTnXCI0AZx5QfbdCW7Gk89aVTjAvzBIUJeEE="},"tinymce/plugins/example/plugin-505b7ee82929e7338700605a4ca63d94dbe6ef2ce2d21104cc01c2adba829868.js":{"logical_path":"tinymce/plugins/example/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":659,"digest":"505b7ee82929e7338700605a4ca63d94dbe6ef2ce2d21104cc01c2adba829868","integrity":"sha256-UFt+6Ckp5zOHAGBaTKY9lNvm7yzi0hEEzAHCrbqCmGg="},"tinymce/plugins/example_dependency/plugin-9bb52c9d45c0f7e4d67c12f546c68095b2d5284aa84ab3a2c45f1d986f464653.js":{"logical_path":"tinymce/plugins/example_dependency/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":74,"digest":"9bb52c9d45c0f7e4d67c12f546c68095b2d5284aa84ab3a2c45f1d986f464653","integrity":"sha256-m7UsnUXA9+TWfBL1RsaAlbLVKEqoSrOixF8dmG9GRlM="},"tinymce/plugins/fullpage/plugin-6cd62d14e4225c1db1d9008a485143d8e8b7a7c699e2ceec16a8012b44875ef5.js":{"logical_path":"tinymce/plugins/fullpage/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":6309,"digest":"6cd62d14e4225c1db1d9008a485143d8e8b7a7c699e2ceec16a8012b44875ef5","integrity":"sha256-bNYtFOQiXB2x2QCKSFFD2Oi3p8aZ4s7sFqgBK0SHXvU="},"tinymce/plugins/fullscreen/plugin-972d5e04901e74c8c657ee88fb3cc245b94aeb005d54adeac00c07a0c5700f7a.js":{"logical_path":"tinymce/plugins/fullscreen/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1676,"digest":"972d5e04901e74c8c657ee88fb3cc245b94aeb005d54adeac00c07a0c5700f7a","integrity":"sha256-ly1eBJAedMjGV+6I+zzCRblK6wBdVK3qwAwHoMVwD3o="},"tinymce/plugins/hr/plugin-92558948637f4349e0f327350ddebdd225313e582f08aacf6cb5549d03fc9a8b.js":{"logical_path":"tinymce/plugins/hr/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":323,"digest":"92558948637f4349e0f327350ddebdd225313e582f08aacf6cb5549d03fc9a8b","integrity":"sha256-klWJSGN/Q0ng8yc1Dd690iUxPlgvCKrPbLVUnQP8mos="},"tinymce/plugins/image/plugin-1c01f4fb87c7523d1ae9bfd7018d883601b59c40edc79c552ef39e8374cb9313.js":{"logical_path":"tinymce/plugins/image/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":8205,"digest":"1c01f4fb87c7523d1ae9bfd7018d883601b59c40edc79c552ef39e8374cb9313","integrity":"sha256-HAH0+4fHUj0a6b/XAY2INgG1nEDtx5xVLvOeg3TLkxM="},"tinymce/plugins/imagetools/plugin-78f93669c67ad2579caa99f1069d5ac7f5dc97af9be40d7b55c036dbe650574f.js":{"logical_path":"tinymce/plugins/imagetools/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":30907,"digest":"78f93669c67ad2579caa99f1069d5ac7f5dc97af9be40d7b55c036dbe650574f","integrity":"sha256-ePk2acZ60lecqpnxBp1ax/Xcl6+b5A17VcA22+ZQV08="},"tinymce/plugins/importcss/plugin-31acbbaaa49b20377e4f5d5a830d9e09babddb9a1c5b7978b843098176e318b5.js":{"logical_path":"tinymce/plugins/importcss/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":2749,"digest":"31acbbaaa49b20377e4f5d5a830d9e09babddb9a1c5b7978b843098176e318b5","integrity":"sha256-May7qqSbIDd+T11agw2eCbq925ocW3l4uEMJgXbjGLU="},"tinymce/plugins/insertdatetime/plugin-923510af4de6343c5ea6b8e679dbaa87d27a7abdaf84ac1f0000ad5722787577.js":{"logical_path":"tinymce/plugins/insertdatetime/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1972,"digest":"923510af4de6343c5ea6b8e679dbaa87d27a7abdaf84ac1f0000ad5722787577","integrity":"sha256-kjUQr03mNDxeprjmeduqh9J6er2vhKwfAACtVyJ4dXc="},"tinymce/plugins/legacyoutput/plugin-ac8239571ea6699f14a76058ea9e53a2a5d9b7e84bed3446d7bd4137b8cdac9f.js":{"logical_path":"tinymce/plugins/legacyoutput/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":3264,"digest":"ac8239571ea6699f14a76058ea9e53a2a5d9b7e84bed3446d7bd4137b8cdac9f","integrity":"sha256-rII5Vx6maZ8Up2BY6p5ToqXZt+hL7TRG171BN7jNrJ8="},"tinymce/plugins/link/plugin-e38346a4005bc814b0117a2dae6eb04ea9a7a8865d658afce406a8394fec0577.js":{"logical_path":"tinymce/plugins/link/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":6995,"digest":"e38346a4005bc814b0117a2dae6eb04ea9a7a8865d658afce406a8394fec0577","integrity":"sha256-44NGpABbyBSwEXotrm6wTqmnqIZdZYr85AaoOU/sBXc="},"tinymce/plugins/lists/plugin-69b25b69974c739637d6459489642fc95bddc8dc564f2b482d7ec94f3082f935.js":{"logical_path":"tinymce/plugins/lists/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":10332,"digest":"69b25b69974c739637d6459489642fc95bddc8dc564f2b482d7ec94f3082f935","integrity":"sha256-abJbaZdMc5Y31kWUiWQvyVvdyNxWTytILX7JTzCC+TU="},"tinymce/plugins/media/plugin-deedd7c7d99c7401ab698f65a1e299819b487e52e662c7866f8700ba8d67a351.js":{"logical_path":"tinymce/plugins/media/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":16223,"digest":"deedd7c7d99c7401ab698f65a1e299819b487e52e662c7866f8700ba8d67a351","integrity":"sha256-3u3Xx9mcdAGraY9loeKZgZtIflLmYseGb4cAuo1no1E="},"tinymce/plugins/nonbreaking/plugin-4b15567cb04f551924ca87994514a09fef17c2c6ea3bb6d50e8ca65dc3243b93.js":{"logical_path":"tinymce/plugins/nonbreaking/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":656,"digest":"4b15567cb04f551924ca87994514a09fef17c2c6ea3bb6d50e8ca65dc3243b93","integrity":"sha256-SxVWfLBPVRkkyoeZRRSgn+8XwsbqO7bVDoymXcMkO5M="},"tinymce/plugins/noneditable/plugin-245b5d236dfea414326bdbcd601333f54d5c2b30a0133cbcbbbae329306b0df3.js":{"logical_path":"tinymce/plugins/noneditable/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1271,"digest":"245b5d236dfea414326bdbcd601333f54d5c2b30a0133cbcbbbae329306b0df3","integrity":"sha256-JFtdI23+pBQya9vNYBMz9U1cKzCgEzy8u7rjKTBrDfM="},"tinymce/plugins/pagebreak/plugin-4c8f35adfd6fb570639318219a4d2ad0e8850619f37ff322ad101683ec00829a.js":{"logical_path":"tinymce/plugins/pagebreak/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1231,"digest":"4c8f35adfd6fb570639318219a4d2ad0e8850619f37ff322ad101683ec00829a","integrity":"sha256-TI81rf1vtXBjkxghmk0q0OiFBhnzf/MirRAWg+wAgpo="},"tinymce/plugins/paste/plugin-f7d8cf6dd7bd24908d5c58c664a54cbe4838c542ec26e35f3c4b198f53f98291.js":{"logical_path":"tinymce/plugins/paste/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":17685,"digest":"f7d8cf6dd7bd24908d5c58c664a54cbe4838c542ec26e35f3c4b198f53f98291","integrity":"sha256-99jPbde9JJCNXFjGZKVMvkg4xULsJuNfPEsZj1P5gpE="},"tinymce/plugins/preview/plugin-d0b7e78697d7a21e237e170bbb0a9c95c40b8dc0118a5ccb6a8347eba1800ade.js":{"logical_path":"tinymce/plugins/preview/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1603,"digest":"d0b7e78697d7a21e237e170bbb0a9c95c40b8dc0118a5ccb6a8347eba1800ade","integrity":"sha256-0LfnhpfXoh4jfhcLuwqclcQLjcARilzLaoNH66GACt4="},"tinymce/plugins/print/plugin-07acb7cbdb293f598245755b4a5a142b9d7b78f6b7a6268e3a447b8941f64472.js":{"logical_path":"tinymce/plugins/print/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":294,"digest":"07acb7cbdb293f598245755b4a5a142b9d7b78f6b7a6268e3a447b8941f64472","integrity":"sha256-B6y3y9spP1mCRXVbSloUK517ePa3piaOOkR7iUH2RHI="},"tinymce/plugins/save/plugin-ef4b15573e862995e82aadd8ede1117ee36a05b335a74640804e24dbca5ebac1.js":{"logical_path":"tinymce/plugins/save/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1151,"digest":"ef4b15573e862995e82aadd8ede1117ee36a05b335a74640804e24dbca5ebac1","integrity":"sha256-70sVVz6GKZXoKq3Y7eERfuNqBbM1p0ZAgE4k28peusE="},"tinymce/plugins/searchreplace/plugin-e1f614dbe20b45955a82405a9aa38c415ab49288d74d0daf881db666eb5549c6.js":{"logical_path":"tinymce/plugins/searchreplace/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":6494,"digest":"e1f614dbe20b45955a82405a9aa38c415ab49288d74d0daf881db666eb5549c6","integrity":"sha256-4fYU2+ILRZVagkBamqOMQVq0kojXTQ2viB22ZutVScY="},"tinymce/plugins/spellchecker/plugin-8c198efb4ebcd3ae3f53b2906bed0265f883bd3fca5f8a30880aac630f4eb787.js":{"logical_path":"tinymce/plugins/spellchecker/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":10049,"digest":"8c198efb4ebcd3ae3f53b2906bed0265f883bd3fca5f8a30880aac630f4eb787","integrity":"sha256-jBmO+068064/U7KQa+0CZfiDvT/KX4owiAqsYw9Ot4c="},"tinymce/plugins/tabfocus/plugin-4b947a94b9cc31d699fed7bb19d2efd9b62fbbd75f0858040bca4ba6402113a0.js":{"logical_path":"tinymce/plugins/tabfocus/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1265,"digest":"4b947a94b9cc31d699fed7bb19d2efd9b62fbbd75f0858040bca4ba6402113a0","integrity":"sha256-S5R6lLnMMdaZ/te7GdLv2bYvu9dfCFgEC8pLpkAhE6A="},"tinymce/plugins/table/plugin-5f3b35988457fcc9d2c2c35601acb041b7b59246dc16d061433cf741cba9ca26.js":{"logical_path":"tinymce/plugins/table/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":46153,"digest":"5f3b35988457fcc9d2c2c35601acb041b7b59246dc16d061433cf741cba9ca26","integrity":"sha256-Xzs1mIRX/MnSwsNWAaywQbe1kkbcFtBhQzz3QcupyiY="},"tinymce/plugins/template/plugin-3a7a1a203ced83abdb7f36897c974eed07a876be4f54809e06ad5b17ec957222.js":{"logical_path":"tinymce/plugins/template/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":4521,"digest":"3a7a1a203ced83abdb7f36897c974eed07a876be4f54809e06ad5b17ec957222","integrity":"sha256-OnoaIDztg6vbfzaJfJdO7Qeodr5PVICeBq1bF+yVciI="},"tinymce/plugins/textcolor/plugin-d05a9d9a6fbdcca3f07c45e6ef1ba8913f48c68a68a871343cdce2075c0ef0b6.js":{"logical_path":"tinymce/plugins/textcolor/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":4146,"digest":"d05a9d9a6fbdcca3f07c45e6ef1ba8913f48c68a68a871343cdce2075c0ef0b6","integrity":"sha256-0Fqdmm+9zKPwfEXm7xuokT9IxopoqHE0PNziB1wO8LY="},"tinymce/plugins/textpattern/plugin-d630f2bc3205e400af41041eb224fe793ae950de5d5f4732f63e36f6819d3f35.js":{"logical_path":"tinymce/plugins/textpattern/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":2746,"digest":"d630f2bc3205e400af41041eb224fe793ae950de5d5f4732f63e36f6819d3f35","integrity":"sha256-1jDyvDIF5ACvQQQesiT+eTrpUN5dX0cy9j429oGdPzU="},"tinymce/plugins/toc/plugin-c20dc2987d4fcd8f859871ec0f2988f3c819c373d2fadad8c5b923b9b749d0ef.js":{"logical_path":"tinymce/plugins/toc/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":2772,"digest":"c20dc2987d4fcd8f859871ec0f2988f3c819c373d2fadad8c5b923b9b749d0ef","integrity":"sha256-wg3CmH1PzY+FmHHsDymI88gZw3PS+trYxbkjubdJ0O8="},"tinymce/plugins/visualblocks/css/visualblocks-e44e30ebe30e4d3763425a9ea38b558ff13106b8e16a30a74ae21b4c174b3417.css":{"logical_path":"tinymce/plugins/visualblocks/css/visualblocks.css","mtime":"2017-05-02T18:30:07+02:00","size":4767,"digest":"e44e30ebe30e4d3763425a9ea38b558ff13106b8e16a30a74ae21b4c174b3417","integrity":"sha256-5E4w6+MOTTdjQlqeo4tVj/ExBrjhajCnSuIbTBdLNBc="},"tinymce/plugins/visualblocks/plugin-6cdc811487cbbee106512812da68f1999fcb3db14f99c5cb4a070be578c9bb0a.js":{"logical_path":"tinymce/plugins/visualblocks/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1154,"digest":"6cdc811487cbbee106512812da68f1999fcb3db14f99c5cb4a070be578c9bb0a","integrity":"sha256-bNyBFIfLvuEGUSgS2mjxmZ/LPbFPmcXLSgcL5XjJuwo="},"tinymce/plugins/visualchars/plugin-0e8c1d7cba0b1471014fe3359a04ff4d4d981070777cdf31f4d95a732b83581f.js":{"logical_path":"tinymce/plugins/visualchars/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1179,"digest":"0e8c1d7cba0b1471014fe3359a04ff4d4d981070777cdf31f4d95a732b83581f","integrity":"sha256-DowdfLoLFHEBT+M1mgT/TU2YEHB3fN8x9NlacyuDWB8="},"tinymce/plugins/wordcount/plugin-d9ec660425815b227f5c3fbdc8b5d066a0530ec824ba37b47169aabd3f864ea7.js":{"logical_path":"tinymce/plugins/wordcount/plugin.js","mtime":"2017-01-05T19:24:14+01:00","size":12048,"digest":"d9ec660425815b227f5c3fbdc8b5d066a0530ec824ba37b47169aabd3f864ea7","integrity":"sha256-2exmBCWBWyJ/XD+9yLXQZqBTDsgkuje0cWmqvT+GTqc="},"tinymce/skins/lightgray/content.inline.min-5bb5c69d072a2d17d11e0c54bc81b6f863e5b5a126060d862970f0c22f5d49ec.css":{"logical_path":"tinymce/skins/lightgray/content.inline.min.css","mtime":"2017-05-02T18:30:07+02:00","size":2770,"digest":"5bb5c69d072a2d17d11e0c54bc81b6f863e5b5a126060d862970f0c22f5d49ec","integrity":"sha256-W7XGnQcqLRfRHgxUvIG2+GPltaEmBg2GKXDwwi9dSew="},"tinymce/skins/lightgray/content.min-92e6da84fb5c2dfcc35f34d311ca1914b19064ada45fa9a08ff2a68c0fc0e657.css":{"logical_path":"tinymce/skins/lightgray/content.min.css","mtime":"2017-05-02T18:30:07+02:00","size":3194,"digest":"92e6da84fb5c2dfcc35f34d311ca1914b19064ada45fa9a08ff2a68c0fc0e657","integrity":"sha256-kubahPtcLfzDXzTTEcoZFLGQZK2kX6mgj/KmjA/A5lc="},"tinymce/skins/lightgray/fonts/tinymce-small-a10fc4343d95b716c16d77463d475be5c079599ea67e1cd2bd3a94d5e7f508f9.eot":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.eot","mtime":"2017-05-02T18:30:07+02:00","size":9492,"digest":"a10fc4343d95b716c16d77463d475be5c079599ea67e1cd2bd3a94d5e7f508f9","integrity":"sha256-oQ/END2VtxbBbXdGPUdb5cB5WZ6mfhzSvTqU1ef1CPk="},"tinymce/skins/lightgray/fonts/tinymce-small-e7773001446ab937e1d8d4bd5e8dbd9b31d112037353a14b319e36dd010ed8ee.svg":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.svg","mtime":"2017-05-02T18:30:07+02:00","size":24727,"digest":"e7773001446ab937e1d8d4bd5e8dbd9b31d112037353a14b319e36dd010ed8ee","integrity":"sha256-53cwAURquTfh2NS9Xo29mzHREgNzU6FLMZ423QEO2O4="},"tinymce/skins/lightgray/fonts/tinymce-small-2f657502906d6f5c3fc8df3a82969114ebe030addfdc061c60c974b0f515fd09.ttf":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.ttf","mtime":"2017-05-02T18:30:07+02:00","size":9304,"digest":"2f657502906d6f5c3fc8df3a82969114ebe030addfdc061c60c974b0f515fd09","integrity":"sha256-L2V1ApBtb1w/yN86gpaRFOvgMK3f3AYcYMl0sPUV/Qk="},"tinymce/skins/lightgray/fonts/tinymce-small-d3efbb678ca6de5632902bd93772746ba2f8e4e2322b953936e12694a183aa31.woff":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce-small.woff","mtime":"2017-05-02T18:30:07+02:00","size":9380,"digest":"d3efbb678ca6de5632902bd93772746ba2f8e4e2322b953936e12694a183aa31","integrity":"sha256-0++7Z4ym3lYykCvZN3J0a6L45OIyK5U5NuEmlKGDqjE="},"tinymce/skins/lightgray/fonts/tinymce-2e9c4a68fde992476e0db9e44128cb1f2e898f0de0b80f552a8acb52bb7ca0db.eot":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.eot","mtime":"2017-05-02T18:30:07+02:00","size":17572,"digest":"2e9c4a68fde992476e0db9e44128cb1f2e898f0de0b80f552a8acb52bb7ca0db","integrity":"sha256-LpxKaP3pkkduDbnkQSjLHy6Jjw3guA9VKorLUrt8oNs="},"tinymce/skins/lightgray/fonts/tinymce-2094ddadc265c7f33570475fc78ef7adcdcb814e49060d17f5b4c4f8d1cb7ec6.svg":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.svg","mtime":"2017-05-02T18:30:07+02:00","size":45991,"digest":"2094ddadc265c7f33570475fc78ef7adcdcb814e49060d17f5b4c4f8d1cb7ec6","integrity":"sha256-IJTdrcJlx/M1cEdfx473rc3LgU5JBg0X9bTE+NHLfsY="},"tinymce/skins/lightgray/fonts/tinymce-477ea2d46c1a975dd492af4c10235fabfd09069595779cce00ea0381ca9b4a20.ttf":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.ttf","mtime":"2017-05-02T18:30:07+02:00","size":17408,"digest":"477ea2d46c1a975dd492af4c10235fabfd09069595779cce00ea0381ca9b4a20","integrity":"sha256-R36i1Gwal13Ukq9MECNfq/0JBpWVd5zOAOoDgcqbSiA="},"tinymce/skins/lightgray/fonts/tinymce-1ebc636bb24cbea637946ba8c22cbf4f35d8343ba9763045d2aee59e3714ae78.woff":{"logical_path":"tinymce/skins/lightgray/fonts/tinymce.woff","mtime":"2017-05-02T18:30:07+02:00","size":17484,"digest":"1ebc636bb24cbea637946ba8c22cbf4f35d8343ba9763045d2aee59e3714ae78","integrity":"sha256-Hrxja7JMvqY3lGuowiy/TzXYNDupdjBF0q7lnjcUrng="},"tinymce/skins/lightgray/img/anchor-2861666fd107d278d4449970615136d06d7f746be9bb19072cf9c8f30e565e1e.gif":{"logical_path":"tinymce/skins/lightgray/img/anchor.gif","mtime":"2017-05-02T18:30:07+02:00","size":53,"digest":"2861666fd107d278d4449970615136d06d7f746be9bb19072cf9c8f30e565e1e","integrity":"sha256-KGFmb9EH0njURJlwYVE20G1/dGvpuxkHLPnI8w5WXh4="},"tinymce/skins/lightgray/img/loader-eb7cfd3d959b2e09c170f532e29f8b825f9bc770b2279fde58e595617753e244.gif":{"logical_path":"tinymce/skins/lightgray/img/loader.gif","mtime":"2017-05-02T18:30:07+02:00","size":2608,"digest":"eb7cfd3d959b2e09c170f532e29f8b825f9bc770b2279fde58e595617753e244","integrity":"sha256-63z9PZWbLgnBcPUy4p+Lgl+bx3CyJ5/eWOWVYXdT4kQ="},"tinymce/skins/lightgray/img/object-e6a15e52bc4a17b085073ba8debd4708ead6ae3d4cbeb3880c65cb7afc489777.gif":{"logical_path":"tinymce/skins/lightgray/img/object.gif","mtime":"2017-05-02T18:30:07+02:00","size":152,"digest":"e6a15e52bc4a17b085073ba8debd4708ead6ae3d4cbeb3880c65cb7afc489777","integrity":"sha256-5qFeUrxKF7CFBzuo3r1HCOrWrj1MvrOIDGXLevxIl3c="},"tinymce/skins/lightgray/img/trans-9cf020d7c3bba7f5ab10cda54aabef934f906d4f9a3acf99e9e7dc6c98579635.gif":{"logical_path":"tinymce/skins/lightgray/img/trans.gif","mtime":"2017-05-02T18:30:07+02:00","size":43,"digest":"9cf020d7c3bba7f5ab10cda54aabef934f906d4f9a3acf99e9e7dc6c98579635","integrity":"sha256-nPAg18O7p/WrEM2lSqvvk0+QbU+aOs+Z6efcbJhXljU="},"tinymce/skins/lightgray/skin.ie7.min-f7e68049f5f1ba278870818429ef905d07846781701c40657d9d24ca6638cced.css":{"logical_path":"tinymce/skins/lightgray/skin.ie7.min.css","mtime":"2017-05-02T18:30:07+02:00","size":34904,"digest":"f7e68049f5f1ba278870818429ef905d07846781701c40657d9d24ca6638cced","integrity":"sha256-9+aASfXxuieIcIGEKe+QXQeEZ4FwHEBlfZ0kymY4zO0="},"tinymce/skins/lightgray/skin.min-6d81e56f6d40695f114abe4db834ccb7bf7d89cfa07c10c2cbaebb8066ace27b.css":{"logical_path":"tinymce/skins/lightgray/skin.min.css","mtime":"2017-05-02T18:30:07+02:00","size":38232,"digest":"6d81e56f6d40695f114abe4db834ccb7bf7d89cfa07c10c2cbaebb8066ace27b","integrity":"sha256-bYHlb21AaV8RSr5NuDTMt799ic+gfBDCy667gGas4ns="},"tinymce/themes/inlite/theme-95446aa7daedd3754400947ecc50a607971ef2068d093e7c9a889c5908ff6f40.js":{"logical_path":"tinymce/themes/inlite/theme.js","mtime":"2017-01-05T19:24:14+01:00","size":16382,"digest":"95446aa7daedd3754400947ecc50a607971ef2068d093e7c9a889c5908ff6f40","integrity":"sha256-lURqp9rt03VEAJR+zFCmB5ce8gaNCT58moicWQj/b0A="},"tinymce/themes/modern/theme-f0a3cccc241fa5d46bde8e19160e3f351368881a7c3cdb0c4ce84da11901064c.js":{"logical_path":"tinymce/themes/modern/theme.js","mtime":"2017-05-02T18:30:07+02:00","size":13155,"digest":"f0a3cccc241fa5d46bde8e19160e3f351368881a7c3cdb0c4ce84da11901064c","integrity":"sha256-8KPMzCQfpdRr3o4ZFg4/NRNoiBp8PNsMTOhNoRkBBkw="},"active_admin-f7ef723200f732c68bf56214e1c6ac931008f3de4eba9dcd29e140696b46f0a0.css":{"logical_path":"active_admin.css","mtime":"2017-05-04T21:49:46+02:00","size":79039,"digest":"f7ef723200f732c68bf56214e1c6ac931008f3de4eba9dcd29e140696b46f0a0","integrity":"sha256-9+9yMgD3MsaL9WIU4caskxAI895Oup3NKeFAaWtG8KA="},"active_admin-2b4f9b9e35d46bbace92d14d078b3568dcdbd0183e52827e5c44c54dc7c96266.js":{"logical_path":"active_admin.js","mtime":"2017-05-04T23:06:57+02:00","size":692870,"digest":"2b4f9b9e35d46bbace92d14d078b3568dcdbd0183e52827e5c44c54dc7c96266","integrity":"sha256-K0+bnjXUa7rOktFNB4s1aNzb0Bg+UoJ+XETFTcfJYmY="},"tinymce-8c0346bfe88f5c5405046ebd1cdbd1c7192d503d93623ffc00d2d7b430116b69.js":{"logical_path":"tinymce.js","mtime":"2017-05-04T23:06:57+02:00","size":971,"digest":"8c0346bfe88f5c5405046ebd1cdbd1c7192d503d93623ffc00d2d7b430116b69","integrity":"sha256-jANGv+iPXFQFBG69HNvRxxktUD2TYj/8ANLXtDARa2k="},"application-12922f8d27744fe5233d0c8f855c532cad64561e248ebaa05442fd6b59f73af2.css":{"logical_path":"application.css","mtime":"2017-05-04T23:06:57+02:00","size":174051,"digest":"12922f8d27744fe5233d0c8f855c532cad64561e248ebaa05442fd6b59f73af2","integrity":"sha256-EpIvjSd0T+UjPQyPhVxTLK1kVh4kjrqgVEL9a1n3OvI="},"brasil-0e293d8c9a86f8a98fea23013e9c365b32fd0022a1496309eeabcd97f7be1288.png":{"logical_path":"brasil.png","mtime":"2017-05-04T22:06:49+02:00","size":660,"digest":"0e293d8c9a86f8a98fea23013e9c365b32fd0022a1496309eeabcd97f7be1288","integrity":"sha256-Dik9jJqG+KmP6iMBPpw2WzL9ACKhSWMJ7qvNl/e+Eog="},"application-88a91754f1835c3e582224375aeeccd1d3dce637f71dded6ca23ce71a228d339.js":{"logical_path":"application.js","mtime":"2017-05-04T23:06:57+02:00","size":2376693,"digest":"88a91754f1835c3e582224375aeeccd1d3dce637f71dded6ca23ce71a228d339","integrity":"sha256-iKkXVPGDXD5YIiQ3Wu7M0dPc5jf3Hd7WyiPOcaIo0zk="},"tinymce/preinit-4eb7f4ac58f2f450d20185cf83ca1d8550d2a1419141bfe70eb5a2c4afe67349.js":{"logical_path":"tinymce/preinit.js","mtime":"2017-05-02T18:30:07+02:00","size":248,"digest":"4eb7f4ac58f2f450d20185cf83ca1d8550d2a1419141bfe70eb5a2c4afe67349","integrity":"sha256-Trf0rFjy9FDSAYXPg8odhVDSoUGRQb/nDrWixK/mc0k="},"tinymce/tinymce-d7f6811cb92d1e7638015669d3ec90175d4cb1309ecad56ff9d757b25fe18172.js":{"logical_path":"tinymce/tinymce.js","mtime":"2017-05-02T18:30:07+02:00","size":1299472,"digest":"d7f6811cb92d1e7638015669d3ec90175d4cb1309ecad56ff9d757b25fe18172","integrity":"sha256-1/aBHLktHnY4AVZp0+yQF11MsTCeytVv+ddXsl/hgXI="},"tinymce/plugins/anchor/plugin-f642e411a2a702256b0069e5baf65ef62d31c34e92e993123aad88e22ee295ce.js":{"logical_path":"tinymce/plugins/anchor/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":1189,"digest":"f642e411a2a702256b0069e5baf65ef62d31c34e92e993123aad88e22ee295ce","integrity":"sha256-9kLkEaKnAiVrAGnluvZe9i0xw06S6ZMSOq2I4i7ilc4="},"tinymce/plugins/importcss/plugin-5386dd0994914eaf7431876cb0b2d9cf505a8d91c0f8fd739c5166bd21f6376d.js":{"logical_path":"tinymce/plugins/importcss/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":2748,"digest":"5386dd0994914eaf7431876cb0b2d9cf505a8d91c0f8fd739c5166bd21f6376d","integrity":"sha256-U4bdCZSRTq90MYdssLLZz1BajZHA+P1znFFmvSH2N20="},"tinymce/plugins/lists/plugin-c56f1732e8069ebaca97437c3b99a69ed2663f558cd50a817ee721661673510c.js":{"logical_path":"tinymce/plugins/lists/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":14636,"digest":"c56f1732e8069ebaca97437c3b99a69ed2663f558cd50a817ee721661673510c","integrity":"sha256-xW8XMugGnrrKl0N8O5mmntJmP1WM1QqBfuchZhZzUQw="},"tinymce/plugins/media/plugin-f5341fd5503efcd25c050b419868be59073ba2e0bf58bf7a73877c052109e0de.js":{"logical_path":"tinymce/plugins/media/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":16453,"digest":"f5341fd5503efcd25c050b419868be59073ba2e0bf58bf7a73877c052109e0de","integrity":"sha256-9TQf1VA+/NJcBQtBmGi+WQc7ouC/WL96c4d8BSEJ4N4="},"tinymce/plugins/paste/plugin-524c40b866f13bbb8fea23d9b8529d00df10b94695e646bd4303eaff9f658e7c.js":{"logical_path":"tinymce/plugins/paste/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":18069,"digest":"524c40b866f13bbb8fea23d9b8529d00df10b94695e646bd4303eaff9f658e7c","integrity":"sha256-UkxAuGbxO7uP6iPZuFKdAN8QuUaV5ka9QwPq/59ljnw="},"tinymce/plugins/table/plugin-58e9f99c4c09431f853051ab6a87504487a9042e520c68e84d9adc59346659a6.js":{"logical_path":"tinymce/plugins/table/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":47127,"digest":"58e9f99c4c09431f853051ab6a87504487a9042e520c68e84d9adc59346659a6","integrity":"sha256-WOn5nEwJQx+FMFGraodQRIepBC5SDGjoTZrcWTRmWaY="},"tinymce/plugins/wordcount/plugin-649a9308c0183e56c3ba78002d7b480ecbba84ff30361c36bcea6ed07ea6be4f.js":{"logical_path":"tinymce/plugins/wordcount/plugin.js","mtime":"2017-05-02T18:30:07+02:00","size":12031,"digest":"649a9308c0183e56c3ba78002d7b480ecbba84ff30361c36bcea6ed07ea6be4f","integrity":"sha256-ZJqTCMAYPlbDungALXtIDsu6hP8wNhw2vOpu0H6mvk8="},"tinymce/themes/inlite/theme-a400085213d427d78d8a1e35089ae6cd00831e51d33a3342a198d1bc62851640.js":{"logical_path":"tinymce/themes/inlite/theme.js","mtime":"2017-05-02T18:30:07+02:00","size":16579,"digest":"a400085213d427d78d8a1e35089ae6cd00831e51d33a3342a198d1bc62851640","integrity":"sha256-pAAIUhPUJ9eNih41CJrmzQCDHlHTOjNCoZjRvGKFFkA="}},"assets":{"active_admin.css":"active_admin-f7ef723200f732c68bf56214e1c6ac931008f3de4eba9dcd29e140696b46f0a0.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-2b4f9b9e35d46bbace92d14d078b3568dcdbd0183e52827e5c44c54dc7c96266.js","layers-2x.png":"layers-2x-066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf.png","layers.png":"layers-1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6.png","marker-icon-2x.png":"marker-icon-2x-2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d.png","marker-icon.png":"marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png","marker-shadow.png":"marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png","tinymce.js":"tinymce-8c0346bfe88f5c5405046ebd1cdbd1c7192d503d93623ffc00d2d7b430116b69.js","application.css":"application-12922f8d27744fe5233d0c8f855c532cad64561e248ebaa05442fd6b59f73af2.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-9fcdd7ddd4d40de29c3809b59688c668b85f5628e219d4cd8a8810b72a64533b.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-88a91754f1835c3e582224375aeeccd1d3dce637f71dded6ca23ce71a228d339.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-bb0e93a050a32df7913e4026b3c88a176998e0e3e073ba06e9b73f6c24227c9c.gif","tinymce/plugins/emoticons/img/smiley-cry.gif":"tinymce/plugins/emoticons/img/smiley-cry-a0c5f3e7a682449c973c9d9f7c46342081c46920686d2353f57aff91ab907f68.gif","tinymce/plugins/emoticons/img/smiley-embarassed.gif":"tinymce/plugins/emoticons/img/smiley-embarassed-d3cafcb50b335672cb5e9f4600ea9ea261dac7828dd28844d4927c393a25618f.gif","tinymce/plugins/emoticons/img/smiley-foot-in-mouth.gif":"tinymce/plugins/emoticons/img/smiley-foot-in-mouth-03fe04d3ed533423ac81f05146584b0c451be3d4a30e76687ceef283ed07071f.gif","tinymce/plugins/emoticons/img/smiley-frown.gif":"tinymce/plugins/emoticons/img/smiley-frown-1b984bf98931dd1debb54461eb9d83e985f2b2999fe14bcb556d6c0921bc83b0.gif","tinymce/plugins/emoticons/img/smiley-innocent.gif":"tinymce/plugins/emoticons/img/smiley-innocent-8db353ef102196f2c6ddf5c4666446de955d7b14fc0957c806c9dbfb48fb0c29.gif","tinymce/plugins/emoticons/img/smiley-kiss.gif":"tinymce/plugins/emoticons/img/smiley-kiss-3154c3665356c13ab10fefdbac1fe187fff978a0052037c99cdc4a97103413f2.gif","tinymce/plugins/emoticons/img/smiley-laughing.gif":"tinymce/plugins/emoticons/img/smiley-laughing-8f6adedcd091975ffead171867a6304d908bb6541a6ccb4919286ec6b7d4551e.gif","tinymce/plugins/emoticons/img/smiley-money-mouth.gif":"tinymce/plugins/emoticons/img/smiley-money-mouth-f0b9f4f22e237f5dbc851f900fed8d7eca4c954ae6fbc606c0cd8be431d0ac80.gif","tinymce/plugins/emoticons/img/smiley-sealed.gif":"tinymce/plugins/emoticons/img/smiley-sealed-9933b442636b6e537df7b564e2c3f7a2873526eea6b022a98eb1e468e5204c32.gif","tinymce/plugins/emoticons/img/smiley-smile.gif":"tinymce/plugins/emoticons/img/smiley-smile-fd89cd460ffcacb7e725e00c0275ef5b3924ce468248e5ff4fb43545571cfa65.gif","tinymce/plugins/emoticons/img/smiley-surprised.gif":"tinymce/plugins/emoticons/img/smiley-surprised-3871f356cb41976d7ae8a5f005e8739e4d014352a8adef9b33f773d81b6e6c01.gif","tinymce/plugins/emoticons/img/smiley-tongue-out.gif":"tinymce/plugins/emoticons/img/smiley-tongue-out-5843c85667a8226dc43be83749fd9fbbc5d20b1577de2b763915d99815d37d47.gif","tinymce/plugins/emoticons/img/smiley-undecided.gif":"tinymce/plugins/emoticons/img/smiley-undecided-d8b9bcbb433951ff3c4ca8dd959ac3844239b98e6d52218833e1485a91f67347.gif","tinymce/plugins/emoticons/img/smiley-wink.gif":"tinymce/plugins/emoticons/img/smiley-wink-2af75ad7b1c08488505513503e34b15f40005e04a2a9568f698f0945d2d8ba1f.gif","tinymce/plugins/emoticons/img/smiley-yell.gif":"tinymce/plugins/emoticons/img/smiley-yell-bba903fbcb46fce8c68b9e01863fd095b3b1d0e6aa72161f3a88d762a5f90a79.gif","tinymce/skins/lightgray/img/anchor.gif":"tinymce/skins/lightgray/img/anchor-2861666fd107d278d4449970615136d06d7f746be9bb19072cf9c8f30e565e1e.gif","tinymce/skins/lightgray/img/loader.gif":"tinymce/skins/lightgray/img/loader-eb7cfd3d959b2e09c170f532e29f8b825f9bc770b2279fde58e595617753e244.gif","tinymce/skins/lightgray/img/object.gif":"tinymce/skins/lightgray/img/object-e6a15e52bc4a17b085073ba8debd4708ead6ae3d4cbeb3880c65cb7afc489777.gif","tinymce/skins/lightgray/img/trans.gif":"tinymce/skins/lightgray/img/trans-9cf020d7c3bba7f5ab10cda54aabef934f906d4f9a3acf99e9e7dc6c98579635.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-275e24af4bf53bfb60f7fef218163106ad0648b8ad384ecfab9b4fd52f48603d.js","tinymce/langs/readme.md":"tinymce/langs/readme-5a8b6a04d57b5c88e3fb7f2a870b8e2d3a48ec03ce6474206c41df78c155b2de.md","tinymce/license.txt":"tinymce/license-5fda611b8191f00121f161a93e7399cdb71789dba923e8ed09b50a1f78d32c5e.txt","tinymce/plugins/advlist/plugin.js":"tinymce/plugins/advlist/plugin-d0bd2b90c1aaf60ddbb048d955f7fbf76e303b5f3227f8e4dc83207548c3fc46.js","tinymce/plugins/anchor/plugin.js":"tinymce/plugins/anchor/plugin-f642e411a2a702256b0069e5baf65ef62d31c34e92e993123aad88e22ee295ce.js","tinymce/plugins/autolink/plugin.js":"tinymce/plugins/autolink/plugin-9d514baa6f3816a43008241db075d724fcb274632fad3db792e87e5feb6c5d0f.js","tinymce/plugins/autoresize/plugin.js":"tinymce/plugins/autoresize/plugin-7cd05d5431f8713c948291ad40ef63eabe44908bd831d5675afacaca296a4d7b.js","tinymce/plugins/autosave/plugin.js":"tinymce/plugins/autosave/plugin-43448ce025b165ed1b509554cadf02cda4ed104ccfae6d0708379f02e981220f.js","tinymce/plugins/bbcode/plugin.js":"tinymce/plugins/bbcode/plugin-dae2432b6477ca1acc3bd6ac7af33c379797c7f2dcf2c82513475d4c234d851d.js","tinymce/plugins/charmap/plugin.js":"tinymce/plugins/charmap/plugin-ee3f9ed5d0135f19975a4609701b4b4546f00ab1afee9b74a38bb3d76ab94eca.js","tinymce/plugins/code/plugin.js":"tinymce/plugins/code/plugin-093b2519070297197c89cabd2b7bf7b7920786fa3ae5b055322fb16d51d113d4.js","tinymce/plugins/codesample/css/prism.css":"tinymce/plugins/codesample/css/prism-1988b66704b4d23e78c6c20c38a6856cbc1f0be96d6d60a3a0b12f4408f1057e.css","tinymce/plugins/codesample/plugin.dev.js":"tinymce/plugins/codesample/plugin.dev.js","tinymce/plugins/codesample/plugin.js":"tinymce/plugins/codesample/plugin-edd0d2e7f821002843d7726182d9a8b1520fcabb753b95ee7956180337be23da.js","tinymce/plugins/colorpicker/plugin.js":"tinymce/plugins/colorpicker/plugin-526e08119d96ecdeeb21c1031cba4b471aba860fbc6ed0f8a010275088c00531.js","tinymce/plugins/contextmenu/plugin.js":"tinymce/plugins/contextmenu/plugin-11ad7a2ff14cc47d12a3dd0170741590335e606b0efaa8dc77837409e54afa0a.js","tinymce/plugins/directionality/plugin.js":"tinymce/plugins/directionality/plugin-6381fb028625726e50977d00f342fa1f60e39dc794e0ea1c7d9feff8846ab9fc.js","tinymce/plugins/emoticons/plugin.js":"tinymce/plugins/emoticons/plugin-75971da62ade77af7e79b3a560c5ad5ac20ee5f88c7ba236e03902777fdec715.js","tinymce/plugins/example/dialog.html":"tinymce/plugins/example/dialog-5c61ad829a494e75c2234019c7941f6dd096ec693cf5a5538c0bf30485097841.html","tinymce/plugins/example/plugin.js":"tinymce/plugins/example/plugin-505b7ee82929e7338700605a4ca63d94dbe6ef2ce2d21104cc01c2adba829868.js","tinymce/plugins/example_dependency/plugin.js":"tinymce/plugins/example_dependency/plugin-9bb52c9d45c0f7e4d67c12f546c68095b2d5284aa84ab3a2c45f1d986f464653.js","tinymce/plugins/fullpage/plugin.js":"tinymce/plugins/fullpage/plugin-6cd62d14e4225c1db1d9008a485143d8e8b7a7c699e2ceec16a8012b44875ef5.js","tinymce/plugins/fullscreen/plugin.js":"tinymce/plugins/fullscreen/plugin-972d5e04901e74c8c657ee88fb3cc245b94aeb005d54adeac00c07a0c5700f7a.js","tinymce/plugins/hr/plugin.js":"tinymce/plugins/hr/plugin-92558948637f4349e0f327350ddebdd225313e582f08aacf6cb5549d03fc9a8b.js","tinymce/plugins/image/plugin.js":"tinymce/plugins/image/plugin-1c01f4fb87c7523d1ae9bfd7018d883601b59c40edc79c552ef39e8374cb9313.js","tinymce/plugins/imagetools/plugin.js":"tinymce/plugins/imagetools/plugin-78f93669c67ad2579caa99f1069d5ac7f5dc97af9be40d7b55c036dbe650574f.js","tinymce/plugins/importcss/plugin.js":"tinymce/plugins/importcss/plugin-5386dd0994914eaf7431876cb0b2d9cf505a8d91c0f8fd739c5166bd21f6376d.js","tinymce/plugins/insertdatetime/plugin.js":"tinymce/plugins/insertdatetime/plugin-923510af4de6343c5ea6b8e679dbaa87d27a7abdaf84ac1f0000ad5722787577.js","tinymce/plugins/layer/plugin.js":"tinymce/plugins/layer/plugin.js","tinymce/plugins/legacyoutput/plugin.js":"tinymce/plugins/legacyoutput/plugin-ac8239571ea6699f14a76058ea9e53a2a5d9b7e84bed3446d7bd4137b8cdac9f.js","tinymce/plugins/link/plugin.js":"tinymce/plugins/link/plugin-e38346a4005bc814b0117a2dae6eb04ea9a7a8865d658afce406a8394fec0577.js","tinymce/plugins/lists/plugin.js":"tinymce/plugins/lists/plugin-c56f1732e8069ebaca97437c3b99a69ed2663f558cd50a817ee721661673510c.js","tinymce/plugins/media/plugin.js":"tinymce/plugins/media/plugin-f5341fd5503efcd25c050b419868be59073ba2e0bf58bf7a73877c052109e0de.js","tinymce/plugins/nonbreaking/plugin.js":"tinymce/plugins/nonbreaking/plugin-4b15567cb04f551924ca87994514a09fef17c2c6ea3bb6d50e8ca65dc3243b93.js","tinymce/plugins/noneditable/plugin.js":"tinymce/plugins/noneditable/plugin-245b5d236dfea414326bdbcd601333f54d5c2b30a0133cbcbbbae329306b0df3.js","tinymce/plugins/pagebreak/plugin.js":"tinymce/plugins/pagebreak/plugin-4c8f35adfd6fb570639318219a4d2ad0e8850619f37ff322ad101683ec00829a.js","tinymce/plugins/paste/plugin.dev.js":"tinymce/plugins/paste/plugin.dev.js","tinymce/plugins/paste/plugin.js":"tinymce/plugins/paste/plugin-524c40b866f13bbb8fea23d9b8529d00df10b94695e646bd4303eaff9f658e7c.js","tinymce/plugins/preview/plugin.js":"tinymce/plugins/preview/plugin-d0b7e78697d7a21e237e170bbb0a9c95c40b8dc0118a5ccb6a8347eba1800ade.js","tinymce/plugins/print/plugin.js":"tinymce/plugins/print/plugin-07acb7cbdb293f598245755b4a5a142b9d7b78f6b7a6268e3a447b8941f64472.js","tinymce/plugins/save/plugin.js":"tinymce/plugins/save/plugin-ef4b15573e862995e82aadd8ede1117ee36a05b335a74640804e24dbca5ebac1.js","tinymce/plugins/searchreplace/plugin.js":"tinymce/plugins/searchreplace/plugin-e1f614dbe20b45955a82405a9aa38c415ab49288d74d0daf881db666eb5549c6.js","tinymce/plugins/spellchecker/plugin.dev.js":"tinymce/plugins/spellchecker/plugin.dev.js","tinymce/plugins/spellchecker/plugin.js":"tinymce/plugins/spellchecker/plugin-8c198efb4ebcd3ae3f53b2906bed0265f883bd3fca5f8a30880aac630f4eb787.js","tinymce/plugins/tabfocus/plugin.js":"tinymce/plugins/tabfocus/plugin-4b947a94b9cc31d699fed7bb19d2efd9b62fbbd75f0858040bca4ba6402113a0.js","tinymce/plugins/table/plugin.dev.js":"tinymce/plugins/table/plugin.dev.js","tinymce/plugins/table/plugin.js":"tinymce/plugins/table/plugin-58e9f99c4c09431f853051ab6a87504487a9042e520c68e84d9adc59346659a6.js","tinymce/plugins/template/plugin.js":"tinymce/plugins/template/plugin-3a7a1a203ced83abdb7f36897c974eed07a876be4f54809e06ad5b17ec957222.js","tinymce/plugins/textcolor/plugin.js":"tinymce/plugins/textcolor/plugin-d05a9d9a6fbdcca3f07c45e6ef1ba8913f48c68a68a871343cdce2075c0ef0b6.js","tinymce/plugins/textpattern/plugin.js":"tinymce/plugins/textpattern/plugin-d630f2bc3205e400af41041eb224fe793ae950de5d5f4732f63e36f6819d3f35.js","tinymce/plugins/toc/plugin.js":"tinymce/plugins/toc/plugin-c20dc2987d4fcd8f859871ec0f2988f3c819c373d2fadad8c5b923b9b749d0ef.js","tinymce/plugins/visualblocks/css/visualblocks.css":"tinymce/plugins/visualblocks/css/visualblocks-e44e30ebe30e4d3763425a9ea38b558ff13106b8e16a30a74ae21b4c174b3417.css","tinymce/plugins/visualblocks/plugin.js":"tinymce/plugins/visualblocks/plugin-6cdc811487cbbee106512812da68f1999fcb3db14f99c5cb4a070be578c9bb0a.js","tinymce/plugins/visualchars/plugin.js":"tinymce/plugins/visualchars/plugin-0e8c1d7cba0b1471014fe3359a04ff4d4d981070777cdf31f4d95a732b83581f.js","tinymce/plugins/wordcount/plugin.js":"tinymce/plugins/wordcount/plugin-649a9308c0183e56c3ba78002d7b480ecbba84ff30361c36bcea6ed07ea6be4f.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-5bb5c69d072a2d17d11e0c54bc81b6f863e5b5a126060d862970f0c22f5d49ec.css","tinymce/skins/lightgray/content.min.css":"tinymce/skins/lightgray/content.min-92e6da84fb5c2dfcc35f34d311ca1914b19064ada45fa9a08ff2a68c0fc0e657.css","tinymce/skins/lightgray/fonts/tinymce-small.eot":"tinymce/skins/lightgray/fonts/tinymce-small-a10fc4343d95b716c16d77463d475be5c079599ea67e1cd2bd3a94d5e7f508f9.eot","tinymce/skins/lightgray/fonts/tinymce-small.svg":"tinymce/skins/lightgray/fonts/tinymce-small-e7773001446ab937e1d8d4bd5e8dbd9b31d112037353a14b319e36dd010ed8ee.svg","tinymce/skins/lightgray/fonts/tinymce-small.ttf":"tinymce/skins/lightgray/fonts/tinymce-small-2f657502906d6f5c3fc8df3a82969114ebe030addfdc061c60c974b0f515fd09.ttf","tinymce/skins/lightgray/fonts/tinymce-small.woff":"tinymce/skins/lightgray/fonts/tinymce-small-d3efbb678ca6de5632902bd93772746ba2f8e4e2322b953936e12694a183aa31.woff","tinymce/skins/lightgray/fonts/tinymce.eot":"tinymce/skins/lightgray/fonts/tinymce-2e9c4a68fde992476e0db9e44128cb1f2e898f0de0b80f552a8acb52bb7ca0db.eot","tinymce/skins/lightgray/fonts/tinymce.svg":"tinymce/skins/lightgray/fonts/tinymce-2094ddadc265c7f33570475fc78ef7adcdcb814e49060d17f5b4c4f8d1cb7ec6.svg","tinymce/skins/lightgray/fonts/tinymce.ttf":"tinymce/skins/lightgray/fonts/tinymce-477ea2d46c1a975dd492af4c10235fabfd09069595779cce00ea0381ca9b4a20.ttf","tinymce/skins/lightgray/fonts/tinymce.woff":"tinymce/skins/lightgray/fonts/tinymce-1ebc636bb24cbea637946ba8c22cbf4f35d8343ba9763045d2aee59e3714ae78.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-f7e68049f5f1ba278870818429ef905d07846781701c40657d9d24ca6638cced.css","tinymce/skins/lightgray/skin.less":"tinymce/skins/lightgray/skin.less","tinymce/skins/lightgray/skin.min.css":"tinymce/skins/lightgray/skin.min-6d81e56f6d40695f114abe4db834ccb7bf7d89cfa07c10c2cbaebb8066ace27b.css","tinymce/themes/inlite/theme.js":"tinymce/themes/inlite/theme-a400085213d427d78d8a1e35089ae6cd00831e51d33a3342a198d1bc62851640.js","tinymce/themes/modern/theme.js":"tinymce/themes/modern/theme-f0a3cccc241fa5d46bde8e19160e3f351368881a7c3cdb0c4ce84da11901064c.js","tinymce/tinymce.js":"tinymce/tinymce-d7f6811cb92d1e7638015669d3ec90175d4cb1309ecad56ff9d757b25fe18172.js","tinymce/langs/ar.js":"tinymce/langs/ar-e47a394dbc461d20aa547fa0c2027a9dad45fe55e18adb0da309045be6ed4109.js","tinymce/langs/ar_SA.js":"tinymce/langs/ar_SA-24b62c76e99e114ae44480a67edbacf74f9fed0ff7afbeacce4966f1bc80333b.js","tinymce/langs/az.js":"tinymce/langs/az-d40cecd6eb423f910f528ae98dcdaba4c2449802621759437174cf8c8c0d5b25.js","tinymce/langs/be.js":"tinymce/langs/be-42e2884c0f2394afa1a17e31e702660eeea0d84618bbd18155cb0313c6eb5f69.js","tinymce/langs/bg_BG.js":"tinymce/langs/bg_BG-2fac9d785519d2791d42881fcc08c6a82e841575785690259d7f64e1b88d7db3.js","tinymce/langs/bn_BD.js":"tinymce/langs/bn_BD-ef48a9094445dfa624af0c35902f60b7949b30585b5e9bc760a225fe2c07cbd3.js","tinymce/langs/bs.js":"tinymce/langs/bs-eb66c6d6910a30b950d7a0e784027ed288e6e5a2c6db7741da3359f2067e2e0e.js","tinymce/langs/ca.js":"tinymce/langs/ca-77ab49d6420318ed7f5fd51b6856bc396c393095bd78ea91dad83e38e5859637.js","tinymce/langs/cs.js":"tinymce/langs/cs-3d7ecd619895e3e4c7e1db06f6526682ef88f22f6e8b50d7b45ed0ae7c206714.js","tinymce/langs/cs_CZ.js":"tinymce/langs/cs_CZ-4b0778d233e200dea350f8361129da2844e23d1c0f2d524aeffa6d34e581368d.js","tinymce/langs/cy.js":"tinymce/langs/cy-a847ff54657ccf76eec362cc14fbc63adb4c67f9b1de023b29d817aba4fec062.js","tinymce/langs/da.js":"tinymce/langs/da-a87f3ae1c442c3bb26b81569687e98072d038269360361ec9728f00d6b17c282.js","tinymce/langs/de.js":"tinymce/langs/de-a10c8f6f1e53da776e7026dffd54ea8ba527492b767e5b54ae5c3974f6953311.js","tinymce/langs/de_AT.js":"tinymce/langs/de_AT-a32eec12ce12b06fc2c8919d2372242e9596712c96dc9bcfff1fc8b7dc458627.js","tinymce/langs/dv.js":"tinymce/langs/dv-182756bf33409a8b76bc684d0a2ab86881718c360d1f1fb3fbc609ce3f5e3f49.js","tinymce/langs/el.js":"tinymce/langs/el-6bbdbf3ccb2b2f22f961eb6749b43fe401c84fd6d89cd3f76a538dc2d06bb071.js","tinymce/langs/en_CA.js":"tinymce/langs/en_CA-1a9457da814b0a1f15687a00f3ae4b38da0301445b52c29a423a38c943d9a431.js","tinymce/langs/en_GB.js":"tinymce/langs/en_GB-a0f7752ff9ddf3ed3d2cd24fee04fe585bca0babc4ce655560c923ad386c0c07.js","tinymce/langs/eo.js":"tinymce/langs/eo-cbac4d7ebcb0679845b852d9354e74586e78cf13eef82de1400cf6b465e203cd.js","tinymce/langs/es.js":"tinymce/langs/es-133cb5bd627c2a299544586bf365859b5a1c96c57d18c90e7853048434a809b6.js","tinymce/langs/es_MX.js":"tinymce/langs/es_MX-352fcdff567919ff09a2e19c56d8059d339bffdbd999bcc82aa6d8340c19bbb9.js","tinymce/langs/et.js":"tinymce/langs/et-2cfc3da0ff39a37f0bd3c896d49604028f231ff0e7d8ebbc30dbfd4b87125f06.js","tinymce/langs/eu.js":"tinymce/langs/eu-8698ae4de236fcd490d0f45d485dbd33d8e9de6eb3db51191e1b4f8ea96a4d06.js","tinymce/langs/fa.js":"tinymce/langs/fa-7eff96595e93d4b0df484dd90c14b7b412966157ed8293f54429e9418882bdb2.js","tinymce/langs/fa_IR.js":"tinymce/langs/fa_IR-2f9d33f16718ae9c1f3642905b58434ed2662fb66448c4d115b8491aa68e7904.js","tinymce/langs/fi.js":"tinymce/langs/fi-6ca2a93d045067a86e8ea973d0b89368054b15eb9b9625bf1ca871608a5cb77c.js","tinymce/langs/fo.js":"tinymce/langs/fo-340609cecd5571e4eacb8fe7bd1343c8553d96d12610fb77d9a812dc6d3635fd.js","tinymce/langs/fr_CH.js":"tinymce/langs/fr_CH-e0f43c0f20727368ff0767f3bf099bc23eed9fe3e81f8bbe514381e786526928.js","tinymce/langs/fr_FR.js":"tinymce/langs/fr_FR-2702de7be93bd1e0d7120ae3c9e637061565186c66886f155ffca0663df25b4c.js","tinymce/langs/ga.js":"tinymce/langs/ga-d2a3de6f28723d75b03f7f42fd3aedfd045d473425ee38f02350b56035383af1.js","tinymce/langs/gd.js":"tinymce/langs/gd-0453e8b97bf3b6cca4065712ec59f20343ad6131735b38547e865177a1c1c490.js","tinymce/langs/gl.js":"tinymce/langs/gl-1ff612222934f558870111f10934110c5656df3bb293c917e4eeceeb6605fcc5.js","tinymce/langs/he_IL.js":"tinymce/langs/he_IL-981f5250a4b8d404b37040ca5a35c30498cbeeb36e298d81bf1592b43fff7656.js","tinymce/langs/hi_IN.js":"tinymce/langs/hi_IN-695e0cdc22974fe45e0f35289f0249ddab87245165c9df765ec1b885ec55f0be.js","tinymce/langs/hr.js":"tinymce/langs/hr-f77f1da2736d13f9a9a86ebf596f592fcc748f2975a0b9904b512d630f2c1a17.js","tinymce/langs/hu_HU.js":"tinymce/langs/hu_HU-1e22021a4f9c61919aa024041af555eac277bfc08ccb8c07fd329b87090a15e5.js","tinymce/langs/hy.js":"tinymce/langs/hy-0d384f3f82cd76793d3e7428a2140ea97f40a943f25ef99a2e3709a3e02b0930.js","tinymce/langs/id.js":"tinymce/langs/id-e9fd018be745b5f14f4af47887420f98c590c607e01d57aca77ef5affe188523.js","tinymce/langs/is_IS.js":"tinymce/langs/is_IS-c6e8c3e7b0e6b447faec3d8d258928f97c84558b29882c056513fb71cf237bfa.js","tinymce/langs/it.js":"tinymce/langs/it-25b5546d48c80ad666b600e5be3a05718b80645721b191785c1fafe1853f4a46.js","tinymce/langs/ja.js":"tinymce/langs/ja-80f0e7414030c32617ff651da1affa0bc85ee514fc9bc81f46edfd8ce0053ac0.js","tinymce/langs/ka_GE.js":"tinymce/langs/ka_GE-3c6b82346a7070a8b6a15ae6e8faeecc5bfe63ad7e616b7de2e8ab8a75ec39c2.js","tinymce/langs/kab.js":"tinymce/langs/kab-3651d08aacd5bda15a04698f202bf616efbf13d3789aadd9ce4f93e430818c13.js","tinymce/langs/kk.js":"tinymce/langs/kk-0c1ba792ed9445c6512f310228f64d1cde7b5a98e0b212cca749dcb2d728fe86.js","tinymce/langs/km_KH.js":"tinymce/langs/km_KH-8bec84e4078db8c2a680260c073dfb486c85b61df62e14ca330d66d2ca2a85b5.js","tinymce/langs/ko.js":"tinymce/langs/ko-ca535bfa388701fb020d24bdfb3f1e3aab127005b54e943cb1bd60b723c8194d.js","tinymce/langs/ko_KR.js":"tinymce/langs/ko_KR-daa3ae950d70260bb0901a5349034952247905523bbc2bcbe527701b6aad8e28.js","tinymce/langs/ku.js":"tinymce/langs/ku-95e2ffe4f0112df658f10327c26f2cdddedc2e2ed769767366ae22a465c14725.js","tinymce/langs/ku_IQ.js":"tinymce/langs/ku_IQ-a212e8d41695989e56834facf72474e1934ef166c3fcaf7e255ef573a3c6e76c.js","tinymce/langs/lb.js":"tinymce/langs/lb-94992279091f1a0be927d5d858d3724037d8d81ee6b63cc9ddde84af6124cc04.js","tinymce/langs/lt.js":"tinymce/langs/lt-ae7cefbe2cb5420770b31eabf92605728135ab5d6a2d2cfca2d19809be67e6a8.js","tinymce/langs/lv.js":"tinymce/langs/lv-e468ed0c78466d1fff9f4957645606d8aa683cb5dc35931e047df97b8e4f5433.js","tinymce/langs/mk_MK.js":"tinymce/langs/mk_MK-55aadb5d7c82c940cfcf42f24423ef0471294c9b02994a6b22e1b4173baa7ec6.js","tinymce/langs/ml.js":"tinymce/langs/ml-685de0808683c02274dcbe9f8de023a4d72a49b040e7526a99e3d5c786ff71ae.js","tinymce/langs/ml_IN.js":"tinymce/langs/ml_IN-1ab433621d6257c7632718048bacc5ed98a302d33cf0438bbbb4200914e01aac.js","tinymce/langs/mn_MN.js":"tinymce/langs/mn_MN-b89f88cb9b0dd7c879e27532b2979c421c1e69648f65a4108a56060f1e1cb009.js","tinymce/langs/nb_NO.js":"tinymce/langs/nb_NO-602ee249e98a26ae24c1ee1311090c68a782050086a835f1e7bd46ebfe7879ef.js","tinymce/langs/nl.js":"tinymce/langs/nl-9c77e9404a27fd7e4bd162ad44538b72d2725b7fee7be7ca3ff801cf217c4968.js","tinymce/langs/pl.js":"tinymce/langs/pl-5cdd1586ec8fa67293f2175cd92edbd0392c4c77b24ed64cfdceccba8d99f152.js","tinymce/langs/pt_BR.js":"tinymce/langs/pt_BR-e3955318582d58bd241a62c3a81afe74e0ae7f31bdc92a562b75243701f114df.js","tinymce/langs/pt_PT.js":"tinymce/langs/pt_PT-1101d080ecd5da091c7ef70b23200adbd5136c5c32f0c79dae665a443d76c35d.js","tinymce/langs/ro.js":"tinymce/langs/ro-484799d182031efab087d749b024014fd06ed06c1e7712b8bf87f3a4a965c2e4.js","tinymce/langs/ru.js":"tinymce/langs/ru-bdd59cb662a7b4d61e770b07014d1791051312c87513ecc1891e80beba617171.js","tinymce/langs/ru_RU.js":"tinymce/langs/ru_RU-9f18ef14f3493cc586cc0b4827fd9e1c2ed3d5790ce536d94f87e2d6aac2a688.js","tinymce/langs/si_LK.js":"tinymce/langs/si_LK-5bfefe9b2fe82574d8ed839df18fd8ff65f1e1cd458b0a843cf1aadd1be704ea.js","tinymce/langs/sk.js":"tinymce/langs/sk-412fa7f982ad2733341203daa035c8ba10035262ca8f5f65e0a41507512209bd.js","tinymce/langs/sl_SI.js":"tinymce/langs/sl_SI-2bea4f3854a992ea840808019dc99467dc173b12454ea6016135234a741671d8.js","tinymce/langs/sr.js":"tinymce/langs/sr-e0b97453a0c1000b19f7ce37fcf86bcacbf9125d81076f60a77686820c7925c6.js","tinymce/langs/sv_SE.js":"tinymce/langs/sv_SE-fa561a7e49fdb42d5913337176fed6bc8a9997eb3ca8f20ecd9520d5391e17be.js","tinymce/langs/ta.js":"tinymce/langs/ta-0fd0e62250fc1a3626ac2111c5adb4194a6a898888bb224414f6dc8adf53d385.js","tinymce/langs/ta_IN.js":"tinymce/langs/ta_IN-32303f65d2378e8202b17b0920b79277a2580872e3a6bd9409aeceef90867dd0.js","tinymce/langs/tg.js":"tinymce/langs/tg-1618248bf0aeda614a37ced9fd28b6623ae24f8453368bfb8c483ce820a3cb34.js","tinymce/langs/th_TH.js":"tinymce/langs/th_TH-5279a379afc886c2a31e22aba9d7ee7a8e3edf4c0785f39aaa20ccd03d46b19a.js","tinymce/langs/tr.js":"tinymce/langs/tr-3765d4a8923ef22864747d9c825c3e0af401e1356e75a5458bed837d486b7673.js","tinymce/langs/tr_TR.js":"tinymce/langs/tr_TR-5c93f80b89e5dd2eb6972f37bf50b76d7705c61f28120b836704a91fa5c14399.js","tinymce/langs/tt.js":"tinymce/langs/tt-22e302672df0a77de14688f58dbedf82de7a26e9089c9535a2a3cd1c0ccd7903.js","tinymce/langs/ug.js":"tinymce/langs/ug-50232038bf7262c90be4f2919c74ff93792cdb82e6064d0f5c86f885b93271c5.js","tinymce/langs/uk.js":"tinymce/langs/uk-f6d7911714c422b178c0249df323cf7ec415a83195d751c7e4024590b1738c42.js","tinymce/langs/uk_UA.js":"tinymce/langs/uk_UA-bd8cd7ca66228d85f8a4cadb651c49981f26e007fa2a952856c8c3d3c1baa66a.js","tinymce/langs/vi.js":"tinymce/langs/vi-6aa451047e4df911c92e10178e6a70147534e438c64df7a88eee37b4a440726e.js","tinymce/langs/vi_VN.js":"tinymce/langs/vi_VN-aea52c2e9397f8a657d54d50a42b3c5be0ca79480555a4fa1e0f5d785c5f6561.js","tinymce/langs/zh_CN.js":"tinymce/langs/zh_CN-8622ec46e2980b6f5baf3b745c6b0187dd2a54ddecbbf69a21f0b3e72f84f6dc.js","tinymce/langs/zh_TW.js":"tinymce/langs/zh_TW-90723da3b889f2a4477d4aaf00ca3e75439998269b36b359ea4caa37e1defb4a.js","leaflet/dist/images/layers.png":"leaflet/dist/images/layers-1dbbe9d028e292f36fcba8f8b3a28d5e8932754fc2215b9ac69e4cdecf5107c6.png","leaflet/dist/images/layers-2x.png":"leaflet/dist/images/layers-2x-066daca850d8ffbef007af00b06eac0015728dee279c51f3cb6c716df7c42edf.png","leaflet/dist/images/marker-icon.png":"leaflet/dist/images/marker-icon-574c3a5cca85f4114085b6841596d62f00d7c892c7b03f28cbfa301deb1dc437.png","leaflet/dist/images/marker-icon-2x.png":"leaflet/dist/images/marker-icon-2x-2d77a2e4c2f08bbac41808324ef946b9a2fe61b6150480d011b72b379c3b238d.png","leaflet/dist/images/marker-shadow.png":"leaflet/dist/images/marker-shadow-264f5c640339f042dd729062cfc04c17f8ea0f29882b538e3848ed8f10edb4da.png","tinymce/preinit.js":"tinymce/preinit-4eb7f4ac58f2f450d20185cf83ca1d8550d2a1419141bfe70eb5a2c4afe67349.js","brasil.png":"brasil-0e293d8c9a86f8a98fea23013e9c365b32fd0022a1496309eeabcd97f7be1288.png"}}
\ No newline at end of file
diff --git a/public/assets/active_admin-2b4f9b9e35d46bbace92d14d078b3568dcdbd0183e52827e5c44c54dc7c96266.js b/public/assets/active_admin-2b4f9b9e35d46bbace92d14d078b3568dcdbd0183e52827e5c44c54dc7c96266.js
new file mode 100644
index 00000000..bba2ad82
--- /dev/null
+++ b/public/assets/active_admin-2b4f9b9e35d46bbace92d14d078b3568dcdbd0183e52827e5c44c54dc7c96266.js
@@ -0,0 +1,23385 @@
+/*!
+ * 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
"},r=function(e){var t=e.dom.select("*[data-mce-id]");return t[0]},i=function(e,t,i){e.undoManager.transact(function(){var o,a;e.insertContent(n(t,i)),o=r(e),o.removeAttribute("data-mce-id"),a=e.dom.select("td,th",o),e.selection.setCursorLocation(a[0],0)})},o=function(e,t){e.execCommand("FormatBlock",!1,t)},a=function(t,n,r){var i,o;i=t.editorUpload.blobCache,o=i.create(e.uuid("mceu"),r,n),i.add(o),t.insertContent(t.dom.createHTML("img",{src:o.blobUri()}))},s=function(e){e.selection.collapse(!1)},l=function(e){e.focus(),t.unlinkSelection(e),s(e)},c=function(e,t,n){e.focus(),e.dom.setAttrib(t,"href",n),s(e)},u=function(e,t){e.execCommand("mceInsertLink",!1,{href:t}),s(e)},d=function(e,t){var n=e.dom.getParent(e.selection.getStart(),"a[href]");n?c(e,n,t):u(e,t)},f=function(e,t){0===t.trim().length?l(e):d(e,t)};return{insertTable:i,formatBlock:o,insertBlob:a,createLink:f,unlink:l}}),a("r",[],function(){var e=function(e){return/^www\.|\.(com|org|edu|gov|uk|net|ca|de|jp|fr|au|us|ru|ch|it|nl|se|no|es|mil)$/i.test(e.trim())},t=function(e){return/^https?:\/\//.test(e.trim())};return{isDomainLike:e,isAbsolute:t}}),a("g",["c","d","o","m","r"],function(e,t,n,r,i){var o=function(e){e.find("textbox").eq(0).each(function(e){e.focus()})},a=function(n,r){var i=t.create(e.extend({type:"form",layout:"flex",direction:"row",padding:5,name:n,spacing:3},r));return i.on("show",function(){o(i)}),i},s=function(e,t){return t?e.show():e.hide()},l=function(e,t){return new n(function(n){e.windowManager.confirm("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(e){var r=e===!0?"http://"+t:t;n(r)})})},c=function(e,t){return!i.isAbsolute(t)&&i.isDomainLike(t)?l(e,t):n.resolve(t)},u=function(e,t){var n={},i=function(){e.focus(),r.unlink(e),t()},o=function(e){var t=e.meta;t&&t.attach&&(n={href:this.value(),attach:t.attach})},l=function(t){if(t.control===this){var n,r="";n=e.dom.getParent(e.selection.getStart(),"a[href]"),n&&(r=e.dom.getAttrib(n,"href")),this.fromJSON({linkurl:r}),s(this.find("#unlink"),n),this.find("#linkurl")[0].focus()}};return a("quicklink",{items:[{type:"button",name:"unlink",icon:"unlink",onclick:i,tooltip:"Remove link"},{type:"filepicker",name:"linkurl",placeholder:"Paste or type a link",filetype:"file",onchange:o},{type:"button",icon:"checkmark",subtype:"primary",tooltip:"Ok",onclick:"submit"}],onshow:l,onsubmit:function(i){c(e,i.data.linkurl).then(function(i){e.undoManager.transact(function(){i===n.href&&(n.attach(),n={}),r.createLink(e,i)}),t()})}})};return{createQuickLinkForm:u}}),s("s",tinymce.geom.Rect),a("t",[],function(){var e=function(e){return{x:e.left,y:e.top,w:e.width,h:e.height}},t=function(e){return{left:e.x,top:e.y,width:e.w,height:e.h,right:e.x+e.w,bottom:e.y+e.h}};return{fromClientRect:e,toClientRect:t}}),a("h",["e","s","t"],function(e,t,n){var r=function(t){var n=e.getViewPort();return{x:t.x+n.x,y:t.y+n.y,w:t.w,h:t.h}},i=function(e){var t=e.getBoundingClientRect();return r({x:t.left,y:t.top,w:Math.max(e.clientWidth,e.offsetWidth),h:Math.max(e.clientHeight,e.offsetHeight)})},o=function(e,t){return i(t)},a=function(e){return i(e.getElement().ownerDocument.body)},s=function(e){return i(e.getContentAreaContainer()||e.getBody())},l=function(e){var t=e.selection.getBoundingClientRect();return t?r(n.fromClientRect(t)):null};return{getElementRect:o,getPageAreaRect:a,getContentAreaRect:s,getSelectionRect:l}}),a("i",["s","t"],function(e,t){var n=function(e,t){return{rect:e,position:t}},r=function(e,t){return{x:t.x,y:t.y,w:e.w,h:e.h}},i=function(t,i,o,a,s){var l,c,u;return l=e.findBestRelativePosition(s,o,a,t),o=e.clamp(o,a),l?(c=e.relativePosition(s,o,l),u=r(s,c),n(u,l)):(o=e.intersect(a,o),o?(l=e.findBestRelativePosition(s,o,a,i))?(c=e.relativePosition(s,o,l),u=r(s,c),n(u,l)):(u=r(s,o),n(u,l)):null)},o=function(e,t,n){return i(["cr-cl","cl-cr"],["bc-tc","bl-tl","br-tr"],e,t,n)},a=function(e,t,n){return i(["tc-bc","bc-tc","tl-bl","bl-tl","tr-br","br-tr"],["bc-tc","bl-tl","br-tr"],e,t,n)},s=function(e,n,r,i){var o;return"function"==typeof e?(o=e({elementRect:t.toClientRect(n),contentAreaRect:t.toClientRect(r),panelRect:t.toClientRect(i)}),t.fromClientRect(o)):i},l=function(e){return e.panelRect};return{calcInsert:o,calc:a,userConstrain:s,defaultHandler:l}}),a("a",["j"],function(e){var t=function(e,t){if(t(e))return!0;throw new Error("Default value doesn't match requested type.")},n=function(e){return function(n,r,i){var o=n.settings;return t(i,e),r in o&&e(o[r])?o[r]:i}},r=function(e,t){return e.split(t).filter(function(e){return e.length>0})},i=function(t,n){var i=function(e){return"string"==typeof e?r(e,/[ ,]/):e},o=function(e,t){return e===!1?[]:t};return e.isArray(t)?t:e.isString(t)?i(t):e.isBoolean(t)?o(t,n):n},o=function(e){return function(n,r,o){var a=r in n.settings?n.settings[r]:o;return t(o,e),i(a,o)}};return{getStringOr:n(e.isString),getBoolOr:n(e.isBoolean),getNumberOr:n(e.isNumber),getHandlerOr:n(e.isFunction),getToolbarItemsOr:o(e.isArray)}}),a("3",["c","d","e","f","g","h","i","a"],function(e,t,n,r,i,o,a,s){return function(){var l,c,u=["bold","italic","|","quicklink","h2","h3","blockquote"],d=["quickimage","quicktable"],f=function(t,n){return e.map(n,function(e){return r.create(t,e.id,e.items)})},p=function(e){return s.getToolbarItemsOr(e,"selection_toolbar",u)},m=function(e){return s.getToolbarItemsOr(e,"insert_toolbar",d)},g=function(e){return e.items().length>0},h=function(n,o){var a=f(n,o).concat([r.create(n,"text",p(n)),r.create(n,"insert",m(n)),i.createQuickLinkForm(n,E)]);return t.create({type:"floatpanel",role:"dialog",classes:"tinymce tinymce-inline arrow",ariaLabel:"Inline toolbar",layout:"flex",direction:"column",align:"stretch",autohide:!1,autofix:!0,fixed:!0,border:1,items:e.grep(a,g),oncancel:function(){n.focus()}})},v=function(e){e&&e.show()},b=function(e,t){e.moveTo(t.x,t.y)},y=function(t,n){n=n?n.substr(0,2):"",e.each({t:"down",b:"up",c:"center"},function(e,r){t.classes.toggle("arrow-"+e,r===n.substr(0,1))}),"cr"===n?(t.classes.toggle("arrow-left",!0),t.classes.toggle("arrow-right",!1)):"cl"===n?(t.classes.toggle("arrow-left",!0),t.classes.toggle("arrow-right",!0)):e.each({l:"left",r:"right"},function(e,r){t.classes.toggle("arrow-"+e,r===n.substr(1,1))})},x=function(e,t){var n=e.items().filter("#"+t);return n.length>0&&(n[0].show(),e.reflow(),!0)},C=function(e,t,r,i){var l,u,d,f;return f=s.getHandlerOr(r,"inline_toolbar_position_handler",a.defaultHandler),l=o.getContentAreaRect(r),u=n.getRect(e.getEl()),d="insert"===t?a.calcInsert(i,l,u):a.calc(i,l,u),!!d&&(u=d.rect,c=i,b(e,a.userConstrain(f,i,l,u)),y(e,d.position),!0)},w=function(e,t,n,r){return v(e),e.items().hide(),x(e,t)?void(C(e,t,n,r)===!1&&E(e)):void E(e)},N=function(){return l.items().filter("form:visible").length>0},k=function(e,t){if(l){if(l.items().hide(),!x(l,t))return void E(l);var r,i,u,d;v(l),l.items().hide(),x(l,t),d=s.getHandlerOr(e,"inline_toolbar_position_handler",a.defaultHandler),r=o.getContentAreaRect(e),i=n.getRect(l.getEl()),u=a.calc(c,r,i),u&&(i=u.rect,b(l,a.userConstrain(d,c,r,i)),y(l,u.position))}},S=function(e,t,n,r){l||(l=h(e,r),l.renderTo(document.body).reflow().moveTo(n.x,n.y),e.nodeChanged()),w(l,t,e,n)},_=function(e,t,n){l&&C(l,t,e,n)},E=function(){l&&l.hide()},T=function(){l&&l.find("toolbar:visible").eq(0).each(function(e){e.focus(!0)})},R=function(){l&&(l.remove(),l=null)},A=function(){return l&&l.visible()&&N()};return{show:S,showForm:k,reposition:_,inForm:A,hide:E,focus:T,remove:R}}}),a("k",["o"],function(e){var t=function(t){return new e(function(e){var n=new FileReader;n.onloadend=function(){e(n.result.split(",")[1])},n.readAsDataURL(t)})};return{blobToBase64:t}}),a("l",["o"],function(e){var t=function(){return new e(function(e){var t;t=document.createElement("input"),t.type="file",t.style.position="fixed",t.style.left=0,t.style.top=0,t.style.opacity=.001,document.body.appendChild(t),t.onchange=function(t){e(Array.prototype.slice.call(t.target.files))},t.click(),t.parentNode.removeChild(t)})};return{pickFile:t}}),a("4",["3","k","l","m"],function(e,t,n,r){var i=function(e){for(var t=function(t){return function(){r.formatBlock(e,t)}},n=1;n<6;n++){var i="h"+n;e.addButton(i,{text:i.toUpperCase(),tooltip:"Heading "+n,stateSelector:i,onclick:t(i),onPostRender:function(){var e=this.getEl().firstChild.firstChild;e.style.fontWeight="bold"}})}},o=function(e,o){e.addButton("quicklink",{icon:"link",tooltip:"Insert/Edit link",stateSelector:"a[href]",onclick:function(){o.showForm(e,"quicklink")}}),e.addButton("quickimage",{icon:"image",tooltip:"Insert image",onclick:function(){n.pickFile().then(function(n){var i=n[0];t.blobToBase64(i).then(function(t){r.insertBlob(e,t,i)})})}}),e.addButton("quicktable",{icon:"table",tooltip:"Insert table",onclick:function(){o.hide(),r.insertTable(e,2,2)}}),i(e)};return{addToEditor:o}}),s("n",tinymce.EditorManager),a("5",["n","e"],function(e,t){var n=function(e,t){var n=function(){e._skinLoaded=!0,e.fire("SkinLoaded"),t()};e.initialized?n():e.on("init",n)},r=function(t){var n=e.baseURL+"/skins/";return t?n+t:n+"lightgray"},i=function(e,t){return e.documentBaseURI.toAbsolute(t)},o=function(e,o){var a=e.settings,s=a.skin_url?i(e,a.skin_url):r(a.skin),l=function(){n(e,o)};t.styleSheetLoader.load(s+"/skin.min.css",l),e.contentCSS.push(s+"/content.inline.min.css")};return{load:o}}),a("8",[],function(){var e=function(e,t){return{id:e,rect:t}},t=function(e,t){for(var n=0;n= constrainRect.x && pos.x + pos.w <= constrainRect.w + constrainRect.x &&
+ pos.y >= constrainRect.y && pos.y + pos.h <= constrainRect.h + constrainRect.y) {
+ return rels[i];
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Inflates the rect in all directions.
+ *
+ * @method inflate
+ * @param {Rect} rect Rect to expand.
+ * @param {Number} w Relative width to expand by.
+ * @param {Number} h Relative height to expand by.
+ * @return {Rect} New expanded rect.
+ */
+ function inflate(rect, w, h) {
+ return create(rect.x - w, rect.y - h, rect.w + w * 2, rect.h + h * 2);
+ }
+
+ /**
+ * Returns the intersection of the specified rectangles.
+ *
+ * @method intersect
+ * @param {Rect} rect The first rectangle to compare.
+ * @param {Rect} cropRect The second rectangle to compare.
+ * @return {Rect} The intersection of the two rectangles or null if they don't intersect.
+ */
+ function intersect(rect, cropRect) {
+ var x1, y1, x2, y2;
+
+ x1 = max(rect.x, cropRect.x);
+ y1 = max(rect.y, cropRect.y);
+ x2 = min(rect.x + rect.w, cropRect.x + cropRect.w);
+ y2 = min(rect.y + rect.h, cropRect.y + cropRect.h);
+
+ if (x2 - x1 < 0 || y2 - y1 < 0) {
+ return null;
+ }
+
+ return create(x1, y1, x2 - x1, y2 - y1);
+ }
+
+ /**
+ * Returns a rect clamped within the specified clamp rect. This forces the
+ * rect to be inside the clamp rect.
+ *
+ * @method clamp
+ * @param {Rect} rect Rectangle to force within clamp rect.
+ * @param {Rect} clampRect Rectable to force within.
+ * @param {Boolean} fixedSize True/false if size should be fixed.
+ * @return {Rect} Clamped rect.
+ */
+ function clamp(rect, clampRect, fixedSize) {
+ var underflowX1, underflowY1, overflowX2, overflowY2,
+ x1, y1, x2, y2, cx2, cy2;
+
+ x1 = rect.x;
+ y1 = rect.y;
+ x2 = rect.x + rect.w;
+ y2 = rect.y + rect.h;
+ cx2 = clampRect.x + clampRect.w;
+ cy2 = clampRect.y + clampRect.h;
+
+ underflowX1 = max(0, clampRect.x - x1);
+ underflowY1 = max(0, clampRect.y - y1);
+ overflowX2 = max(0, x2 - cx2);
+ overflowY2 = max(0, y2 - cy2);
+
+ x1 += underflowX1;
+ y1 += underflowY1;
+
+ if (fixedSize) {
+ x2 += underflowX1;
+ y2 += underflowY1;
+ x1 -= overflowX2;
+ y1 -= overflowY2;
+ }
+
+ x2 -= overflowX2;
+ y2 -= overflowY2;
+
+ return create(x1, y1, x2 - x1, y2 - y1);
+ }
+
+ /**
+ * Creates a new rectangle object.
+ *
+ * @method create
+ * @param {Number} x Rectangle x location.
+ * @param {Number} y Rectangle y location.
+ * @param {Number} w Rectangle width.
+ * @param {Number} h Rectangle height.
+ * @return {Rect} New rectangle object.
+ */
+ function create(x, y, w, h) {
+ return {x: x, y: y, w: w, h: h};
+ }
+
+ /**
+ * Creates a new rectangle object form a clientRects object.
+ *
+ * @method fromClientRect
+ * @param {ClientRect} clientRect DOM ClientRect object.
+ * @return {Rect} New rectangle object.
+ */
+ function fromClientRect(clientRect) {
+ return create(clientRect.left, clientRect.top, clientRect.width, clientRect.height);
+ }
+
+ return {
+ inflate: inflate,
+ relativePosition: relativePosition,
+ findBestRelativePosition: findBestRelativePosition,
+ intersect: intersect,
+ clamp: clamp,
+ create: create,
+ fromClientRect: fromClientRect
+ };
+});
+
+// Included from: js/tinymce/classes/util/Promise.js
+
+/**
+ * Promise.js
+ *
+ * Released under LGPL License.
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
+ *
+ * Promise polyfill under MIT license: https://github.com/taylorhakes/promise-polyfill
+ *
+ * License: http://www.tinymce.com/license
+ * Contributing: http://www.tinymce.com/contributing
+ */
+
+/* eslint-disable */
+/* jshint ignore:start */
+
+/**
+ * Modifed to be a feature fill and wrapped as tinymce module.
+ */
+define("tinymce/util/Promise", [], function() {
+ if (window.Promise) {
+ return window.Promise;
+ }
+
+ // Use polyfill for setImmediate for performance gains
+ var asap = Promise.immediateFn || (typeof setImmediate === 'function' && setImmediate) ||
+ function(fn) { setTimeout(fn, 1); };
+
+ // Polyfill for Function.prototype.bind
+ function bind(fn, thisArg) {
+ return function() {
+ fn.apply(thisArg, arguments);
+ };
+ }
+
+ var isArray = Array.isArray || function(value) { return Object.prototype.toString.call(value) === "[object Array]"; };
+
+ function Promise(fn) {
+ if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new');
+ if (typeof fn !== 'function') throw new TypeError('not a function');
+ this._state = null;
+ this._value = null;
+ this._deferreds = [];
+
+ doResolve(fn, bind(resolve, this), bind(reject, this));
+ }
+
+ function handle(deferred) {
+ var me = this;
+ if (this._state === null) {
+ this._deferreds.push(deferred);
+ return;
+ }
+ asap(function() {
+ var cb = me._state ? deferred.onFulfilled : deferred.onRejected;
+ if (cb === null) {
+ (me._state ? deferred.resolve : deferred.reject)(me._value);
+ return;
+ }
+ var ret;
+ try {
+ ret = cb(me._value);
+ }
+ catch (e) {
+ deferred.reject(e);
+ return;
+ }
+ deferred.resolve(ret);
+ });
+ }
+
+ function resolve(newValue) {
+ try { //Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
+ if (newValue === this) throw new TypeError('A promise cannot be resolved with itself.');
+ if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
+ var then = newValue.then;
+ if (typeof then === 'function') {
+ doResolve(bind(then, newValue), bind(resolve, this), bind(reject, this));
+ return;
+ }
+ }
+ this._state = true;
+ this._value = newValue;
+ finale.call(this);
+ } catch (e) { reject.call(this, e); }
+ }
+
+ function reject(newValue) {
+ this._state = false;
+ this._value = newValue;
+ finale.call(this);
+ }
+
+ function finale() {
+ for (var i = 0, len = this._deferreds.length; i < len; i++) {
+ handle.call(this, this._deferreds[i]);
+ }
+ this._deferreds = null;
+ }
+
+ function Handler(onFulfilled, onRejected, resolve, reject){
+ this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
+ this.onRejected = typeof onRejected === 'function' ? onRejected : null;
+ this.resolve = resolve;
+ this.reject = reject;
+ }
+
+ /**
+ * Take a potentially misbehaving resolver function and make sure
+ * onFulfilled and onRejected are only called once.
+ *
+ * Makes no guarantees about asynchrony.
+ */
+ function doResolve(fn, onFulfilled, onRejected) {
+ var done = false;
+ try {
+ fn(function (value) {
+ if (done) return;
+ done = true;
+ onFulfilled(value);
+ }, function (reason) {
+ if (done) return;
+ done = true;
+ onRejected(reason);
+ });
+ } catch (ex) {
+ if (done) return;
+ done = true;
+ onRejected(ex);
+ }
+ }
+
+ Promise.prototype['catch'] = function (onRejected) {
+ return this.then(null, onRejected);
+ };
+
+ Promise.prototype.then = function(onFulfilled, onRejected) {
+ var me = this;
+ return new Promise(function(resolve, reject) {
+ handle.call(me, new Handler(onFulfilled, onRejected, resolve, reject));
+ });
+ };
+
+ Promise.all = function () {
+ var args = Array.prototype.slice.call(arguments.length === 1 && isArray(arguments[0]) ? arguments[0] : arguments);
+
+ return new Promise(function (resolve, reject) {
+ if (args.length === 0) return resolve([]);
+ var remaining = args.length;
+ function res(i, val) {
+ try {
+ if (val && (typeof val === 'object' || typeof val === 'function')) {
+ var then = val.then;
+ if (typeof then === 'function') {
+ then.call(val, function (val) { res(i, val); }, reject);
+ return;
+ }
+ }
+ args[i] = val;
+ if (--remaining === 0) {
+ resolve(args);
+ }
+ } catch (ex) {
+ reject(ex);
+ }
+ }
+ for (var i = 0; i < args.length; i++) {
+ res(i, args[i]);
+ }
+ });
+ };
+
+ Promise.resolve = function (value) {
+ if (value && typeof value === 'object' && value.constructor === Promise) {
+ return value;
+ }
+
+ return new Promise(function (resolve) {
+ resolve(value);
+ });
+ };
+
+ Promise.reject = function (value) {
+ return new Promise(function (resolve, reject) {
+ reject(value);
+ });
+ };
+
+ Promise.race = function (values) {
+ return new Promise(function (resolve, reject) {
+ for(var i = 0, len = values.length; i < len; i++) {
+ values[i].then(resolve, reject);
+ }
+ });
+ };
+
+ return Promise;
+});
+
+/* jshint ignore:end */
+/* eslint-enable */
+
+// Included from: js/tinymce/classes/util/Delay.js
+
+/**
+ * Delay.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 working with delayed actions like setTimeout.
+ *
+ * @class tinymce.util.Delay
+ */
+define("tinymce/util/Delay", [
+ "tinymce/util/Promise"
+], function(Promise) {
+ var requestAnimationFramePromise;
+
+ function requestAnimationFrame(callback, element) {
+ var i, requestAnimationFrameFunc = window.requestAnimationFrame, vendors = ['ms', 'moz', 'webkit'];
+
+ function featurefill(callback) {
+ window.setTimeout(callback, 0);
+ }
+
+ for (i = 0; i < vendors.length && !requestAnimationFrameFunc; i++) {
+ requestAnimationFrameFunc = window[vendors[i] + 'RequestAnimationFrame'];
+ }
+
+ if (!requestAnimationFrameFunc) {
+ requestAnimationFrameFunc = featurefill;
+ }
+
+ requestAnimationFrameFunc(callback, element);
+ }
+
+ function wrappedSetTimeout(callback, time) {
+ if (typeof time != 'number') {
+ time = 0;
+ }
+
+ return setTimeout(callback, time);
+ }
+
+ function wrappedSetInterval(callback, time) {
+ if (typeof time != 'number') {
+ time = 1; // IE 8 needs it to be > 0
+ }
+
+ return setInterval(callback, time);
+ }
+
+ function wrappedClearTimeout(id) {
+ return clearTimeout(id);
+ }
+
+ function wrappedClearInterval(id) {
+ return clearInterval(id);
+ }
+
+ function debounce(callback, time) {
+ var timer, func;
+
+ func = function() {
+ var args = arguments;
+
+ clearTimeout(timer);
+
+ timer = wrappedSetTimeout(function() {
+ callback.apply(this, args);
+ }, time);
+ };
+
+ func.stop = function() {
+ clearTimeout(timer);
+ };
+
+ return func;
+ }
+
+ return {
+ /**
+ * Requests an animation frame and fallbacks to a timeout on older browsers.
+ *
+ * @method requestAnimationFrame
+ * @param {function} callback Callback to execute when a new frame is available.
+ * @param {DOMElement} element Optional element to scope it to.
+ */
+ requestAnimationFrame: function(callback, element) {
+ if (requestAnimationFramePromise) {
+ requestAnimationFramePromise.then(callback);
+ return;
+ }
+
+ requestAnimationFramePromise = new Promise(function(resolve) {
+ if (!element) {
+ element = document.body;
+ }
+
+ requestAnimationFrame(resolve, element);
+ }).then(callback);
+ },
+
+ /**
+ * Sets a timer in ms and executes the specified callback when the timer runs out.
+ *
+ * @method setTimeout
+ * @param {function} callback Callback to execute when timer runs out.
+ * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
+ * @return {Number} Timeout id number.
+ */
+ setTimeout: wrappedSetTimeout,
+
+ /**
+ * Sets an interval timer in ms and executes the specified callback at every interval of that time.
+ *
+ * @method setInterval
+ * @param {function} callback Callback to execute when interval time runs out.
+ * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
+ * @return {Number} Timeout id number.
+ */
+ setInterval: wrappedSetInterval,
+
+ /**
+ * Sets an editor timeout it's similar to setTimeout except that it checks if the editor instance is
+ * still alive when the callback gets executed.
+ *
+ * @method setEditorTimeout
+ * @param {tinymce.Editor} editor Editor instance to check the removed state on.
+ * @param {function} callback Callback to execute when timer runs out.
+ * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
+ * @return {Number} Timeout id number.
+ */
+ setEditorTimeout: function(editor, callback, time) {
+ return wrappedSetTimeout(function() {
+ if (!editor.removed) {
+ callback();
+ }
+ }, time);
+ },
+
+ /**
+ * Sets an interval timer it's similar to setInterval except that it checks if the editor instance is
+ * still alive when the callback gets executed.
+ *
+ * @method setEditorInterval
+ * @param {function} callback Callback to execute when interval time runs out.
+ * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
+ * @return {Number} Timeout id number.
+ */
+ setEditorInterval: function(editor, callback, time) {
+ var timer;
+
+ timer = wrappedSetInterval(function() {
+ if (!editor.removed) {
+ callback();
+ } else {
+ clearInterval(timer);
+ }
+ }, time);
+
+ return timer;
+ },
+
+ /**
+ * Creates debounced callback function that only gets executed once within the specified time.
+ *
+ * @method debounce
+ * @param {function} callback Callback to execute when timer finishes.
+ * @param {Number} time Optional time to wait before the callback is executed, defaults to 0.
+ * @return {Function} debounced function callback.
+ */
+ debounce: debounce,
+
+ // Throttle needs to be debounce due to backwards compatibility.
+ throttle: debounce,
+
+ /**
+ * Clears an interval timer so it won't execute.
+ *
+ * @method clearInterval
+ * @param {Number} Interval timer id number.
+ */
+ clearInterval: wrappedClearInterval,
+
+ /**
+ * Clears an timeout timer so it won't execute.
+ *
+ * @method clearTimeout
+ * @param {Number} Timeout timer id number.
+ */
+ clearTimeout: wrappedClearTimeout
+ };
+});
+
+// Included from: js/tinymce/classes/Env.js
+
+/**
+ * Env.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 contains various environment constants like browser versions etc.
+ * Normally you don't want to sniff specific browser versions but sometimes you have
+ * to when it's impossible to feature detect. So use this with care.
+ *
+ * @class tinymce.Env
+ * @static
+ */
+define("tinymce/Env", [], function() {
+ var nav = navigator, userAgent = nav.userAgent;
+ var opera, webkit, ie, ie11, ie12, gecko, mac, iDevice, android, fileApi, phone, tablet, windowsPhone;
+
+ function matchMediaQuery(query) {
+ return "matchMedia" in window ? matchMedia(query).matches : false;
+ }
+
+ opera = window.opera && window.opera.buildNumber;
+ android = /Android/.test(userAgent);
+ webkit = /WebKit/.test(userAgent);
+ ie = !webkit && !opera && (/MSIE/gi).test(userAgent) && (/Explorer/gi).test(nav.appName);
+ ie = ie && /MSIE (\w+)\./.exec(userAgent)[1];
+ ie11 = userAgent.indexOf('Trident/') != -1 && (userAgent.indexOf('rv:') != -1 || nav.appName.indexOf('Netscape') != -1) ? 11 : false;
+ ie12 = (userAgent.indexOf('Edge/') != -1 && !ie && !ie11) ? 12 : false;
+ ie = ie || ie11 || ie12;
+ gecko = !webkit && !ie11 && /Gecko/.test(userAgent);
+ mac = userAgent.indexOf('Mac') != -1;
+ iDevice = /(iPad|iPhone)/.test(userAgent);
+ fileApi = "FormData" in window && "FileReader" in window && "URL" in window && !!URL.createObjectURL;
+ phone = matchMediaQuery("only screen and (max-device-width: 480px)") && (android || iDevice);
+ tablet = matchMediaQuery("only screen and (min-width: 800px)") && (android || iDevice);
+ windowsPhone = userAgent.indexOf('Windows Phone') != -1;
+
+ if (ie12) {
+ webkit = false;
+ }
+
+ // Is a iPad/iPhone and not on iOS5 sniff the WebKit version since older iOS WebKit versions
+ // says it has contentEditable support but there is no visible caret.
+ var contentEditable = !iDevice || fileApi || userAgent.match(/AppleWebKit\/(\d*)/)[1] >= 534;
+
+ return {
+ /**
+ * Constant that is true if the browser is Opera.
+ *
+ * @property opera
+ * @type Boolean
+ * @final
+ */
+ opera: opera,
+
+ /**
+ * Constant that is true if the browser is WebKit (Safari/Chrome).
+ *
+ * @property webKit
+ * @type Boolean
+ * @final
+ */
+ webkit: webkit,
+
+ /**
+ * Constant that is more than zero if the browser is IE.
+ *
+ * @property ie
+ * @type Boolean
+ * @final
+ */
+ ie: ie,
+
+ /**
+ * Constant that is true if the browser is Gecko.
+ *
+ * @property gecko
+ * @type Boolean
+ * @final
+ */
+ gecko: gecko,
+
+ /**
+ * Constant that is true if the os is Mac OS.
+ *
+ * @property mac
+ * @type Boolean
+ * @final
+ */
+ mac: mac,
+
+ /**
+ * Constant that is true if the os is iOS.
+ *
+ * @property iOS
+ * @type Boolean
+ * @final
+ */
+ iOS: iDevice,
+
+ /**
+ * Constant that is true if the os is android.
+ *
+ * @property android
+ * @type Boolean
+ * @final
+ */
+ android: android,
+
+ /**
+ * Constant that is true if the browser supports editing.
+ *
+ * @property contentEditable
+ * @type Boolean
+ * @final
+ */
+ contentEditable: contentEditable,
+
+ /**
+ * Transparent image data url.
+ *
+ * @property transparentSrc
+ * @type Boolean
+ * @final
+ */
+ transparentSrc: "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7",
+
+ /**
+ * Returns true/false if the browser can or can't place the caret after a inline block like an image.
+ *
+ * @property noCaretAfter
+ * @type Boolean
+ * @final
+ */
+ caretAfter: ie != 8,
+
+ /**
+ * Constant that is true if the browser supports native DOM Ranges. IE 9+.
+ *
+ * @property range
+ * @type Boolean
+ */
+ range: window.getSelection && "Range" in window,
+
+ /**
+ * Returns the IE document mode for non IE browsers this will fake IE 10.
+ *
+ * @property documentMode
+ * @type Number
+ */
+ documentMode: ie && !ie12 ? (document.documentMode || 7) : 10,
+
+ /**
+ * Constant that is true if the browser has a modern file api.
+ *
+ * @property fileApi
+ * @type Boolean
+ */
+ fileApi: fileApi,
+
+ /**
+ * Constant that is true if the browser supports contentEditable=false regions.
+ *
+ * @property ceFalse
+ * @type Boolean
+ */
+ ceFalse: (ie === false || ie > 8),
+
+ /**
+ * Constant if CSP mode is possible or not. Meaning we can't use script urls for the iframe.
+ */
+ canHaveCSP: (ie === false || ie > 11),
+
+ desktop: !phone && !tablet,
+ windowsPhone: windowsPhone
+ };
+});
+
+// Included from: js/tinymce/classes/dom/EventUtils.js
+
+/**
+ * EventUtils.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 loopfunc:true*/
+/*eslint no-loop-func:0 */
+
+/**
+ * This class wraps the browsers native event logic with more convenient methods.
+ *
+ * @class tinymce.dom.EventUtils
+ */
+define("tinymce/dom/EventUtils", [
+ "tinymce/util/Delay",
+ "tinymce/Env"
+], function(Delay, Env) {
+ "use strict";
+
+ var eventExpandoPrefix = "mce-data-";
+ var mouseEventRe = /^(?:mouse|contextmenu)|click/;
+ var deprecated = {
+ keyLocation: 1, layerX: 1, layerY: 1, returnValue: 1,
+ webkitMovementX: 1, webkitMovementY: 1, keyIdentifier: 1
+ };
+
+ /**
+ * Binds a native event to a callback on the speified target.
+ */
+ function addEvent(target, name, callback, capture) {
+ if (target.addEventListener) {
+ target.addEventListener(name, callback, capture || false);
+ } else if (target.attachEvent) {
+ target.attachEvent('on' + name, callback);
+ }
+ }
+
+ /**
+ * Unbinds a native event callback on the specified target.
+ */
+ function removeEvent(target, name, callback, capture) {
+ if (target.removeEventListener) {
+ target.removeEventListener(name, callback, capture || false);
+ } else if (target.detachEvent) {
+ target.detachEvent('on' + name, callback);
+ }
+ }
+
+ /**
+ * Gets the event target based on shadow dom properties like path and deepPath.
+ */
+ function getTargetFromShadowDom(event, defaultTarget) {
+ var path, target = defaultTarget;
+
+ // When target element is inside Shadow DOM we need to take first element from path
+ // otherwise we'll get Shadow Root parent, not actual target element
+
+ // Normalize target for WebComponents v0 implementation (in Chrome)
+ path = event.path;
+ if (path && path.length > 0) {
+ target = path[0];
+ }
+
+ // Normalize target for WebComponents v1 implementation (standard)
+ if (event.deepPath) {
+ path = event.deepPath();
+ if (path && path.length > 0) {
+ target = path[0];
+ }
+ }
+
+ return target;
+ }
+
+ /**
+ * Normalizes a native event object or just adds the event specific methods on a custom event.
+ */
+ function fix(originalEvent, data) {
+ var name, event = data || {}, undef;
+
+ // Dummy function that gets replaced on the delegation state functions
+ function returnFalse() {
+ return false;
+ }
+
+ // Dummy function that gets replaced on the delegation state functions
+ function returnTrue() {
+ return true;
+ }
+
+ // Copy all properties from the original event
+ for (name in originalEvent) {
+ // layerX/layerY is deprecated in Chrome and produces a warning
+ if (!deprecated[name]) {
+ event[name] = originalEvent[name];
+ }
+ }
+
+ // Normalize target IE uses srcElement
+ if (!event.target) {
+ event.target = event.srcElement || document;
+ }
+
+ // Experimental shadow dom support
+ if (Env.experimentalShadowDom) {
+ event.target = getTargetFromShadowDom(originalEvent, event.target);
+ }
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if (originalEvent && mouseEventRe.test(originalEvent.type) && originalEvent.pageX === undef && originalEvent.clientX !== undef) {
+ var eventDoc = event.target.ownerDocument || document;
+ var doc = eventDoc.documentElement;
+ var body = eventDoc.body;
+
+ event.pageX = originalEvent.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) -
+ (doc && doc.clientLeft || body && body.clientLeft || 0);
+
+ event.pageY = originalEvent.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) -
+ (doc && doc.clientTop || body && body.clientTop || 0);
+ }
+
+ // Add preventDefault method
+ event.preventDefault = function() {
+ event.isDefaultPrevented = returnTrue;
+
+ // Execute preventDefault on the original event object
+ if (originalEvent) {
+ if (originalEvent.preventDefault) {
+ originalEvent.preventDefault();
+ } else {
+ originalEvent.returnValue = false; // IE
+ }
+ }
+ };
+
+ // Add stopPropagation
+ event.stopPropagation = function() {
+ event.isPropagationStopped = returnTrue;
+
+ // Execute stopPropagation on the original event object
+ if (originalEvent) {
+ if (originalEvent.stopPropagation) {
+ originalEvent.stopPropagation();
+ } else {
+ originalEvent.cancelBubble = true; // IE
+ }
+ }
+ };
+
+ // Add stopImmediatePropagation
+ event.stopImmediatePropagation = function() {
+ event.isImmediatePropagationStopped = returnTrue;
+ event.stopPropagation();
+ };
+
+ // Add event delegation states
+ if (!event.isDefaultPrevented) {
+ event.isDefaultPrevented = returnFalse;
+ event.isPropagationStopped = returnFalse;
+ event.isImmediatePropagationStopped = returnFalse;
+ }
+
+ // Add missing metaKey for IE 8
+ if (typeof event.metaKey == 'undefined') {
+ event.metaKey = false;
+ }
+
+ return event;
+ }
+
+ /**
+ * Bind a DOMContentLoaded event across browsers and executes the callback once the page DOM is initialized.
+ * It will also set/check the domLoaded state of the event_utils instance so ready isn't called multiple times.
+ */
+ function bindOnReady(win, callback, eventUtils) {
+ var doc = win.document, event = {type: 'ready'};
+
+ if (eventUtils.domLoaded) {
+ callback(event);
+ return;
+ }
+
+ // Gets called when the DOM is ready
+ function readyHandler() {
+ if (!eventUtils.domLoaded) {
+ eventUtils.domLoaded = true;
+ callback(event);
+ }
+ }
+
+ function waitForDomLoaded() {
+ // Check complete or interactive state if there is a body
+ // element on some iframes IE 8 will produce a null body
+ if (doc.readyState === "complete" || (doc.readyState === "interactive" && doc.body)) {
+ removeEvent(doc, "readystatechange", waitForDomLoaded);
+ readyHandler();
+ }
+ }
+
+ function tryScroll() {
+ try {
+ // If IE is used, use the trick by Diego Perini licensed under MIT by request to the author.
+ // http://javascript.nwbox.com/IEContentLoaded/
+ doc.documentElement.doScroll("left");
+ } catch (ex) {
+ Delay.setTimeout(tryScroll);
+ return;
+ }
+
+ readyHandler();
+ }
+
+ // Use W3C method
+ if (doc.addEventListener) {
+ if (doc.readyState === "complete") {
+ readyHandler();
+ } else {
+ addEvent(win, 'DOMContentLoaded', readyHandler);
+ }
+ } else {
+ // Use IE method
+ addEvent(doc, "readystatechange", waitForDomLoaded);
+
+ // Wait until we can scroll, when we can the DOM is initialized
+ if (doc.documentElement.doScroll && win.self === win.top) {
+ tryScroll();
+ }
+ }
+
+ // Fallback if any of the above methods should fail for some odd reason
+ addEvent(win, 'load', readyHandler);
+ }
+
+ /**
+ * This class enables you to bind/unbind native events to elements and normalize it's behavior across browsers.
+ */
+ function EventUtils() {
+ var self = this, events = {}, count, expando, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
+
+ expando = eventExpandoPrefix + (+new Date()).toString(32);
+ hasMouseEnterLeave = "onmouseenter" in document.documentElement;
+ hasFocusIn = "onfocusin" in document.documentElement;
+ mouseEnterLeave = {mouseenter: 'mouseover', mouseleave: 'mouseout'};
+ count = 1;
+
+ // State if the DOMContentLoaded was executed or not
+ self.domLoaded = false;
+ self.events = events;
+
+ /**
+ * Executes all event handler callbacks for a specific event.
+ *
+ * @private
+ * @param {Event} evt Event object.
+ * @param {String} id Expando id value to look for.
+ */
+ function executeHandlers(evt, id) {
+ var callbackList, i, l, callback, container = events[id];
+
+ callbackList = container && container[evt.type];
+ if (callbackList) {
+ for (i = 0, l = callbackList.length; i < l; i++) {
+ callback = callbackList[i];
+
+ // Check if callback exists might be removed if a unbind is called inside the callback
+ if (callback && callback.func.call(callback.scope, evt) === false) {
+ evt.preventDefault();
+ }
+
+ // Should we stop propagation to immediate listeners
+ if (evt.isImmediatePropagationStopped()) {
+ return;
+ }
+ }
+ }
+ }
+
+ /**
+ * Binds a callback to an event on the specified target.
+ *
+ * @method bind
+ * @param {Object} target Target node/window or custom object.
+ * @param {String} names Name of the event to bind.
+ * @param {function} callback Callback function to execute when the event occurs.
+ * @param {Object} scope Scope to call the callback function on, defaults to target.
+ * @return {function} Callback function that got bound.
+ */
+ self.bind = function(target, names, callback, scope) {
+ var id, callbackList, i, name, fakeName, nativeHandler, capture, win = window;
+
+ // Native event handler function patches the event and executes the callbacks for the expando
+ function defaultNativeHandler(evt) {
+ executeHandlers(fix(evt || win.event), id);
+ }
+
+ // Don't bind to text nodes or comments
+ if (!target || target.nodeType === 3 || target.nodeType === 8) {
+ return;
+ }
+
+ // Create or get events id for the target
+ if (!target[expando]) {
+ id = count++;
+ target[expando] = id;
+ events[id] = {};
+ } else {
+ id = target[expando];
+ }
+
+ // Setup the specified scope or use the target as a default
+ scope = scope || target;
+
+ // Split names and bind each event, enables you to bind multiple events with one call
+ names = names.split(' ');
+ i = names.length;
+ while (i--) {
+ name = names[i];
+ nativeHandler = defaultNativeHandler;
+ fakeName = capture = false;
+
+ // Use ready instead of DOMContentLoaded
+ if (name === "DOMContentLoaded") {
+ name = "ready";
+ }
+
+ // DOM is already ready
+ if (self.domLoaded && name === "ready" && target.readyState == 'complete') {
+ callback.call(scope, fix({type: name}));
+ continue;
+ }
+
+ // Handle mouseenter/mouseleaver
+ if (!hasMouseEnterLeave) {
+ fakeName = mouseEnterLeave[name];
+
+ if (fakeName) {
+ nativeHandler = function(evt) {
+ var current, related;
+
+ current = evt.currentTarget;
+ related = evt.relatedTarget;
+
+ // Check if related is inside the current target if it's not then the event should
+ // be ignored since it's a mouseover/mouseout inside the element
+ if (related && current.contains) {
+ // Use contains for performance
+ related = current.contains(related);
+ } else {
+ while (related && related !== current) {
+ related = related.parentNode;
+ }
+ }
+
+ // Fire fake event
+ if (!related) {
+ evt = fix(evt || win.event);
+ evt.type = evt.type === 'mouseout' ? 'mouseleave' : 'mouseenter';
+ evt.target = current;
+ executeHandlers(evt, id);
+ }
+ };
+ }
+ }
+
+ // Fake bubbling of focusin/focusout
+ if (!hasFocusIn && (name === "focusin" || name === "focusout")) {
+ capture = true;
+ fakeName = name === "focusin" ? "focus" : "blur";
+ nativeHandler = function(evt) {
+ evt = fix(evt || win.event);
+ evt.type = evt.type === 'focus' ? 'focusin' : 'focusout';
+ executeHandlers(evt, id);
+ };
+ }
+
+ // Setup callback list and bind native event
+ callbackList = events[id][name];
+ if (!callbackList) {
+ events[id][name] = callbackList = [{func: callback, scope: scope}];
+ callbackList.fakeName = fakeName;
+ callbackList.capture = capture;
+ //callbackList.callback = callback;
+
+ // Add the nativeHandler to the callback list so that we can later unbind it
+ callbackList.nativeHandler = nativeHandler;
+
+ // Check if the target has native events support
+
+ if (name === "ready") {
+ bindOnReady(target, nativeHandler, self);
+ } else {
+ addEvent(target, fakeName || name, nativeHandler, capture);
+ }
+ } else {
+ if (name === "ready" && self.domLoaded) {
+ callback({type: name});
+ } else {
+ // If it already has an native handler then just push the callback
+ callbackList.push({func: callback, scope: scope});
+ }
+ }
+ }
+
+ target = callbackList = 0; // Clean memory for IE
+
+ return callback;
+ };
+
+ /**
+ * Unbinds the specified event by name, name and callback or all events on the target.
+ *
+ * @method unbind
+ * @param {Object} target Target node/window or custom object.
+ * @param {String} names Optional event name to unbind.
+ * @param {function} callback Optional callback function to unbind.
+ * @return {EventUtils} Event utils instance.
+ */
+ self.unbind = function(target, names, callback) {
+ var id, callbackList, i, ci, name, eventMap;
+
+ // Don't bind to text nodes or comments
+ if (!target || target.nodeType === 3 || target.nodeType === 8) {
+ return self;
+ }
+
+ // Unbind event or events if the target has the expando
+ id = target[expando];
+ if (id) {
+ eventMap = events[id];
+
+ // Specific callback
+ if (names) {
+ names = names.split(' ');
+ i = names.length;
+ while (i--) {
+ name = names[i];
+ callbackList = eventMap[name];
+
+ // Unbind the event if it exists in the map
+ if (callbackList) {
+ // Remove specified callback
+ if (callback) {
+ ci = callbackList.length;
+ while (ci--) {
+ if (callbackList[ci].func === callback) {
+ var nativeHandler = callbackList.nativeHandler;
+ var fakeName = callbackList.fakeName, capture = callbackList.capture;
+
+ // Clone callbackList since unbind inside a callback would otherwise break the handlers loop
+ callbackList = callbackList.slice(0, ci).concat(callbackList.slice(ci + 1));
+ callbackList.nativeHandler = nativeHandler;
+ callbackList.fakeName = fakeName;
+ callbackList.capture = capture;
+
+ eventMap[name] = callbackList;
+ }
+ }
+ }
+
+ // Remove all callbacks if there isn't a specified callback or there is no callbacks left
+ if (!callback || callbackList.length === 0) {
+ delete eventMap[name];
+ removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
+ }
+ }
+ }
+ } else {
+ // All events for a specific element
+ for (name in eventMap) {
+ callbackList = eventMap[name];
+ removeEvent(target, callbackList.fakeName || name, callbackList.nativeHandler, callbackList.capture);
+ }
+
+ eventMap = {};
+ }
+
+ // Check if object is empty, if it isn't then we won't remove the expando map
+ for (name in eventMap) {
+ return self;
+ }
+
+ // Delete event object
+ delete events[id];
+
+ // Remove expando from target
+ try {
+ // IE will fail here since it can't delete properties from window
+ delete target[expando];
+ } catch (ex) {
+ // IE will set it to null
+ target[expando] = null;
+ }
+ }
+
+ return self;
+ };
+
+ /**
+ * Fires the specified event on the specified target.
+ *
+ * @method fire
+ * @param {Object} target Target node/window or custom object.
+ * @param {String} name Event name to fire.
+ * @param {Object} args Optional arguments to send to the observers.
+ * @return {EventUtils} Event utils instance.
+ */
+ self.fire = function(target, name, args) {
+ var id;
+
+ // Don't bind to text nodes or comments
+ if (!target || target.nodeType === 3 || target.nodeType === 8) {
+ return self;
+ }
+
+ // Build event object by patching the args
+ args = fix(null, args);
+ args.type = name;
+ args.target = target;
+
+ do {
+ // Found an expando that means there is listeners to execute
+ id = target[expando];
+ if (id) {
+ executeHandlers(args, id);
+ }
+
+ // Walk up the DOM
+ target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
+ } while (target && !args.isPropagationStopped());
+
+ return self;
+ };
+
+ /**
+ * Removes all bound event listeners for the specified target. This will also remove any bound
+ * listeners to child nodes within that target.
+ *
+ * @method clean
+ * @param {Object} target Target node/window object.
+ * @return {EventUtils} Event utils instance.
+ */
+ self.clean = function(target) {
+ var i, children, unbind = self.unbind;
+
+ // Don't bind to text nodes or comments
+ if (!target || target.nodeType === 3 || target.nodeType === 8) {
+ return self;
+ }
+
+ // Unbind any element on the specified target
+ if (target[expando]) {
+ unbind(target);
+ }
+
+ // Target doesn't have getElementsByTagName it's probably a window object then use it's document to find the children
+ if (!target.getElementsByTagName) {
+ target = target.document;
+ }
+
+ // Remove events from each child element
+ if (target && target.getElementsByTagName) {
+ unbind(target);
+
+ children = target.getElementsByTagName('*');
+ i = children.length;
+ while (i--) {
+ target = children[i];
+
+ if (target[expando]) {
+ unbind(target);
+ }
+ }
+ }
+
+ return self;
+ };
+
+ /**
+ * Destroys the event object. Call this on IE to remove memory leaks.
+ */
+ self.destroy = function() {
+ events = {};
+ };
+
+ // Legacy function for canceling events
+ self.cancel = function(e) {
+ if (e) {
+ e.preventDefault();
+ e.stopImmediatePropagation();
+ }
+
+ return false;
+ };
+ }
+
+ EventUtils.Event = new EventUtils();
+ EventUtils.Event.bind(window, 'ready', function() {});
+
+ return EventUtils;
+});
+
+// Included from: js/tinymce/classes/dom/Sizzle.js
+
+/**
+ * Sizzle.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
+ */
+
+/*jshint bitwise:false, expr:true, noempty:false, sub:true, eqnull:true, latedef:false, maxlen:255 */
+/*eslint-disable */
+
+/**
+ * Sizzle CSS Selector Engine v@VERSION
+ * http://sizzlejs.com/
+ *
+ * Copyright 2008, 2014 jQuery Foundation, Inc. and other contributors
+ * Released under the MIT license
+ * http://jquery.org/license
+ *
+ * Date: @DATE
+ */
+define("tinymce/dom/Sizzle", [], function() {
+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" + -(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
+ strundefined = typeof undefined,
+ 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 if we can't use a native one
+ indexOf = arr.indexOf || function( elem ) {
+ var i = 0,
+ len = this.length;
+ for ( ; i < len; i++ ) {
+ if ( this[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
+ 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 );
+ };
+
+// 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 match, elem, m, nodeType,
+ // QSA vars
+ i, groups, old, nid, newContext, newSelector;
+
+ if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) {
+ setDocument( context );
+ }
+
+ context = context || document;
+ results = results || [];
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( documentIsHTML && !seed ) {
+
+ // Shortcuts
+ if ( (match = rquickExpr.exec( selector )) ) {
+ // Speed-up: Sizzle("#ID")
+ if ( (m = match[1]) ) {
+ if ( nodeType === 9 ) {
+ elem = context.getElementById( m );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document (jQuery #6963)
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE, Opera, and Webkit return items
+ // by name instead of ID
+ if ( elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ } else {
+ return results;
+ }
+ } else {
+ // Context is not a document
+ if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) &&
+ contains( context, elem ) && elem.id === m ) {
+ results.push( elem );
+ return results;
+ }
+ }
+
+ // Speed-up: Sizzle("TAG")
+ } else if ( match[2] ) {
+ push.apply( results, context.getElementsByTagName( selector ) );
+ return results;
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( (m = match[3]) && support.getElementsByClassName ) {
+ push.apply( results, context.getElementsByClassName( m ) );
+ return results;
+ }
+ }
+
+ // QSA path
+ if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) {
+ nid = old = expando;
+ newContext = context;
+ newSelector = nodeType === 9 && selector;
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ groups = tokenize( selector );
+
+ if ( (old = context.getAttribute("id")) ) {
+ nid = old.replace( rescape, "\\$&" );
+ } else {
+ context.setAttribute( "id", nid );
+ }
+ nid = "[id='" + nid + "'] ";
+
+ i = groups.length;
+ while ( i-- ) {
+ groups[i] = nid + toSelector( groups[i] );
+ }
+ newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context;
+ newSelector = groups.join(",");
+ }
+
+ if ( newSelector ) {
+ try {
+ push.apply( results,
+ newContext.querySelectorAll( newSelector )
+ );
+ return results;
+ } catch(qsaError) {
+ } finally {
+ if ( !old ) {
+ 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 = attrs.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 !== strundefined && 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,
+ doc = node ? node.ownerDocument || node : preferredDoc,
+ parent = doc.defaultView;
+
+ function getTop(win) {
+ // Edge throws a lovely Object expected if you try to get top on a detached reference see #2642
+ try {
+ return win.top;
+ } catch (ex) {
+ // Ignore
+ }
+
+ return null;
+ }
+
+ // If no document and documentElement is available, return
+ if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) {
+ return document;
+ }
+
+ // Set our document
+ document = doc;
+ docElem = doc.documentElement;
+
+ // Support tests
+ documentIsHTML = !isXML( doc );
+
+ // Support: IE>8
+ // If iframe document is assigned to "document" variable and if iframe has been reloaded,
+ // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
+ // IE6-8 do not support the defaultView property so parent will be undefined
+ if ( parent && parent !== getTop(parent) ) {
+ // IE11 does not have attachEvent, so all must suffer
+ if ( parent.addEventListener ) {
+ parent.addEventListener( "unload", function() {
+ setDocument();
+ }, false );
+ } else if ( parent.attachEvent ) {
+ parent.attachEvent( "onunload", function() {
+ setDocument();
+ });
+ }
+ }
+
+ /* 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( doc.createComment("") );
+ return !div.getElementsByTagName("*").length;
+ });
+
+ // Support: IE<9
+ support.getElementsByClassName = rnative.test( doc.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 !doc.getElementsByName || !doc.getElementsByName( expando ).length;
+ });
+
+ // ID find and filter
+ if ( support.getById ) {
+ Expr.find["ID"] = function( id, context ) {
+ if ( typeof context.getElementById !== strundefined && documentIsHTML ) {
+ var m = context.getElementById( id );
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [ 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 !== strundefined && elem.getAttributeNode("id");
+ return node && node.value === attrId;
+ };
+ };
+ }
+
+ // Tag
+ Expr.find["TAG"] = support.getElementsByTagName ?
+ function( tag, context ) {
+ if ( typeof context.getElementsByTagName !== strundefined ) {
+ return context.getElementsByTagName( tag );
+ }
+ } :
+ function( tag, context ) {
+ var elem,
+ tmp = [],
+ i = 0,
+ 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 ( 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( doc.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
+ 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 + ")" );
+ }
+
+ // 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");
+ }
+ });
+
+ assert(function( div ) {
+ // Support: Windows 8 Native Apps
+ // The type and name attributes are restricted during .innerHTML assignment
+ var input = doc.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 does not implement inclusive descendent
+ // 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 === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) {
+ return -1;
+ }
+ if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) {
+ return 1;
+ }
+
+ // Maintain original order
+ return sortInput ?
+ ( indexOf.call( sortInput, a ) - indexOf.call( 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 === doc ? -1 :
+ b === doc ? 1 :
+ aup ? -1 :
+ bup ? 1 :
+ sortInput ?
+ ( indexOf.call( sortInput, a ) - indexOf.call( 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 doc;
+};
+
+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 &&
+ ( !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 !== strundefined && 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 + " " ).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, outerCache, node, diff, nodeIndex, start,
+ dir = simple !== forward ? "nextSibling" : "previousSibling",
+ parent = elem.parentNode,
+ name = ofType && elem.nodeName.toLowerCase(),
+ useCache = !xml && !ofType;
+
+ 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
+ outerCache = parent[ expando ] || (parent[ expando ] = {});
+ cache = outerCache[ type ] || [];
+ nodeIndex = cache[0] === dirruns && cache[1];
+ diff = cache[0] === dirruns && 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 ) {
+ outerCache[ type ] = [ dirruns, nodeIndex, diff ];
+ break;
+ }
+ }
+
+ // Use previously-cached element index if available
+ } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) {
+ diff = cache[1];
+
+ // xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
+ } else {
+ // 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 ) {
+ (node[ expando ] || (node[ expando ] = {}))[ 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.call( 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 );
+ 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, outerCache,
+ newCache = [ dirruns, doneName ];
+
+ // We can't set arbitrary data on XML nodes, so they don't benefit from dir 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 ] = {});
+ if ( (oldCache = outerCache[ 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
+ outerCache[ 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.call( 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.call( checkContext, elem ) > -1;
+ }, implicitRelative, true ),
+ matchers = [ function( elem, context, xml ) {
+ return ( !leadingRelative && ( xml || context !== outermostContext ) ) || (
+ (checkContext = context).nodeType ?
+ matchContext( elem, context, xml ) :
+ matchAnyContext( elem, context, xml ) );
+ } ];
+
+ 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;
+ }
+
+ // Add elements passing elementMatchers directly to results
+ // Keep `i` a string if there are no elements so `matchedCount` will be "00" below
+ // 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;
+ while ( (matcher = elementMatchers[j++]) ) {
+ if ( matcher( elem, context, 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 );
+ }
+ }
+ }
+
+ // Apply set filters to unmatched elements
+ matchedCount += i;
+ 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 no seed and only one group
+ if ( match.length === 1 ) {
+
+ // Take a shortcut and set the context if the root 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,
+ 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;
+ }
+ });
+}
+
+// EXPOSE
+return Sizzle;
+});
+
+/*eslint-enable */
+
+// Included from: js/tinymce/classes/util/Arr.js
+
+/**
+ * Arr.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
+ */
+
+/**
+ * Array utility class.
+ *
+ * @private
+ * @class tinymce.util.Arr
+ */
+define("tinymce/util/Arr", [], function() {
+ var isArray = Array.isArray || function(obj) {
+ return Object.prototype.toString.call(obj) === "[object Array]";
+ };
+
+ function toArray(obj) {
+ var array = obj, i, l;
+
+ if (!isArray(obj)) {
+ array = [];
+ for (i = 0, l = obj.length; i < l; i++) {
+ array[i] = obj[i];
+ }
+ }
+
+ return array;
+ }
+
+ function each(o, cb, s) {
+ var n, l;
+
+ if (!o) {
+ return 0;
+ }
+
+ s = s || o;
+
+ if (o.length !== undefined) {
+ // Indexed arrays, needed for Safari
+ for (n = 0, l = o.length; n < l; n++) {
+ if (cb.call(s, o[n], n, o) === false) {
+ return 0;
+ }
+ }
+ } else {
+ // Hashtables
+ for (n in o) {
+ if (o.hasOwnProperty(n)) {
+ if (cb.call(s, o[n], n, o) === false) {
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 1;
+ }
+
+ function map(array, callback) {
+ var out = [];
+
+ each(array, function(item, index) {
+ out.push(callback(item, index, array));
+ });
+
+ return out;
+ }
+
+ function filter(a, f) {
+ var o = [];
+
+ each(a, function(v, index) {
+ if (!f || f(v, index, a)) {
+ o.push(v);
+ }
+ });
+
+ return o;
+ }
+
+ function indexOf(a, v) {
+ var i, l;
+
+ if (a) {
+ for (i = 0, l = a.length; i < l; i++) {
+ if (a[i] === v) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ }
+
+ function reduce(collection, iteratee, accumulator, thisArg) {
+ var i = 0;
+
+ if (arguments.length < 3) {
+ accumulator = collection[0];
+ }
+
+ for (; i < collection.length; i++) {
+ accumulator = iteratee.call(thisArg, accumulator, collection[i], i);
+ }
+
+ return accumulator;
+ }
+
+ function findIndex(array, predicate, thisArg) {
+ var i, l;
+
+ for (i = 0, l = array.length; i < l; i++) {
+ if (predicate.call(thisArg, array[i], i, array)) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ function find(array, predicate, thisArg) {
+ var idx = findIndex(array, predicate, thisArg);
+
+ if (idx !== -1) {
+ return array[idx];
+ }
+
+ return undefined;
+ }
+
+ function last(collection) {
+ return collection[collection.length - 1];
+ }
+
+ return {
+ isArray: isArray,
+ toArray: toArray,
+ each: each,
+ map: map,
+ filter: filter,
+ indexOf: indexOf,
+ reduce: reduce,
+ findIndex: findIndex,
+ find: find,
+ last: last
+ };
+});
+
+// Included from: js/tinymce/classes/util/Tools.js
+
+/**
+ * Tools.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 contains various utlity functions. These are also exposed
+ * directly on the tinymce namespace.
+ *
+ * @class tinymce.util.Tools
+ */
+define("tinymce/util/Tools", [
+ "tinymce/Env",
+ "tinymce/util/Arr"
+], function(Env, Arr) {
+ /**
+ * Removes whitespace from the beginning and end of a string.
+ *
+ * @method trim
+ * @param {String} s String to remove whitespace from.
+ * @return {String} New string with removed whitespace.
+ */
+ var whiteSpaceRegExp = /^\s*|\s*$/g;
+
+ function trim(str) {
+ return (str === null || str === undefined) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
+ }
+
+ /**
+ * Checks if a object is of a specific type for example an array.
+ *
+ * @method is
+ * @param {Object} obj Object to check type of.
+ * @param {string} type Optional type to check for.
+ * @return {Boolean} true/false if the object is of the specified type.
+ */
+ function is(obj, type) {
+ if (!type) {
+ return obj !== undefined;
+ }
+
+ if (type == 'array' && Arr.isArray(obj)) {
+ return true;
+ }
+
+ return typeof obj == type;
+ }
+
+ /**
+ * Makes a name/object map out of an array with names.
+ *
+ * @method makeMap
+ * @param {Array/String} items Items to make map out of.
+ * @param {String} delim Optional delimiter to split string by.
+ * @param {Object} map Optional map to add items to.
+ * @return {Object} Name/value map of items.
+ */
+ function makeMap(items, delim, map) {
+ var i;
+
+ items = items || [];
+ delim = delim || ',';
+
+ if (typeof items == "string") {
+ items = items.split(delim);
+ }
+
+ map = map || {};
+
+ i = items.length;
+ while (i--) {
+ map[items[i]] = {};
+ }
+
+ return map;
+ }
+
+ /**
+ * JavaScript does not protect hasOwnProperty method, so it is possible to overwrite it. This is
+ * object independent version.
+ *
+ * @param {Object} obj
+ * @param {String} prop
+ * @returns {Boolean}
+ */
+ function hasOwnProperty(obj, prop) {
+ return Object.prototype.hasOwnProperty.call(obj, prop);
+ }
+
+ /**
+ * Creates a class, subclass or static singleton.
+ * More details on this method can be found in the Wiki.
+ *
+ * @method create
+ * @param {String} s Class name, inheritance and prefix.
+ * @param {Object} p Collection of methods to add to the class.
+ * @param {Object} root Optional root object defaults to the global window object.
+ * @example
+ * // Creates a basic class
+ * tinymce.create('tinymce.somepackage.SomeClass', {
+ * SomeClass: function() {
+ * // Class constructor
+ * },
+ *
+ * method: function() {
+ * // Some method
+ * }
+ * });
+ *
+ * // Creates a basic subclass class
+ * tinymce.create('tinymce.somepackage.SomeSubClass:tinymce.somepackage.SomeClass', {
+ * SomeSubClass: function() {
+ * // Class constructor
+ * this.parent(); // Call parent constructor
+ * },
+ *
+ * method: function() {
+ * // Some method
+ * this.parent(); // Call parent method
+ * },
+ *
+ * 'static': {
+ * staticMethod: function() {
+ * // Static method
+ * }
+ * }
+ * });
+ *
+ * // Creates a singleton/static class
+ * tinymce.create('static tinymce.somepackage.SomeSingletonClass', {
+ * method: function() {
+ * // Some method
+ * }
+ * });
+ */
+ function create(s, p, root) {
+ var self = this, sp, ns, cn, scn, c, de = 0;
+
+ // Parse : :
+ s = /^((static) )?([\w.]+)(:([\w.]+))?/.exec(s);
+ cn = s[3].match(/(^|\.)(\w+)$/i)[2]; // Class name
+
+ // Create namespace for new class
+ ns = self.createNS(s[3].replace(/\.\w+$/, ''), root);
+
+ // Class already exists
+ if (ns[cn]) {
+ return;
+ }
+
+ // Make pure static class
+ if (s[2] == 'static') {
+ ns[cn] = p;
+
+ if (this.onCreate) {
+ this.onCreate(s[2], s[3], ns[cn]);
+ }
+
+ return;
+ }
+
+ // Create default constructor
+ if (!p[cn]) {
+ p[cn] = function() {};
+ de = 1;
+ }
+
+ // Add constructor and methods
+ ns[cn] = p[cn];
+ self.extend(ns[cn].prototype, p);
+
+ // Extend
+ if (s[5]) {
+ sp = self.resolve(s[5]).prototype;
+ scn = s[5].match(/\.(\w+)$/i)[1]; // Class name
+
+ // Extend constructor
+ c = ns[cn];
+ if (de) {
+ // Add passthrough constructor
+ ns[cn] = function() {
+ return sp[scn].apply(this, arguments);
+ };
+ } else {
+ // Add inherit constructor
+ ns[cn] = function() {
+ this.parent = sp[scn];
+ return c.apply(this, arguments);
+ };
+ }
+ ns[cn].prototype[cn] = ns[cn];
+
+ // Add super methods
+ self.each(sp, function(f, n) {
+ ns[cn].prototype[n] = sp[n];
+ });
+
+ // Add overridden methods
+ self.each(p, function(f, n) {
+ // Extend methods if needed
+ if (sp[n]) {
+ ns[cn].prototype[n] = function() {
+ this.parent = sp[n];
+ return f.apply(this, arguments);
+ };
+ } else {
+ if (n != cn) {
+ ns[cn].prototype[n] = f;
+ }
+ }
+ });
+ }
+
+ // Add static methods
+ /*jshint sub:true*/
+ /*eslint dot-notation:0*/
+ self.each(p['static'], function(f, n) {
+ ns[cn][n] = f;
+ });
+ }
+
+ function extend(obj, ext) {
+ var i, l, name, args = arguments, value;
+
+ for (i = 1, l = args.length; i < l; i++) {
+ ext = args[i];
+ for (name in ext) {
+ if (ext.hasOwnProperty(name)) {
+ value = ext[name];
+
+ if (value !== undefined) {
+ obj[name] = value;
+ }
+ }
+ }
+ }
+
+ return obj;
+ }
+
+ /**
+ * Executed the specified function for each item in a object tree.
+ *
+ * @method walk
+ * @param {Object} o Object tree to walk though.
+ * @param {function} f Function to call for each item.
+ * @param {String} n Optional name of collection inside the objects to walk for example childNodes.
+ * @param {String} s Optional scope to execute the function in.
+ */
+ function walk(o, f, n, s) {
+ s = s || this;
+
+ if (o) {
+ if (n) {
+ o = o[n];
+ }
+
+ Arr.each(o, function(o, i) {
+ if (f.call(s, o, i, n) === false) {
+ return false;
+ }
+
+ walk(o, f, n, s);
+ });
+ }
+ }
+
+ /**
+ * Creates a namespace on a specific object.
+ *
+ * @method createNS
+ * @param {String} n Namespace to create for example a.b.c.d.
+ * @param {Object} o Optional object to add namespace to, defaults to window.
+ * @return {Object} New namespace object the last item in path.
+ * @example
+ * // Create some namespace
+ * tinymce.createNS('tinymce.somepackage.subpackage');
+ *
+ * // Add a singleton
+ * var tinymce.somepackage.subpackage.SomeSingleton = {
+ * method: function() {
+ * // Some method
+ * }
+ * };
+ */
+ function createNS(n, o) {
+ var i, v;
+
+ o = o || window;
+
+ n = n.split('.');
+ for (i = 0; i < n.length; i++) {
+ v = n[i];
+
+ if (!o[v]) {
+ o[v] = {};
+ }
+
+ o = o[v];
+ }
+
+ return o;
+ }
+
+ /**
+ * Resolves a string and returns the object from a specific structure.
+ *
+ * @method resolve
+ * @param {String} n Path to resolve for example a.b.c.d.
+ * @param {Object} o Optional object to search though, defaults to window.
+ * @return {Object} Last object in path or null if it couldn't be resolved.
+ * @example
+ * // Resolve a path into an object reference
+ * var obj = tinymce.resolve('a.b.c.d');
+ */
+ function resolve(n, o) {
+ var i, l;
+
+ o = o || window;
+
+ n = n.split('.');
+ for (i = 0, l = n.length; i < l; i++) {
+ o = o[n[i]];
+
+ if (!o) {
+ break;
+ }
+ }
+
+ return o;
+ }
+
+ /**
+ * Splits a string but removes the whitespace before and after each value.
+ *
+ * @method explode
+ * @param {string} s String to split.
+ * @param {string} d Delimiter to split by.
+ * @example
+ * // Split a string into an array with a,b,c
+ * var arr = tinymce.explode('a, b, c');
+ */
+ function explode(s, d) {
+ if (!s || is(s, 'array')) {
+ return s;
+ }
+
+ return Arr.map(s.split(d || ','), trim);
+ }
+
+ function _addCacheSuffix(url) {
+ var cacheSuffix = Env.cacheSuffix;
+
+ if (cacheSuffix) {
+ url += (url.indexOf('?') === -1 ? '?' : '&') + cacheSuffix;
+ }
+
+ return url;
+ }
+
+ return {
+ trim: trim,
+
+ /**
+ * Returns true/false if the object is an array or not.
+ *
+ * @method isArray
+ * @param {Object} obj Object to check.
+ * @return {boolean} true/false state if the object is an array or not.
+ */
+ isArray: Arr.isArray,
+
+ is: is,
+
+ /**
+ * Converts the specified object into a real JavaScript array.
+ *
+ * @method toArray
+ * @param {Object} obj Object to convert into array.
+ * @return {Array} Array object based in input.
+ */
+ toArray: Arr.toArray,
+ makeMap: makeMap,
+
+ /**
+ * Performs an iteration of all items in a collection such as an object or array. This method will execure the
+ * callback function for each item in the collection, if the callback returns false the iteration will terminate.
+ * The callback has the following format: cb(value, key_or_index).
+ *
+ * @method each
+ * @param {Object} o Collection to iterate.
+ * @param {function} cb Callback function to execute for each item.
+ * @param {Object} s Optional scope to execute the callback in.
+ * @example
+ * // Iterate an array
+ * tinymce.each([1,2,3], function(v, i) {
+ * console.debug("Value: " + v + ", Index: " + i);
+ * });
+ *
+ * // Iterate an object
+ * tinymce.each({a: 1, b: 2, c: 3], function(v, k) {
+ * console.debug("Value: " + v + ", Key: " + k);
+ * });
+ */
+ each: Arr.each,
+
+ /**
+ * Creates a new array by the return value of each iteration function call. This enables you to convert
+ * one array list into another.
+ *
+ * @method map
+ * @param {Array} array Array of items to iterate.
+ * @param {function} callback Function to call for each item. It's return value will be the new value.
+ * @return {Array} Array with new values based on function return values.
+ */
+ map: Arr.map,
+
+ /**
+ * Filters out items from the input array by calling the specified function for each item.
+ * If the function returns false the item will be excluded if it returns true it will be included.
+ *
+ * @method grep
+ * @param {Array} a Array of items to loop though.
+ * @param {function} f Function to call for each item. Include/exclude depends on it's return value.
+ * @return {Array} New array with values imported and filtered based in input.
+ * @example
+ * // Filter out some items, this will return an array with 4 and 5
+ * var items = tinymce.grep([1,2,3,4,5], function(v) {return v > 3;});
+ */
+ grep: Arr.filter,
+
+ /**
+ * Returns an index of the item or -1 if item is not present in the array.
+ *
+ * @method inArray
+ * @param {any} item Item to search for.
+ * @param {Array} arr Array to search in.
+ * @return {Number} index of the item or -1 if item was not found.
+ */
+ inArray: Arr.indexOf,
+
+ hasOwn: hasOwnProperty,
+
+ extend: extend,
+ create: create,
+ walk: walk,
+ createNS: createNS,
+ resolve: resolve,
+ explode: explode,
+ _addCacheSuffix: _addCacheSuffix
+ };
+});
+
+// Included from: js/tinymce/classes/dom/DomQuery.js
+
+/**
+ * DomQuery.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 mimics most of the jQuery API:
+ *
+ * This is whats currently implemented:
+ * - Utility functions
+ * - DOM traversial
+ * - DOM manipulation
+ * - Event binding
+ *
+ * This is not currently implemented:
+ * - Dimension
+ * - Ajax
+ * - Animation
+ * - Advanced chaining
+ *
+ * @example
+ * var $ = tinymce.dom.DomQuery;
+ * $('p').attr('attr', 'value').addClass('class');
+ *
+ * @class tinymce.dom.DomQuery
+ */
+define("tinymce/dom/DomQuery", [
+ "tinymce/dom/EventUtils",
+ "tinymce/dom/Sizzle",
+ "tinymce/util/Tools",
+ "tinymce/Env"
+], function(EventUtils, Sizzle, Tools, Env) {
+ var doc = document, push = Array.prototype.push, slice = Array.prototype.slice;
+ var rquickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/;
+ var Event = EventUtils.Event, undef;
+ var skipUniques = Tools.makeMap('children,contents,next,prev');
+
+ function isDefined(obj) {
+ return typeof obj !== 'undefined';
+ }
+
+ function isString(obj) {
+ return typeof obj === 'string';
+ }
+
+ function isWindow(obj) {
+ return obj && obj == obj.window;
+ }
+
+ function createFragment(html, fragDoc) {
+ var frag, node, container;
+
+ fragDoc = fragDoc || doc;
+ container = fragDoc.createElement('div');
+ frag = fragDoc.createDocumentFragment();
+ container.innerHTML = html;
+
+ while ((node = container.firstChild)) {
+ frag.appendChild(node);
+ }
+
+ return frag;
+ }
+
+ function domManipulate(targetNodes, sourceItem, callback, reverse) {
+ var i;
+
+ if (isString(sourceItem)) {
+ sourceItem = createFragment(sourceItem, getElementDocument(targetNodes[0]));
+ } else if (sourceItem.length && !sourceItem.nodeType) {
+ sourceItem = DomQuery.makeArray(sourceItem);
+
+ if (reverse) {
+ for (i = sourceItem.length - 1; i >= 0; i--) {
+ domManipulate(targetNodes, sourceItem[i], callback, reverse);
+ }
+ } else {
+ for (i = 0; i < sourceItem.length; i++) {
+ domManipulate(targetNodes, sourceItem[i], callback, reverse);
+ }
+ }
+
+ return targetNodes;
+ }
+
+ if (sourceItem.nodeType) {
+ i = targetNodes.length;
+ while (i--) {
+ callback.call(targetNodes[i], sourceItem);
+ }
+ }
+
+ return targetNodes;
+ }
+
+ function hasClass(node, className) {
+ return node && className && (' ' + node.className + ' ').indexOf(' ' + className + ' ') !== -1;
+ }
+
+ function wrap(elements, wrapper, all) {
+ var lastParent, newWrapper;
+
+ wrapper = DomQuery(wrapper)[0];
+
+ elements.each(function() {
+ var self = this;
+
+ if (!all || lastParent != self.parentNode) {
+ lastParent = self.parentNode;
+ newWrapper = wrapper.cloneNode(false);
+ self.parentNode.insertBefore(newWrapper, self);
+ newWrapper.appendChild(self);
+ } else {
+ newWrapper.appendChild(self);
+ }
+ });
+
+ return elements;
+ }
+
+ var numericCssMap = Tools.makeMap('fillOpacity fontWeight lineHeight opacity orphans widows zIndex zoom', ' ');
+ var booleanMap = Tools.makeMap('checked compact declare defer disabled ismap multiple nohref noshade nowrap readonly selected', ' ');
+ var propFix = {
+ 'for': 'htmlFor',
+ 'class': 'className',
+ 'readonly': 'readOnly'
+ };
+ var cssFix = {
+ 'float': 'cssFloat'
+ };
+
+ var attrHooks = {}, cssHooks = {};
+
+ function DomQuery(selector, context) {
+ /*eslint new-cap:0 */
+ return new DomQuery.fn.init(selector, context);
+ }
+
+ function inArray(item, array) {
+ var i;
+
+ if (array.indexOf) {
+ return array.indexOf(item);
+ }
+
+ i = array.length;
+ while (i--) {
+ if (array[i] === item) {
+ return i;
+ }
+ }
+
+ return -1;
+ }
+
+ var whiteSpaceRegExp = /^\s*|\s*$/g;
+
+ function trim(str) {
+ return (str === null || str === undef) ? '' : ("" + str).replace(whiteSpaceRegExp, '');
+ }
+
+ function each(obj, callback) {
+ var length, key, i, undef, value;
+
+ if (obj) {
+ length = obj.length;
+
+ if (length === undef) {
+ // Loop object items
+ for (key in obj) {
+ if (obj.hasOwnProperty(key)) {
+ value = obj[key];
+ if (callback.call(value, key, value) === false) {
+ break;
+ }
+ }
+ }
+ } else {
+ // Loop array items
+ for (i = 0; i < length; i++) {
+ value = obj[i];
+ if (callback.call(value, i, value) === false) {
+ break;
+ }
+ }
+ }
+ }
+
+ return obj;
+ }
+
+ function grep(array, callback) {
+ var out = [];
+
+ each(array, function(i, item) {
+ if (callback(item, i)) {
+ out.push(item);
+ }
+ });
+
+ return out;
+ }
+
+ function getElementDocument(element) {
+ if (!element) {
+ return doc;
+ }
+
+ if (element.nodeType == 9) {
+ return element;
+ }
+
+ return element.ownerDocument;
+ }
+
+ DomQuery.fn = DomQuery.prototype = {
+ constructor: DomQuery,
+
+ /**
+ * Selector for the current set.
+ *
+ * @property selector
+ * @type String
+ */
+ selector: "",
+
+ /**
+ * Context used to create the set.
+ *
+ * @property context
+ * @type Element
+ */
+ context: null,
+
+ /**
+ * Number of items in the current set.
+ *
+ * @property length
+ * @type Number
+ */
+ length: 0,
+
+ /**
+ * Constructs a new DomQuery instance with the specified selector or context.
+ *
+ * @constructor
+ * @method init
+ * @param {String/Array/DomQuery} selector Optional CSS selector/Array or array like object or HTML string.
+ * @param {Document/Element} context Optional context to search in.
+ */
+ init: function(selector, context) {
+ var self = this, match, node;
+
+ if (!selector) {
+ return self;
+ }
+
+ if (selector.nodeType) {
+ self.context = self[0] = selector;
+ self.length = 1;
+
+ return self;
+ }
+
+ if (context && context.nodeType) {
+ self.context = context;
+ } else {
+ if (context) {
+ return DomQuery(selector).attr(context);
+ }
+
+ self.context = context = document;
+ }
+
+ if (isString(selector)) {
+ self.selector = selector;
+
+ if (selector.charAt(0) === "<" && selector.charAt(selector.length - 1) === ">" && selector.length >= 3) {
+ match = [null, selector, null];
+ } else {
+ match = rquickExpr.exec(selector);
+ }
+
+ if (match) {
+ if (match[1]) {
+ node = createFragment(selector, getElementDocument(context)).firstChild;
+
+ while (node) {
+ push.call(self, node);
+ node = node.nextSibling;
+ }
+ } else {
+ node = getElementDocument(context).getElementById(match[2]);
+
+ if (!node) {
+ return self;
+ }
+
+ if (node.id !== match[2]) {
+ return self.find(selector);
+ }
+
+ self.length = 1;
+ self[0] = node;
+ }
+ } else {
+ return DomQuery(context).find(selector);
+ }
+ } else {
+ this.add(selector, false);
+ }
+
+ return self;
+ },
+
+ /**
+ * Converts the current set to an array.
+ *
+ * @method toArray
+ * @return {Array} Array of all nodes in set.
+ */
+ toArray: function() {
+ return Tools.toArray(this);
+ },
+
+ /**
+ * Adds new nodes to the set.
+ *
+ * @method add
+ * @param {Array/tinymce.dom.DomQuery} items Array of all nodes to add to set.
+ * @param {Boolean} sort Optional sort flag that enables sorting of elements.
+ * @return {tinymce.dom.DomQuery} New instance with nodes added.
+ */
+ add: function(items, sort) {
+ var self = this, nodes, i;
+
+ if (isString(items)) {
+ return self.add(DomQuery(items));
+ }
+
+ if (sort !== false) {
+ nodes = DomQuery.unique(self.toArray().concat(DomQuery.makeArray(items)));
+ self.length = nodes.length;
+ for (i = 0; i < nodes.length; i++) {
+ self[i] = nodes[i];
+ }
+ } else {
+ push.apply(self, DomQuery.makeArray(items));
+ }
+
+ return self;
+ },
+
+ /**
+ * Sets/gets attributes on the elements in the current set.
+ *
+ * @method attr
+ * @param {String/Object} name Name of attribute to get or an object with attributes to set.
+ * @param {String} value Optional value to set.
+ * @return {tinymce.dom.DomQuery/String} Current set or the specified attribute when only the name is specified.
+ */
+ attr: function(name, value) {
+ var self = this, hook;
+
+ if (typeof name === "object") {
+ each(name, function(name, value) {
+ self.attr(name, value);
+ });
+ } else if (isDefined(value)) {
+ this.each(function() {
+ var hook;
+
+ if (this.nodeType === 1) {
+ hook = attrHooks[name];
+ if (hook && hook.set) {
+ hook.set(this, value);
+ return;
+ }
+
+ if (value === null) {
+ this.removeAttribute(name, 2);
+ } else {
+ this.setAttribute(name, value, 2);
+ }
+ }
+ });
+ } else {
+ if (self[0] && self[0].nodeType === 1) {
+ hook = attrHooks[name];
+ if (hook && hook.get) {
+ return hook.get(self[0], name);
+ }
+
+ if (booleanMap[name]) {
+ return self.prop(name) ? name : undef;
+ }
+
+ value = self[0].getAttribute(name, 2);
+
+ if (value === null) {
+ value = undef;
+ }
+ }
+
+ return value;
+ }
+
+ return self;
+ },
+
+ /**
+ * Removes attributse on the elements in the current set.
+ *
+ * @method removeAttr
+ * @param {String/Object} name Name of attribute to remove.
+ * @return {tinymce.dom.DomQuery/String} Current set.
+ */
+ removeAttr: function(name) {
+ return this.attr(name, null);
+ },
+
+ /**
+ * Sets/gets properties on the elements in the current set.
+ *
+ * @method attr
+ * @param {String/Object} name Name of property to get or an object with properties to set.
+ * @param {String} value Optional value to set.
+ * @return {tinymce.dom.DomQuery/String} Current set or the specified property when only the name is specified.
+ */
+ prop: function(name, value) {
+ var self = this;
+
+ name = propFix[name] || name;
+
+ if (typeof name === "object") {
+ each(name, function(name, value) {
+ self.prop(name, value);
+ });
+ } else if (isDefined(value)) {
+ this.each(function() {
+ if (this.nodeType == 1) {
+ this[name] = value;
+ }
+ });
+ } else {
+ if (self[0] && self[0].nodeType && name in self[0]) {
+ return self[0][name];
+ }
+
+ return value;
+ }
+
+ return self;
+ },
+
+ /**
+ * Sets/gets styles on the elements in the current set.
+ *
+ * @method css
+ * @param {String/Object} name Name of style to get or an object with styles to set.
+ * @param {String} value Optional value to set.
+ * @return {tinymce.dom.DomQuery/String} Current set or the specified style when only the name is specified.
+ */
+ css: function(name, value) {
+ var self = this, elm, hook;
+
+ function camel(name) {
+ return name.replace(/-(\D)/g, function(a, b) {
+ return b.toUpperCase();
+ });
+ }
+
+ function dashed(name) {
+ return name.replace(/[A-Z]/g, function(a) {
+ return '-' + a;
+ });
+ }
+
+ if (typeof name === "object") {
+ each(name, function(name, value) {
+ self.css(name, value);
+ });
+ } else {
+ if (isDefined(value)) {
+ name = camel(name);
+
+ // Default px suffix on these
+ if (typeof value === 'number' && !numericCssMap[name]) {
+ value += 'px';
+ }
+
+ self.each(function() {
+ var style = this.style;
+
+ hook = cssHooks[name];
+ if (hook && hook.set) {
+ hook.set(this, value);
+ return;
+ }
+
+ try {
+ this.style[cssFix[name] || name] = value;
+ } catch (ex) {
+ // Ignore
+ }
+
+ if (value === null || value === '') {
+ if (style.removeProperty) {
+ style.removeProperty(dashed(name));
+ } else {
+ style.removeAttribute(name);
+ }
+ }
+ });
+ } else {
+ elm = self[0];
+
+ hook = cssHooks[name];
+ if (hook && hook.get) {
+ return hook.get(elm);
+ }
+
+ if (elm.ownerDocument.defaultView) {
+ try {
+ return elm.ownerDocument.defaultView.getComputedStyle(elm, null).getPropertyValue(dashed(name));
+ } catch (ex) {
+ return undef;
+ }
+ } else if (elm.currentStyle) {
+ return elm.currentStyle[camel(name)];
+ }
+ }
+ }
+
+ return self;
+ },
+
+ /**
+ * Removes all nodes in set from the document.
+ *
+ * @method remove
+ * @return {tinymce.dom.DomQuery} Current set with the removed nodes.
+ */
+ remove: function() {
+ var self = this, node, i = this.length;
+
+ while (i--) {
+ node = self[i];
+ Event.clean(node);
+
+ if (node.parentNode) {
+ node.parentNode.removeChild(node);
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Empties all elements in set.
+ *
+ * @method empty
+ * @return {tinymce.dom.DomQuery} Current set with the empty nodes.
+ */
+ empty: function() {
+ var self = this, node, i = this.length;
+
+ while (i--) {
+ node = self[i];
+ while (node.firstChild) {
+ node.removeChild(node.firstChild);
+ }
+ }
+
+ return this;
+ },
+
+ /**
+ * Sets or gets the HTML of the current set or first set node.
+ *
+ * @method html
+ * @param {String} value Optional innerHTML value to set on each element.
+ * @return {tinymce.dom.DomQuery/String} Current set or the innerHTML of the first element.
+ */
+ html: function(value) {
+ var self = this, i;
+
+ if (isDefined(value)) {
+ i = self.length;
+
+ try {
+ while (i--) {
+ self[i].innerHTML = value;
+ }
+ } catch (ex) {
+ // Workaround for "Unknown runtime error" when DIV is added to P on IE
+ DomQuery(self[i]).empty().append(value);
+ }
+
+ return self;
+ }
+
+ return self[0] ? self[0].innerHTML : '';
+ },
+
+ /**
+ * Sets or gets the text of the current set or first set node.
+ *
+ * @method text
+ * @param {String} value Optional innerText value to set on each element.
+ * @return {tinymce.dom.DomQuery/String} Current set or the innerText of the first element.
+ */
+ text: function(value) {
+ var self = this, i;
+
+ if (isDefined(value)) {
+ i = self.length;
+ while (i--) {
+ if ("innerText" in self[i]) {
+ self[i].innerText = value;
+ } else {
+ self[0].textContent = value;
+ }
+ }
+
+ return self;
+ }
+
+ return self[0] ? (self[0].innerText || self[0].textContent) : '';
+ },
+
+ /**
+ * Appends the specified node/html or node set to the current set nodes.
+ *
+ * @method append
+ * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to append to each element in set.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ append: function() {
+ return domManipulate(this, arguments, function(node) {
+ // Either element or Shadow Root
+ if (this.nodeType === 1 || (this.host && this.host.nodeType === 1)) {
+ this.appendChild(node);
+ }
+ });
+ },
+
+ /**
+ * Prepends the specified node/html or node set to the current set nodes.
+ *
+ * @method prepend
+ * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to prepend to each element in set.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ prepend: function() {
+ return domManipulate(this, arguments, function(node) {
+ // Either element or Shadow Root
+ if (this.nodeType === 1 || (this.host && this.host.nodeType === 1)) {
+ this.insertBefore(node, this.firstChild);
+ }
+ }, true);
+ },
+
+ /**
+ * Adds the specified elements before current set nodes.
+ *
+ * @method before
+ * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add before to each element in set.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ before: function() {
+ var self = this;
+
+ if (self[0] && self[0].parentNode) {
+ return domManipulate(self, arguments, function(node) {
+ this.parentNode.insertBefore(node, this);
+ });
+ }
+
+ return self;
+ },
+
+ /**
+ * Adds the specified elements after current set nodes.
+ *
+ * @method after
+ * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to add after to each element in set.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ after: function() {
+ var self = this;
+
+ if (self[0] && self[0].parentNode) {
+ return domManipulate(self, arguments, function(node) {
+ this.parentNode.insertBefore(node, this.nextSibling);
+ }, true);
+ }
+
+ return self;
+ },
+
+ /**
+ * Appends the specified set nodes to the specified selector/instance.
+ *
+ * @method appendTo
+ * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to append the current set to.
+ * @return {tinymce.dom.DomQuery} Current set with the appended nodes.
+ */
+ appendTo: function(val) {
+ DomQuery(val).append(this);
+
+ return this;
+ },
+
+ /**
+ * Prepends the specified set nodes to the specified selector/instance.
+ *
+ * @method prependTo
+ * @param {String/Element/Array/tinymce.dom.DomQuery} val Item to prepend the current set to.
+ * @return {tinymce.dom.DomQuery} Current set with the prepended nodes.
+ */
+ prependTo: function(val) {
+ DomQuery(val).prepend(this);
+
+ return this;
+ },
+
+ /**
+ * Replaces the nodes in set with the specified content.
+ *
+ * @method replaceWith
+ * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to replace nodes with.
+ * @return {tinymce.dom.DomQuery} Set with replaced nodes.
+ */
+ replaceWith: function(content) {
+ return this.before(content).remove();
+ },
+
+ /**
+ * Wraps all elements in set with the specified wrapper.
+ *
+ * @method wrap
+ * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
+ * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
+ */
+ wrap: function(content) {
+ return wrap(this, content);
+ },
+
+ /**
+ * Wraps all nodes in set with the specified wrapper. If the nodes are siblings all of them
+ * will be wrapped in the same wrapper.
+ *
+ * @method wrapAll
+ * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
+ * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
+ */
+ wrapAll: function(content) {
+ return wrap(this, content, true);
+ },
+
+ /**
+ * Wraps all elements inner contents in set with the specified wrapper.
+ *
+ * @method wrapInner
+ * @param {String/Element/Array/tinymce.dom.DomQuery} content Content to wrap nodes with.
+ * @return {tinymce.dom.DomQuery} Set with wrapped nodes.
+ */
+ wrapInner: function(content) {
+ this.each(function() {
+ DomQuery(this).contents().wrapAll(content);
+ });
+
+ return this;
+ },
+
+ /**
+ * Unwraps all elements by removing the parent element of each item in set.
+ *
+ * @method unwrap
+ * @return {tinymce.dom.DomQuery} Set with unwrapped nodes.
+ */
+ unwrap: function() {
+ return this.parent().each(function() {
+ DomQuery(this).replaceWith(this.childNodes);
+ });
+ },
+
+ /**
+ * Clones all nodes in set.
+ *
+ * @method clone
+ * @return {tinymce.dom.DomQuery} Set with cloned nodes.
+ */
+ clone: function() {
+ var result = [];
+
+ this.each(function() {
+ result.push(this.cloneNode(true));
+ });
+
+ return DomQuery(result);
+ },
+
+ /**
+ * Adds the specified class name to the current set elements.
+ *
+ * @method addClass
+ * @param {String} className Class name to add.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ addClass: function(className) {
+ return this.toggleClass(className, true);
+ },
+
+ /**
+ * Removes the specified class name to the current set elements.
+ *
+ * @method removeClass
+ * @param {String} className Class name to remove.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ removeClass: function(className) {
+ return this.toggleClass(className, false);
+ },
+
+ /**
+ * Toggles the specified class name on the current set elements.
+ *
+ * @method toggleClass
+ * @param {String} className Class name to add/remove.
+ * @param {Boolean} state Optional state to toggle on/off.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ toggleClass: function(className, state) {
+ var self = this;
+
+ // Functions are not supported
+ if (typeof className != 'string') {
+ return self;
+ }
+
+ if (className.indexOf(' ') !== -1) {
+ each(className.split(' '), function() {
+ self.toggleClass(this, state);
+ });
+ } else {
+ self.each(function(index, node) {
+ var existingClassName, classState;
+
+ classState = hasClass(node, className);
+ if (classState !== state) {
+ existingClassName = node.className;
+
+ if (classState) {
+ node.className = trim((" " + existingClassName + " ").replace(' ' + className + ' ', ' '));
+ } else {
+ node.className += existingClassName ? ' ' + className : className;
+ }
+ }
+ });
+ }
+
+ return self;
+ },
+
+ /**
+ * Returns true/false if the first item in set has the specified class.
+ *
+ * @method hasClass
+ * @param {String} className Class name to check for.
+ * @return {Boolean} True/false if the set has the specified class.
+ */
+ hasClass: function(className) {
+ return hasClass(this[0], className);
+ },
+
+ /**
+ * Executes the callback function for each item DomQuery collection. If you return false in the
+ * callback it will break the loop.
+ *
+ * @method each
+ * @param {function} callback Callback function to execute for each item.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ each: function(callback) {
+ return each(this, callback);
+ },
+
+ /**
+ * Binds an event with callback function to the elements in set.
+ *
+ * @method on
+ * @param {String} name Name of the event to bind.
+ * @param {function} callback Callback function to execute when the event occurs.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ on: function(name, callback) {
+ return this.each(function() {
+ Event.bind(this, name, callback);
+ });
+ },
+
+ /**
+ * Unbinds an event with callback function to the elements in set.
+ *
+ * @method off
+ * @param {String} name Optional name of the event to bind.
+ * @param {function} callback Optional callback function to execute when the event occurs.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ off: function(name, callback) {
+ return this.each(function() {
+ Event.unbind(this, name, callback);
+ });
+ },
+
+ /**
+ * Triggers the specified event by name or event object.
+ *
+ * @method trigger
+ * @param {String/Object} name Name of the event to trigger or event object.
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ trigger: function(name) {
+ return this.each(function() {
+ if (typeof name == 'object') {
+ Event.fire(this, name.type, name);
+ } else {
+ Event.fire(this, name);
+ }
+ });
+ },
+
+ /**
+ * Shows all elements in set.
+ *
+ * @method show
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ show: function() {
+ return this.css('display', '');
+ },
+
+ /**
+ * Hides all elements in set.
+ *
+ * @method hide
+ * @return {tinymce.dom.DomQuery} Current set.
+ */
+ hide: function() {
+ return this.css('display', 'none');
+ },
+
+ /**
+ * Slices the current set.
+ *
+ * @method slice
+ * @param {Number} start Start index to slice at.
+ * @param {Number} end Optional end index to end slice at.
+ * @return {tinymce.dom.DomQuery} Sliced set.
+ */
+ slice: function() {
+ return new DomQuery(slice.apply(this, arguments));
+ },
+
+ /**
+ * Makes the set equal to the specified index.
+ *
+ * @method eq
+ * @param {Number} index Index to set it equal to.
+ * @return {tinymce.dom.DomQuery} Single item set.
+ */
+ eq: function(index) {
+ return index === -1 ? this.slice(index) : this.slice(index, +index + 1);
+ },
+
+ /**
+ * Makes the set equal to first element in set.
+ *
+ * @method first
+ * @return {tinymce.dom.DomQuery} Single item set.
+ */
+ first: function() {
+ return this.eq(0);
+ },
+
+ /**
+ * Makes the set equal to last element in set.
+ *
+ * @method last
+ * @return {tinymce.dom.DomQuery} Single item set.
+ */
+ last: function() {
+ return this.eq(-1);
+ },
+
+ /**
+ * Finds elements by the specified selector for each element in set.
+ *
+ * @method find
+ * @param {String} selector Selector to find elements by.
+ * @return {tinymce.dom.DomQuery} Set with matches elements.
+ */
+ find: function(selector) {
+ var i, l, ret = [];
+
+ for (i = 0, l = this.length; i < l; i++) {
+ DomQuery.find(selector, this[i], ret);
+ }
+
+ return DomQuery(ret);
+ },
+
+ /**
+ * Filters the current set with the specified selector.
+ *
+ * @method filter
+ * @param {String/function} selector Selector to filter elements by.
+ * @return {tinymce.dom.DomQuery} Set with filtered elements.
+ */
+ filter: function(selector) {
+ if (typeof selector == 'function') {
+ return DomQuery(grep(this.toArray(), function(item, i) {
+ return selector(i, item);
+ }));
+ }
+
+ return DomQuery(DomQuery.filter(selector, this.toArray()));
+ },
+
+ /**
+ * Gets the current node or any parent matching the specified selector.
+ *
+ * @method closest
+ * @param {String/Element/tinymce.dom.DomQuery} selector Selector or element to find.
+ * @return {tinymce.dom.DomQuery} Set with closest elements.
+ */
+ closest: function(selector) {
+ var result = [];
+
+ if (selector instanceof DomQuery) {
+ selector = selector[0];
+ }
+
+ this.each(function(i, node) {
+ while (node) {
+ if (typeof selector == 'string' && DomQuery(node).is(selector)) {
+ result.push(node);
+ break;
+ } else if (node == selector) {
+ result.push(node);
+ break;
+ }
+
+ node = node.parentNode;
+ }
+ });
+
+ return DomQuery(result);
+ },
+
+ /**
+ * Returns the offset of the first element in set or sets the top/left css properties of all elements in set.
+ *
+ * @method offset
+ * @param {Object} offset Optional offset object to set on each item.
+ * @return {Object/tinymce.dom.DomQuery} Returns the first element offset or the current set if you specified an offset.
+ */
+ offset: function(offset) {
+ var elm, doc, docElm;
+ var x = 0, y = 0, pos;
+
+ if (!offset) {
+ elm = this[0];
+
+ if (elm) {
+ doc = elm.ownerDocument;
+ docElm = doc.documentElement;
+
+ if (elm.getBoundingClientRect) {
+ pos = elm.getBoundingClientRect();
+ x = pos.left + (docElm.scrollLeft || doc.body.scrollLeft) - docElm.clientLeft;
+ y = pos.top + (docElm.scrollTop || doc.body.scrollTop) - docElm.clientTop;
+ }
+ }
+
+ return {
+ left: x,
+ top: y
+ };
+ }
+
+ return this.css(offset);
+ },
+
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+ };
+
+ // Static members
+ Tools.extend(DomQuery, {
+ /**
+ * Extends the specified object with one or more objects.
+ *
+ * @static
+ * @method extend
+ * @param {Object} target Target object to extend with new items.
+ * @param {Object..} object Object to extend the target with.
+ * @return {Object} Extended input object.
+ */
+ extend: Tools.extend,
+
+ /**
+ * Creates an array out of an array like object.
+ *
+ * @static
+ * @method makeArray
+ * @param {Object} object Object to convert to array.
+ * @return {Array} Array produced from object.
+ */
+ makeArray: function(object) {
+ if (isWindow(object) || object.nodeType) {
+ return [object];
+ }
+
+ return Tools.toArray(object);
+ },
+
+ /**
+ * Returns the index of the specified item inside the array.
+ *
+ * @static
+ * @method inArray
+ * @param {Object} item Item to look for.
+ * @param {Array} array Array to look for item in.
+ * @return {Number} Index of the item or -1.
+ */
+ inArray: inArray,
+
+ /**
+ * Returns true/false if the specified object is an array or not.
+ *
+ * @static
+ * @method isArray
+ * @param {Object} array Object to check if it's an array or not.
+ * @return {Boolean} True/false if the object is an array.
+ */
+ isArray: Tools.isArray,
+
+ /**
+ * Executes the callback function for each item in array/object. If you return false in the
+ * callback it will break the loop.
+ *
+ * @static
+ * @method each
+ * @param {Object} obj Object to iterate.
+ * @param {function} callback Callback function to execute for each item.
+ */
+ each: each,
+
+ /**
+ * Removes whitespace from the beginning and end of a string.
+ *
+ * @static
+ * @method trim
+ * @param {String} str String to remove whitespace from.
+ * @return {String} New string with removed whitespace.
+ */
+ trim: trim,
+
+ /**
+ * Filters out items from the input array by calling the specified function for each item.
+ * If the function returns false the item will be excluded if it returns true it will be included.
+ *
+ * @static
+ * @method grep
+ * @param {Array} array Array of items to loop though.
+ * @param {function} callback Function to call for each item. Include/exclude depends on it's return value.
+ * @return {Array} New array with values imported and filtered based in input.
+ * @example
+ * // Filter out some items, this will return an array with 4 and 5
+ * var items = DomQuery.grep([1, 2, 3, 4, 5], function(v) {return v > 3;});
+ */
+ grep: grep,
+
+ // Sizzle
+ find: Sizzle,
+ expr: Sizzle.selectors,
+ unique: Sizzle.uniqueSort,
+ text: Sizzle.getText,
+ contains: Sizzle.contains,
+ filter: function(expr, elems, not) {
+ var i = elems.length;
+
+ if (not) {
+ expr = ":not(" + expr + ")";
+ }
+
+ while (i--) {
+ if (elems[i].nodeType != 1) {
+ elems.splice(i, 1);
+ }
+ }
+
+ if (elems.length === 1) {
+ elems = DomQuery.find.matchesSelector(elems[0], expr) ? [elems[0]] : [];
+ } else {
+ elems = DomQuery.find.matches(expr, elems);
+ }
+
+ return elems;
+ }
+ });
+
+ function dir(el, prop, until) {
+ var matched = [], cur = el[prop];
+
+ if (typeof until != 'string' && until instanceof DomQuery) {
+ until = until[0];
+ }
+
+ while (cur && cur.nodeType !== 9) {
+ if (until !== undefined) {
+ if (cur === until) {
+ break;
+ }
+
+ if (typeof until == 'string' && DomQuery(cur).is(until)) {
+ break;
+ }
+ }
+
+ if (cur.nodeType === 1) {
+ matched.push(cur);
+ }
+
+ cur = cur[prop];
+ }
+
+ return matched;
+ }
+
+ function sibling(node, siblingName, nodeType, until) {
+ var result = [];
+
+ if (until instanceof DomQuery) {
+ until = until[0];
+ }
+
+ for (; node; node = node[siblingName]) {
+ if (nodeType && node.nodeType !== nodeType) {
+ continue;
+ }
+
+ if (until !== undefined) {
+ if (node === until) {
+ break;
+ }
+
+ if (typeof until == 'string' && DomQuery(node).is(until)) {
+ break;
+ }
+ }
+
+ result.push(node);
+ }
+
+ return result;
+ }
+
+ function firstSibling(node, siblingName, nodeType) {
+ for (node = node[siblingName]; node; node = node[siblingName]) {
+ if (node.nodeType == nodeType) {
+ return node;
+ }
+ }
+
+ return null;
+ }
+
+ each({
+ /**
+ * Returns a new collection with the parent of each item in current collection matching the optional selector.
+ *
+ * @method parent
+ * @param {Element/tinymce.dom.DomQuery} node Node to match parents against.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
+ */
+ parent: function(node) {
+ var parent = node.parentNode;
+
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+
+ /**
+ * Returns a new collection with the all the parents of each item in current collection matching the optional selector.
+ *
+ * @method parents
+ * @param {Element/tinymce.dom.DomQuery} node Node to match parents against.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
+ */
+ parents: function(node) {
+ return dir(node, "parentNode");
+ },
+
+ /**
+ * Returns a new collection with next sibling of each item in current collection matching the optional selector.
+ *
+ * @method next
+ * @param {Element/tinymce.dom.DomQuery} node Node to match the next element against.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
+ */
+ next: function(node) {
+ return firstSibling(node, 'nextSibling', 1);
+ },
+
+ /**
+ * Returns a new collection with previous sibling of each item in current collection matching the optional selector.
+ *
+ * @method prev
+ * @param {Element/tinymce.dom.DomQuery} node Node to match the previous element against.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
+ */
+ prev: function(node) {
+ return firstSibling(node, 'previousSibling', 1);
+ },
+
+ /**
+ * Returns all child elements matching the optional selector.
+ *
+ * @method children
+ * @param {Element/tinymce.dom.DomQuery} node Node to match the elements against.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
+ */
+ children: function(node) {
+ return sibling(node.firstChild, 'nextSibling', 1);
+ },
+
+ /**
+ * Returns all child nodes matching the optional selector.
+ *
+ * @method contents
+ * @param {Element/tinymce.dom.DomQuery} node Node to get the contents of.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
+ */
+ contents: function(node) {
+ return Tools.toArray((node.nodeName === "iframe" ? node.contentDocument || node.contentWindow.document : node).childNodes);
+ }
+ }, function(name, fn) {
+ DomQuery.fn[name] = function(selector) {
+ var self = this, result = [];
+
+ self.each(function() {
+ var nodes = fn.call(result, this, selector, result);
+
+ if (nodes) {
+ if (DomQuery.isArray(nodes)) {
+ result.push.apply(result, nodes);
+ } else {
+ result.push(nodes);
+ }
+ }
+ });
+
+ // If traversing on multiple elements we might get the same elements twice
+ if (this.length > 1) {
+ if (!skipUniques[name]) {
+ result = DomQuery.unique(result);
+ }
+
+ if (name.indexOf('parents') === 0) {
+ result = result.reverse();
+ }
+ }
+
+ result = DomQuery(result);
+
+ if (selector) {
+ return result.filter(selector);
+ }
+
+ return result;
+ };
+ });
+
+ each({
+ /**
+ * Returns a new collection with the all the parents until the matching selector/element
+ * of each item in current collection matching the optional selector.
+ *
+ * @method parentsUntil
+ * @param {Element/tinymce.dom.DomQuery} node Node to find parent of.
+ * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching parents.
+ */
+ parentsUntil: function(node, until) {
+ return dir(node, "parentNode", until);
+ },
+
+ /**
+ * Returns a new collection with all next siblings of each item in current collection matching the optional selector.
+ *
+ * @method nextUntil
+ * @param {Element/tinymce.dom.DomQuery} node Node to find next siblings on.
+ * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
+ */
+ nextUntil: function(node, until) {
+ return sibling(node, 'nextSibling', 1, until).slice(1);
+ },
+
+ /**
+ * Returns a new collection with all previous siblings of each item in current collection matching the optional selector.
+ *
+ * @method prevUntil
+ * @param {Element/tinymce.dom.DomQuery} node Node to find previous siblings on.
+ * @param {String/Element/tinymce.dom.DomQuery} until Until the matching selector or element.
+ * @return {tinymce.dom.DomQuery} New DomQuery instance with all matching elements.
+ */
+ prevUntil: function(node, until) {
+ return sibling(node, 'previousSibling', 1, until).slice(1);
+ }
+ }, function(name, fn) {
+ DomQuery.fn[name] = function(selector, filter) {
+ var self = this, result = [];
+
+ self.each(function() {
+ var nodes = fn.call(result, this, selector, result);
+
+ if (nodes) {
+ if (DomQuery.isArray(nodes)) {
+ result.push.apply(result, nodes);
+ } else {
+ result.push(nodes);
+ }
+ }
+ });
+
+ // If traversing on multiple elements we might get the same elements twice
+ if (this.length > 1) {
+ result = DomQuery.unique(result);
+
+ if (name.indexOf('parents') === 0 || name === 'prevUntil') {
+ result = result.reverse();
+ }
+ }
+
+ result = DomQuery(result);
+
+ if (filter) {
+ return result.filter(filter);
+ }
+
+ return result;
+ };
+ });
+
+ /**
+ * Returns true/false if the current set items matches the selector.
+ *
+ * @method is
+ * @param {String} selector Selector to match the elements against.
+ * @return {Boolean} True/false if the current set matches the selector.
+ */
+ DomQuery.fn.is = function(selector) {
+ return !!selector && this.filter(selector).length > 0;
+ };
+
+ DomQuery.fn.init.prototype = DomQuery.fn;
+
+ DomQuery.overrideDefaults = function(callback) {
+ var defaults;
+
+ function sub(selector, context) {
+ defaults = defaults || callback();
+
+ if (arguments.length === 0) {
+ selector = defaults.element;
+ }
+
+ if (!context) {
+ context = defaults.context;
+ }
+
+ return new sub.fn.init(selector, context);
+ }
+
+ DomQuery.extend(sub, this);
+
+ return sub;
+ };
+
+ function appendHooks(targetHooks, prop, hooks) {
+ each(hooks, function(name, func) {
+ targetHooks[name] = targetHooks[name] || {};
+ targetHooks[name][prop] = func;
+ });
+ }
+
+ if (Env.ie && Env.ie < 8) {
+ appendHooks(attrHooks, 'get', {
+ maxlength: function(elm) {
+ var value = elm.maxLength;
+
+ if (value === 0x7fffffff) {
+ return undef;
+ }
+
+ return value;
+ },
+
+ size: function(elm) {
+ var value = elm.size;
+
+ if (value === 20) {
+ return undef;
+ }
+
+ return value;
+ },
+
+ 'class': function(elm) {
+ return elm.className;
+ },
+
+ style: function(elm) {
+ var value = elm.style.cssText;
+
+ if (value.length === 0) {
+ return undef;
+ }
+
+ return value;
+ }
+ });
+
+ appendHooks(attrHooks, 'set', {
+ 'class': function(elm, value) {
+ elm.className = value;
+ },
+
+ style: function(elm, value) {
+ elm.style.cssText = value;
+ }
+ });
+ }
+
+ if (Env.ie && Env.ie < 9) {
+ /*jshint sub:true */
+ /*eslint dot-notation: 0*/
+ cssFix['float'] = 'styleFloat';
+
+ appendHooks(cssHooks, 'set', {
+ opacity: function(elm, value) {
+ var style = elm.style;
+
+ if (value === null || value === '') {
+ style.removeAttribute('filter');
+ } else {
+ style.zoom = 1;
+ style.filter = 'alpha(opacity=' + (value * 100) + ')';
+ }
+ }
+ });
+ }
+
+ DomQuery.attrHooks = attrHooks;
+ DomQuery.cssHooks = cssHooks;
+
+ return DomQuery;
+});
+
+// Included from: js/tinymce/classes/html/Styles.js
+
+/**
+ * Styles.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 parse CSS styles it also compresses styles to reduce the output size.
+ *
+ * @example
+ * var Styles = new tinymce.html.Styles({
+ * url_converter: function(url) {
+ * return url;
+ * }
+ * });
+ *
+ * styles = Styles.parse('border: 1px solid red');
+ * styles.color = 'red';
+ *
+ * console.log(new tinymce.html.StyleSerializer().serialize(styles));
+ *
+ * @class tinymce.html.Styles
+ * @version 3.4
+ */
+define("tinymce/html/Styles", [], function() {
+ return function(settings, schema) {
+ /*jshint maxlen:255 */
+ /*eslint max-len:0 */
+ var rgbRegExp = /rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,
+ urlOrStrRegExp = /(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,
+ styleRegExp = /\s*([^:]+):\s*([^;]+);?/g,
+ trimRightRegExp = /\s+$/,
+ i, encodingLookup = {}, encodingItems, validStyles, invalidStyles, invisibleChar = '\uFEFF';
+
+ settings = settings || {};
+
+ if (schema) {
+ validStyles = schema.getValidStyles();
+ invalidStyles = schema.getInvalidStyles();
+ }
+
+ encodingItems = ('\\" \\\' \\; \\: ; : ' + invisibleChar).split(' ');
+ for (i = 0; i < encodingItems.length; i++) {
+ encodingLookup[encodingItems[i]] = invisibleChar + i;
+ encodingLookup[invisibleChar + i] = encodingItems[i];
+ }
+
+ function toHex(match, r, g, b) {
+ function hex(val) {
+ val = parseInt(val, 10).toString(16);
+
+ return val.length > 1 ? val : '0' + val; // 0 -> 00
+ }
+
+ return '#' + hex(r) + hex(g) + hex(b);
+ }
+
+ return {
+ /**
+ * Parses the specified RGB color value and returns a hex version of that color.
+ *
+ * @method toHex
+ * @param {String} color RGB string value like rgb(1,2,3)
+ * @return {String} Hex version of that RGB value like #FF00FF.
+ */
+ toHex: function(color) {
+ return color.replace(rgbRegExp, toHex);
+ },
+
+ /**
+ * Parses the specified style value into an object collection. This parser will also
+ * merge and remove any redundant items that browsers might have added. It will also convert non hex
+ * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
+ *
+ * @method parse
+ * @param {String} css Style value to parse for example: border:1px solid red;.
+ * @return {Object} Object representation of that style like {border: '1px solid red'}
+ */
+ parse: function(css) {
+ var styles = {}, matches, name, value, isEncoded, urlConverter = settings.url_converter;
+ var urlConverterScope = settings.url_converter_scope || this;
+
+ function compress(prefix, suffix, noJoin) {
+ var top, right, bottom, left;
+
+ top = styles[prefix + '-top' + suffix];
+ if (!top) {
+ return;
+ }
+
+ right = styles[prefix + '-right' + suffix];
+ if (!right) {
+ return;
+ }
+
+ bottom = styles[prefix + '-bottom' + suffix];
+ if (!bottom) {
+ return;
+ }
+
+ left = styles[prefix + '-left' + suffix];
+ if (!left) {
+ return;
+ }
+
+ var box = [top, right, bottom, left];
+ i = box.length - 1;
+ while (i--) {
+ if (box[i] !== box[i + 1]) {
+ break;
+ }
+ }
+
+ if (i > -1 && noJoin) {
+ return;
+ }
+
+ styles[prefix + suffix] = i == -1 ? box[0] : box.join(' ');
+ delete styles[prefix + '-top' + suffix];
+ delete styles[prefix + '-right' + suffix];
+ delete styles[prefix + '-bottom' + suffix];
+ delete styles[prefix + '-left' + suffix];
+ }
+
+ /**
+ * Checks if the specific style can be compressed in other words if all border-width are equal.
+ */
+ function canCompress(key) {
+ var value = styles[key], i;
+
+ if (!value) {
+ return;
+ }
+
+ value = value.split(' ');
+ i = value.length;
+ while (i--) {
+ if (value[i] !== value[0]) {
+ return false;
+ }
+ }
+
+ styles[key] = value[0];
+
+ return true;
+ }
+
+ /**
+ * Compresses multiple styles into one style.
+ */
+ function compress2(target, a, b, c) {
+ if (!canCompress(a)) {
+ return;
+ }
+
+ if (!canCompress(b)) {
+ return;
+ }
+
+ if (!canCompress(c)) {
+ return;
+ }
+
+ // Compress
+ styles[target] = styles[a] + ' ' + styles[b] + ' ' + styles[c];
+ delete styles[a];
+ delete styles[b];
+ delete styles[c];
+ }
+
+ // Encodes the specified string by replacing all \" \' ; : with _
+ function encode(str) {
+ isEncoded = true;
+
+ return encodingLookup[str];
+ }
+
+ // Decodes the specified string by replacing all _ with it's original value \" \' etc
+ // It will also decode the \" \' if keep_slashes is set to fale or omitted
+ function decode(str, keep_slashes) {
+ if (isEncoded) {
+ str = str.replace(/\uFEFF[0-9]/g, function(str) {
+ return encodingLookup[str];
+ });
+ }
+
+ if (!keep_slashes) {
+ str = str.replace(/\\([\'\";:])/g, "$1");
+ }
+
+ return str;
+ }
+
+ function decodeSingleHexSequence(escSeq) {
+ return String.fromCharCode(parseInt(escSeq.slice(1), 16));
+ }
+
+ function decodeHexSequences(value) {
+ return value.replace(/\\[0-9a-f]+/gi, decodeSingleHexSequence);
+ }
+
+ function processUrl(match, url, url2, url3, str, str2) {
+ str = str || str2;
+
+ if (str) {
+ str = decode(str);
+
+ // Force strings into single quote format
+ return "'" + str.replace(/\'/g, "\\'") + "'";
+ }
+
+ url = decode(url || url2 || url3);
+
+ if (!settings.allow_script_urls) {
+ var scriptUrl = url.replace(/[\s\r\n]+/g, '');
+
+ if (/(java|vb)script:/i.test(scriptUrl)) {
+ return "";
+ }
+
+ if (!settings.allow_svg_data_urls && /^data:image\/svg/i.test(scriptUrl)) {
+ return "";
+ }
+ }
+
+ // Convert the URL to relative/absolute depending on config
+ if (urlConverter) {
+ url = urlConverter.call(urlConverterScope, url, 'style');
+ }
+
+ // Output new URL format
+ return "url('" + url.replace(/\'/g, "\\'") + "')";
+ }
+
+ if (css) {
+ css = css.replace(/[\u0000-\u001F]/g, '');
+
+ // Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
+ css = css.replace(/\\[\"\';:\uFEFF]/g, encode).replace(/\"[^\"]+\"|\'[^\']+\'/g, function(str) {
+ return str.replace(/[;:]/g, encode);
+ });
+
+ // Parse styles
+ while ((matches = styleRegExp.exec(css))) {
+ styleRegExp.lastIndex = matches.index + matches[0].length;
+ name = matches[1].replace(trimRightRegExp, '').toLowerCase();
+ value = matches[2].replace(trimRightRegExp, '');
+
+ if (name && value) {
+ // Decode escaped sequences like \65 -> e
+ name = decodeHexSequences(name);
+ value = decodeHexSequences(value);
+
+ // Skip properties with double quotes and sequences like \" \' in their names
+ // See 'mXSS Attacks: Attacking well-secured Web-Applications by using innerHTML Mutations'
+ // https://cure53.de/fp170.pdf
+ if (name.indexOf(invisibleChar) !== -1 || name.indexOf('"') !== -1) {
+ continue;
+ }
+
+ // Don't allow behavior name or expression/comments within the values
+ if (!settings.allow_script_urls && (name == "behavior" || /expression\s*\(|\/\*|\*\//.test(value))) {
+ continue;
+ }
+
+ // Opera will produce 700 instead of bold in their style values
+ if (name === 'font-weight' && value === '700') {
+ value = 'bold';
+ } else if (name === 'color' || name === 'background-color') { // Lowercase colors like RED
+ value = value.toLowerCase();
+ }
+
+ // Convert RGB colors to HEX
+ value = value.replace(rgbRegExp, toHex);
+
+ // Convert URLs and force them into url('value') format
+ value = value.replace(urlOrStrRegExp, processUrl);
+ styles[name] = isEncoded ? decode(value, true) : value;
+ }
+ }
+ // Compress the styles to reduce it's size for example IE will expand styles
+ compress("border", "", true);
+ compress("border", "-width");
+ compress("border", "-color");
+ compress("border", "-style");
+ compress("padding", "");
+ compress("margin", "");
+ compress2('border', 'border-width', 'border-style', 'border-color');
+
+ // Remove pointless border, IE produces these
+ if (styles.border === 'medium none') {
+ delete styles.border;
+ }
+
+ // IE 11 will produce a border-image: none when getting the style attribute from
+ // So let us assume it shouldn't be there
+ if (styles['border-image'] === 'none') {
+ delete styles['border-image'];
+ }
+ }
+
+ return styles;
+ },
+
+ /**
+ * Serializes the specified style object into a string.
+ *
+ * @method serialize
+ * @param {Object} styles Object to serialize as string for example: {border: '1px solid red'}
+ * @param {String} elementName Optional element name, if specified only the styles that matches the schema will be serialized.
+ * @return {String} String representation of the style object for example: border: 1px solid red.
+ */
+ serialize: function(styles, elementName) {
+ var css = '', name, value;
+
+ function serializeStyles(name) {
+ var styleList, i, l, value;
+
+ styleList = validStyles[name];
+ if (styleList) {
+ for (i = 0, l = styleList.length; i < l; i++) {
+ name = styleList[i];
+ value = styles[name];
+
+ if (value) {
+ css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
+ }
+ }
+ }
+ }
+
+ function isValid(name, elementName) {
+ var styleMap;
+
+ styleMap = invalidStyles['*'];
+ if (styleMap && styleMap[name]) {
+ return false;
+ }
+
+ styleMap = invalidStyles[elementName];
+ if (styleMap && styleMap[name]) {
+ return false;
+ }
+
+ return true;
+ }
+
+ // Serialize styles according to schema
+ if (elementName && validStyles) {
+ // Serialize global styles and element specific styles
+ serializeStyles('*');
+ serializeStyles(elementName);
+ } else {
+ // Output the styles in the order they are inside the object
+ for (name in styles) {
+ value = styles[name];
+
+ if (value && (!invalidStyles || isValid(name, elementName))) {
+ css += (css.length > 0 ? ' ' : '') + name + ': ' + value + ';';
+ }
+ }
+ }
+
+ return css;
+ }
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/dom/TreeWalker.js
+
+/**
+ * TreeWalker.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
+ */
+
+/**
+ * TreeWalker class enables you to walk the DOM in a linear manner.
+ *
+ * @class tinymce.dom.TreeWalker
+ * @example
+ * var walker = new tinymce.dom.TreeWalker(startNode);
+ *
+ * do {
+ * console.log(walker.current());
+ * } while (walker.next());
+ */
+define("tinymce/dom/TreeWalker", [], function() {
+ /**
+ * Constructs a new TreeWalker instance.
+ *
+ * @constructor
+ * @method TreeWalker
+ * @param {Node} startNode Node to start walking from.
+ * @param {node} rootNode Optional root node to never walk out of.
+ */
+ return function(startNode, rootNode) {
+ var node = startNode;
+
+ function findSibling(node, startName, siblingName, shallow) {
+ var sibling, parent;
+
+ if (node) {
+ // Walk into nodes if it has a start
+ if (!shallow && node[startName]) {
+ return node[startName];
+ }
+
+ // Return the sibling if it has one
+ if (node != rootNode) {
+ sibling = node[siblingName];
+ if (sibling) {
+ return sibling;
+ }
+
+ // Walk up the parents to look for siblings
+ for (parent = node.parentNode; parent && parent != rootNode; parent = parent.parentNode) {
+ sibling = parent[siblingName];
+ if (sibling) {
+ return sibling;
+ }
+ }
+ }
+ }
+ }
+
+ function findPreviousNode(node, startName, siblingName, shallow) {
+ var sibling, parent, child;
+
+ if (node) {
+ sibling = node[siblingName];
+ if (rootNode && sibling === rootNode) {
+ return;
+ }
+
+ if (sibling) {
+ if (!shallow) {
+ // Walk up the parents to look for siblings
+ for (child = sibling[startName]; child; child = child[startName]) {
+ if (!child[startName]) {
+ return child;
+ }
+ }
+ }
+
+ return sibling;
+ }
+
+ parent = node.parentNode;
+ if (parent && parent !== rootNode) {
+ return parent;
+ }
+ }
+ }
+
+ /**
+ * Returns the current node.
+ *
+ * @method current
+ * @return {Node} Current node where the walker is.
+ */
+ this.current = function() {
+ return node;
+ };
+
+ /**
+ * Walks to the next node in tree.
+ *
+ * @method next
+ * @return {Node} Current node where the walker is after moving to the next node.
+ */
+ this.next = function(shallow) {
+ node = findSibling(node, 'firstChild', 'nextSibling', shallow);
+ return node;
+ };
+
+ /**
+ * Walks to the previous node in tree.
+ *
+ * @method prev
+ * @return {Node} Current node where the walker is after moving to the previous node.
+ */
+ this.prev = function(shallow) {
+ node = findSibling(node, 'lastChild', 'previousSibling', shallow);
+ return node;
+ };
+
+ this.prev2 = function(shallow) {
+ node = findPreviousNode(node, 'lastChild', 'previousSibling', shallow);
+ return node;
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/dom/Range.js
+
+/**
+ * Range.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
+ */
+
+/**
+ * Old IE Range.
+ *
+ * @private
+ * @class tinymce.dom.Range
+ */
+define("tinymce/dom/Range", [
+ "tinymce/util/Tools"
+], function(Tools) {
+ // Range constructor
+ function Range(dom) {
+ var self = this,
+ doc = dom.doc,
+ EXTRACT = 0,
+ CLONE = 1,
+ DELETE = 2,
+ TRUE = true,
+ FALSE = false,
+ START_OFFSET = 'startOffset',
+ START_CONTAINER = 'startContainer',
+ END_CONTAINER = 'endContainer',
+ END_OFFSET = 'endOffset',
+ extend = Tools.extend,
+ nodeIndex = dom.nodeIndex;
+
+ function createDocumentFragment() {
+ return doc.createDocumentFragment();
+ }
+
+ function setStart(n, o) {
+ _setEndPoint(TRUE, n, o);
+ }
+
+ function setEnd(n, o) {
+ _setEndPoint(FALSE, n, o);
+ }
+
+ function setStartBefore(n) {
+ setStart(n.parentNode, nodeIndex(n));
+ }
+
+ function setStartAfter(n) {
+ setStart(n.parentNode, nodeIndex(n) + 1);
+ }
+
+ function setEndBefore(n) {
+ setEnd(n.parentNode, nodeIndex(n));
+ }
+
+ function setEndAfter(n) {
+ setEnd(n.parentNode, nodeIndex(n) + 1);
+ }
+
+ function collapse(ts) {
+ if (ts) {
+ self[END_CONTAINER] = self[START_CONTAINER];
+ self[END_OFFSET] = self[START_OFFSET];
+ } else {
+ self[START_CONTAINER] = self[END_CONTAINER];
+ self[START_OFFSET] = self[END_OFFSET];
+ }
+
+ self.collapsed = TRUE;
+ }
+
+ function selectNode(n) {
+ setStartBefore(n);
+ setEndAfter(n);
+ }
+
+ function selectNodeContents(n) {
+ setStart(n, 0);
+ setEnd(n, n.nodeType === 1 ? n.childNodes.length : n.nodeValue.length);
+ }
+
+ function compareBoundaryPoints(h, r) {
+ var sc = self[START_CONTAINER], so = self[START_OFFSET], ec = self[END_CONTAINER], eo = self[END_OFFSET],
+ rsc = r.startContainer, rso = r.startOffset, rec = r.endContainer, reo = r.endOffset;
+
+ // Check START_TO_START
+ if (h === 0) {
+ return _compareBoundaryPoints(sc, so, rsc, rso);
+ }
+
+ // Check START_TO_END
+ if (h === 1) {
+ return _compareBoundaryPoints(ec, eo, rsc, rso);
+ }
+
+ // Check END_TO_END
+ if (h === 2) {
+ return _compareBoundaryPoints(ec, eo, rec, reo);
+ }
+
+ // Check END_TO_START
+ if (h === 3) {
+ return _compareBoundaryPoints(sc, so, rec, reo);
+ }
+ }
+
+ function deleteContents() {
+ _traverse(DELETE);
+ }
+
+ function extractContents() {
+ return _traverse(EXTRACT);
+ }
+
+ function cloneContents() {
+ return _traverse(CLONE);
+ }
+
+ function insertNode(n) {
+ var startContainer = this[START_CONTAINER],
+ startOffset = this[START_OFFSET], nn, o;
+
+ // Node is TEXT_NODE or CDATA
+ if ((startContainer.nodeType === 3 || startContainer.nodeType === 4) && startContainer.nodeValue) {
+ if (!startOffset) {
+ // At the start of text
+ startContainer.parentNode.insertBefore(n, startContainer);
+ } else if (startOffset >= startContainer.nodeValue.length) {
+ // At the end of text
+ dom.insertAfter(n, startContainer);
+ } else {
+ // Middle, need to split
+ nn = startContainer.splitText(startOffset);
+ startContainer.parentNode.insertBefore(n, nn);
+ }
+ } else {
+ // Insert element node
+ if (startContainer.childNodes.length > 0) {
+ o = startContainer.childNodes[startOffset];
+ }
+
+ if (o) {
+ startContainer.insertBefore(n, o);
+ } else {
+ if (startContainer.nodeType == 3) {
+ dom.insertAfter(n, startContainer);
+ } else {
+ startContainer.appendChild(n);
+ }
+ }
+ }
+ }
+
+ function surroundContents(n) {
+ var f = self.extractContents();
+
+ self.insertNode(n);
+ n.appendChild(f);
+ self.selectNode(n);
+ }
+
+ function cloneRange() {
+ return extend(new Range(dom), {
+ startContainer: self[START_CONTAINER],
+ startOffset: self[START_OFFSET],
+ endContainer: self[END_CONTAINER],
+ endOffset: self[END_OFFSET],
+ collapsed: self.collapsed,
+ commonAncestorContainer: self.commonAncestorContainer
+ });
+ }
+
+ // Private methods
+
+ function _getSelectedNode(container, offset) {
+ var child;
+
+ // TEXT_NODE
+ if (container.nodeType == 3) {
+ return container;
+ }
+
+ if (offset < 0) {
+ return container;
+ }
+
+ child = container.firstChild;
+ while (child && offset > 0) {
+ --offset;
+ child = child.nextSibling;
+ }
+
+ if (child) {
+ return child;
+ }
+
+ return container;
+ }
+
+ function _isCollapsed() {
+ return (self[START_CONTAINER] == self[END_CONTAINER] && self[START_OFFSET] == self[END_OFFSET]);
+ }
+
+ function _compareBoundaryPoints(containerA, offsetA, containerB, offsetB) {
+ var c, offsetC, n, cmnRoot, childA, childB;
+
+ // In the first case the boundary-points have the same container. A is before B
+ // if its offset is less than the offset of B, A is equal to B if its offset is
+ // equal to the offset of B, and A is after B if its offset is greater than the
+ // offset of B.
+ if (containerA == containerB) {
+ if (offsetA == offsetB) {
+ return 0; // equal
+ }
+
+ if (offsetA < offsetB) {
+ return -1; // before
+ }
+
+ return 1; // after
+ }
+
+ // In the second case a child node C of the container of A is an ancestor
+ // container of B. In this case, A is before B if the offset of A is less than or
+ // equal to the index of the child node C and A is after B otherwise.
+ c = containerB;
+ while (c && c.parentNode != containerA) {
+ c = c.parentNode;
+ }
+
+ if (c) {
+ offsetC = 0;
+ n = containerA.firstChild;
+
+ while (n != c && offsetC < offsetA) {
+ offsetC++;
+ n = n.nextSibling;
+ }
+
+ if (offsetA <= offsetC) {
+ return -1; // before
+ }
+
+ return 1; // after
+ }
+
+ // In the third case a child node C of the container of B is an ancestor container
+ // of A. In this case, A is before B if the index of the child node C is less than
+ // the offset of B and A is after B otherwise.
+ c = containerA;
+ while (c && c.parentNode != containerB) {
+ c = c.parentNode;
+ }
+
+ if (c) {
+ offsetC = 0;
+ n = containerB.firstChild;
+
+ while (n != c && offsetC < offsetB) {
+ offsetC++;
+ n = n.nextSibling;
+ }
+
+ if (offsetC < offsetB) {
+ return -1; // before
+ }
+
+ return 1; // after
+ }
+
+ // In the fourth case, none of three other cases hold: the containers of A and B
+ // are siblings or descendants of sibling nodes. In this case, A is before B if
+ // the container of A is before the container of B in a pre-order traversal of the
+ // Ranges' context tree and A is after B otherwise.
+ cmnRoot = dom.findCommonAncestor(containerA, containerB);
+ childA = containerA;
+
+ while (childA && childA.parentNode != cmnRoot) {
+ childA = childA.parentNode;
+ }
+
+ if (!childA) {
+ childA = cmnRoot;
+ }
+
+ childB = containerB;
+ while (childB && childB.parentNode != cmnRoot) {
+ childB = childB.parentNode;
+ }
+
+ if (!childB) {
+ childB = cmnRoot;
+ }
+
+ if (childA == childB) {
+ return 0; // equal
+ }
+
+ n = cmnRoot.firstChild;
+ while (n) {
+ if (n == childA) {
+ return -1; // before
+ }
+
+ if (n == childB) {
+ return 1; // after
+ }
+
+ n = n.nextSibling;
+ }
+ }
+
+ function _setEndPoint(st, n, o) {
+ var ec, sc;
+
+ if (st) {
+ self[START_CONTAINER] = n;
+ self[START_OFFSET] = o;
+ } else {
+ self[END_CONTAINER] = n;
+ self[END_OFFSET] = o;
+ }
+
+ // If one boundary-point of a Range is set to have a root container
+ // other than the current one for the Range, the Range is collapsed to
+ // the new position. This enforces the restriction that both boundary-
+ // points of a Range must have the same root container.
+ ec = self[END_CONTAINER];
+ while (ec.parentNode) {
+ ec = ec.parentNode;
+ }
+
+ sc = self[START_CONTAINER];
+ while (sc.parentNode) {
+ sc = sc.parentNode;
+ }
+
+ if (sc == ec) {
+ // The start position of a Range is guaranteed to never be after the
+ // end position. To enforce this restriction, if the start is set to
+ // be at a position after the end, the Range is collapsed to that
+ // position.
+ if (_compareBoundaryPoints(self[START_CONTAINER], self[START_OFFSET], self[END_CONTAINER], self[END_OFFSET]) > 0) {
+ self.collapse(st);
+ }
+ } else {
+ self.collapse(st);
+ }
+
+ self.collapsed = _isCollapsed();
+ self.commonAncestorContainer = dom.findCommonAncestor(self[START_CONTAINER], self[END_CONTAINER]);
+ }
+
+ function _traverse(how) {
+ var c, endContainerDepth = 0, startContainerDepth = 0, p, depthDiff, startNode, endNode, sp, ep;
+
+ if (self[START_CONTAINER] == self[END_CONTAINER]) {
+ return _traverseSameContainer(how);
+ }
+
+ for (c = self[END_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
+ if (p == self[START_CONTAINER]) {
+ return _traverseCommonStartContainer(c, how);
+ }
+
+ ++endContainerDepth;
+ }
+
+ for (c = self[START_CONTAINER], p = c.parentNode; p; c = p, p = p.parentNode) {
+ if (p == self[END_CONTAINER]) {
+ return _traverseCommonEndContainer(c, how);
+ }
+
+ ++startContainerDepth;
+ }
+
+ depthDiff = startContainerDepth - endContainerDepth;
+
+ startNode = self[START_CONTAINER];
+ while (depthDiff > 0) {
+ startNode = startNode.parentNode;
+ depthDiff--;
+ }
+
+ endNode = self[END_CONTAINER];
+ while (depthDiff < 0) {
+ endNode = endNode.parentNode;
+ depthDiff++;
+ }
+
+ // ascend the ancestor hierarchy until we have a common parent.
+ for (sp = startNode.parentNode, ep = endNode.parentNode; sp != ep; sp = sp.parentNode, ep = ep.parentNode) {
+ startNode = sp;
+ endNode = ep;
+ }
+
+ return _traverseCommonAncestors(startNode, endNode, how);
+ }
+
+ function _traverseSameContainer(how) {
+ var frag, s, sub, n, cnt, sibling, xferNode, start, len;
+
+ if (how != DELETE) {
+ frag = createDocumentFragment();
+ }
+
+ // If selection is empty, just return the fragment
+ if (self[START_OFFSET] == self[END_OFFSET]) {
+ return frag;
+ }
+
+ // Text node needs special case handling
+ if (self[START_CONTAINER].nodeType == 3) { // TEXT_NODE
+ // get the substring
+ s = self[START_CONTAINER].nodeValue;
+ sub = s.substring(self[START_OFFSET], self[END_OFFSET]);
+
+ // set the original text node to its new value
+ if (how != CLONE) {
+ n = self[START_CONTAINER];
+ start = self[START_OFFSET];
+ len = self[END_OFFSET] - self[START_OFFSET];
+
+ if (start === 0 && len >= n.nodeValue.length - 1) {
+ n.parentNode.removeChild(n);
+ } else {
+ n.deleteData(start, len);
+ }
+
+ // Nothing is partially selected, so collapse to start point
+ self.collapse(TRUE);
+ }
+
+ if (how == DELETE) {
+ return;
+ }
+
+ if (sub.length > 0) {
+ frag.appendChild(doc.createTextNode(sub));
+ }
+
+ return frag;
+ }
+
+ // Copy nodes between the start/end offsets.
+ n = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]);
+ cnt = self[END_OFFSET] - self[START_OFFSET];
+
+ while (n && cnt > 0) {
+ sibling = n.nextSibling;
+ xferNode = _traverseFullySelected(n, how);
+
+ if (frag) {
+ frag.appendChild(xferNode);
+ }
+
+ --cnt;
+ n = sibling;
+ }
+
+ // Nothing is partially selected, so collapse to start point
+ if (how != CLONE) {
+ self.collapse(TRUE);
+ }
+
+ return frag;
+ }
+
+ function _traverseCommonStartContainer(endAncestor, how) {
+ var frag, n, endIdx, cnt, sibling, xferNode;
+
+ if (how != DELETE) {
+ frag = createDocumentFragment();
+ }
+
+ n = _traverseRightBoundary(endAncestor, how);
+
+ if (frag) {
+ frag.appendChild(n);
+ }
+
+ endIdx = nodeIndex(endAncestor);
+ cnt = endIdx - self[START_OFFSET];
+
+ if (cnt <= 0) {
+ // Collapse to just before the endAncestor, which
+ // is partially selected.
+ if (how != CLONE) {
+ self.setEndBefore(endAncestor);
+ self.collapse(FALSE);
+ }
+
+ return frag;
+ }
+
+ n = endAncestor.previousSibling;
+ while (cnt > 0) {
+ sibling = n.previousSibling;
+ xferNode = _traverseFullySelected(n, how);
+
+ if (frag) {
+ frag.insertBefore(xferNode, frag.firstChild);
+ }
+
+ --cnt;
+ n = sibling;
+ }
+
+ // Collapse to just before the endAncestor, which
+ // is partially selected.
+ if (how != CLONE) {
+ self.setEndBefore(endAncestor);
+ self.collapse(FALSE);
+ }
+
+ return frag;
+ }
+
+ function _traverseCommonEndContainer(startAncestor, how) {
+ var frag, startIdx, n, cnt, sibling, xferNode;
+
+ if (how != DELETE) {
+ frag = createDocumentFragment();
+ }
+
+ n = _traverseLeftBoundary(startAncestor, how);
+ if (frag) {
+ frag.appendChild(n);
+ }
+
+ startIdx = nodeIndex(startAncestor);
+ ++startIdx; // Because we already traversed it
+
+ cnt = self[END_OFFSET] - startIdx;
+ n = startAncestor.nextSibling;
+ while (n && cnt > 0) {
+ sibling = n.nextSibling;
+ xferNode = _traverseFullySelected(n, how);
+
+ if (frag) {
+ frag.appendChild(xferNode);
+ }
+
+ --cnt;
+ n = sibling;
+ }
+
+ if (how != CLONE) {
+ self.setStartAfter(startAncestor);
+ self.collapse(TRUE);
+ }
+
+ return frag;
+ }
+
+ function _traverseCommonAncestors(startAncestor, endAncestor, how) {
+ var n, frag, startOffset, endOffset, cnt, sibling, nextSibling;
+
+ if (how != DELETE) {
+ frag = createDocumentFragment();
+ }
+
+ n = _traverseLeftBoundary(startAncestor, how);
+ if (frag) {
+ frag.appendChild(n);
+ }
+
+ startOffset = nodeIndex(startAncestor);
+ endOffset = nodeIndex(endAncestor);
+ ++startOffset;
+
+ cnt = endOffset - startOffset;
+ sibling = startAncestor.nextSibling;
+
+ while (cnt > 0) {
+ nextSibling = sibling.nextSibling;
+ n = _traverseFullySelected(sibling, how);
+
+ if (frag) {
+ frag.appendChild(n);
+ }
+
+ sibling = nextSibling;
+ --cnt;
+ }
+
+ n = _traverseRightBoundary(endAncestor, how);
+
+ if (frag) {
+ frag.appendChild(n);
+ }
+
+ if (how != CLONE) {
+ self.setStartAfter(startAncestor);
+ self.collapse(TRUE);
+ }
+
+ return frag;
+ }
+
+ function _traverseRightBoundary(root, how) {
+ var next = _getSelectedNode(self[END_CONTAINER], self[END_OFFSET] - 1), parent, clonedParent;
+ var prevSibling, clonedChild, clonedGrandParent, isFullySelected = next != self[END_CONTAINER];
+
+ if (next == root) {
+ return _traverseNode(next, isFullySelected, FALSE, how);
+ }
+
+ parent = next.parentNode;
+ clonedParent = _traverseNode(parent, FALSE, FALSE, how);
+
+ while (parent) {
+ while (next) {
+ prevSibling = next.previousSibling;
+ clonedChild = _traverseNode(next, isFullySelected, FALSE, how);
+
+ if (how != DELETE) {
+ clonedParent.insertBefore(clonedChild, clonedParent.firstChild);
+ }
+
+ isFullySelected = TRUE;
+ next = prevSibling;
+ }
+
+ if (parent == root) {
+ return clonedParent;
+ }
+
+ next = parent.previousSibling;
+ parent = parent.parentNode;
+
+ clonedGrandParent = _traverseNode(parent, FALSE, FALSE, how);
+
+ if (how != DELETE) {
+ clonedGrandParent.appendChild(clonedParent);
+ }
+
+ clonedParent = clonedGrandParent;
+ }
+ }
+
+ function _traverseLeftBoundary(root, how) {
+ var next = _getSelectedNode(self[START_CONTAINER], self[START_OFFSET]), isFullySelected = next != self[START_CONTAINER];
+ var parent, clonedParent, nextSibling, clonedChild, clonedGrandParent;
+
+ if (next == root) {
+ return _traverseNode(next, isFullySelected, TRUE, how);
+ }
+
+ parent = next.parentNode;
+ clonedParent = _traverseNode(parent, FALSE, TRUE, how);
+
+ while (parent) {
+ while (next) {
+ nextSibling = next.nextSibling;
+ clonedChild = _traverseNode(next, isFullySelected, TRUE, how);
+
+ if (how != DELETE) {
+ clonedParent.appendChild(clonedChild);
+ }
+
+ isFullySelected = TRUE;
+ next = nextSibling;
+ }
+
+ if (parent == root) {
+ return clonedParent;
+ }
+
+ next = parent.nextSibling;
+ parent = parent.parentNode;
+
+ clonedGrandParent = _traverseNode(parent, FALSE, TRUE, how);
+
+ if (how != DELETE) {
+ clonedGrandParent.appendChild(clonedParent);
+ }
+
+ clonedParent = clonedGrandParent;
+ }
+ }
+
+ function _traverseNode(n, isFullySelected, isLeft, how) {
+ var txtValue, newNodeValue, oldNodeValue, offset, newNode;
+
+ if (isFullySelected) {
+ return _traverseFullySelected(n, how);
+ }
+
+ // TEXT_NODE
+ if (n.nodeType == 3) {
+ txtValue = n.nodeValue;
+
+ if (isLeft) {
+ offset = self[START_OFFSET];
+ newNodeValue = txtValue.substring(offset);
+ oldNodeValue = txtValue.substring(0, offset);
+ } else {
+ offset = self[END_OFFSET];
+ newNodeValue = txtValue.substring(0, offset);
+ oldNodeValue = txtValue.substring(offset);
+ }
+
+ if (how != CLONE) {
+ n.nodeValue = oldNodeValue;
+ }
+
+ if (how == DELETE) {
+ return;
+ }
+
+ newNode = dom.clone(n, FALSE);
+ newNode.nodeValue = newNodeValue;
+
+ return newNode;
+ }
+
+ if (how == DELETE) {
+ return;
+ }
+
+ return dom.clone(n, FALSE);
+ }
+
+ function _traverseFullySelected(n, how) {
+ if (how != DELETE) {
+ return how == CLONE ? dom.clone(n, TRUE) : n;
+ }
+
+ n.parentNode.removeChild(n);
+ }
+
+ function toStringIE() {
+ return dom.create('body', null, cloneContents()).outerText;
+ }
+
+ extend(self, {
+ // Initial states
+ startContainer: doc,
+ startOffset: 0,
+ endContainer: doc,
+ endOffset: 0,
+ collapsed: TRUE,
+ commonAncestorContainer: doc,
+
+ // Range constants
+ START_TO_START: 0,
+ START_TO_END: 1,
+ END_TO_END: 2,
+ END_TO_START: 3,
+
+ // Public methods
+ setStart: setStart,
+ setEnd: setEnd,
+ setStartBefore: setStartBefore,
+ setStartAfter: setStartAfter,
+ setEndBefore: setEndBefore,
+ setEndAfter: setEndAfter,
+ collapse: collapse,
+ selectNode: selectNode,
+ selectNodeContents: selectNodeContents,
+ compareBoundaryPoints: compareBoundaryPoints,
+ deleteContents: deleteContents,
+ extractContents: extractContents,
+ cloneContents: cloneContents,
+ insertNode: insertNode,
+ surroundContents: surroundContents,
+ cloneRange: cloneRange,
+ toStringIE: toStringIE
+ });
+
+ return self;
+ }
+
+ // Older IE versions doesn't let you override toString by it's constructor so we have to stick it in the prototype
+ Range.prototype.toString = function() {
+ return this.toStringIE();
+ };
+
+ return Range;
+});
+
+// Included from: js/tinymce/classes/html/Entities.js
+
+/**
+ * Entities.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 bitwise:false */
+/*eslint no-bitwise:0 */
+
+/**
+ * Entity encoder class.
+ *
+ * @class tinymce.html.Entities
+ * @static
+ * @version 3.4
+ */
+define("tinymce/html/Entities", [
+ "tinymce/util/Tools"
+], function(Tools) {
+ var makeMap = Tools.makeMap;
+
+ var namedEntities, baseEntities, reverseEntities,
+ attrsCharsRegExp = /[&<>\"\u0060\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
+ textCharsRegExp = /[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
+ rawCharsRegExp = /[<>&\"\']/g,
+ entityRegExp = /([a-z0-9]+);?|&([a-z0-9]+);/gi,
+ asciiMap = {
+ 128: "\u20AC", 130: "\u201A", 131: "\u0192", 132: "\u201E", 133: "\u2026", 134: "\u2020",
+ 135: "\u2021", 136: "\u02C6", 137: "\u2030", 138: "\u0160", 139: "\u2039", 140: "\u0152",
+ 142: "\u017D", 145: "\u2018", 146: "\u2019", 147: "\u201C", 148: "\u201D", 149: "\u2022",
+ 150: "\u2013", 151: "\u2014", 152: "\u02DC", 153: "\u2122", 154: "\u0161", 155: "\u203A",
+ 156: "\u0153", 158: "\u017E", 159: "\u0178"
+ };
+
+ // Raw entities
+ baseEntities = {
+ '\"': '"', // Needs to be escaped since the YUI compressor would otherwise break the code
+ "'": ''',
+ '<': '<',
+ '>': '>',
+ '&': '&',
+ '\u0060': '`'
+ };
+
+ // Reverse lookup table for raw entities
+ reverseEntities = {
+ '<': '<',
+ '>': '>',
+ '&': '&',
+ '"': '"',
+ ''': "'"
+ };
+
+ // Decodes text by using the browser
+ function nativeDecode(text) {
+ var elm;
+
+ elm = document.createElement("div");
+ elm.innerHTML = text;
+
+ return elm.textContent || elm.innerText || text;
+ }
+
+ // Build a two way lookup table for the entities
+ function buildEntitiesLookup(items, radix) {
+ var i, chr, entity, lookup = {};
+
+ if (items) {
+ items = items.split(',');
+ radix = radix || 10;
+
+ // Build entities lookup table
+ for (i = 0; i < items.length; i += 2) {
+ chr = String.fromCharCode(parseInt(items[i], radix));
+
+ // Only add non base entities
+ if (!baseEntities[chr]) {
+ entity = '&' + items[i + 1] + ';';
+ lookup[chr] = entity;
+ lookup[entity] = chr;
+ }
+ }
+
+ return lookup;
+ }
+ }
+
+ // Unpack entities lookup where the numbers are in radix 32 to reduce the size
+ namedEntities = buildEntitiesLookup(
+ '50,nbsp,51,iexcl,52,cent,53,pound,54,curren,55,yen,56,brvbar,57,sect,58,uml,59,copy,' +
+ '5a,ordf,5b,laquo,5c,not,5d,shy,5e,reg,5f,macr,5g,deg,5h,plusmn,5i,sup2,5j,sup3,5k,acute,' +
+ '5l,micro,5m,para,5n,middot,5o,cedil,5p,sup1,5q,ordm,5r,raquo,5s,frac14,5t,frac12,5u,frac34,' +
+ '5v,iquest,60,Agrave,61,Aacute,62,Acirc,63,Atilde,64,Auml,65,Aring,66,AElig,67,Ccedil,' +
+ '68,Egrave,69,Eacute,6a,Ecirc,6b,Euml,6c,Igrave,6d,Iacute,6e,Icirc,6f,Iuml,6g,ETH,6h,Ntilde,' +
+ '6i,Ograve,6j,Oacute,6k,Ocirc,6l,Otilde,6m,Ouml,6n,times,6o,Oslash,6p,Ugrave,6q,Uacute,' +
+ '6r,Ucirc,6s,Uuml,6t,Yacute,6u,THORN,6v,szlig,70,agrave,71,aacute,72,acirc,73,atilde,74,auml,' +
+ '75,aring,76,aelig,77,ccedil,78,egrave,79,eacute,7a,ecirc,7b,euml,7c,igrave,7d,iacute,7e,icirc,' +
+ '7f,iuml,7g,eth,7h,ntilde,7i,ograve,7j,oacute,7k,ocirc,7l,otilde,7m,ouml,7n,divide,7o,oslash,' +
+ '7p,ugrave,7q,uacute,7r,ucirc,7s,uuml,7t,yacute,7u,thorn,7v,yuml,ci,fnof,sh,Alpha,si,Beta,' +
+ 'sj,Gamma,sk,Delta,sl,Epsilon,sm,Zeta,sn,Eta,so,Theta,sp,Iota,sq,Kappa,sr,Lambda,ss,Mu,' +
+ 'st,Nu,su,Xi,sv,Omicron,t0,Pi,t1,Rho,t3,Sigma,t4,Tau,t5,Upsilon,t6,Phi,t7,Chi,t8,Psi,' +
+ 't9,Omega,th,alpha,ti,beta,tj,gamma,tk,delta,tl,epsilon,tm,zeta,tn,eta,to,theta,tp,iota,' +
+ 'tq,kappa,tr,lambda,ts,mu,tt,nu,tu,xi,tv,omicron,u0,pi,u1,rho,u2,sigmaf,u3,sigma,u4,tau,' +
+ 'u5,upsilon,u6,phi,u7,chi,u8,psi,u9,omega,uh,thetasym,ui,upsih,um,piv,812,bull,816,hellip,' +
+ '81i,prime,81j,Prime,81u,oline,824,frasl,88o,weierp,88h,image,88s,real,892,trade,89l,alefsym,' +
+ '8cg,larr,8ch,uarr,8ci,rarr,8cj,darr,8ck,harr,8dl,crarr,8eg,lArr,8eh,uArr,8ei,rArr,8ej,dArr,' +
+ '8ek,hArr,8g0,forall,8g2,part,8g3,exist,8g5,empty,8g7,nabla,8g8,isin,8g9,notin,8gb,ni,8gf,prod,' +
+ '8gh,sum,8gi,minus,8gn,lowast,8gq,radic,8gt,prop,8gu,infin,8h0,ang,8h7,and,8h8,or,8h9,cap,8ha,cup,' +
+ '8hb,int,8hk,there4,8hs,sim,8i5,cong,8i8,asymp,8j0,ne,8j1,equiv,8j4,le,8j5,ge,8k2,sub,8k3,sup,8k4,' +
+ 'nsub,8k6,sube,8k7,supe,8kl,oplus,8kn,otimes,8l5,perp,8m5,sdot,8o8,lceil,8o9,rceil,8oa,lfloor,8ob,' +
+ 'rfloor,8p9,lang,8pa,rang,9ea,loz,9j0,spades,9j3,clubs,9j5,hearts,9j6,diams,ai,OElig,aj,oelig,b0,' +
+ 'Scaron,b1,scaron,bo,Yuml,m6,circ,ms,tilde,802,ensp,803,emsp,809,thinsp,80c,zwnj,80d,zwj,80e,lrm,' +
+ '80f,rlm,80j,ndash,80k,mdash,80o,lsquo,80p,rsquo,80q,sbquo,80s,ldquo,80t,rdquo,80u,bdquo,810,dagger,' +
+ '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
+
+ var Entities = {
+ /**
+ * Encodes the specified string using raw entities. This means only the required XML base entities will be encoded.
+ *
+ * @method encodeRaw
+ * @param {String} text Text to encode.
+ * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
+ * @return {String} Entity encoded text.
+ */
+ encodeRaw: function(text, attr) {
+ return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
+ return baseEntities[chr] || chr;
+ });
+ },
+
+ /**
+ * Encoded the specified text with both the attributes and text entities. This function will produce larger text contents
+ * since it doesn't know if the context is within a attribute or text node. This was added for compatibility
+ * and is exposed as the DOMUtils.encode function.
+ *
+ * @method encodeAllRaw
+ * @param {String} text Text to encode.
+ * @return {String} Entity encoded text.
+ */
+ encodeAllRaw: function(text) {
+ return ('' + text).replace(rawCharsRegExp, function(chr) {
+ return baseEntities[chr] || chr;
+ });
+ },
+
+ /**
+ * Encodes the specified string using numeric entities. The core entities will be
+ * encoded as named ones but all non lower ascii characters will be encoded into numeric entities.
+ *
+ * @method encodeNumeric
+ * @param {String} text Text to encode.
+ * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
+ * @return {String} Entity encoded text.
+ */
+ encodeNumeric: function(text, attr) {
+ return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
+ // Multi byte sequence convert it to a single entity
+ if (chr.length > 1) {
+ return '' + (((chr.charCodeAt(0) - 0xD800) * 0x400) + (chr.charCodeAt(1) - 0xDC00) + 0x10000) + ';';
+ }
+
+ return baseEntities[chr] || '' + chr.charCodeAt(0) + ';';
+ });
+ },
+
+ /**
+ * Encodes the specified string using named entities. The core entities will be encoded
+ * as named ones but all non lower ascii characters will be encoded into named entities.
+ *
+ * @method encodeNamed
+ * @param {String} text Text to encode.
+ * @param {Boolean} attr Optional flag to specify if the text is attribute contents.
+ * @param {Object} entities Optional parameter with entities to use.
+ * @return {String} Entity encoded text.
+ */
+ encodeNamed: function(text, attr, entities) {
+ entities = entities || namedEntities;
+
+ return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
+ return baseEntities[chr] || entities[chr] || chr;
+ });
+ },
+
+ /**
+ * Returns an encode function based on the name(s) and it's optional entities.
+ *
+ * @method getEncodeFunc
+ * @param {String} name Comma separated list of encoders for example named,numeric.
+ * @param {String} entities Optional parameter with entities to use instead of the built in set.
+ * @return {function} Encode function to be used.
+ */
+ getEncodeFunc: function(name, entities) {
+ entities = buildEntitiesLookup(entities) || namedEntities;
+
+ function encodeNamedAndNumeric(text, attr) {
+ return text.replace(attr ? attrsCharsRegExp : textCharsRegExp, function(chr) {
+ return baseEntities[chr] || entities[chr] || '' + chr.charCodeAt(0) + ';' || chr;
+ });
+ }
+
+ function encodeCustomNamed(text, attr) {
+ return Entities.encodeNamed(text, attr, entities);
+ }
+
+ // Replace + with , to be compatible with previous TinyMCE versions
+ name = makeMap(name.replace(/\+/g, ','));
+
+ // Named and numeric encoder
+ if (name.named && name.numeric) {
+ return encodeNamedAndNumeric;
+ }
+
+ // Named encoder
+ if (name.named) {
+ // Custom names
+ if (entities) {
+ return encodeCustomNamed;
+ }
+
+ return Entities.encodeNamed;
+ }
+
+ // Numeric
+ if (name.numeric) {
+ return Entities.encodeNumeric;
+ }
+
+ // Raw encoder
+ return Entities.encodeRaw;
+ },
+
+ /**
+ * Decodes the specified string, this will replace entities with raw UTF characters.
+ *
+ * @method decode
+ * @param {String} text Text to entity decode.
+ * @return {String} Entity decoded string.
+ */
+ decode: function(text) {
+ return text.replace(entityRegExp, function(all, numeric) {
+ if (numeric) {
+ if (numeric.charAt(0).toLowerCase() === 'x') {
+ numeric = parseInt(numeric.substr(1), 16);
+ } else {
+ numeric = parseInt(numeric, 10);
+ }
+
+ // Support upper UTF
+ if (numeric > 0xFFFF) {
+ numeric -= 0x10000;
+
+ return String.fromCharCode(0xD800 + (numeric >> 10), 0xDC00 + (numeric & 0x3FF));
+ }
+
+ return asciiMap[numeric] || String.fromCharCode(numeric);
+ }
+
+ return reverseEntities[all] || namedEntities[all] || nativeDecode(all);
+ });
+ }
+ };
+
+ return Entities;
+});
+
+// Included from: js/tinymce/classes/dom/StyleSheetLoader.js
+
+/**
+ * StyleSheetLoader.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 loading of external stylesheets and fires events when these are loaded.
+ *
+ * @class tinymce.dom.StyleSheetLoader
+ * @private
+ */
+define("tinymce/dom/StyleSheetLoader", [
+ "tinymce/util/Tools",
+ "tinymce/util/Delay"
+], function(Tools, Delay) {
+ "use strict";
+
+ return function(document, settings) {
+ var idCount = 0, loadedStates = {}, maxLoadTime;
+
+ settings = settings || {};
+ maxLoadTime = settings.maxLoadTime || 5000;
+
+ function appendToHead(node) {
+ document.getElementsByTagName('head')[0].appendChild(node);
+ }
+
+ /**
+ * Loads the specified css style sheet file and call the loadedCallback once it's finished loading.
+ *
+ * @method load
+ * @param {String} url Url to be loaded.
+ * @param {Function} loadedCallback Callback to be executed when loaded.
+ * @param {Function} errorCallback Callback to be executed when failed loading.
+ */
+ function load(url, loadedCallback, errorCallback) {
+ var link, style, startTime, state;
+
+ function passed() {
+ var callbacks = state.passed, i = callbacks.length;
+
+ while (i--) {
+ callbacks[i]();
+ }
+
+ state.status = 2;
+ state.passed = [];
+ state.failed = [];
+ }
+
+ function failed() {
+ var callbacks = state.failed, i = callbacks.length;
+
+ while (i--) {
+ callbacks[i]();
+ }
+
+ state.status = 3;
+ state.passed = [];
+ state.failed = [];
+ }
+
+ // Sniffs for older WebKit versions that have the link.onload but a broken one
+ function isOldWebKit() {
+ var webKitChunks = navigator.userAgent.match(/WebKit\/(\d*)/);
+ return !!(webKitChunks && webKitChunks[1] < 536);
+ }
+
+ // Calls the waitCallback until the test returns true or the timeout occurs
+ function wait(testCallback, waitCallback) {
+ if (!testCallback()) {
+ // Wait for timeout
+ if ((new Date().getTime()) - startTime < maxLoadTime) {
+ Delay.setTimeout(waitCallback);
+ } else {
+ failed();
+ }
+ }
+ }
+
+ // Workaround for WebKit that doesn't properly support the onload event for link elements
+ // Or WebKit that fires the onload event before the StyleSheet is added to the document
+ function waitForWebKitLinkLoaded() {
+ wait(function() {
+ var styleSheets = document.styleSheets, styleSheet, i = styleSheets.length, owner;
+
+ while (i--) {
+ styleSheet = styleSheets[i];
+ owner = styleSheet.ownerNode ? styleSheet.ownerNode : styleSheet.owningElement;
+ if (owner && owner.id === link.id) {
+ passed();
+ return true;
+ }
+ }
+ }, waitForWebKitLinkLoaded);
+ }
+
+ // Workaround for older Geckos that doesn't have any onload event for StyleSheets
+ function waitForGeckoLinkLoaded() {
+ wait(function() {
+ try {
+ // Accessing the cssRules will throw an exception until the CSS file is loaded
+ var cssRules = style.sheet.cssRules;
+ passed();
+ return !!cssRules;
+ } catch (ex) {
+ // Ignore
+ }
+ }, waitForGeckoLinkLoaded);
+ }
+
+ url = Tools._addCacheSuffix(url);
+
+ if (!loadedStates[url]) {
+ state = {
+ passed: [],
+ failed: []
+ };
+
+ loadedStates[url] = state;
+ } else {
+ state = loadedStates[url];
+ }
+
+ if (loadedCallback) {
+ state.passed.push(loadedCallback);
+ }
+
+ if (errorCallback) {
+ state.failed.push(errorCallback);
+ }
+
+ // Is loading wait for it to pass
+ if (state.status == 1) {
+ return;
+ }
+
+ // Has finished loading and was success
+ if (state.status == 2) {
+ passed();
+ return;
+ }
+
+ // Has finished loading and was a failure
+ if (state.status == 3) {
+ failed();
+ return;
+ }
+
+ // Start loading
+ state.status = 1;
+ link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.type = 'text/css';
+ link.id = 'u' + (idCount++);
+ link.async = false;
+ link.defer = false;
+ startTime = new Date().getTime();
+
+ // Feature detect onload on link element and sniff older webkits since it has an broken onload event
+ if ("onload" in link && !isOldWebKit()) {
+ link.onload = waitForWebKitLinkLoaded;
+ link.onerror = failed;
+ } else {
+ // Sniff for old Firefox that doesn't support the onload event on link elements
+ // TODO: Remove this in the future when everyone uses modern browsers
+ if (navigator.userAgent.indexOf("Firefox") > 0) {
+ style = document.createElement('style');
+ style.textContent = '@import "' + url + '"';
+ waitForGeckoLinkLoaded();
+ appendToHead(style);
+ return;
+ }
+
+ // Use the id owner on older webkits
+ waitForWebKitLinkLoaded();
+ }
+
+ appendToHead(link);
+ link.href = url;
+ }
+
+ this.load = load;
+ };
+});
+
+// Included from: js/tinymce/classes/dom/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
+ */
+
+/**
+ * Utility class for various DOM manipulation and retrieval functions.
+ *
+ * @class tinymce.dom.DOMUtils
+ * @example
+ * // Add a class to an element by id in the page
+ * tinymce.DOM.addClass('someid', 'someclass');
+ *
+ * // Add a class to an element by id inside the editor
+ * tinymce.activeEditor.dom.addClass('someid', 'someclass');
+ */
+define("tinymce/dom/DOMUtils", [
+ "tinymce/dom/Sizzle",
+ "tinymce/dom/DomQuery",
+ "tinymce/html/Styles",
+ "tinymce/dom/EventUtils",
+ "tinymce/dom/TreeWalker",
+ "tinymce/dom/Range",
+ "tinymce/html/Entities",
+ "tinymce/Env",
+ "tinymce/util/Tools",
+ "tinymce/dom/StyleSheetLoader"
+], function(Sizzle, $, Styles, EventUtils, TreeWalker, Range, Entities, Env, Tools, StyleSheetLoader) {
+ // Shorten names
+ var each = Tools.each, is = Tools.is, grep = Tools.grep, trim = Tools.trim;
+ var isIE = Env.ie;
+ var simpleSelectorRe = /^([a-z0-9],?)+$/i;
+ var whiteSpaceRegExp = /^[ \t\r\n]*$/;
+
+ function setupAttrHooks(domUtils, settings) {
+ var attrHooks = {}, keepValues = settings.keep_values, keepUrlHook;
+
+ keepUrlHook = {
+ set: function($elm, value, name) {
+ if (settings.url_converter) {
+ value = settings.url_converter.call(settings.url_converter_scope || domUtils, value, name, $elm[0]);
+ }
+
+ $elm.attr('data-mce-' + name, value).attr(name, value);
+ },
+
+ get: function($elm, name) {
+ return $elm.attr('data-mce-' + name) || $elm.attr(name);
+ }
+ };
+
+ attrHooks = {
+ style: {
+ set: function($elm, value) {
+ if (value !== null && typeof value === 'object') {
+ $elm.css(value);
+ return;
+ }
+
+ if (keepValues) {
+ $elm.attr('data-mce-style', value);
+ }
+
+ $elm.attr('style', value);
+ },
+
+ get: function($elm) {
+ var value = $elm.attr('data-mce-style') || $elm.attr('style');
+
+ value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName);
+
+ return value;
+ }
+ }
+ };
+
+ if (keepValues) {
+ attrHooks.href = attrHooks.src = keepUrlHook;
+ }
+
+ return attrHooks;
+ }
+
+ function updateInternalStyleAttr(domUtils, $elm) {
+ var value = $elm.attr('style');
+
+ value = domUtils.serializeStyle(domUtils.parseStyle(value), $elm[0].nodeName);
+
+ if (!value) {
+ value = null;
+ }
+
+ $elm.attr('data-mce-style', value);
+ }
+
+ function nodeIndex(node, normalized) {
+ var idx = 0, lastNodeType, nodeType;
+
+ if (node) {
+ for (lastNodeType = node.nodeType, node = node.previousSibling; node; node = node.previousSibling) {
+ nodeType = node.nodeType;
+
+ // Normalize text nodes
+ if (normalized && nodeType == 3) {
+ if (nodeType == lastNodeType || !node.nodeValue.length) {
+ continue;
+ }
+ }
+ idx++;
+ lastNodeType = nodeType;
+ }
+ }
+
+ return idx;
+ }
+
+ /**
+ * Constructs a new DOMUtils instance. Consult the Wiki for more details on settings etc for this class.
+ *
+ * @constructor
+ * @method DOMUtils
+ * @param {Document} doc Document reference to bind the utility class to.
+ * @param {settings} settings Optional settings collection.
+ */
+ function DOMUtils(doc, settings) {
+ var self = this, blockElementsMap;
+
+ self.doc = doc;
+ self.win = window;
+ self.files = {};
+ self.counter = 0;
+ self.stdMode = !isIE || doc.documentMode >= 8;
+ self.boxModel = !isIE || doc.compatMode == "CSS1Compat" || self.stdMode;
+ self.styleSheetLoader = new StyleSheetLoader(doc);
+ self.boundEvents = [];
+ self.settings = settings = settings || {};
+ self.schema = settings.schema;
+ self.styles = new Styles({
+ url_converter: settings.url_converter,
+ url_converter_scope: settings.url_converter_scope
+ }, settings.schema);
+
+ self.fixDoc(doc);
+ self.events = settings.ownEvents ? new EventUtils(settings.proxy) : EventUtils.Event;
+ self.attrHooks = setupAttrHooks(self, settings);
+ blockElementsMap = settings.schema ? settings.schema.getBlockElements() : {};
+ self.$ = $.overrideDefaults(function() {
+ return {
+ context: doc,
+ element: self.getRoot()
+ };
+ });
+
+ /**
+ * Returns true/false if the specified element is a block element or not.
+ *
+ * @method isBlock
+ * @param {Node/String} node Element/Node to check.
+ * @return {Boolean} True/False state if the node is a block element or not.
+ */
+ self.isBlock = function(node) {
+ // Fix for #5446
+ if (!node) {
+ return false;
+ }
+
+ // This function is called in module pattern style since it might be executed with the wrong this scope
+ var type = node.nodeType;
+
+ // If it's a node then check the type and use the nodeName
+ if (type) {
+ return !!(type === 1 && blockElementsMap[node.nodeName]);
+ }
+
+ return !!blockElementsMap[node];
+ };
+ }
+
+ DOMUtils.prototype = {
+ $$: function(elm) {
+ if (typeof elm == 'string') {
+ elm = this.get(elm);
+ }
+
+ return this.$(elm);
+ },
+
+ root: null,
+
+ fixDoc: function(doc) {
+ var settings = this.settings, name;
+
+ if (isIE && settings.schema) {
+ // Add missing HTML 4/5 elements to IE
+ ('abbr article aside audio canvas ' +
+ 'details figcaption figure footer ' +
+ 'header hgroup mark menu meter nav ' +
+ 'output progress section summary ' +
+ 'time video').replace(/\w+/g, function(name) {
+ doc.createElement(name);
+ });
+
+ // Create all custom elements
+ for (name in settings.schema.getCustomElements()) {
+ doc.createElement(name);
+ }
+ }
+ },
+
+ clone: function(node, deep) {
+ var self = this, clone, doc;
+
+ // TODO: Add feature detection here in the future
+ if (!isIE || node.nodeType !== 1 || deep) {
+ return node.cloneNode(deep);
+ }
+
+ doc = self.doc;
+
+ // Make a HTML5 safe shallow copy
+ if (!deep) {
+ clone = doc.createElement(node.nodeName);
+
+ // Copy attribs
+ each(self.getAttribs(node), function(attr) {
+ self.setAttrib(clone, attr.nodeName, self.getAttrib(node, attr.nodeName));
+ });
+
+ return clone;
+ }
+
+ return clone.firstChild;
+ },
+
+ /**
+ * Returns the root node of the document. This is normally the body but might be a DIV. Parents like getParent will not
+ * go above the point of this root node.
+ *
+ * @method getRoot
+ * @return {Element} Root element for the utility class.
+ */
+ getRoot: function() {
+ var self = this;
+
+ return self.settings.root_element || self.doc.body;
+ },
+
+ /**
+ * Returns the viewport of the window.
+ *
+ * @method getViewPort
+ * @param {Window} win Optional window to get viewport of.
+ * @return {Object} Viewport object with fields x, y, w and h.
+ */
+ getViewPort: function(win) {
+ var doc, rootElm;
+
+ win = !win ? this.win : win;
+ doc = win.document;
+ rootElm = this.boxModel ? doc.documentElement : doc.body;
+
+ // Returns viewport size excluding scrollbars
+ return {
+ x: win.pageXOffset || rootElm.scrollLeft,
+ y: win.pageYOffset || rootElm.scrollTop,
+ w: win.innerWidth || rootElm.clientWidth,
+ h: win.innerHeight || rootElm.clientHeight
+ };
+ },
+
+ /**
+ * Returns the rectangle for a specific element.
+ *
+ * @method getRect
+ * @param {Element/String} elm Element object or element ID to get rectangle from.
+ * @return {object} Rectangle for specified element object with x, y, w, h fields.
+ */
+ getRect: function(elm) {
+ var self = this, pos, size;
+
+ elm = self.get(elm);
+ pos = self.getPos(elm);
+ size = self.getSize(elm);
+
+ return {
+ x: pos.x, y: pos.y,
+ w: size.w, h: size.h
+ };
+ },
+
+ /**
+ * Returns the size dimensions of the specified element.
+ *
+ * @method getSize
+ * @param {Element/String} elm Element object or element ID to get rectangle from.
+ * @return {object} Rectangle for specified element object with w, h fields.
+ */
+ getSize: function(elm) {
+ var self = this, w, h;
+
+ elm = self.get(elm);
+ w = self.getStyle(elm, 'width');
+ h = self.getStyle(elm, 'height');
+
+ // Non pixel value, then force offset/clientWidth
+ if (w.indexOf('px') === -1) {
+ w = 0;
+ }
+
+ // Non pixel value, then force offset/clientWidth
+ if (h.indexOf('px') === -1) {
+ h = 0;
+ }
+
+ return {
+ w: parseInt(w, 10) || elm.offsetWidth || elm.clientWidth,
+ h: parseInt(h, 10) || elm.offsetHeight || elm.clientHeight
+ };
+ },
+
+ /**
+ * Returns a node by the specified selector function. This function will
+ * loop through all parent nodes and call the specified function for each node.
+ * If the function then returns true indicating that it has found what it was looking for, the loop execution will then end
+ * and the node it found will be returned.
+ *
+ * @method getParent
+ * @param {Node/String} node DOM node to search parents on or ID string.
+ * @param {function} selector Selection function or CSS selector to execute on each node.
+ * @param {Node} root Optional root element, never go beyond this point.
+ * @return {Node} DOM Node or null if it wasn't found.
+ */
+ getParent: function(node, selector, root) {
+ return this.getParents(node, selector, root, false);
+ },
+
+ /**
+ * Returns a node list of all parents matching the specified selector function or pattern.
+ * If the function then returns true indicating that it has found what it was looking for and that node will be collected.
+ *
+ * @method getParents
+ * @param {Node/String} node DOM node to search parents on or ID string.
+ * @param {function} selector Selection function to execute on each node or CSS pattern.
+ * @param {Node} root Optional root element, never go beyond this point.
+ * @return {Array} Array of nodes or null if it wasn't found.
+ */
+ getParents: function(node, selector, root, collect) {
+ var self = this, selectorVal, result = [];
+
+ node = self.get(node);
+ collect = collect === undefined;
+
+ // Default root on inline mode
+ root = root || (self.getRoot().nodeName != 'BODY' ? self.getRoot().parentNode : null);
+
+ // Wrap node name as func
+ if (is(selector, 'string')) {
+ selectorVal = selector;
+
+ if (selector === '*') {
+ selector = function(node) {
+ return node.nodeType == 1;
+ };
+ } else {
+ selector = function(node) {
+ return self.is(node, selectorVal);
+ };
+ }
+ }
+
+ while (node) {
+ if (node == root || !node.nodeType || node.nodeType === 9) {
+ break;
+ }
+
+ if (!selector || selector(node)) {
+ if (collect) {
+ result.push(node);
+ } else {
+ return node;
+ }
+ }
+
+ node = node.parentNode;
+ }
+
+ return collect ? result : null;
+ },
+
+ /**
+ * Returns the specified element by ID or the input element if it isn't a string.
+ *
+ * @method get
+ * @param {String/Element} n Element id to look for or element to just pass though.
+ * @return {Element} Element matching the specified id or null if it wasn't found.
+ */
+ get: function(elm) {
+ var name;
+
+ if (elm && this.doc && typeof elm == 'string') {
+ name = elm;
+ elm = this.doc.getElementById(elm);
+
+ // IE and Opera returns meta elements when they match the specified input ID, but getElementsByName seems to do the trick
+ if (elm && elm.id !== name) {
+ return this.doc.getElementsByName(name)[1];
+ }
+ }
+
+ return elm;
+ },
+
+ /**
+ * Returns the next node that matches selector or function
+ *
+ * @method getNext
+ * @param {Node} node Node to find siblings from.
+ * @param {String/function} selector Selector CSS expression or function.
+ * @return {Node} Next node item matching the selector or null if it wasn't found.
+ */
+ getNext: function(node, selector) {
+ return this._findSib(node, selector, 'nextSibling');
+ },
+
+ /**
+ * Returns the previous node that matches selector or function
+ *
+ * @method getPrev
+ * @param {Node} node Node to find siblings from.
+ * @param {String/function} selector Selector CSS expression or function.
+ * @return {Node} Previous node item matching the selector or null if it wasn't found.
+ */
+ getPrev: function(node, selector) {
+ return this._findSib(node, selector, 'previousSibling');
+ },
+
+ // #ifndef jquery
+
+ /**
+ * Selects specific elements by a CSS level 3 pattern. For example "div#a1 p.test".
+ * This function is optimized for the most common patterns needed in TinyMCE but it also performs well enough
+ * on more complex patterns.
+ *
+ * @method select
+ * @param {String} selector CSS level 3 pattern to select/find elements by.
+ * @param {Object} scope Optional root element/scope element to search in.
+ * @return {Array} Array with all matched elements.
+ * @example
+ * // Adds a class to all paragraphs in the currently active editor
+ * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
+ *
+ * // Adds a class to all spans that have the test class in the currently active editor
+ * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('span.test'), 'someclass')
+ */
+ select: function(selector, scope) {
+ var self = this;
+
+ /*eslint new-cap:0 */
+ return Sizzle(selector, self.get(scope) || self.settings.root_element || self.doc, []);
+ },
+
+ /**
+ * Returns true/false if the specified element matches the specified css pattern.
+ *
+ * @method is
+ * @param {Node/NodeList} elm DOM node to match or an array of nodes to match.
+ * @param {String} selector CSS pattern to match the element against.
+ */
+ is: function(elm, selector) {
+ var i;
+
+ // If it isn't an array then try to do some simple selectors instead of Sizzle for to boost performance
+ if (elm.length === undefined) {
+ // Simple all selector
+ if (selector === '*') {
+ return elm.nodeType == 1;
+ }
+
+ // Simple selector just elements
+ if (simpleSelectorRe.test(selector)) {
+ selector = selector.toLowerCase().split(/,/);
+ elm = elm.nodeName.toLowerCase();
+
+ for (i = selector.length - 1; i >= 0; i--) {
+ if (selector[i] == elm) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+
+ // Is non element
+ if (elm.nodeType && elm.nodeType != 1) {
+ return false;
+ }
+
+ var elms = elm.nodeType ? [elm] : elm;
+
+ /*eslint new-cap:0 */
+ return Sizzle(selector, elms[0].ownerDocument || elms[0], null, elms).length > 0;
+ },
+
+ // #endif
+
+ /**
+ * Adds the specified element to another element or elements.
+ *
+ * @method add
+ * @param {String/Element/Array} parentElm Element id string, DOM node element or array of ids or elements to add to.
+ * @param {String/Element} name Name of new element to add or existing element to add.
+ * @param {Object} attrs Optional object collection with arguments to add to the new element(s).
+ * @param {String} html Optional inner HTML contents to add for each element.
+ * @param {Boolean} create Optional flag if the element should be created or added.
+ * @return {Element/Array} Element that got created, or an array of created elements if multiple input elements
+ * were passed in.
+ * @example
+ * // Adds a new paragraph to the end of the active editor
+ * tinymce.activeEditor.dom.add(tinymce.activeEditor.getBody(), 'p', {title: 'my title'}, 'Some content');
+ */
+ add: function(parentElm, name, attrs, html, create) {
+ var self = this;
+
+ return this.run(parentElm, function(parentElm) {
+ var newElm;
+
+ newElm = is(name, 'string') ? self.doc.createElement(name) : name;
+ self.setAttribs(newElm, attrs);
+
+ if (html) {
+ if (html.nodeType) {
+ newElm.appendChild(html);
+ } else {
+ self.setHTML(newElm, html);
+ }
+ }
+
+ return !create ? parentElm.appendChild(newElm) : newElm;
+ });
+ },
+
+ /**
+ * Creates a new element.
+ *
+ * @method create
+ * @param {String} name Name of new element.
+ * @param {Object} attrs Optional object name/value collection with element attributes.
+ * @param {String} html Optional HTML string to set as inner HTML of the element.
+ * @return {Element} HTML DOM node element that got created.
+ * @example
+ * // Adds an element where the caret/selection is in the active editor
+ * var el = tinymce.activeEditor.dom.create('div', {id: 'test', 'class': 'myclass'}, 'some content');
+ * tinymce.activeEditor.selection.setNode(el);
+ */
+ create: function(name, attrs, html) {
+ return this.add(this.doc.createElement(name), name, attrs, html, 1);
+ },
+
+ /**
+ * Creates HTML string for element. The element will be closed unless an empty inner HTML string is passed in.
+ *
+ * @method createHTML
+ * @param {String} name Name of new element.
+ * @param {Object} attrs Optional object name/value collection with element attributes.
+ * @param {String} html Optional HTML string to set as inner HTML of the element.
+ * @return {String} String with new HTML element, for example: test.
+ * @example
+ * // Creates a html chunk and inserts it at the current selection/caret location
+ * tinymce.activeEditor.selection.setContent(tinymce.activeEditor.dom.createHTML('a', {href: 'test.html'}, 'some line'));
+ */
+ createHTML: function(name, attrs, html) {
+ var outHtml = '', key;
+
+ outHtml += '<' + name;
+
+ for (key in attrs) {
+ if (attrs.hasOwnProperty(key) && attrs[key] !== null && typeof attrs[key] != 'undefined') {
+ outHtml += ' ' + key + '="' + this.encode(attrs[key]) + '"';
+ }
+ }
+
+ // A call to tinymce.is doesn't work for some odd reason on IE9 possible bug inside their JS runtime
+ if (typeof html != "undefined") {
+ return outHtml + '>' + html + '' + name + '>';
+ }
+
+ return outHtml + ' />';
+ },
+
+ /**
+ * Creates a document fragment out of the specified HTML string.
+ *
+ * @method createFragment
+ * @param {String} html Html string to create fragment from.
+ * @return {DocumentFragment} Document fragment node.
+ */
+ createFragment: function(html) {
+ var frag, node, doc = this.doc, container;
+
+ container = doc.createElement("div");
+ frag = doc.createDocumentFragment();
+
+ if (html) {
+ container.innerHTML = html;
+ }
+
+ while ((node = container.firstChild)) {
+ frag.appendChild(node);
+ }
+
+ return frag;
+ },
+
+ /**
+ * Removes/deletes the specified element(s) from the DOM.
+ *
+ * @method remove
+ * @param {String/Element/Array} node ID of element or DOM element object or array containing multiple elements/ids.
+ * @param {Boolean} keepChildren Optional state to keep children or not. If set to true all children will be
+ * placed at the location of the removed element.
+ * @return {Element/Array} HTML DOM element that got removed, or an array of removed elements if multiple input elements
+ * were passed in.
+ * @example
+ * // Removes all paragraphs in the active editor
+ * tinymce.activeEditor.dom.remove(tinymce.activeEditor.dom.select('p'));
+ *
+ * // Removes an element by id in the document
+ * tinymce.DOM.remove('mydiv');
+ */
+ remove: function(node, keepChildren) {
+ node = this.$$(node);
+
+ if (keepChildren) {
+ node.each(function() {
+ var child;
+
+ while ((child = this.firstChild)) {
+ if (child.nodeType == 3 && child.data.length === 0) {
+ this.removeChild(child);
+ } else {
+ this.parentNode.insertBefore(child, this);
+ }
+ }
+ }).remove();
+ } else {
+ node.remove();
+ }
+
+ return node.length > 1 ? node.toArray() : node[0];
+ },
+
+ /**
+ * Sets the CSS style value on a HTML element. The name can be a camelcase string
+ * or the CSS style name like background-color.
+ *
+ * @method setStyle
+ * @param {String/Element/Array} elm HTML element/Array of elements to set CSS style value on.
+ * @param {String} name Name of the style value to set.
+ * @param {String} value Value to set on the style.
+ * @example
+ * // Sets a style value on all paragraphs in the currently active editor
+ * tinymce.activeEditor.dom.setStyle(tinymce.activeEditor.dom.select('p'), 'background-color', 'red');
+ *
+ * // Sets a style value to an element by id in the current document
+ * tinymce.DOM.setStyle('mydiv', 'background-color', 'red');
+ */
+ setStyle: function(elm, name, value) {
+ elm = this.$$(elm).css(name, value);
+
+ if (this.settings.update_styles) {
+ updateInternalStyleAttr(this, elm);
+ }
+ },
+
+ /**
+ * Returns the current style or runtime/computed value of an element.
+ *
+ * @method getStyle
+ * @param {String/Element} elm HTML element or element id string to get style from.
+ * @param {String} name Style name to return.
+ * @param {Boolean} computed Computed style.
+ * @return {String} Current style or computed style value of an element.
+ */
+ getStyle: function(elm, name, computed) {
+ elm = this.$$(elm);
+
+ if (computed) {
+ return elm.css(name);
+ }
+
+ // Camelcase it, if needed
+ name = name.replace(/-(\D)/g, function(a, b) {
+ return b.toUpperCase();
+ });
+
+ if (name == 'float') {
+ name = Env.ie && Env.ie < 12 ? 'styleFloat' : 'cssFloat';
+ }
+
+ return elm[0] && elm[0].style ? elm[0].style[name] : undefined;
+ },
+
+ /**
+ * Sets multiple styles on the specified element(s).
+ *
+ * @method setStyles
+ * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set styles on.
+ * @param {Object} styles Name/Value collection of style items to add to the element(s).
+ * @example
+ * // Sets styles on all paragraphs in the currently active editor
+ * tinymce.activeEditor.dom.setStyles(tinymce.activeEditor.dom.select('p'), {'background-color': 'red', 'color': 'green'});
+ *
+ * // Sets styles to an element by id in the current document
+ * tinymce.DOM.setStyles('mydiv', {'background-color': 'red', 'color': 'green'});
+ */
+ setStyles: function(elm, styles) {
+ elm = this.$$(elm).css(styles);
+
+ if (this.settings.update_styles) {
+ updateInternalStyleAttr(this, elm);
+ }
+ },
+
+ /**
+ * Removes all attributes from an element or elements.
+ *
+ * @method removeAllAttribs
+ * @param {Element/String/Array} e DOM element, element id string or array of elements/ids to remove attributes from.
+ */
+ removeAllAttribs: function(e) {
+ return this.run(e, function(e) {
+ var i, attrs = e.attributes;
+ for (i = attrs.length - 1; i >= 0; i--) {
+ e.removeAttributeNode(attrs.item(i));
+ }
+ });
+ },
+
+ /**
+ * Sets the specified attribute of an element or elements.
+ *
+ * @method setAttrib
+ * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attribute on.
+ * @param {String} name Name of attribute to set.
+ * @param {String} value Value to set on the attribute - if this value is falsy like null, 0 or '' it will remove
+ * the attribute instead.
+ * @example
+ * // Sets class attribute on all paragraphs in the active editor
+ * tinymce.activeEditor.dom.setAttrib(tinymce.activeEditor.dom.select('p'), 'class', 'myclass');
+ *
+ * // Sets class attribute on a specific element in the current page
+ * tinymce.dom.setAttrib('mydiv', 'class', 'myclass');
+ */
+ setAttrib: function(elm, name, value) {
+ var self = this, originalValue, hook, settings = self.settings;
+
+ if (value === '') {
+ value = null;
+ }
+
+ elm = self.$$(elm);
+ originalValue = elm.attr(name);
+
+ if (!elm.length) {
+ return;
+ }
+
+ hook = self.attrHooks[name];
+ if (hook && hook.set) {
+ hook.set(elm, value, name);
+ } else {
+ elm.attr(name, value);
+ }
+
+ if (originalValue != value && settings.onSetAttrib) {
+ settings.onSetAttrib({
+ attrElm: elm,
+ attrName: name,
+ attrValue: value
+ });
+ }
+ },
+
+ /**
+ * Sets two or more specified attributes of an element or elements.
+ *
+ * @method setAttribs
+ * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set attributes on.
+ * @param {Object} attrs Name/Value collection of attribute items to add to the element(s).
+ * @example
+ * // Sets class and title attributes on all paragraphs in the active editor
+ * tinymce.activeEditor.dom.setAttribs(tinymce.activeEditor.dom.select('p'), {'class': 'myclass', title: 'some title'});
+ *
+ * // Sets class and title attributes on a specific element in the current page
+ * tinymce.DOM.setAttribs('mydiv', {'class': 'myclass', title: 'some title'});
+ */
+ setAttribs: function(elm, attrs) {
+ var self = this;
+
+ self.$$(elm).each(function(i, node) {
+ each(attrs, function(value, name) {
+ self.setAttrib(node, name, value);
+ });
+ });
+ },
+
+ /**
+ * Returns the specified attribute by name.
+ *
+ * @method getAttrib
+ * @param {String/Element} elm Element string id or DOM element to get attribute from.
+ * @param {String} name Name of attribute to get.
+ * @param {String} defaultVal Optional default value to return if the attribute didn't exist.
+ * @return {String} Attribute value string, default value or null if the attribute wasn't found.
+ */
+ getAttrib: function(elm, name, defaultVal) {
+ var self = this, hook, value;
+
+ elm = self.$$(elm);
+
+ if (elm.length) {
+ hook = self.attrHooks[name];
+
+ if (hook && hook.get) {
+ value = hook.get(elm, name);
+ } else {
+ value = elm.attr(name);
+ }
+ }
+
+ if (typeof value == 'undefined') {
+ value = defaultVal || '';
+ }
+
+ return value;
+ },
+
+ /**
+ * Returns the absolute x, y position of a node. The position will be returned in an object with x, y fields.
+ *
+ * @method getPos
+ * @param {Element/String} elm HTML element or element id to get x, y position from.
+ * @param {Element} rootElm Optional root element to stop calculations at.
+ * @return {object} Absolute position of the specified element object with x, y fields.
+ */
+ getPos: function(elm, rootElm) {
+ var self = this, x = 0, y = 0, offsetParent, doc = self.doc, body = doc.body, pos;
+
+ elm = self.get(elm);
+ rootElm = rootElm || body;
+
+ if (elm) {
+ // Use getBoundingClientRect if it exists since it's faster than looping offset nodes
+ // Fallback to offsetParent calculations if the body isn't static better since it stops at the body root
+ if (rootElm === body && elm.getBoundingClientRect && $(body).css('position') === 'static') {
+ pos = elm.getBoundingClientRect();
+ rootElm = self.boxModel ? doc.documentElement : body;
+
+ // Add scroll offsets from documentElement or body since IE with the wrong box model will use d.body and so do WebKit
+ // Also remove the body/documentelement clientTop/clientLeft on IE 6, 7 since they offset the position
+ x = pos.left + (doc.documentElement.scrollLeft || body.scrollLeft) - rootElm.clientLeft;
+ y = pos.top + (doc.documentElement.scrollTop || body.scrollTop) - rootElm.clientTop;
+
+ return {x: x, y: y};
+ }
+
+ offsetParent = elm;
+ while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
+ x += offsetParent.offsetLeft || 0;
+ y += offsetParent.offsetTop || 0;
+ offsetParent = offsetParent.offsetParent;
+ }
+
+ offsetParent = elm.parentNode;
+ while (offsetParent && offsetParent != rootElm && offsetParent.nodeType) {
+ x -= offsetParent.scrollLeft || 0;
+ y -= offsetParent.scrollTop || 0;
+ offsetParent = offsetParent.parentNode;
+ }
+ }
+
+ return {x: x, y: y};
+ },
+
+ /**
+ * Parses the specified style value into an object collection. This parser will also
+ * merge and remove any redundant items that browsers might have added. It will also convert non-hex
+ * colors to hex values. Urls inside the styles will also be converted to absolute/relative based on settings.
+ *
+ * @method parseStyle
+ * @param {String} cssText Style value to parse, for example: border:1px solid red;.
+ * @return {Object} Object representation of that style, for example: {border: '1px solid red'}
+ */
+ parseStyle: function(cssText) {
+ return this.styles.parse(cssText);
+ },
+
+ /**
+ * Serializes the specified style object into a string.
+ *
+ * @method serializeStyle
+ * @param {Object} styles Object to serialize as string, for example: {border: '1px solid red'}
+ * @param {String} name Optional element name.
+ * @return {String} String representation of the style object, for example: border: 1px solid red.
+ */
+ serializeStyle: function(styles, name) {
+ return this.styles.serialize(styles, name);
+ },
+
+ /**
+ * Adds a style element at the top of the document with the specified cssText content.
+ *
+ * @method addStyle
+ * @param {String} cssText CSS Text style to add to top of head of document.
+ */
+ addStyle: function(cssText) {
+ var self = this, doc = self.doc, head, styleElm;
+
+ // Prevent inline from loading the same styles twice
+ if (self !== DOMUtils.DOM && doc === document) {
+ var addedStyles = DOMUtils.DOM.addedStyles;
+
+ addedStyles = addedStyles || [];
+ if (addedStyles[cssText]) {
+ return;
+ }
+
+ addedStyles[cssText] = true;
+ DOMUtils.DOM.addedStyles = addedStyles;
+ }
+
+ // Create style element if needed
+ styleElm = doc.getElementById('mceDefaultStyles');
+ if (!styleElm) {
+ styleElm = doc.createElement('style');
+ styleElm.id = 'mceDefaultStyles';
+ styleElm.type = 'text/css';
+
+ head = doc.getElementsByTagName('head')[0];
+ if (head.firstChild) {
+ head.insertBefore(styleElm, head.firstChild);
+ } else {
+ head.appendChild(styleElm);
+ }
+ }
+
+ // Append style data to old or new style element
+ if (styleElm.styleSheet) {
+ styleElm.styleSheet.cssText += cssText;
+ } else {
+ styleElm.appendChild(doc.createTextNode(cssText));
+ }
+ },
+
+ /**
+ * Imports/loads the specified CSS file into the document bound to the class.
+ *
+ * @method loadCSS
+ * @param {String} url URL to CSS file to load.
+ * @example
+ * // Loads a CSS file dynamically into the current document
+ * tinymce.DOM.loadCSS('somepath/some.css');
+ *
+ * // Loads a CSS file into the currently active editor instance
+ * tinymce.activeEditor.dom.loadCSS('somepath/some.css');
+ *
+ * // Loads a CSS file into an editor instance by id
+ * tinymce.get('someid').dom.loadCSS('somepath/some.css');
+ *
+ * // Loads multiple CSS files into the current document
+ * tinymce.DOM.loadCSS('somepath/some.css,somepath/someother.css');
+ */
+ loadCSS: function(url) {
+ var self = this, doc = self.doc, head;
+
+ // Prevent inline from loading the same CSS file twice
+ if (self !== DOMUtils.DOM && doc === document) {
+ DOMUtils.DOM.loadCSS(url);
+ return;
+ }
+
+ if (!url) {
+ url = '';
+ }
+
+ head = doc.getElementsByTagName('head')[0];
+
+ each(url.split(','), function(url) {
+ var link;
+
+ url = Tools._addCacheSuffix(url);
+
+ if (self.files[url]) {
+ return;
+ }
+
+ self.files[url] = true;
+ link = self.create('link', {rel: 'stylesheet', href: url});
+
+ // IE 8 has a bug where dynamically loading stylesheets would produce a 1 item remaining bug
+ // This fix seems to resolve that issue by recalcing the document once a stylesheet finishes loading
+ // It's ugly but it seems to work fine.
+ if (isIE && doc.documentMode && doc.recalc) {
+ link.onload = function() {
+ if (doc.recalc) {
+ doc.recalc();
+ }
+
+ link.onload = null;
+ };
+ }
+
+ head.appendChild(link);
+ });
+ },
+
+ /**
+ * Adds a class to the specified element or elements.
+ *
+ * @method addClass
+ * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
+ * @param {String} cls Class name to add to each element.
+ * @return {String/Array} String with new class value or array with new class values for all elements.
+ * @example
+ * // Adds a class to all paragraphs in the active editor
+ * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'myclass');
+ *
+ * // Adds a class to a specific element in the current page
+ * tinymce.DOM.addClass('mydiv', 'myclass');
+ */
+ addClass: function(elm, cls) {
+ this.$$(elm).addClass(cls);
+ },
+
+ /**
+ * Removes a class from the specified element or elements.
+ *
+ * @method removeClass
+ * @param {String/Element/Array} elm Element ID string or DOM element or array with elements or IDs.
+ * @param {String} cls Class name to remove from each element.
+ * @return {String/Array} String of remaining class name(s), or an array of strings if multiple input elements
+ * were passed in.
+ * @example
+ * // Removes a class from all paragraphs in the active editor
+ * tinymce.activeEditor.dom.removeClass(tinymce.activeEditor.dom.select('p'), 'myclass');
+ *
+ * // Removes a class from a specific element in the current page
+ * tinymce.DOM.removeClass('mydiv', 'myclass');
+ */
+ removeClass: function(elm, cls) {
+ this.toggleClass(elm, cls, false);
+ },
+
+ /**
+ * Returns true if the specified element has the specified class.
+ *
+ * @method hasClass
+ * @param {String/Element} elm HTML element or element id string to check CSS class on.
+ * @param {String} cls CSS class to check for.
+ * @return {Boolean} true/false if the specified element has the specified class.
+ */
+ hasClass: function(elm, cls) {
+ return this.$$(elm).hasClass(cls);
+ },
+
+ /**
+ * Toggles the specified class on/off.
+ *
+ * @method toggleClass
+ * @param {Element} elm Element to toggle class on.
+ * @param {[type]} cls Class to toggle on/off.
+ * @param {[type]} state Optional state to set.
+ */
+ toggleClass: function(elm, cls, state) {
+ this.$$(elm).toggleClass(cls, state).each(function() {
+ if (this.className === '') {
+ $(this).attr('class', null);
+ }
+ });
+ },
+
+ /**
+ * Shows the specified element(s) by ID by setting the "display" style.
+ *
+ * @method show
+ * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to show.
+ */
+ show: function(elm) {
+ this.$$(elm).show();
+ },
+
+ /**
+ * Hides the specified element(s) by ID by setting the "display" style.
+ *
+ * @method hide
+ * @param {String/Element/Array} elm ID of DOM element or DOM element or array with elements or IDs to hide.
+ * @example
+ * // Hides an element by id in the document
+ * tinymce.DOM.hide('myid');
+ */
+ hide: function(elm) {
+ this.$$(elm).hide();
+ },
+
+ /**
+ * Returns true/false if the element is hidden or not by checking the "display" style.
+ *
+ * @method isHidden
+ * @param {String/Element} elm Id or element to check display state on.
+ * @return {Boolean} true/false if the element is hidden or not.
+ */
+ isHidden: function(elm) {
+ return this.$$(elm).css('display') == 'none';
+ },
+
+ /**
+ * Returns a unique id. This can be useful when generating elements on the fly.
+ * This method will not check if the element already exists.
+ *
+ * @method uniqueId
+ * @param {String} prefix Optional prefix to add in front of all ids - defaults to "mce_".
+ * @return {String} Unique id.
+ */
+ uniqueId: function(prefix) {
+ return (!prefix ? 'mce_' : prefix) + (this.counter++);
+ },
+
+ /**
+ * Sets the specified HTML content inside the element or elements. The HTML will first be processed. This means
+ * URLs will get converted, hex color values fixed etc. Check processHTML for details.
+ *
+ * @method setHTML
+ * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set HTML inside of.
+ * @param {String} html HTML content to set as inner HTML of the element.
+ * @example
+ * // Sets the inner HTML of all paragraphs in the active editor
+ * tinymce.activeEditor.dom.setHTML(tinymce.activeEditor.dom.select('p'), 'some inner html');
+ *
+ * // Sets the inner HTML of an element by id in the document
+ * tinymce.DOM.setHTML('mydiv', 'some inner html');
+ */
+ setHTML: function(elm, html) {
+ elm = this.$$(elm);
+
+ if (isIE) {
+ elm.each(function(i, target) {
+ if (target.canHaveHTML === false) {
+ return;
+ }
+
+ // Remove all child nodes, IE keeps empty text nodes in DOM
+ while (target.firstChild) {
+ target.removeChild(target.firstChild);
+ }
+
+ try {
+ // IE will remove comments from the beginning
+ // unless you padd the contents with something
+ target.innerHTML = ' ' + html;
+ target.removeChild(target.firstChild);
+ } catch (ex) {
+ // IE sometimes produces an unknown runtime error on innerHTML if it's a div inside a p
+ $('').html(' ' + html).contents().slice(1).appendTo(target);
+ }
+
+ return html;
+ });
+ } else {
+ elm.html(html);
+ }
+ },
+
+ /**
+ * Returns the outer HTML of an element.
+ *
+ * @method getOuterHTML
+ * @param {String/Element} elm Element ID or element object to get outer HTML from.
+ * @return {String} Outer HTML string.
+ * @example
+ * tinymce.DOM.getOuterHTML(editorElement);
+ * tinymce.activeEditor.getOuterHTML(tinymce.activeEditor.getBody());
+ */
+ getOuterHTML: function(elm) {
+ elm = this.get(elm);
+
+ // Older FF doesn't have outerHTML 3.6 is still used by some orgaizations
+ return elm.nodeType == 1 && "outerHTML" in elm ? elm.outerHTML : $('').append($(elm).clone()).html();
+ },
+
+ /**
+ * Sets the specified outer HTML on an element or elements.
+ *
+ * @method setOuterHTML
+ * @param {Element/String/Array} elm DOM element, element id string or array of elements/ids to set outer HTML on.
+ * @param {Object} html HTML code to set as outer value for the element.
+ * @example
+ * // Sets the outer HTML of all paragraphs in the active editor
+ * tinymce.activeEditor.dom.setOuterHTML(tinymce.activeEditor.dom.select('p'), '
some html
');
+ *
+ * // Sets the outer HTML of an element by id in the document
+ * tinymce.DOM.setOuterHTML('mydiv', '
some html
');
+ */
+ setOuterHTML: function(elm, html) {
+ var self = this;
+
+ self.$$(elm).each(function() {
+ try {
+ // Older FF doesn't have outerHTML 3.6 is still used by some organizations
+ if ("outerHTML" in this) {
+ this.outerHTML = html;
+ return;
+ }
+ } catch (ex) {
+ // Ignore
+ }
+
+ // OuterHTML for IE it sometimes produces an "unknown runtime error"
+ self.remove($(this).html(html), true);
+ });
+ },
+
+ /**
+ * Entity decodes a string. This method decodes any HTML entities, such as å.
+ *
+ * @method decode
+ * @param {String} s String to decode entities on.
+ * @return {String} Entity decoded string.
+ */
+ decode: Entities.decode,
+
+ /**
+ * Entity encodes a string. This method encodes the most common entities, such as <>"&.
+ *
+ * @method encode
+ * @param {String} text String to encode with entities.
+ * @return {String} Entity encoded string.
+ */
+ encode: Entities.encodeAllRaw,
+
+ /**
+ * Inserts an element after the reference element.
+ *
+ * @method insertAfter
+ * @param {Element} node Element to insert after the reference.
+ * @param {Element/String/Array} referenceNode Reference element, element id or array of elements to insert after.
+ * @return {Element/Array} Element that got added or an array with elements.
+ */
+ insertAfter: function(node, referenceNode) {
+ referenceNode = this.get(referenceNode);
+
+ return this.run(node, function(node) {
+ var parent, nextSibling;
+
+ parent = referenceNode.parentNode;
+ nextSibling = referenceNode.nextSibling;
+
+ if (nextSibling) {
+ parent.insertBefore(node, nextSibling);
+ } else {
+ parent.appendChild(node);
+ }
+
+ return node;
+ });
+ },
+
+ /**
+ * Replaces the specified element or elements with the new element specified. The new element will
+ * be cloned if multiple input elements are passed in.
+ *
+ * @method replace
+ * @param {Element} newElm New element to replace old ones with.
+ * @param {Element/String/Array} oldElm Element DOM node, element id or array of elements or ids to replace.
+ * @param {Boolean} keepChildren Optional keep children state, if set to true child nodes from the old object will be added
+ * to new ones.
+ */
+ replace: function(newElm, oldElm, keepChildren) {
+ var self = this;
+
+ return self.run(oldElm, function(oldElm) {
+ if (is(oldElm, 'array')) {
+ newElm = newElm.cloneNode(true);
+ }
+
+ if (keepChildren) {
+ each(grep(oldElm.childNodes), function(node) {
+ newElm.appendChild(node);
+ });
+ }
+
+ return oldElm.parentNode.replaceChild(newElm, oldElm);
+ });
+ },
+
+ /**
+ * Renames the specified element and keeps its attributes and children.
+ *
+ * @method rename
+ * @param {Element} elm Element to rename.
+ * @param {String} name Name of the new element.
+ * @return {Element} New element or the old element if it needed renaming.
+ */
+ rename: function(elm, name) {
+ var self = this, newElm;
+
+ if (elm.nodeName != name.toUpperCase()) {
+ // Rename block element
+ newElm = self.create(name);
+
+ // Copy attribs to new block
+ each(self.getAttribs(elm), function(attrNode) {
+ self.setAttrib(newElm, attrNode.nodeName, self.getAttrib(elm, attrNode.nodeName));
+ });
+
+ // Replace block
+ self.replace(newElm, elm, 1);
+ }
+
+ return newElm || elm;
+ },
+
+ /**
+ * Find the common ancestor of two elements. This is a shorter method than using the DOM Range logic.
+ *
+ * @method findCommonAncestor
+ * @param {Element} a Element to find common ancestor of.
+ * @param {Element} b Element to find common ancestor of.
+ * @return {Element} Common ancestor element of the two input elements.
+ */
+ findCommonAncestor: function(a, b) {
+ var ps = a, pe;
+
+ while (ps) {
+ pe = b;
+
+ while (pe && ps != pe) {
+ pe = pe.parentNode;
+ }
+
+ if (ps == pe) {
+ break;
+ }
+
+ ps = ps.parentNode;
+ }
+
+ if (!ps && a.ownerDocument) {
+ return a.ownerDocument.documentElement;
+ }
+
+ return ps;
+ },
+
+ /**
+ * Parses the specified RGB color value and returns a hex version of that color.
+ *
+ * @method toHex
+ * @param {String} rgbVal RGB string value like rgb(1,2,3)
+ * @return {String} Hex version of that RGB value like #FF00FF.
+ */
+ toHex: function(rgbVal) {
+ return this.styles.toHex(Tools.trim(rgbVal));
+ },
+
+ /**
+ * Executes the specified function on the element by id or dom element node or array of elements/id.
+ *
+ * @method run
+ * @param {String/Element/Array} elm ID or DOM element object or array with ids or elements.
+ * @param {function} func Function to execute for each item.
+ * @param {Object} scope Optional scope to execute the function in.
+ * @return {Object/Array} Single object, or an array of objects if multiple input elements were passed in.
+ */
+ run: function(elm, func, scope) {
+ var self = this, result;
+
+ if (typeof elm === 'string') {
+ elm = self.get(elm);
+ }
+
+ if (!elm) {
+ return false;
+ }
+
+ scope = scope || this;
+ if (!elm.nodeType && (elm.length || elm.length === 0)) {
+ result = [];
+
+ each(elm, function(elm, i) {
+ if (elm) {
+ if (typeof elm == 'string') {
+ elm = self.get(elm);
+ }
+
+ result.push(func.call(scope, elm, i));
+ }
+ });
+
+ return result;
+ }
+
+ return func.call(scope, elm);
+ },
+
+ /**
+ * Returns a NodeList with attributes for the element.
+ *
+ * @method getAttribs
+ * @param {HTMLElement/string} elm Element node or string id to get attributes from.
+ * @return {NodeList} NodeList with attributes.
+ */
+ getAttribs: function(elm) {
+ var attrs;
+
+ elm = this.get(elm);
+
+ if (!elm) {
+ return [];
+ }
+
+ if (isIE) {
+ attrs = [];
+
+ // Object will throw exception in IE
+ if (elm.nodeName == 'OBJECT') {
+ return elm.attributes;
+ }
+
+ // IE doesn't keep the selected attribute if you clone option elements
+ if (elm.nodeName === 'OPTION' && this.getAttrib(elm, 'selected')) {
+ attrs.push({specified: 1, nodeName: 'selected'});
+ }
+
+ // It's crazy that this is faster in IE but it's because it returns all attributes all the time
+ var attrRegExp = /<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi;
+ elm.cloneNode(false).outerHTML.replace(attrRegExp, '').replace(/[\w:\-]+/gi, function(a) {
+ attrs.push({specified: 1, nodeName: a});
+ });
+
+ return attrs;
+ }
+
+ return elm.attributes;
+ },
+
+ /**
+ * Returns true/false if the specified node is to be considered empty or not.
+ *
+ * @example
+ * tinymce.DOM.isEmpty(node, {img: true});
+ * @method isEmpty
+ * @param {Object} elements Optional name/value object with elements that are automatically treated as non-empty elements.
+ * @return {Boolean} true/false if the node is empty or not.
+ */
+ isEmpty: function(node, elements) {
+ var self = this, i, attributes, type, whitespace, walker, name, brCount = 0;
+
+ node = node.firstChild;
+ if (node) {
+ walker = new TreeWalker(node, node.parentNode);
+ elements = elements || (self.schema ? self.schema.getNonEmptyElements() : null);
+ whitespace = self.schema ? self.schema.getWhiteSpaceElements() : {};
+
+ do {
+ type = node.nodeType;
+
+ if (type === 1) {
+ // Ignore bogus elements
+ var bogusVal = node.getAttribute('data-mce-bogus');
+ if (bogusVal) {
+ node = walker.next(bogusVal === 'all');
+ continue;
+ }
+
+ // Keep empty elements like
+ name = node.nodeName.toLowerCase();
+ if (elements && elements[name]) {
+ // Ignore single BR elements in blocks like
or
+ if (name === 'br') {
+ brCount++;
+ node = walker.next();
+ continue;
+ }
+
+ return false;
+ }
+
+ // Keep elements with data-bookmark attributes or name attribute like
+ attributes = self.getAttribs(node);
+ i = attributes.length;
+ while (i--) {
+ name = attributes[i].nodeName;
+ if (name === "name" || name === 'data-mce-bookmark') {
+ return false;
+ }
+ }
+ }
+
+ // Keep comment nodes
+ if (type == 8) {
+ return false;
+ }
+
+ // Keep non whitespace text nodes
+ if (type === 3 && !whiteSpaceRegExp.test(node.nodeValue)) {
+ return false;
+ }
+
+ // Keep whitespace preserve elements
+ if (type === 3 && node.parentNode && whitespace[node.parentNode.nodeName] && whiteSpaceRegExp.test(node.nodeValue)) {
+ return false;
+ }
+
+ node = walker.next();
+ } while (node);
+ }
+
+ return brCount <= 1;
+ },
+
+ /**
+ * Creates a new DOM Range object. This will use the native DOM Range API if it's
+ * available. If it's not, it will fall back to the custom TinyMCE implementation.
+ *
+ * @method createRng
+ * @return {DOMRange} DOM Range object.
+ * @example
+ * var rng = tinymce.DOM.createRng();
+ * alert(rng.startContainer + "," + rng.startOffset);
+ */
+ createRng: function() {
+ var doc = this.doc;
+
+ return doc.createRange ? doc.createRange() : new Range(this);
+ },
+
+ /**
+ * Returns the index of the specified node within its parent.
+ *
+ * @method nodeIndex
+ * @param {Node} node Node to look for.
+ * @param {boolean} normalized Optional true/false state if the index is what it would be after a normalization.
+ * @return {Number} Index of the specified node.
+ */
+ nodeIndex: nodeIndex,
+
+ /**
+ * Splits an element into two new elements and places the specified split
+ * element or elements between the new ones. For example splitting the paragraph at the bold element in
+ * this example
abcabc123
would produce
abc
abc
123
.
+ *
+ * @method split
+ * @param {Element} parentElm Parent element to split.
+ * @param {Element} splitElm Element to split at.
+ * @param {Element} replacementElm Optional replacement element to replace the split element with.
+ * @return {Element} Returns the split element or the replacement element if that is specified.
+ */
+ split: function(parentElm, splitElm, replacementElm) {
+ var self = this, r = self.createRng(), bef, aft, pa;
+
+ // W3C valid browsers tend to leave empty nodes to the left/right side of the contents - this makes sense
+ // but we don't want that in our code since it serves no purpose for the end user
+ // For example splitting this html at the bold element:
+ //
text 1CHOPtext 2
+ // would produce:
+ //
text 1
CHOP
text 2
+ // this function will then trim off empty edges and produce:
+ //
text 1
CHOP
text 2
+ function trimNode(node) {
+ var i, children = node.childNodes, type = node.nodeType;
+
+ function surroundedBySpans(node) {
+ var previousIsSpan = node.previousSibling && node.previousSibling.nodeName == 'SPAN';
+ var nextIsSpan = node.nextSibling && node.nextSibling.nodeName == 'SPAN';
+ return previousIsSpan && nextIsSpan;
+ }
+
+ if (type == 1 && node.getAttribute('data-mce-type') == 'bookmark') {
+ return;
+ }
+
+ for (i = children.length - 1; i >= 0; i--) {
+ trimNode(children[i]);
+ }
+
+ if (type != 9) {
+ // Keep non whitespace text nodes
+ if (type == 3 && node.nodeValue.length > 0) {
+ // If parent element isn't a block or there isn't any useful contents for example "
"
+ // Also keep text nodes with only spaces if surrounded by spans.
+ // eg. "
ab
" should keep space between a and b
+ var trimmedLength = trim(node.nodeValue).length;
+ if (!self.isBlock(node.parentNode) || trimmedLength > 0 || trimmedLength === 0 && surroundedBySpans(node)) {
+ return;
+ }
+ } else if (type == 1) {
+ // If the only child is a bookmark then move it up
+ children = node.childNodes;
+
+ // TODO fix this complex if
+ if (children.length == 1 && children[0] && children[0].nodeType == 1 &&
+ children[0].getAttribute('data-mce-type') == 'bookmark') {
+ node.parentNode.insertBefore(children[0], node);
+ }
+
+ // Keep non empty elements or img, hr etc
+ if (children.length || /^(br|hr|input|img)$/i.test(node.nodeName)) {
+ return;
+ }
+ }
+
+ self.remove(node);
+ }
+
+ return node;
+ }
+
+ if (parentElm && splitElm) {
+ // Get before chunk
+ r.setStart(parentElm.parentNode, self.nodeIndex(parentElm));
+ r.setEnd(splitElm.parentNode, self.nodeIndex(splitElm));
+ bef = r.extractContents();
+
+ // Get after chunk
+ r = self.createRng();
+ r.setStart(splitElm.parentNode, self.nodeIndex(splitElm) + 1);
+ r.setEnd(parentElm.parentNode, self.nodeIndex(parentElm) + 1);
+ aft = r.extractContents();
+
+ // Insert before chunk
+ pa = parentElm.parentNode;
+ pa.insertBefore(trimNode(bef), parentElm);
+
+ // Insert middle chunk
+ if (replacementElm) {
+ pa.insertBefore(replacementElm, parentElm);
+ //pa.replaceChild(replacementElm, splitElm);
+ } else {
+ pa.insertBefore(splitElm, parentElm);
+ }
+
+ // Insert after chunk
+ pa.insertBefore(trimNode(aft), parentElm);
+ self.remove(parentElm);
+
+ return replacementElm || splitElm;
+ }
+ },
+
+ /**
+ * Adds an event handler to the specified object.
+ *
+ * @method bind
+ * @param {Element/Document/Window/Array} target Target element to bind events to.
+ * handler to or an array of elements/ids/documents.
+ * @param {String} name Name of event handler to add, for example: click.
+ * @param {function} func Function to execute when the event occurs.
+ * @param {Object} scope Optional scope to execute the function in.
+ * @return {function} Function callback handler the same as the one passed in.
+ */
+ bind: function(target, name, func, scope) {
+ var self = this;
+
+ if (Tools.isArray(target)) {
+ var i = target.length;
+
+ while (i--) {
+ target[i] = self.bind(target[i], name, func, scope);
+ }
+
+ return target;
+ }
+
+ // Collect all window/document events bound by editor instance
+ if (self.settings.collect && (target === self.doc || target === self.win)) {
+ self.boundEvents.push([target, name, func, scope]);
+ }
+
+ return self.events.bind(target, name, func, scope || self);
+ },
+
+ /**
+ * Removes the specified event handler by name and function from an element or collection of elements.
+ *
+ * @method unbind
+ * @param {Element/Document/Window/Array} target Target element to unbind events on.
+ * @param {String} name Event handler name, for example: "click"
+ * @param {function} func Function to remove.
+ * @return {bool/Array} Bool state of true if the handler was removed, or an array of states if multiple input elements
+ * were passed in.
+ */
+ unbind: function(target, name, func) {
+ var self = this, i;
+
+ if (Tools.isArray(target)) {
+ i = target.length;
+
+ while (i--) {
+ target[i] = self.unbind(target[i], name, func);
+ }
+
+ return target;
+ }
+
+ // Remove any bound events matching the input
+ if (self.boundEvents && (target === self.doc || target === self.win)) {
+ i = self.boundEvents.length;
+
+ while (i--) {
+ var item = self.boundEvents[i];
+
+ if (target == item[0] && (!name || name == item[1]) && (!func || func == item[2])) {
+ this.events.unbind(item[0], item[1], item[2]);
+ }
+ }
+ }
+
+ return this.events.unbind(target, name, func);
+ },
+
+ /**
+ * Fires the specified event name with object on target.
+ *
+ * @method fire
+ * @param {Node/Document/Window} target Target element or object to fire event on.
+ * @param {String} name Name of the event to fire.
+ * @param {Object} evt Event object to send.
+ * @return {Event} Event object.
+ */
+ fire: function(target, name, evt) {
+ return this.events.fire(target, name, evt);
+ },
+
+ // Returns the content editable state of a node
+ getContentEditable: function(node) {
+ var contentEditable;
+
+ // Check type
+ if (!node || node.nodeType != 1) {
+ return null;
+ }
+
+ // Check for fake content editable
+ contentEditable = node.getAttribute("data-mce-contenteditable");
+ if (contentEditable && contentEditable !== "inherit") {
+ return contentEditable;
+ }
+
+ // Check for real content editable
+ return node.contentEditable !== "inherit" ? node.contentEditable : null;
+ },
+
+ getContentEditableParent: function(node) {
+ var root = this.getRoot(), state = null;
+
+ for (; node && node !== root; node = node.parentNode) {
+ state = this.getContentEditable(node);
+
+ if (state !== null) {
+ break;
+ }
+ }
+
+ return state;
+ },
+
+ /**
+ * Destroys all internal references to the DOM to solve IE leak issues.
+ *
+ * @method destroy
+ */
+ destroy: function() {
+ var self = this;
+
+ // Unbind all events bound to window/document by editor instance
+ if (self.boundEvents) {
+ var i = self.boundEvents.length;
+
+ while (i--) {
+ var item = self.boundEvents[i];
+ this.events.unbind(item[0], item[1], item[2]);
+ }
+
+ self.boundEvents = null;
+ }
+
+ // Restore sizzle document to window.document
+ // Since the current document might be removed producing "Permission denied" on IE see #6325
+ if (Sizzle.setDocument) {
+ Sizzle.setDocument();
+ }
+
+ self.win = self.doc = self.root = self.events = self.frag = null;
+ },
+
+ isChildOf: function(node, parent) {
+ while (node) {
+ if (parent === node) {
+ return true;
+ }
+
+ node = node.parentNode;
+ }
+
+ return false;
+ },
+
+ // #ifdef debug
+
+ dumpRng: function(r) {
+ return (
+ 'startContainer: ' + r.startContainer.nodeName +
+ ', startOffset: ' + r.startOffset +
+ ', endContainer: ' + r.endContainer.nodeName +
+ ', endOffset: ' + r.endOffset
+ );
+ },
+
+ // #endif
+
+ _findSib: function(node, selector, name) {
+ var self = this, func = selector;
+
+ if (node) {
+ // If expression make a function of it using is
+ if (typeof func == 'string') {
+ func = function(node) {
+ return self.is(node, selector);
+ };
+ }
+
+ // Loop all siblings
+ for (node = node[name]; node; node = node[name]) {
+ if (func(node)) {
+ return node;
+ }
+ }
+ }
+
+ return null;
+ }
+ };
+
+ /**
+ * Instance of DOMUtils for the current document.
+ *
+ * @static
+ * @property DOM
+ * @type tinymce.dom.DOMUtils
+ * @example
+ * // Example of how to add a class to some element by id
+ * tinymce.DOM.addClass('someid', 'someclass');
+ */
+ DOMUtils.DOM = new DOMUtils(document);
+ DOMUtils.nodeIndex = nodeIndex;
+
+ return DOMUtils;
+});
+
+// Included from: js/tinymce/classes/dom/ScriptLoader.js
+
+/**
+ * ScriptLoader.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
+ */
+
+/*globals console*/
+
+/**
+ * This class handles asynchronous/synchronous loading of JavaScript files it will execute callbacks
+ * when various items gets loaded. This class is useful to load external JavaScript files.
+ *
+ * @class tinymce.dom.ScriptLoader
+ * @example
+ * // Load a script from a specific URL using the global script loader
+ * tinymce.ScriptLoader.load('somescript.js');
+ *
+ * // Load a script using a unique instance of the script loader
+ * var scriptLoader = new tinymce.dom.ScriptLoader();
+ *
+ * scriptLoader.load('somescript.js');
+ *
+ * // Load multiple scripts
+ * var scriptLoader = new tinymce.dom.ScriptLoader();
+ *
+ * scriptLoader.add('somescript1.js');
+ * scriptLoader.add('somescript2.js');
+ * scriptLoader.add('somescript3.js');
+ *
+ * scriptLoader.loadQueue(function() {
+ * alert('All scripts are now loaded.');
+ * });
+ */
+define("tinymce/dom/ScriptLoader", [
+ "tinymce/dom/DOMUtils",
+ "tinymce/util/Tools"
+], function(DOMUtils, Tools) {
+ var DOM = DOMUtils.DOM;
+ var each = Tools.each, grep = Tools.grep;
+
+ var isFunction = function (f) {
+ return typeof f === 'function';
+ };
+
+ function ScriptLoader() {
+ var QUEUED = 0,
+ LOADING = 1,
+ LOADED = 2,
+ FAILED = 3,
+ states = {},
+ queue = [],
+ scriptLoadedCallbacks = {},
+ queueLoadedCallbacks = [],
+ loading = 0,
+ undef;
+
+ /**
+ * Loads a specific script directly without adding it to the load queue.
+ *
+ * @method load
+ * @param {String} url Absolute URL to script to add.
+ * @param {function} callback Optional success callback function when the script loaded successfully.
+ * @param {function} callback Optional failure callback function when the script failed to load.
+ */
+ function loadScript(url, success, failure) {
+ var dom = DOM, elm, id;
+
+ // Execute callback when script is loaded
+ function done() {
+ dom.remove(id);
+
+ if (elm) {
+ elm.onreadystatechange = elm.onload = elm = null;
+ }
+
+ success();
+ }
+
+ function error() {
+ /*eslint no-console:0 */
+
+ // We can't mark it as done if there is a load error since
+ // A) We don't want to produce 404 errors on the server and
+ // B) the onerror event won't fire on all browsers.
+ // done();
+
+ if (isFunction(failure)) {
+ failure();
+ } else {
+ // Report the error so it's easier for people to spot loading errors
+ if (typeof console !== "undefined" && console.log) {
+ console.log("Failed to load script: " + url);
+ }
+ }
+ }
+
+ id = dom.uniqueId();
+
+ // Create new script element
+ elm = document.createElement('script');
+ elm.id = id;
+ elm.type = 'text/javascript';
+ elm.src = Tools._addCacheSuffix(url);
+
+ // Seems that onreadystatechange works better on IE 10 onload seems to fire incorrectly
+ if ("onreadystatechange" in elm) {
+ elm.onreadystatechange = function() {
+ if (/loaded|complete/.test(elm.readyState)) {
+ done();
+ }
+ };
+ } else {
+ elm.onload = done;
+ }
+
+ // Add onerror event will get fired on some browsers but not all of them
+ elm.onerror = error;
+
+ // Add script to document
+ (document.getElementsByTagName('head')[0] || document.body).appendChild(elm);
+ }
+
+ /**
+ * Returns true/false if a script has been loaded or not.
+ *
+ * @method isDone
+ * @param {String} url URL to check for.
+ * @return {Boolean} true/false if the URL is loaded.
+ */
+ this.isDone = function(url) {
+ return states[url] == LOADED;
+ };
+
+ /**
+ * Marks a specific script to be loaded. This can be useful if a script got loaded outside
+ * the script loader or to skip it from loading some script.
+ *
+ * @method markDone
+ * @param {string} url Absolute URL to the script to mark as loaded.
+ */
+ this.markDone = function(url) {
+ states[url] = LOADED;
+ };
+
+ /**
+ * Adds a specific script to the load queue of the script loader.
+ *
+ * @method add
+ * @param {String} url Absolute URL to script to add.
+ * @param {function} success Optional success callback function to execute when the script loades successfully.
+ * @param {Object} scope Optional scope to execute callback in.
+ * @param {function} failure Optional failure callback function to execute when the script failed to load.
+ */
+ this.add = this.load = function(url, success, scope, failure) {
+ var state = states[url];
+
+ // Add url to load queue
+ if (state == undef) {
+ queue.push(url);
+ states[url] = QUEUED;
+ }
+
+ if (success) {
+ // Store away callback for later execution
+ if (!scriptLoadedCallbacks[url]) {
+ scriptLoadedCallbacks[url] = [];
+ }
+
+ scriptLoadedCallbacks[url].push({
+ success: success,
+ failure: failure,
+ scope: scope || this
+ });
+ }
+ };
+
+ this.remove = function(url) {
+ delete states[url];
+ delete scriptLoadedCallbacks[url];
+ };
+
+ /**
+ * Starts the loading of the queue.
+ *
+ * @method loadQueue
+ * @param {function} success Optional callback to execute when all queued items are loaded.
+ * @param {function} failure Optional callback to execute when queued items failed to load.
+ * @param {Object} scope Optional scope to execute the callback in.
+ */
+ this.loadQueue = function(success, scope, failure) {
+ this.loadScripts(queue, success, scope, failure);
+ };
+
+ /**
+ * Loads the specified queue of files and executes the callback ones they are loaded.
+ * This method is generally not used outside this class but it might be useful in some scenarios.
+ *
+ * @method loadScripts
+ * @param {Array} scripts Array of queue items to load.
+ * @param {function} callback Optional callback to execute when scripts is loaded successfully.
+ * @param {Object} scope Optional scope to execute callback in.
+ * @param {function} callback Optional callback to execute if scripts failed to load.
+ */
+ this.loadScripts = function(scripts, success, scope, failure) {
+ var loadScripts, failures = [];
+
+ function execCallbacks(name, url) {
+ // Execute URL callback functions
+ each(scriptLoadedCallbacks[url], function(callback) {
+ if (isFunction(callback[name])) {
+ callback[name].call(callback.scope);
+ }
+ });
+
+ scriptLoadedCallbacks[url] = undef;
+ }
+
+ queueLoadedCallbacks.push({
+ success: success,
+ failure: failure,
+ scope: scope || this
+ });
+
+ loadScripts = function() {
+ var loadingScripts = grep(scripts);
+
+ // Current scripts has been handled
+ scripts.length = 0;
+
+ // Load scripts that needs to be loaded
+ each(loadingScripts, function(url) {
+ // Script is already loaded then execute script callbacks directly
+ if (states[url] === LOADED) {
+ execCallbacks('success', url);
+ return;
+ }
+
+ if (states[url] === FAILED) {
+ execCallbacks('failure', url);
+ return;
+ }
+
+ // Is script not loading then start loading it
+ if (states[url] !== LOADING) {
+ states[url] = LOADING;
+ loading++;
+
+ loadScript(url, function() {
+ states[url] = LOADED;
+ loading--;
+
+ execCallbacks('success', url);
+
+ // Load more scripts if they where added by the recently loaded script
+ loadScripts();
+ }, function () {
+ states[url] = FAILED;
+ loading--;
+
+ failures.push(url);
+ execCallbacks('failure', url);
+
+ // Load more scripts if they where added by the recently loaded script
+ loadScripts();
+ });
+ }
+ });
+
+ // No scripts are currently loading then execute all pending queue loaded callbacks
+ if (!loading) {
+ each(queueLoadedCallbacks, function(callback) {
+ if (failures.length === 0) {
+ if (isFunction(callback.success)) {
+ callback.success.call(callback.scope);
+ }
+ } else {
+ if (isFunction(callback.failure)) {
+ callback.failure.call(callback.scope, failures);
+ }
+ }
+ });
+
+ queueLoadedCallbacks.length = 0;
+ }
+ };
+
+ loadScripts();
+ };
+ }
+
+ ScriptLoader.ScriptLoader = new ScriptLoader();
+
+ return ScriptLoader;
+});
+
+// Included from: js/tinymce/classes/AddOnManager.js
+
+/**
+ * AddOnManager.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 loading of themes/plugins or other add-ons and their language packs.
+ *
+ * @class tinymce.AddOnManager
+ */
+define("tinymce/AddOnManager", [
+ "tinymce/dom/ScriptLoader",
+ "tinymce/util/Tools"
+], function(ScriptLoader, Tools) {
+ var each = Tools.each;
+
+ function AddOnManager() {
+ var self = this;
+
+ self.items = [];
+ self.urls = {};
+ self.lookup = {};
+ }
+
+ AddOnManager.prototype = {
+ /**
+ * Returns the specified add on by the short name.
+ *
+ * @method get
+ * @param {String} name Add-on to look for.
+ * @return {tinymce.Theme/tinymce.Plugin} Theme or plugin add-on instance or undefined.
+ */
+ get: function(name) {
+ if (this.lookup[name]) {
+ return this.lookup[name].instance;
+ }
+
+ return undefined;
+ },
+
+ dependencies: function(name) {
+ var result;
+
+ if (this.lookup[name]) {
+ result = this.lookup[name].dependencies;
+ }
+
+ return result || [];
+ },
+
+ /**
+ * Loads a language pack for the specified add-on.
+ *
+ * @method requireLangPack
+ * @param {String} name Short name of the add-on.
+ * @param {String} languages Optional comma or space separated list of languages to check if it matches the name.
+ */
+ requireLangPack: function(name, languages) {
+ var language = AddOnManager.language;
+
+ if (language && AddOnManager.languageLoad !== false) {
+ if (languages) {
+ languages = ',' + languages + ',';
+
+ // Load short form sv.js or long form sv_SE.js
+ if (languages.indexOf(',' + language.substr(0, 2) + ',') != -1) {
+ language = language.substr(0, 2);
+ } else if (languages.indexOf(',' + language + ',') == -1) {
+ return;
+ }
+ }
+
+ ScriptLoader.ScriptLoader.add(this.urls[name] + '/langs/' + language + '.js');
+ }
+ },
+
+ /**
+ * Adds a instance of the add-on by it's short name.
+ *
+ * @method add
+ * @param {String} id Short name/id for the add-on.
+ * @param {tinymce.Theme/tinymce.Plugin} addOn Theme or plugin to add.
+ * @return {tinymce.Theme/tinymce.Plugin} The same theme or plugin instance that got passed in.
+ * @example
+ * // Create a simple plugin
+ * tinymce.create('tinymce.plugins.TestPlugin', {
+ * TestPlugin: function(ed, url) {
+ * ed.on('click', function(e) {
+ * ed.windowManager.alert('Hello World!');
+ * });
+ * }
+ * });
+ *
+ * // Register plugin using the add method
+ * tinymce.PluginManager.add('test', tinymce.plugins.TestPlugin);
+ *
+ * // Initialize TinyMCE
+ * tinymce.init({
+ * ...
+ * plugins: '-test' // Init the plugin but don't try to load it
+ * });
+ */
+ add: function(id, addOn, dependencies) {
+ this.items.push(addOn);
+ this.lookup[id] = {instance: addOn, dependencies: dependencies};
+
+ return addOn;
+ },
+
+ remove: function(name) {
+ delete this.urls[name];
+ delete this.lookup[name];
+ },
+
+ createUrl: function(baseUrl, dep) {
+ if (typeof dep === "object") {
+ return dep;
+ }
+
+ return {prefix: baseUrl.prefix, resource: dep, suffix: baseUrl.suffix};
+ },
+
+ /**
+ * Add a set of components that will make up the add-on. Using the url of the add-on name as the base url.
+ * This should be used in development mode. A new compressor/javascript munger process will ensure that the
+ * components are put together into the plugin.js file and compressed correctly.
+ *
+ * @method addComponents
+ * @param {String} pluginName name of the plugin to load scripts from (will be used to get the base url for the plugins).
+ * @param {Array} scripts Array containing the names of the scripts to load.
+ */
+ addComponents: function(pluginName, scripts) {
+ var pluginUrl = this.urls[pluginName];
+
+ each(scripts, function(script) {
+ ScriptLoader.ScriptLoader.add(pluginUrl + "/" + script);
+ });
+ },
+
+ /**
+ * Loads an add-on from a specific url.
+ *
+ * @method load
+ * @param {String} name Short name of the add-on that gets loaded.
+ * @param {String} addOnUrl URL to the add-on that will get loaded.
+ * @param {function} success Optional success callback to execute when an add-on is loaded.
+ * @param {Object} scope Optional scope to execute the callback in.
+ * @param {function} failure Optional failure callback to execute when an add-on failed to load.
+ * @example
+ * // Loads a plugin from an external URL
+ * tinymce.PluginManager.load('myplugin', '/some/dir/someplugin/plugin.js');
+ *
+ * // Initialize TinyMCE
+ * tinymce.init({
+ * ...
+ * plugins: '-myplugin' // Don't try to load it again
+ * });
+ */
+ load: function(name, addOnUrl, success, scope, failure) {
+ var self = this, url = addOnUrl;
+
+ function loadDependencies() {
+ var dependencies = self.dependencies(name);
+
+ each(dependencies, function(dep) {
+ var newUrl = self.createUrl(addOnUrl, dep);
+
+ self.load(newUrl.resource, newUrl, undefined, undefined);
+ });
+
+ if (success) {
+ if (scope) {
+ success.call(scope);
+ } else {
+ success.call(ScriptLoader);
+ }
+ }
+ }
+
+ if (self.urls[name]) {
+ return;
+ }
+
+ if (typeof addOnUrl === "object") {
+ url = addOnUrl.prefix + addOnUrl.resource + addOnUrl.suffix;
+ }
+
+ if (url.indexOf('/') !== 0 && url.indexOf('://') == -1) {
+ url = AddOnManager.baseURL + '/' + url;
+ }
+
+ self.urls[name] = url.substring(0, url.lastIndexOf('/'));
+
+ if (self.lookup[name]) {
+ loadDependencies();
+ } else {
+ ScriptLoader.ScriptLoader.add(url, loadDependencies, scope, failure);
+ }
+ }
+ };
+
+ AddOnManager.PluginManager = new AddOnManager();
+ AddOnManager.ThemeManager = new AddOnManager();
+
+ return AddOnManager;
+});
+
+/**
+ * TinyMCE theme class.
+ *
+ * @class tinymce.Theme
+ */
+
+/**
+ * This method is responsible for rendering/generating the overall user interface with toolbars, buttons, iframe containers etc.
+ *
+ * @method renderUI
+ * @param {Object} obj Object parameter containing the targetNode DOM node that will be replaced visually with an editor instance.
+ * @return {Object} an object with items like iframeContainer, editorContainer, sizeContainer, deltaWidth, deltaHeight.
+ */
+
+/**
+ * Plugin base class, this is a pseudo class that describes how a plugin is to be created for TinyMCE. The methods below are all optional.
+ *
+ * @class tinymce.Plugin
+ * @example
+ * tinymce.PluginManager.add('example', function(editor, url) {
+ * // Add a button that opens a window
+ * editor.addButton('example', {
+ * text: 'My button',
+ * icon: false,
+ * onclick: function() {
+ * // Open window
+ * editor.windowManager.open({
+ * title: 'Example plugin',
+ * body: [
+ * {type: 'textbox', name: 'title', label: 'Title'}
+ * ],
+ * onsubmit: function(e) {
+ * // Insert content when the window form is submitted
+ * editor.insertContent('Title: ' + e.data.title);
+ * }
+ * });
+ * }
+ * });
+ *
+ * // Adds a menu item to the tools menu
+ * editor.addMenuItem('example', {
+ * text: 'Example plugin',
+ * context: 'tools',
+ * onclick: function() {
+ * // Open window with a specific url
+ * editor.windowManager.open({
+ * title: 'TinyMCE site',
+ * url: 'http://www.tinymce.com',
+ * width: 800,
+ * height: 600,
+ * buttons: [{
+ * text: 'Close',
+ * onclick: 'close'
+ * }]
+ * });
+ * }
+ * });
+ * });
+ */
+
+// Included from: js/tinymce/classes/dom/NodeType.js
+
+/**
+ * NodeType.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 various node validation functions.
+ *
+ * @private
+ * @class tinymce.dom.NodeType
+ */
+define("tinymce/dom/NodeType", [], function() {
+ function isNodeType(type) {
+ return function(node) {
+ return !!node && node.nodeType == type;
+ };
+ }
+
+ var isElement = isNodeType(1);
+
+ function matchNodeNames(names) {
+ names = names.toLowerCase().split(' ');
+
+ return function(node) {
+ var i, name;
+
+ if (node && node.nodeType) {
+ name = node.nodeName.toLowerCase();
+
+ for (i = 0; i < names.length; i++) {
+ if (name === names[i]) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ };
+ }
+
+ function matchStyleValues(name, values) {
+ values = values.toLowerCase().split(' ');
+
+ return function(node) {
+ var i, cssValue;
+
+ if (isElement(node)) {
+ for (i = 0; i < values.length; i++) {
+ cssValue = getComputedStyle(node, null).getPropertyValue(name);
+ if (cssValue === values[i]) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ };
+ }
+
+ function hasPropValue(propName, propValue) {
+ return function(node) {
+ return isElement(node) && node[propName] === propValue;
+ };
+ }
+
+ function hasAttributeValue(attrName, attrValue) {
+ return function(node) {
+ return isElement(node) && node.getAttribute(attrName) === attrValue;
+ };
+ }
+
+ function isBogus(node) {
+ return isElement(node) && node.hasAttribute('data-mce-bogus');
+ }
+
+ function hasContentEditableState(value) {
+ return function(node) {
+ if (isElement(node)) {
+ if (node.contentEditable === value) {
+ return true;
+ }
+
+ if (node.getAttribute('data-mce-contenteditable') === value) {
+ return true;
+ }
+ }
+
+ return false;
+ };
+ }
+
+ return {
+ isText: isNodeType(3),
+ isElement: isElement,
+ isComment: isNodeType(8),
+ isBr: matchNodeNames('br'),
+ isContentEditableTrue: hasContentEditableState('true'),
+ isContentEditableFalse: hasContentEditableState('false'),
+ matchNodeNames: matchNodeNames,
+ hasPropValue: hasPropValue,
+ hasAttributeValue: hasAttributeValue,
+ matchStyleValues: matchStyleValues,
+ isBogus: isBogus
+ };
+});
+
+// Included from: js/tinymce/classes/text/Zwsp.js
+
+/**
+ * Zwsp.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 for working with zero width space
+ * characters used as character containers etc.
+ *
+ * @private
+ * @class tinymce.text.Zwsp
+ * @example
+ * var isZwsp = Zwsp.isZwsp('\uFEFF');
+ * var abc = Zwsp.trim('a\uFEFFc');
+ */
+define("tinymce/text/Zwsp", [], function() {
+ var ZWSP = '\uFEFF';
+
+ function isZwsp(chr) {
+ return chr == ZWSP;
+ }
+
+ function trim(str) {
+ return str.replace(new RegExp(ZWSP, 'g'), '');
+ }
+
+ return {
+ isZwsp: isZwsp,
+ ZWSP: ZWSP,
+ trim: trim
+ };
+});
+
+// Included from: js/tinymce/classes/caret/CaretContainer.js
+
+/**
+ * CaretContainer.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 handles caret containers. A caret container is a node that
+ * holds the caret for positional purposes.
+ *
+ * @private
+ * @class tinymce.caret.CaretContainer
+ */
+define("tinymce/caret/CaretContainer", [
+ "tinymce/dom/NodeType",
+ "tinymce/text/Zwsp"
+], function(NodeType, Zwsp) {
+ var isElement = NodeType.isElement,
+ isText = NodeType.isText;
+
+ function isCaretContainerBlock(node) {
+ if (isText(node)) {
+ node = node.parentNode;
+ }
+
+ return isElement(node) && node.hasAttribute('data-mce-caret');
+ }
+
+ function isCaretContainerInline(node) {
+ return isText(node) && Zwsp.isZwsp(node.data);
+ }
+
+ function isCaretContainer(node) {
+ return isCaretContainerBlock(node) || isCaretContainerInline(node);
+ }
+
+ function removeNode(node) {
+ var parentNode = node.parentNode;
+ if (parentNode) {
+ parentNode.removeChild(node);
+ }
+ }
+
+ function getNodeValue(node) {
+ try {
+ return node.nodeValue;
+ } catch (ex) {
+ // IE sometimes produces "Invalid argument" on nodes
+ return "";
+ }
+ }
+
+ function setNodeValue(node, text) {
+ if (text.length === 0) {
+ removeNode(node);
+ } else {
+ node.nodeValue = text;
+ }
+ }
+
+ function insertInline(node, before) {
+ var doc, sibling, textNode, parentNode;
+
+ doc = node.ownerDocument;
+ textNode = doc.createTextNode(Zwsp.ZWSP);
+ parentNode = node.parentNode;
+
+ if (!before) {
+ sibling = node.nextSibling;
+ if (isText(sibling)) {
+ if (isCaretContainer(sibling)) {
+ return sibling;
+ }
+
+ if (startsWithCaretContainer(sibling)) {
+ sibling.splitText(1);
+ return sibling;
+ }
+ }
+
+ if (node.nextSibling) {
+ parentNode.insertBefore(textNode, node.nextSibling);
+ } else {
+ parentNode.appendChild(textNode);
+ }
+ } else {
+ sibling = node.previousSibling;
+ if (isText(sibling)) {
+ if (isCaretContainer(sibling)) {
+ return sibling;
+ }
+
+ if (endsWithCaretContainer(sibling)) {
+ return sibling.splitText(sibling.data.length - 1);
+ }
+ }
+
+ parentNode.insertBefore(textNode, node);
+ }
+
+ return textNode;
+ }
+
+ function createBogusBr() {
+ var br = document.createElement('br');
+ br.setAttribute('data-mce-bogus', '1');
+ return br;
+ }
+
+ function insertBlock(blockName, node, before) {
+ var doc, blockNode, parentNode;
+
+ doc = node.ownerDocument;
+ blockNode = doc.createElement(blockName);
+ blockNode.setAttribute('data-mce-caret', before ? 'before' : 'after');
+ blockNode.setAttribute('data-mce-bogus', 'all');
+ blockNode.appendChild(createBogusBr());
+ parentNode = node.parentNode;
+
+ if (!before) {
+ if (node.nextSibling) {
+ parentNode.insertBefore(blockNode, node.nextSibling);
+ } else {
+ parentNode.appendChild(blockNode);
+ }
+ } else {
+ parentNode.insertBefore(blockNode, node);
+ }
+
+ return blockNode;
+ }
+
+ function hasContent(node) {
+ return node.firstChild !== node.lastChild || !NodeType.isBr(node.firstChild);
+ }
+
+ function remove(caretContainerNode) {
+ if (isElement(caretContainerNode) && isCaretContainer(caretContainerNode)) {
+ if (hasContent(caretContainerNode)) {
+ caretContainerNode.removeAttribute('data-mce-caret');
+ } else {
+ removeNode(caretContainerNode);
+ }
+ }
+
+ if (isText(caretContainerNode)) {
+ var text = Zwsp.trim(getNodeValue(caretContainerNode));
+ setNodeValue(caretContainerNode, text);
+ }
+ }
+
+ function startsWithCaretContainer(node) {
+ return isText(node) && node.data[0] == Zwsp.ZWSP;
+ }
+
+ function endsWithCaretContainer(node) {
+ return isText(node) && node.data[node.data.length - 1] == Zwsp.ZWSP;
+ }
+
+ function trimBogusBr(elm) {
+ var brs = elm.getElementsByTagName('br');
+ var lastBr = brs[brs.length - 1];
+ if (NodeType.isBogus(lastBr)) {
+ lastBr.parentNode.removeChild(lastBr);
+ }
+ }
+
+ function showCaretContainerBlock(caretContainer) {
+ if (caretContainer && caretContainer.hasAttribute('data-mce-caret')) {
+ trimBogusBr(caretContainer);
+ caretContainer.removeAttribute('data-mce-caret');
+ caretContainer.removeAttribute('data-mce-bogus');
+ caretContainer.removeAttribute('style');
+ caretContainer.removeAttribute('_moz_abspos');
+ return caretContainer;
+ }
+
+ return null;
+ }
+
+ return {
+ isCaretContainer: isCaretContainer,
+ isCaretContainerBlock: isCaretContainerBlock,
+ isCaretContainerInline: isCaretContainerInline,
+ showCaretContainerBlock: showCaretContainerBlock,
+ insertInline: insertInline,
+ insertBlock: insertBlock,
+ hasContent: hasContent,
+ remove: remove,
+ startsWithCaretContainer: startsWithCaretContainer,
+ endsWithCaretContainer: endsWithCaretContainer
+ };
+});
+
+// Included from: js/tinymce/classes/dom/RangeUtils.js
+
+/**
+ * RangeUtils.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 contains a few utility methods for ranges.
+ *
+ * @class tinymce.dom.RangeUtils
+ */
+define("tinymce/dom/RangeUtils", [
+ "tinymce/util/Tools",
+ "tinymce/dom/TreeWalker",
+ "tinymce/dom/NodeType",
+ "tinymce/dom/Range",
+ "tinymce/caret/CaretContainer"
+], function(Tools, TreeWalker, NodeType, Range, CaretContainer) {
+ var each = Tools.each,
+ isContentEditableTrue = NodeType.isContentEditableTrue,
+ isContentEditableFalse = NodeType.isContentEditableFalse,
+ isCaretContainer = CaretContainer.isCaretContainer;
+
+ function hasCeProperty(node) {
+ return isContentEditableTrue(node) || isContentEditableFalse(node);
+ }
+
+ function getEndChild(container, index) {
+ var childNodes = container.childNodes;
+
+ index--;
+
+ if (index > childNodes.length - 1) {
+ index = childNodes.length - 1;
+ } else if (index < 0) {
+ index = 0;
+ }
+
+ return childNodes[index] || container;
+ }
+
+ function findParent(node, rootNode, predicate) {
+ while (node && node !== rootNode) {
+ if (predicate(node)) {
+ return node;
+ }
+
+ node = node.parentNode;
+ }
+
+ return null;
+ }
+
+ function hasParent(node, rootNode, predicate) {
+ return findParent(node, rootNode, predicate) !== null;
+ }
+
+ function isFormatterCaret(node) {
+ return node.id === '_mce_caret';
+ }
+
+ function isCeFalseCaretContainer(node, rootNode) {
+ return isCaretContainer(node) && hasParent(node, rootNode, isFormatterCaret) === false;
+ }
+
+ function RangeUtils(dom) {
+ /**
+ * Walks the specified range like object and executes the callback for each sibling collection it finds.
+ *
+ * @private
+ * @method walk
+ * @param {Object} rng Range like object.
+ * @param {function} callback Callback function to execute for each sibling collection.
+ */
+ this.walk = function(rng, callback) {
+ var startContainer = rng.startContainer,
+ startOffset = rng.startOffset,
+ endContainer = rng.endContainer,
+ endOffset = rng.endOffset,
+ ancestor, startPoint,
+ endPoint, node, parent, siblings, nodes;
+
+ // Handle table cell selection the table plugin enables
+ // you to fake select table cells and perform formatting actions on them
+ nodes = dom.select('td[data-mce-selected],th[data-mce-selected]');
+ if (nodes.length > 0) {
+ each(nodes, function(node) {
+ callback([node]);
+ });
+
+ return;
+ }
+
+ /**
+ * Excludes start/end text node if they are out side the range
+ *
+ * @private
+ * @param {Array} nodes Nodes to exclude items from.
+ * @return {Array} Array with nodes excluding the start/end container if needed.
+ */
+ function exclude(nodes) {
+ var node;
+
+ // First node is excluded
+ node = nodes[0];
+ if (node.nodeType === 3 && node === startContainer && startOffset >= node.nodeValue.length) {
+ nodes.splice(0, 1);
+ }
+
+ // Last node is excluded
+ node = nodes[nodes.length - 1];
+ if (endOffset === 0 && nodes.length > 0 && node === endContainer && node.nodeType === 3) {
+ nodes.splice(nodes.length - 1, 1);
+ }
+
+ return nodes;
+ }
+
+ /**
+ * Collects siblings
+ *
+ * @private
+ * @param {Node} node Node to collect siblings from.
+ * @param {String} name Name of the sibling to check for.
+ * @param {Node} end_node
+ * @return {Array} Array of collected siblings.
+ */
+ function collectSiblings(node, name, end_node) {
+ var siblings = [];
+
+ for (; node && node != end_node; node = node[name]) {
+ siblings.push(node);
+ }
+
+ return siblings;
+ }
+
+ /**
+ * Find an end point this is the node just before the common ancestor root.
+ *
+ * @private
+ * @param {Node} node Node to start at.
+ * @param {Node} root Root/ancestor element to stop just before.
+ * @return {Node} Node just before the root element.
+ */
+ function findEndPoint(node, root) {
+ do {
+ if (node.parentNode == root) {
+ return node;
+ }
+
+ node = node.parentNode;
+ } while (node);
+ }
+
+ function walkBoundary(start_node, end_node, next) {
+ var siblingName = next ? 'nextSibling' : 'previousSibling';
+
+ for (node = start_node, parent = node.parentNode; node && node != end_node; node = parent) {
+ parent = node.parentNode;
+ siblings = collectSiblings(node == start_node ? node : node[siblingName], siblingName);
+
+ if (siblings.length) {
+ if (!next) {
+ siblings.reverse();
+ }
+
+ callback(exclude(siblings));
+ }
+ }
+ }
+
+ // If index based start position then resolve it
+ if (startContainer.nodeType == 1 && startContainer.hasChildNodes()) {
+ startContainer = startContainer.childNodes[startOffset];
+ }
+
+ // If index based end position then resolve it
+ if (endContainer.nodeType == 1 && endContainer.hasChildNodes()) {
+ endContainer = getEndChild(endContainer, endOffset);
+ }
+
+ // Same container
+ if (startContainer == endContainer) {
+ return callback(exclude([startContainer]));
+ }
+
+ // Find common ancestor and end points
+ ancestor = dom.findCommonAncestor(startContainer, endContainer);
+
+ // Process left side
+ for (node = startContainer; node; node = node.parentNode) {
+ if (node === endContainer) {
+ return walkBoundary(startContainer, ancestor, true);
+ }
+
+ if (node === ancestor) {
+ break;
+ }
+ }
+
+ // Process right side
+ for (node = endContainer; node; node = node.parentNode) {
+ if (node === startContainer) {
+ return walkBoundary(endContainer, ancestor);
+ }
+
+ if (node === ancestor) {
+ break;
+ }
+ }
+
+ // Find start/end point
+ startPoint = findEndPoint(startContainer, ancestor) || startContainer;
+ endPoint = findEndPoint(endContainer, ancestor) || endContainer;
+
+ // Walk left leaf
+ walkBoundary(startContainer, startPoint, true);
+
+ // Walk the middle from start to end point
+ siblings = collectSiblings(
+ startPoint == startContainer ? startPoint : startPoint.nextSibling,
+ 'nextSibling',
+ endPoint == endContainer ? endPoint.nextSibling : endPoint
+ );
+
+ if (siblings.length) {
+ callback(exclude(siblings));
+ }
+
+ // Walk right leaf
+ walkBoundary(endContainer, endPoint);
+ };
+
+ /**
+ * Splits the specified range at it's start/end points.
+ *
+ * @private
+ * @param {Range/RangeObject} rng Range to split.
+ * @return {Object} Range position object.
+ */
+ this.split = function(rng) {
+ var startContainer = rng.startContainer,
+ startOffset = rng.startOffset,
+ endContainer = rng.endContainer,
+ endOffset = rng.endOffset;
+
+ function splitText(node, offset) {
+ return node.splitText(offset);
+ }
+
+ // Handle single text node
+ if (startContainer == endContainer && startContainer.nodeType == 3) {
+ if (startOffset > 0 && startOffset < startContainer.nodeValue.length) {
+ endContainer = splitText(startContainer, startOffset);
+ startContainer = endContainer.previousSibling;
+
+ if (endOffset > startOffset) {
+ endOffset = endOffset - startOffset;
+ startContainer = endContainer = splitText(endContainer, endOffset).previousSibling;
+ endOffset = endContainer.nodeValue.length;
+ startOffset = 0;
+ } else {
+ endOffset = 0;
+ }
+ }
+ } else {
+ // Split startContainer text node if needed
+ if (startContainer.nodeType == 3 && startOffset > 0 && startOffset < startContainer.nodeValue.length) {
+ startContainer = splitText(startContainer, startOffset);
+ startOffset = 0;
+ }
+
+ // Split endContainer text node if needed
+ if (endContainer.nodeType == 3 && endOffset > 0 && endOffset < endContainer.nodeValue.length) {
+ endContainer = splitText(endContainer, endOffset).previousSibling;
+ endOffset = endContainer.nodeValue.length;
+ }
+ }
+
+ return {
+ startContainer: startContainer,
+ startOffset: startOffset,
+ endContainer: endContainer,
+ endOffset: endOffset
+ };
+ };
+
+ /**
+ * Normalizes the specified range by finding the closest best suitable caret location.
+ *
+ * @private
+ * @param {Range} rng Range to normalize.
+ * @return {Boolean} True/false if the specified range was normalized or not.
+ */
+ this.normalize = function(rng) {
+ var normalized, collapsed;
+
+ function normalizeEndPoint(start) {
+ var container, offset, walker, body = dom.getRoot(), node, nonEmptyElementsMap;
+ var directionLeft, isAfterNode;
+
+ function isTableCell(node) {
+ return node && /^(TD|TH|CAPTION)$/.test(node.nodeName);
+ }
+
+ function hasBrBeforeAfter(node, left) {
+ var walker = new TreeWalker(node, dom.getParent(node.parentNode, dom.isBlock) || body);
+
+ while ((node = walker[left ? 'prev' : 'next']())) {
+ if (node.nodeName === "BR") {
+ return true;
+ }
+ }
+ }
+
+ function hasContentEditableFalseParent(node) {
+ while (node && node != body) {
+ if (isContentEditableFalse(node)) {
+ return true;
+ }
+
+ node = node.parentNode;
+ }
+
+ return false;
+ }
+
+ function isPrevNode(node, name) {
+ return node.previousSibling && node.previousSibling.nodeName == name;
+ }
+
+ // Walks the dom left/right to find a suitable text node to move the endpoint into
+ // It will only walk within the current parent block or body and will stop if it hits a block or a BR/IMG
+ function findTextNodeRelative(left, startNode) {
+ var walker, lastInlineElement, parentBlockContainer;
+
+ startNode = startNode || container;
+ parentBlockContainer = dom.getParent(startNode.parentNode, dom.isBlock) || body;
+
+ // Lean left before the BR element if it's the only BR within a block element. Gecko bug: #6680
+ // This:
|
becomes
|
+ if (left && startNode.nodeName == 'BR' && isAfterNode && dom.isEmpty(parentBlockContainer)) {
+ container = startNode.parentNode;
+ offset = dom.nodeIndex(startNode);
+ normalized = true;
+ return;
+ }
+
+ // Walk left until we hit a text node we can move to or a block/br/img
+ walker = new TreeWalker(startNode, parentBlockContainer);
+ while ((node = walker[left ? 'prev' : 'next']())) {
+ // Break if we hit a non content editable node
+ if (dom.getContentEditableParent(node) === "false" || isCeFalseCaretContainer(node, dom.getRoot())) {
+ return;
+ }
+
+ // Found text node that has a length
+ if (node.nodeType === 3 && node.nodeValue.length > 0) {
+ container = node;
+ offset = left ? node.nodeValue.length : 0;
+ normalized = true;
+ return;
+ }
+
+ // Break if we find a block or a BR/IMG/INPUT etc
+ if (dom.isBlock(node) || nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
+ return;
+ }
+
+ lastInlineElement = node;
+ }
+
+ // Only fetch the last inline element when in caret mode for now
+ if (collapsed && lastInlineElement) {
+ container = lastInlineElement;
+ normalized = true;
+ offset = 0;
+ }
+ }
+
+ container = rng[(start ? 'start' : 'end') + 'Container'];
+ offset = rng[(start ? 'start' : 'end') + 'Offset'];
+ isAfterNode = container.nodeType == 1 && offset === container.childNodes.length;
+ nonEmptyElementsMap = dom.schema.getNonEmptyElements();
+ directionLeft = start;
+
+ if (isCaretContainer(container)) {
+ return;
+ }
+
+ if (container.nodeType == 1 && offset > container.childNodes.length - 1) {
+ directionLeft = false;
+ }
+
+ // If the container is a document move it to the body element
+ if (container.nodeType === 9) {
+ container = dom.getRoot();
+ offset = 0;
+ }
+
+ // If the container is body try move it into the closest text node or position
+ if (container === body) {
+ // If start is before/after a image, table etc
+ if (directionLeft) {
+ node = container.childNodes[offset > 0 ? offset - 1 : 0];
+ if (node) {
+ if (isCaretContainer(node)) {
+ return;
+ }
+
+ if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
+ return;
+ }
+ }
+ }
+
+ // Resolve the index
+ if (container.hasChildNodes()) {
+ offset = Math.min(!directionLeft && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1);
+ container = container.childNodes[offset];
+ offset = 0;
+
+ // Don't normalize non collapsed selections like
[a
]
+ if (!collapsed && container === body.lastChild && container.nodeName === 'TABLE') {
+ return;
+ }
+
+ if (hasContentEditableFalseParent(container) || isCaretContainer(container)) {
+ return;
+ }
+
+ // Don't walk into elements that doesn't have any child nodes like a IMG
+ if (container.hasChildNodes() && !/TABLE/.test(container.nodeName)) {
+ // Walk the DOM to find a text node to place the caret at or a BR
+ node = container;
+ walker = new TreeWalker(container, body);
+
+ do {
+ if (isContentEditableFalse(node) || isCaretContainer(node)) {
+ normalized = false;
+ break;
+ }
+
+ // Found a text node use that position
+ if (node.nodeType === 3 && node.nodeValue.length > 0) {
+ offset = directionLeft ? 0 : node.nodeValue.length;
+ container = node;
+ normalized = true;
+ break;
+ }
+
+ // Found a BR/IMG element that we can place the caret before
+ if (nonEmptyElementsMap[node.nodeName.toLowerCase()] && !isTableCell(node)) {
+ offset = dom.nodeIndex(node);
+ container = node.parentNode;
+
+ // Put caret after image when moving the end point
+ if (node.nodeName == "IMG" && !directionLeft) {
+ offset++;
+ }
+
+ normalized = true;
+ break;
+ }
+ } while ((node = (directionLeft ? walker.next() : walker.prev())));
+ }
+ }
+ }
+
+ // Lean the caret to the left if possible
+ if (collapsed) {
+ // So this: x|x
+ // Becomes: x|x
+ // Seems that only gecko has issues with this
+ if (container.nodeType === 3 && offset === 0) {
+ findTextNodeRelative(true);
+ }
+
+ // Lean left into empty inline elements when the caret is before a BR
+ // So this: |
+ // Becomes: |
+ // Seems that only gecko has issues with this.
+ // Special edge case for
+ if (container.nodeType === 1) {
+ node = container.childNodes[offset];
+
+ // Offset is after the containers last child
+ // then use the previous child for normalization
+ if (!node) {
+ node = container.childNodes[offset - 1];
+ }
+
+ if (node && node.nodeName === 'BR' && !isPrevNode(node, 'A') &&
+ !hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
+ findTextNodeRelative(true, node);
+ }
+ }
+ }
+
+ // Lean the start of the selection right if possible
+ // So this: x[x]
+ // Becomes: x[x]
+ if (directionLeft && !collapsed && container.nodeType === 3 && offset === container.nodeValue.length) {
+ findTextNodeRelative(false);
+ }
+
+ // Set endpoint if it was normalized
+ if (normalized) {
+ rng['set' + (start ? 'Start' : 'End')](container, offset);
+ }
+ }
+
+ collapsed = rng.collapsed;
+
+ normalizeEndPoint(true);
+
+ if (!collapsed) {
+ normalizeEndPoint();
+ }
+
+ // If it was collapsed then make sure it still is
+ if (normalized && collapsed) {
+ rng.collapse(true);
+ }
+
+ return normalized;
+ };
+ }
+
+ /**
+ * Compares two ranges and checks if they are equal.
+ *
+ * @static
+ * @method compareRanges
+ * @param {DOMRange} rng1 First range to compare.
+ * @param {DOMRange} rng2 First range to compare.
+ * @return {Boolean} true/false if the ranges are equal.
+ */
+ RangeUtils.compareRanges = function(rng1, rng2) {
+ if (rng1 && rng2) {
+ // Compare native IE ranges
+ if (rng1.item || rng1.duplicate) {
+ // Both are control ranges and the selected element matches
+ if (rng1.item && rng2.item && rng1.item(0) === rng2.item(0)) {
+ return true;
+ }
+
+ // Both are text ranges and the range matches
+ if (rng1.isEqual && rng2.isEqual && rng2.isEqual(rng1)) {
+ return true;
+ }
+ } else {
+ // Compare w3c ranges
+ return rng1.startContainer == rng2.startContainer && rng1.startOffset == rng2.startOffset;
+ }
+ }
+
+ return false;
+ };
+
+ /**
+ * Finds the closest selection rect tries to get the range from that.
+ */
+ function findClosestIeRange(clientX, clientY, doc) {
+ var element, rng, rects;
+
+ element = doc.elementFromPoint(clientX, clientY);
+ rng = doc.body.createTextRange();
+
+ if (!element || element.tagName == 'HTML') {
+ element = doc.body;
+ }
+
+ rng.moveToElementText(element);
+ rects = Tools.toArray(rng.getClientRects());
+
+ rects = rects.sort(function(a, b) {
+ a = Math.abs(Math.max(a.top - clientY, a.bottom - clientY));
+ b = Math.abs(Math.max(b.top - clientY, b.bottom - clientY));
+
+ return a - b;
+ });
+
+ if (rects.length > 0) {
+ clientY = (rects[0].bottom + rects[0].top) / 2;
+
+ try {
+ rng.moveToPoint(clientX, clientY);
+ rng.collapse(true);
+
+ return rng;
+ } catch (ex) {
+ // At least we tried
+ }
+ }
+
+ return null;
+ }
+
+ function moveOutOfContentEditableFalse(rng, rootNode) {
+ var parentElement = rng && rng.parentElement ? rng.parentElement() : null;
+ return isContentEditableFalse(findParent(parentElement, rootNode, hasCeProperty)) ? null : rng;
+ }
+
+ /**
+ * Gets the caret range for the given x/y location.
+ *
+ * @static
+ * @method getCaretRangeFromPoint
+ * @param {Number} clientX X coordinate for range
+ * @param {Number} clientY Y coordinate for range
+ * @param {Document} doc Document that x/y are relative to
+ * @returns {Range} caret range
+ */
+ RangeUtils.getCaretRangeFromPoint = function(clientX, clientY, doc) {
+ var rng, point;
+
+ if (doc.caretPositionFromPoint) {
+ point = doc.caretPositionFromPoint(clientX, clientY);
+ rng = doc.createRange();
+ rng.setStart(point.offsetNode, point.offset);
+ rng.collapse(true);
+ } else if (doc.caretRangeFromPoint) {
+ rng = doc.caretRangeFromPoint(clientX, clientY);
+ } else if (doc.body.createTextRange) {
+ rng = doc.body.createTextRange();
+
+ try {
+ rng.moveToPoint(clientX, clientY);
+ rng.collapse(true);
+ } catch (ex) {
+ rng = findClosestIeRange(clientX, clientY, doc);
+ }
+
+ return moveOutOfContentEditableFalse(rng, doc.body);
+ }
+
+ return rng;
+ };
+
+ RangeUtils.getSelectedNode = function(range) {
+ var startContainer = range.startContainer,
+ startOffset = range.startOffset;
+
+ if (startContainer.hasChildNodes() && range.endOffset == startOffset + 1) {
+ return startContainer.childNodes[startOffset];
+ }
+
+ return null;
+ };
+
+ RangeUtils.getNode = function(container, offset) {
+ if (container.nodeType == 1 && container.hasChildNodes()) {
+ if (offset >= container.childNodes.length) {
+ offset = container.childNodes.length - 1;
+ }
+
+ container = container.childNodes[offset];
+ }
+
+ return container;
+ };
+
+ return RangeUtils;
+});
+
+// Included from: js/tinymce/classes/NodeChange.js
+
+/**
+ * NodeChange.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 nodechange event dispatching both manual and through selection change events.
+ *
+ * @class tinymce.NodeChange
+ * @private
+ */
+define("tinymce/NodeChange", [
+ "tinymce/dom/RangeUtils",
+ "tinymce/Env",
+ "tinymce/util/Delay"
+], function(RangeUtils, Env, Delay) {
+ return function(editor) {
+ var lastRng, lastPath = [];
+
+ /**
+ * Returns true/false if the current element path has been changed or not.
+ *
+ * @private
+ * @return {Boolean} True if the element path is the same false if it's not.
+ */
+ function isSameElementPath(startElm) {
+ var i, currentPath;
+
+ currentPath = editor.$(startElm).parentsUntil(editor.getBody()).add(startElm);
+ if (currentPath.length === lastPath.length) {
+ for (i = currentPath.length; i >= 0; i--) {
+ if (currentPath[i] !== lastPath[i]) {
+ break;
+ }
+ }
+
+ if (i === -1) {
+ lastPath = currentPath;
+ return true;
+ }
+ }
+
+ lastPath = currentPath;
+
+ return false;
+ }
+
+ // Gecko doesn't support the "selectionchange" event
+ if (!('onselectionchange' in editor.getDoc())) {
+ editor.on('NodeChange Click MouseUp KeyUp Focus', function(e) {
+ var nativeRng, fakeRng;
+
+ // Since DOM Ranges mutate on modification
+ // of the DOM we need to clone it's contents
+ nativeRng = editor.selection.getRng();
+ fakeRng = {
+ startContainer: nativeRng.startContainer,
+ startOffset: nativeRng.startOffset,
+ endContainer: nativeRng.endContainer,
+ endOffset: nativeRng.endOffset
+ };
+
+ // Always treat nodechange as a selectionchange since applying
+ // formatting to the current range wouldn't update the range but it's parent
+ if (e.type == 'nodechange' || !RangeUtils.compareRanges(fakeRng, lastRng)) {
+ editor.fire('SelectionChange');
+ }
+
+ lastRng = fakeRng;
+ });
+ }
+
+ // IE has a bug where it fires a selectionchange on right click that has a range at the start of the body
+ // When the contextmenu event fires the selection is located at the right location
+ editor.on('contextmenu', function() {
+ editor.fire('SelectionChange');
+ });
+
+ // Selection change is delayed ~200ms on IE when you click inside the current range
+ editor.on('SelectionChange', function() {
+ var startElm = editor.selection.getStart(true);
+
+ // IE 8 will fire a selectionchange event with an incorrect selection
+ // when focusing out of table cells. Click inside cell -> toolbar = Invalid SelectionChange event
+ if (!Env.range && editor.selection.isCollapsed()) {
+ return;
+ }
+
+ if (!isSameElementPath(startElm) && editor.dom.isChildOf(startElm, editor.getBody())) {
+ editor.nodeChanged({selectionChange: true});
+ }
+ });
+
+ // Fire an extra nodeChange on mouseup for compatibility reasons
+ editor.on('MouseUp', function(e) {
+ if (!e.isDefaultPrevented()) {
+ // Delay nodeChanged call for WebKit edge case issue where the range
+ // isn't updated until after you click outside a selected image
+ if (editor.selection.getNode().nodeName == 'IMG') {
+ Delay.setEditorTimeout(editor, function() {
+ editor.nodeChanged();
+ });
+ } else {
+ editor.nodeChanged();
+ }
+ }
+ });
+
+ /**
+ * Dispatches out a onNodeChange event to all observers. This method should be called when you
+ * need to update the UI states or element path etc.
+ *
+ * @method nodeChanged
+ * @param {Object} args Optional args to pass to NodeChange event handlers.
+ */
+ this.nodeChanged = function(args) {
+ var selection = editor.selection, node, parents, root;
+
+ // Fix for bug #1896577 it seems that this can not be fired while the editor is loading
+ if (editor.initialized && selection && !editor.settings.disable_nodechange && !editor.readonly) {
+ // Get start node
+ root = editor.getBody();
+ node = selection.getStart() || root;
+
+ // Make sure the node is within the editor root or is the editor root
+ if (node.ownerDocument != editor.getDoc() || !editor.dom.isChildOf(node, root)) {
+ node = root;
+ }
+
+ // Edge case for
|
+ if (node.nodeName == 'IMG' && selection.isCollapsed()) {
+ node = node.parentNode;
+ }
+
+ // Get parents and add them to object
+ parents = [];
+ editor.dom.getParent(node, function(node) {
+ if (node === root) {
+ return true;
+ }
+
+ parents.push(node);
+ });
+
+ args = args || {};
+ args.element = node;
+ args.parents = parents;
+
+ editor.fire('NodeChange', args);
+ }
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/html/Node.js
+
+/**
+ * Node.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 minimalistic implementation of a DOM like node used by the DomParser class.
+ *
+ * @example
+ * var node = new tinymce.html.Node('strong', 1);
+ * someRoot.append(node);
+ *
+ * @class tinymce.html.Node
+ * @version 3.4
+ */
+define("tinymce/html/Node", [], function() {
+ var whiteSpaceRegExp = /^[ \t\r\n]*$/, typeLookup = {
+ '#text': 3,
+ '#comment': 8,
+ '#cdata': 4,
+ '#pi': 7,
+ '#doctype': 10,
+ '#document-fragment': 11
+ };
+
+ // Walks the tree left/right
+ function walk(node, root_node, prev) {
+ var sibling, parent, startName = prev ? 'lastChild' : 'firstChild', siblingName = prev ? 'prev' : 'next';
+
+ // Walk into nodes if it has a start
+ if (node[startName]) {
+ return node[startName];
+ }
+
+ // Return the sibling if it has one
+ if (node !== root_node) {
+ sibling = node[siblingName];
+
+ if (sibling) {
+ return sibling;
+ }
+
+ // Walk up the parents to look for siblings
+ for (parent = node.parent; parent && parent !== root_node; parent = parent.parent) {
+ sibling = parent[siblingName];
+
+ if (sibling) {
+ return sibling;
+ }
+ }
+ }
+ }
+
+ /**
+ * Constructs a new Node instance.
+ *
+ * @constructor
+ * @method Node
+ * @param {String} name Name of the node type.
+ * @param {Number} type Numeric type representing the node.
+ */
+ function Node(name, type) {
+ this.name = name;
+ this.type = type;
+
+ if (type === 1) {
+ this.attributes = [];
+ this.attributes.map = {};
+ }
+ }
+
+ Node.prototype = {
+ /**
+ * Replaces the current node with the specified one.
+ *
+ * @example
+ * someNode.replace(someNewNode);
+ *
+ * @method replace
+ * @param {tinymce.html.Node} node Node to replace the current node with.
+ * @return {tinymce.html.Node} The old node that got replaced.
+ */
+ replace: function(node) {
+ var self = this;
+
+ if (node.parent) {
+ node.remove();
+ }
+
+ self.insert(node, self);
+ self.remove();
+
+ return self;
+ },
+
+ /**
+ * Gets/sets or removes an attribute by name.
+ *
+ * @example
+ * someNode.attr("name", "value"); // Sets an attribute
+ * console.log(someNode.attr("name")); // Gets an attribute
+ * someNode.attr("name", null); // Removes an attribute
+ *
+ * @method attr
+ * @param {String} name Attribute name to set or get.
+ * @param {String} value Optional value to set.
+ * @return {String/tinymce.html.Node} String or undefined on a get operation or the current node on a set operation.
+ */
+ attr: function(name, value) {
+ var self = this, attrs, i, undef;
+
+ if (typeof name !== "string") {
+ for (i in name) {
+ self.attr(i, name[i]);
+ }
+
+ return self;
+ }
+
+ if ((attrs = self.attributes)) {
+ if (value !== undef) {
+ // Remove attribute
+ if (value === null) {
+ if (name in attrs.map) {
+ delete attrs.map[name];
+
+ i = attrs.length;
+ while (i--) {
+ if (attrs[i].name === name) {
+ attrs = attrs.splice(i, 1);
+ return self;
+ }
+ }
+ }
+
+ return self;
+ }
+
+ // Set attribute
+ if (name in attrs.map) {
+ // Set attribute
+ i = attrs.length;
+ while (i--) {
+ if (attrs[i].name === name) {
+ attrs[i].value = value;
+ break;
+ }
+ }
+ } else {
+ attrs.push({name: name, value: value});
+ }
+
+ attrs.map[name] = value;
+
+ return self;
+ }
+
+ return attrs.map[name];
+ }
+ },
+
+ /**
+ * Does a shallow clones the node into a new node. It will also exclude id attributes since
+ * there should only be one id per document.
+ *
+ * @example
+ * var clonedNode = node.clone();
+ *
+ * @method clone
+ * @return {tinymce.html.Node} New copy of the original node.
+ */
+ clone: function() {
+ var self = this, clone = new Node(self.name, self.type), i, l, selfAttrs, selfAttr, cloneAttrs;
+
+ // Clone element attributes
+ if ((selfAttrs = self.attributes)) {
+ cloneAttrs = [];
+ cloneAttrs.map = {};
+
+ for (i = 0, l = selfAttrs.length; i < l; i++) {
+ selfAttr = selfAttrs[i];
+
+ // Clone everything except id
+ if (selfAttr.name !== 'id') {
+ cloneAttrs[cloneAttrs.length] = {name: selfAttr.name, value: selfAttr.value};
+ cloneAttrs.map[selfAttr.name] = selfAttr.value;
+ }
+ }
+
+ clone.attributes = cloneAttrs;
+ }
+
+ clone.value = self.value;
+ clone.shortEnded = self.shortEnded;
+
+ return clone;
+ },
+
+ /**
+ * Wraps the node in in another node.
+ *
+ * @example
+ * node.wrap(wrapperNode);
+ *
+ * @method wrap
+ */
+ wrap: function(wrapper) {
+ var self = this;
+
+ self.parent.insert(wrapper, self);
+ wrapper.append(self);
+
+ return self;
+ },
+
+ /**
+ * Unwraps the node in other words it removes the node but keeps the children.
+ *
+ * @example
+ * node.unwrap();
+ *
+ * @method unwrap
+ */
+ unwrap: function() {
+ var self = this, node, next;
+
+ for (node = self.firstChild; node;) {
+ next = node.next;
+ self.insert(node, self, true);
+ node = next;
+ }
+
+ self.remove();
+ },
+
+ /**
+ * Removes the node from it's parent.
+ *
+ * @example
+ * node.remove();
+ *
+ * @method remove
+ * @return {tinymce.html.Node} Current node that got removed.
+ */
+ remove: function() {
+ var self = this, parent = self.parent, next = self.next, prev = self.prev;
+
+ if (parent) {
+ if (parent.firstChild === self) {
+ parent.firstChild = next;
+
+ if (next) {
+ next.prev = null;
+ }
+ } else {
+ prev.next = next;
+ }
+
+ if (parent.lastChild === self) {
+ parent.lastChild = prev;
+
+ if (prev) {
+ prev.next = null;
+ }
+ } else {
+ next.prev = prev;
+ }
+
+ self.parent = self.next = self.prev = null;
+ }
+
+ return self;
+ },
+
+ /**
+ * Appends a new node as a child of the current node.
+ *
+ * @example
+ * node.append(someNode);
+ *
+ * @method append
+ * @param {tinymce.html.Node} node Node to append as a child of the current one.
+ * @return {tinymce.html.Node} The node that got appended.
+ */
+ append: function(node) {
+ var self = this, last;
+
+ if (node.parent) {
+ node.remove();
+ }
+
+ last = self.lastChild;
+ if (last) {
+ last.next = node;
+ node.prev = last;
+ self.lastChild = node;
+ } else {
+ self.lastChild = self.firstChild = node;
+ }
+
+ node.parent = self;
+
+ return node;
+ },
+
+ /**
+ * Inserts a node at a specific position as a child of the current node.
+ *
+ * @example
+ * parentNode.insert(newChildNode, oldChildNode);
+ *
+ * @method insert
+ * @param {tinymce.html.Node} node Node to insert as a child of the current node.
+ * @param {tinymce.html.Node} ref_node Reference node to set node before/after.
+ * @param {Boolean} before Optional state to insert the node before the reference node.
+ * @return {tinymce.html.Node} The node that got inserted.
+ */
+ insert: function(node, ref_node, before) {
+ var parent;
+
+ if (node.parent) {
+ node.remove();
+ }
+
+ parent = ref_node.parent || this;
+
+ if (before) {
+ if (ref_node === parent.firstChild) {
+ parent.firstChild = node;
+ } else {
+ ref_node.prev.next = node;
+ }
+
+ node.prev = ref_node.prev;
+ node.next = ref_node;
+ ref_node.prev = node;
+ } else {
+ if (ref_node === parent.lastChild) {
+ parent.lastChild = node;
+ } else {
+ ref_node.next.prev = node;
+ }
+
+ node.next = ref_node.next;
+ node.prev = ref_node;
+ ref_node.next = node;
+ }
+
+ node.parent = parent;
+
+ return node;
+ },
+
+ /**
+ * Get all children by name.
+ *
+ * @method getAll
+ * @param {String} name Name of the child nodes to collect.
+ * @return {Array} Array with child nodes matchin the specified name.
+ */
+ getAll: function(name) {
+ var self = this, node, collection = [];
+
+ for (node = self.firstChild; node; node = walk(node, self)) {
+ if (node.name === name) {
+ collection.push(node);
+ }
+ }
+
+ return collection;
+ },
+
+ /**
+ * Removes all children of the current node.
+ *
+ * @method empty
+ * @return {tinymce.html.Node} The current node that got cleared.
+ */
+ empty: function() {
+ var self = this, nodes, i, node;
+
+ // Remove all children
+ if (self.firstChild) {
+ nodes = [];
+
+ // Collect the children
+ for (node = self.firstChild; node; node = walk(node, self)) {
+ nodes.push(node);
+ }
+
+ // Remove the children
+ i = nodes.length;
+ while (i--) {
+ node = nodes[i];
+ node.parent = node.firstChild = node.lastChild = node.next = node.prev = null;
+ }
+ }
+
+ self.firstChild = self.lastChild = null;
+
+ return self;
+ },
+
+ /**
+ * Returns true/false if the node is to be considered empty or not.
+ *
+ * @example
+ * node.isEmpty({img: true});
+ * @method isEmpty
+ * @param {Object} elements Name/value object with elements that are automatically treated as non empty elements.
+ * @param {Object} whitespace Name/value object with elements that are automatically treated whitespace preservables.
+ * @return {Boolean} true/false if the node is empty or not.
+ */
+ isEmpty: function(elements, whitespace) {
+ var self = this, node = self.firstChild, i, name;
+
+ whitespace = whitespace || {};
+
+ if (node) {
+ do {
+ if (node.type === 1) {
+ // Ignore bogus elements
+ if (node.attributes.map['data-mce-bogus']) {
+ continue;
+ }
+
+ // Keep empty elements like
+ if (elements[node.name]) {
+ return false;
+ }
+
+ // Keep bookmark nodes and name attribute like
+ i = node.attributes.length;
+ while (i--) {
+ name = node.attributes[i].name;
+ if (name === "name" || name.indexOf('data-mce-bookmark') === 0) {
+ return false;
+ }
+ }
+ }
+
+ // Keep comments
+ if (node.type === 8) {
+ return false;
+ }
+
+ // Keep non whitespace text nodes
+ if (node.type === 3 && !whiteSpaceRegExp.test(node.value)) {
+ return false;
+ }
+
+ // Keep whitespace preserve elements
+ if (node.type === 3 && node.parent && whitespace[node.parent.name] && whiteSpaceRegExp.test(node.value)) {
+ return false;
+ }
+ } while ((node = walk(node, self)));
+ }
+
+ return true;
+ },
+
+ /**
+ * Walks to the next or previous node and returns that node or null if it wasn't found.
+ *
+ * @method walk
+ * @param {Boolean} prev Optional previous node state defaults to false.
+ * @return {tinymce.html.Node} Node that is next to or previous of the current node.
+ */
+ walk: function(prev) {
+ return walk(this, null, prev);
+ }
+ };
+
+ /**
+ * Creates a node of a specific type.
+ *
+ * @static
+ * @method create
+ * @param {String} name Name of the node type to create for example "b" or "#text".
+ * @param {Object} attrs Name/value collection of attributes that will be applied to elements.
+ */
+ Node.create = function(name, attrs) {
+ var node, attrName;
+
+ // Create node
+ node = new Node(name, typeLookup[name] || 1);
+
+ // Add attributes if needed
+ if (attrs) {
+ for (attrName in attrs) {
+ node.attr(attrName, attrs[attrName]);
+ }
+ }
+
+ return node;
+ };
+
+ return Node;
+});
+
+// Included from: js/tinymce/classes/html/Schema.js
+
+/**
+ * Schema.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
+ */
+
+/**
+ * Schema validator class.
+ *
+ * @class tinymce.html.Schema
+ * @example
+ * if (tinymce.activeEditor.schema.isValidChild('p', 'span'))
+ * alert('span is valid child of p.');
+ *
+ * if (tinymce.activeEditor.schema.getElementRule('p'))
+ * alert('P is a valid element.');
+ *
+ * @class tinymce.html.Schema
+ * @version 3.4
+ */
+define("tinymce/html/Schema", [
+ "tinymce/util/Tools"
+], function(Tools) {
+ var mapCache = {}, dummyObj = {};
+ var makeMap = Tools.makeMap, each = Tools.each, extend = Tools.extend, explode = Tools.explode, inArray = Tools.inArray;
+
+ function split(items, delim) {
+ items = Tools.trim(items);
+ return items ? items.split(delim || ' ') : [];
+ }
+
+ /**
+ * Builds a schema lookup table
+ *
+ * @private
+ * @param {String} type html4, html5 or html5-strict schema type.
+ * @return {Object} Schema lookup table.
+ */
+ function compileSchema(type) {
+ var schema = {}, globalAttributes, blockContent;
+ var phrasingContent, flowContent, html4BlockContent, html4PhrasingContent;
+
+ function add(name, attributes, children) {
+ var ni, attributesOrder, element;
+
+ function arrayToMap(array, obj) {
+ var map = {}, i, l;
+
+ for (i = 0, l = array.length; i < l; i++) {
+ map[array[i]] = obj || {};
+ }
+
+ return map;
+ }
+
+ children = children || [];
+ attributes = attributes || "";
+
+ if (typeof children === "string") {
+ children = split(children);
+ }
+
+ name = split(name);
+ ni = name.length;
+ while (ni--) {
+ attributesOrder = split([globalAttributes, attributes].join(' '));
+
+ element = {
+ attributes: arrayToMap(attributesOrder),
+ attributesOrder: attributesOrder,
+ children: arrayToMap(children, dummyObj)
+ };
+
+ schema[name[ni]] = element;
+ }
+ }
+
+ function addAttrs(name, attributes) {
+ var ni, schemaItem, i, l;
+
+ name = split(name);
+ ni = name.length;
+ attributes = split(attributes);
+ while (ni--) {
+ schemaItem = schema[name[ni]];
+ for (i = 0, l = attributes.length; i < l; i++) {
+ schemaItem.attributes[attributes[i]] = {};
+ schemaItem.attributesOrder.push(attributes[i]);
+ }
+ }
+ }
+
+ // Use cached schema
+ if (mapCache[type]) {
+ return mapCache[type];
+ }
+
+ // Attributes present on all elements
+ globalAttributes = "id accesskey class dir lang style tabindex title";
+
+ // Event attributes can be opt-in/opt-out
+ /*eventAttributes = split("onabort onblur oncancel oncanplay oncanplaythrough onchange onclick onclose oncontextmenu oncuechange " +
+ "ondblclick ondrag ondragend ondragenter ondragleave ondragover ondragstart ondrop ondurationchange onemptied onended " +
+ "onerror onfocus oninput oninvalid onkeydown onkeypress onkeyup onload onloadeddata onloadedmetadata onloadstart " +
+ "onmousedown onmousemove onmouseout onmouseover onmouseup onmousewheel onpause onplay onplaying onprogress onratechange " +
+ "onreset onscroll onseeked onseeking onseeking onselect onshow onstalled onsubmit onsuspend ontimeupdate onvolumechange " +
+ "onwaiting"
+ );*/
+
+ // Block content elements
+ blockContent =
+ "address blockquote div dl fieldset form h1 h2 h3 h4 h5 h6 hr menu ol p pre table ul";
+
+ // Phrasing content elements from the HTML5 spec (inline)
+ phrasingContent =
+ "a abbr b bdo br button cite code del dfn em embed i iframe img input ins kbd " +
+ "label map noscript object q s samp script select small span strong sub sup " +
+ "textarea u var #text #comment"
+ ;
+
+ // Add HTML5 items to globalAttributes, blockContent, phrasingContent
+ if (type != "html4") {
+ globalAttributes += " contenteditable contextmenu draggable dropzone " +
+ "hidden spellcheck translate";
+ blockContent += " article aside details dialog figure header footer hgroup section nav";
+ phrasingContent += " audio canvas command datalist mark meter output picture " +
+ "progress time wbr video ruby bdi keygen";
+ }
+
+ // Add HTML4 elements unless it's html5-strict
+ if (type != "html5-strict") {
+ globalAttributes += " xml:lang";
+
+ html4PhrasingContent = "acronym applet basefont big font strike tt";
+ phrasingContent = [phrasingContent, html4PhrasingContent].join(' ');
+
+ each(split(html4PhrasingContent), function(name) {
+ add(name, "", phrasingContent);
+ });
+
+ html4BlockContent = "center dir isindex noframes";
+ blockContent = [blockContent, html4BlockContent].join(' ');
+
+ // Flow content elements from the HTML5 spec (block+inline)
+ flowContent = [blockContent, phrasingContent].join(' ');
+
+ each(split(html4BlockContent), function(name) {
+ add(name, "", flowContent);
+ });
+ }
+
+ // Flow content elements from the HTML5 spec (block+inline)
+ flowContent = flowContent || [blockContent, phrasingContent].join(" ");
+
+ // HTML4 base schema TODO: Move HTML5 specific attributes to HTML5 specific if statement
+ // Schema items , ,
+ add("html", "manifest", "head body");
+ add("head", "", "base command link meta noscript script style title");
+ add("title hr noscript br");
+ add("base", "href target");
+ add("link", "href rel media hreflang type sizes hreflang");
+ add("meta", "name http-equiv content charset");
+ add("style", "media type scoped");
+ add("script", "src async defer type charset");
+ add("body", "onafterprint onbeforeprint onbeforeunload onblur onerror onfocus " +
+ "onhashchange onload onmessage onoffline ononline onpagehide onpageshow " +
+ "onpopstate onresize onscroll onstorage onunload", flowContent);
+ add("address dt dd div caption", "", flowContent);
+ add("h1 h2 h3 h4 h5 h6 pre p abbr code var samp kbd sub sup i b u bdo span legend em strong small s cite dfn", "", phrasingContent);
+ add("blockquote", "cite", flowContent);
+ add("ol", "reversed start type", "li");
+ add("ul", "", "li");
+ add("li", "value", flowContent);
+ add("dl", "", "dt dd");
+ add("a", "href target rel media hreflang type", phrasingContent);
+ add("q", "cite", phrasingContent);
+ add("ins del", "cite datetime", flowContent);
+ add("img", "src sizes srcset alt usemap ismap width height");
+ add("iframe", "src name width height", flowContent);
+ add("embed", "src type width height");
+ add("object", "data type typemustmatch name usemap form width height", [flowContent, "param"].join(' '));
+ add("param", "name value");
+ add("map", "name", [flowContent, "area"].join(' '));
+ add("area", "alt coords shape href target rel media hreflang type");
+ add("table", "border", "caption colgroup thead tfoot tbody tr" + (type == "html4" ? " col" : ""));
+ add("colgroup", "span", "col");
+ add("col", "span");
+ add("tbody thead tfoot", "", "tr");
+ add("tr", "", "td th");
+ add("td", "colspan rowspan headers", flowContent);
+ add("th", "colspan rowspan headers scope abbr", flowContent);
+ add("form", "accept-charset action autocomplete enctype method name novalidate target", flowContent);
+ add("fieldset", "disabled form name", [flowContent, "legend"].join(' '));
+ add("label", "form for", phrasingContent);
+ add("input", "accept alt autocomplete checked dirname disabled form formaction formenctype formmethod formnovalidate " +
+ "formtarget height list max maxlength min multiple name pattern readonly required size src step type value width"
+ );
+ add("button", "disabled form formaction formenctype formmethod formnovalidate formtarget name type value",
+ type == "html4" ? flowContent : phrasingContent);
+ add("select", "disabled form multiple name required size", "option optgroup");
+ add("optgroup", "disabled label", "option");
+ add("option", "disabled label selected value");
+ add("textarea", "cols dirname disabled form maxlength name readonly required rows wrap");
+ add("menu", "type label", [flowContent, "li"].join(' '));
+ add("noscript", "", flowContent);
+
+ // Extend with HTML5 elements
+ if (type != "html4") {
+ add("wbr");
+ add("ruby", "", [phrasingContent, "rt rp"].join(' '));
+ add("figcaption", "", flowContent);
+ add("mark rt rp summary bdi", "", phrasingContent);
+ add("canvas", "width height", flowContent);
+ add("video", "src crossorigin poster preload autoplay mediagroup loop " +
+ "muted controls width height buffered", [flowContent, "track source"].join(' '));
+ add("audio", "src crossorigin preload autoplay mediagroup loop muted controls " +
+ "buffered volume", [flowContent, "track source"].join(' '));
+ add("picture", "", "img source");
+ add("source", "src srcset type media sizes");
+ add("track", "kind src srclang label default");
+ add("datalist", "", [phrasingContent, "option"].join(' '));
+ add("article section nav aside header footer", "", flowContent);
+ add("hgroup", "", "h1 h2 h3 h4 h5 h6");
+ add("figure", "", [flowContent, "figcaption"].join(' '));
+ add("time", "datetime", phrasingContent);
+ add("dialog", "open", flowContent);
+ add("command", "type label icon disabled checked radiogroup command");
+ add("output", "for form name", phrasingContent);
+ add("progress", "value max", phrasingContent);
+ add("meter", "value min max low high optimum", phrasingContent);
+ add("details", "open", [flowContent, "summary"].join(' '));
+ add("keygen", "autofocus challenge disabled form keytype name");
+ }
+
+ // Extend with HTML4 attributes unless it's html5-strict
+ if (type != "html5-strict") {
+ addAttrs("script", "language xml:space");
+ addAttrs("style", "xml:space");
+ addAttrs("object", "declare classid code codebase codetype archive standby align border hspace vspace");
+ addAttrs("embed", "align name hspace vspace");
+ addAttrs("param", "valuetype type");
+ addAttrs("a", "charset name rev shape coords");
+ addAttrs("br", "clear");
+ addAttrs("applet", "codebase archive code object alt name width height align hspace vspace");
+ addAttrs("img", "name longdesc align border hspace vspace");
+ addAttrs("iframe", "longdesc frameborder marginwidth marginheight scrolling align");
+ addAttrs("font basefont", "size color face");
+ addAttrs("input", "usemap align");
+ addAttrs("select", "onchange");
+ addAttrs("textarea");
+ addAttrs("h1 h2 h3 h4 h5 h6 div p legend caption", "align");
+ addAttrs("ul", "type compact");
+ addAttrs("li", "type");
+ addAttrs("ol dl menu dir", "compact");
+ addAttrs("pre", "width xml:space");
+ addAttrs("hr", "align noshade size width");
+ addAttrs("isindex", "prompt");
+ addAttrs("table", "summary width frame rules cellspacing cellpadding align bgcolor");
+ addAttrs("col", "width align char charoff valign");
+ addAttrs("colgroup", "width align char charoff valign");
+ addAttrs("thead", "align char charoff valign");
+ addAttrs("tr", "align char charoff valign bgcolor");
+ addAttrs("th", "axis align char charoff valign nowrap bgcolor width height");
+ addAttrs("form", "accept");
+ addAttrs("td", "abbr axis scope align char charoff valign nowrap bgcolor width height");
+ addAttrs("tfoot", "align char charoff valign");
+ addAttrs("tbody", "align char charoff valign");
+ addAttrs("area", "nohref");
+ addAttrs("body", "background bgcolor text link vlink alink");
+ }
+
+ // Extend with HTML5 attributes unless it's html4
+ if (type != "html4") {
+ addAttrs("input button select textarea", "autofocus");
+ addAttrs("input textarea", "placeholder");
+ addAttrs("a", "download");
+ addAttrs("link script img", "crossorigin");
+ addAttrs("iframe", "sandbox seamless allowfullscreen"); // Excluded: srcdoc
+ }
+
+ // Special: iframe, ruby, video, audio, label
+
+ // Delete children of the same name from it's parent
+ // For example: form can't have a child of the name form
+ each(split('a form meter progress dfn'), function(name) {
+ if (schema[name]) {
+ delete schema[name].children[name];
+ }
+ });
+
+ // Delete header, footer, sectioning and heading content descendants
+ /*each('dt th address', function(name) {
+ delete schema[name].children[name];
+ });*/
+
+ // Caption can't have tables
+ delete schema.caption.children.table;
+
+ // Delete scripts by default due to possible XSS
+ delete schema.script;
+
+ // TODO: LI:s can only have value if parent is OL
+
+ // TODO: Handle transparent elements
+ // a ins del canvas map
+
+ mapCache[type] = schema;
+
+ return schema;
+ }
+
+ function compileElementMap(value, mode) {
+ var styles;
+
+ if (value) {
+ styles = {};
+
+ if (typeof value == 'string') {
+ value = {
+ '*': value
+ };
+ }
+
+ // Convert styles into a rule list
+ each(value, function(value, key) {
+ styles[key] = styles[key.toUpperCase()] = mode == 'map' ? makeMap(value, /[, ]/) : explode(value, /[, ]/);
+ });
+ }
+
+ return styles;
+ }
+
+ /**
+ * Constructs a new Schema instance.
+ *
+ * @constructor
+ * @method Schema
+ * @param {Object} settings Name/value settings object.
+ */
+ return function(settings) {
+ var self = this, elements = {}, children = {}, patternElements = [], validStyles, invalidStyles, schemaItems;
+ var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, validClasses;
+ var blockElementsMap, nonEmptyElementsMap, moveCaretBeforeOnEnterElementsMap, textBlockElementsMap, textInlineElementsMap;
+ var customElementsMap = {}, specialElements = {};
+
+ // Creates an lookup table map object for the specified option or the default value
+ function createLookupTable(option, default_value, extendWith) {
+ var value = settings[option];
+
+ if (!value) {
+ // Get cached default map or make it if needed
+ value = mapCache[option];
+
+ if (!value) {
+ value = makeMap(default_value, ' ', makeMap(default_value.toUpperCase(), ' '));
+ value = extend(value, extendWith);
+
+ mapCache[option] = value;
+ }
+ } else {
+ // Create custom map
+ value = makeMap(value, /[, ]/, makeMap(value.toUpperCase(), /[, ]/));
+ }
+
+ return value;
+ }
+
+ settings = settings || {};
+ schemaItems = compileSchema(settings.schema);
+
+ // Allow all elements and attributes if verify_html is set to false
+ if (settings.verify_html === false) {
+ settings.valid_elements = '*[*]';
+ }
+
+ validStyles = compileElementMap(settings.valid_styles);
+ invalidStyles = compileElementMap(settings.invalid_styles, 'map');
+ validClasses = compileElementMap(settings.valid_classes, 'map');
+
+ // Setup map objects
+ whiteSpaceElementsMap = createLookupTable(
+ 'whitespace_elements',
+ 'pre script noscript style textarea video audio iframe object code'
+ );
+ selfClosingElementsMap = createLookupTable('self_closing_elements', 'colgroup dd dt li option p td tfoot th thead tr');
+ shortEndedElementsMap = createLookupTable('short_ended_elements', 'area base basefont br col frame hr img input isindex link ' +
+ 'meta param embed source wbr track');
+ boolAttrMap = createLookupTable('boolean_attributes', 'checked compact declare defer disabled ismap multiple nohref noresize ' +
+ 'noshade nowrap readonly selected autoplay loop controls');
+ nonEmptyElementsMap = createLookupTable('non_empty_elements', 'td th iframe video audio object ' +
+ 'script pre code', shortEndedElementsMap);
+ moveCaretBeforeOnEnterElementsMap = createLookupTable('move_caret_before_on_enter_elements', 'table', nonEmptyElementsMap);
+ textBlockElementsMap = createLookupTable('text_block_elements', 'h1 h2 h3 h4 h5 h6 p div address pre form ' +
+ 'blockquote center dir fieldset header footer article section hgroup aside nav figure');
+ blockElementsMap = createLookupTable('block_elements', 'hr table tbody thead tfoot ' +
+ 'th tr td li ol ul caption dl dt dd noscript menu isindex option ' +
+ 'datalist select optgroup figcaption', textBlockElementsMap);
+ textInlineElementsMap = createLookupTable('text_inline_elements', 'span strong b em i font strike u var cite ' +
+ 'dfn code mark q sup sub samp');
+
+ each((settings.special || 'script noscript style textarea').split(' '), function(name) {
+ specialElements[name] = new RegExp('<\/' + name + '[^>]*>', 'gi');
+ });
+
+ // Converts a wildcard expression string to a regexp for example *a will become /.*a/.
+ function patternToRegExp(str) {
+ return new RegExp('^' + str.replace(/([?+*])/g, '.$1') + '$');
+ }
+
+ // Parses the specified valid_elements string and adds to the current rules
+ // This function is a bit hard to read since it's heavily optimized for speed
+ function addValidElements(validElements) {
+ var ei, el, ai, al, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
+ prefix, outputName, globalAttributes, globalAttributesOrder, key, value,
+ elementRuleRegExp = /^([#+\-])?([^\[!\/]+)(?:\/([^\[!]+))?(?:(!?)\[([^\]]+)\])?$/,
+ attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
+ hasPatternsRegExp = /[*?+]/;
+
+ if (validElements) {
+ // Split valid elements into an array with rules
+ validElements = split(validElements, ',');
+
+ if (elements['@']) {
+ globalAttributes = elements['@'].attributes;
+ globalAttributesOrder = elements['@'].attributesOrder;
+ }
+
+ // Loop all rules
+ for (ei = 0, el = validElements.length; ei < el; ei++) {
+ // Parse element rule
+ matches = elementRuleRegExp.exec(validElements[ei]);
+ if (matches) {
+ // Setup local names for matches
+ prefix = matches[1];
+ elementName = matches[2];
+ outputName = matches[3];
+ attrData = matches[5];
+
+ // Create new attributes and attributesOrder
+ attributes = {};
+ attributesOrder = [];
+
+ // Create the new element
+ element = {
+ attributes: attributes,
+ attributesOrder: attributesOrder
+ };
+
+ // Padd empty elements prefix
+ if (prefix === '#') {
+ element.paddEmpty = true;
+ }
+
+ // Remove empty elements prefix
+ if (prefix === '-') {
+ element.removeEmpty = true;
+ }
+
+ if (matches[4] === '!') {
+ element.removeEmptyAttrs = true;
+ }
+
+ // Copy attributes from global rule into current rule
+ if (globalAttributes) {
+ for (key in globalAttributes) {
+ attributes[key] = globalAttributes[key];
+ }
+
+ attributesOrder.push.apply(attributesOrder, globalAttributesOrder);
+ }
+
+ // Attributes defined
+ if (attrData) {
+ attrData = split(attrData, '|');
+ for (ai = 0, al = attrData.length; ai < al; ai++) {
+ matches = attrRuleRegExp.exec(attrData[ai]);
+ if (matches) {
+ attr = {};
+ attrType = matches[1];
+ attrName = matches[2].replace(/::/g, ':');
+ prefix = matches[3];
+ value = matches[4];
+
+ // Required
+ if (attrType === '!') {
+ element.attributesRequired = element.attributesRequired || [];
+ element.attributesRequired.push(attrName);
+ attr.required = true;
+ }
+
+ // Denied from global
+ if (attrType === '-') {
+ delete attributes[attrName];
+ attributesOrder.splice(inArray(attributesOrder, attrName), 1);
+ continue;
+ }
+
+ // Default value
+ if (prefix) {
+ // Default value
+ if (prefix === '=') {
+ element.attributesDefault = element.attributesDefault || [];
+ element.attributesDefault.push({name: attrName, value: value});
+ attr.defaultValue = value;
+ }
+
+ // Forced value
+ if (prefix === ':') {
+ element.attributesForced = element.attributesForced || [];
+ element.attributesForced.push({name: attrName, value: value});
+ attr.forcedValue = value;
+ }
+
+ // Required values
+ if (prefix === '<') {
+ attr.validValues = makeMap(value, '?');
+ }
+ }
+
+ // Check for attribute patterns
+ if (hasPatternsRegExp.test(attrName)) {
+ element.attributePatterns = element.attributePatterns || [];
+ attr.pattern = patternToRegExp(attrName);
+ element.attributePatterns.push(attr);
+ } else {
+ // Add attribute to order list if it doesn't already exist
+ if (!attributes[attrName]) {
+ attributesOrder.push(attrName);
+ }
+
+ attributes[attrName] = attr;
+ }
+ }
+ }
+ }
+
+ // Global rule, store away these for later usage
+ if (!globalAttributes && elementName == '@') {
+ globalAttributes = attributes;
+ globalAttributesOrder = attributesOrder;
+ }
+
+ // Handle substitute elements such as b/strong
+ if (outputName) {
+ element.outputName = elementName;
+ elements[outputName] = element;
+ }
+
+ // Add pattern or exact element
+ if (hasPatternsRegExp.test(elementName)) {
+ element.pattern = patternToRegExp(elementName);
+ patternElements.push(element);
+ } else {
+ elements[elementName] = element;
+ }
+ }
+ }
+ }
+ }
+
+ function setValidElements(validElements) {
+ elements = {};
+ patternElements = [];
+
+ addValidElements(validElements);
+
+ each(schemaItems, function(element, name) {
+ children[name] = element.children;
+ });
+ }
+
+ // Adds custom non HTML elements to the schema
+ function addCustomElements(customElements) {
+ var customElementRegExp = /^(~)?(.+)$/;
+
+ if (customElements) {
+ // Flush cached items since we are altering the default maps
+ mapCache.text_block_elements = mapCache.block_elements = null;
+
+ each(split(customElements, ','), function(rule) {
+ var matches = customElementRegExp.exec(rule),
+ inline = matches[1] === '~',
+ cloneName = inline ? 'span' : 'div',
+ name = matches[2];
+
+ children[name] = children[cloneName];
+ customElementsMap[name] = cloneName;
+
+ // If it's not marked as inline then add it to valid block elements
+ if (!inline) {
+ blockElementsMap[name.toUpperCase()] = {};
+ blockElementsMap[name] = {};
+ }
+
+ // Add elements clone if needed
+ if (!elements[name]) {
+ var customRule = elements[cloneName];
+
+ customRule = extend({}, customRule);
+ delete customRule.removeEmptyAttrs;
+ delete customRule.removeEmpty;
+
+ elements[name] = customRule;
+ }
+
+ // Add custom elements at span/div positions
+ each(children, function(element, elmName) {
+ if (element[cloneName]) {
+ children[elmName] = element = extend({}, children[elmName]);
+ element[name] = element[cloneName];
+ }
+ });
+ });
+ }
+ }
+
+ // Adds valid children to the schema object
+ function addValidChildren(validChildren) {
+ var childRuleRegExp = /^([+\-]?)(\w+)\[([^\]]+)\]$/;
+
+ // Invalidate the schema cache if the schema is mutated
+ mapCache[settings.schema] = null;
+
+ if (validChildren) {
+ each(split(validChildren, ','), function(rule) {
+ var matches = childRuleRegExp.exec(rule), parent, prefix;
+
+ if (matches) {
+ prefix = matches[1];
+
+ // Add/remove items from default
+ if (prefix) {
+ parent = children[matches[2]];
+ } else {
+ parent = children[matches[2]] = {'#comment': {}};
+ }
+
+ parent = children[matches[2]];
+
+ each(split(matches[3], '|'), function(child) {
+ if (prefix === '-') {
+ delete parent[child];
+ } else {
+ parent[child] = {};
+ }
+ });
+ }
+ });
+ }
+ }
+
+ function getElementRule(name) {
+ var element = elements[name], i;
+
+ // Exact match found
+ if (element) {
+ return element;
+ }
+
+ // No exact match then try the patterns
+ i = patternElements.length;
+ while (i--) {
+ element = patternElements[i];
+
+ if (element.pattern.test(name)) {
+ return element;
+ }
+ }
+ }
+
+ if (!settings.valid_elements) {
+ // No valid elements defined then clone the elements from the schema spec
+ each(schemaItems, function(element, name) {
+ elements[name] = {
+ attributes: element.attributes,
+ attributesOrder: element.attributesOrder
+ };
+
+ children[name] = element.children;
+ });
+
+ // Switch these on HTML4
+ if (settings.schema != "html5") {
+ each(split('strong/b em/i'), function(item) {
+ item = split(item, '/');
+ elements[item[1]].outputName = item[0];
+ });
+ }
+
+ // Add default alt attribute for images, removed since alt="" is treated as presentational.
+ // elements.img.attributesDefault = [{name: 'alt', value: ''}];
+
+ // Remove these if they are empty by default
+ each(split('ol ul sub sup blockquote span font a table tbody tr strong em b i'), function(name) {
+ if (elements[name]) {
+ elements[name].removeEmpty = true;
+ }
+ });
+
+ // Padd these by default
+ each(split('p h1 h2 h3 h4 h5 h6 th td pre div address caption'), function(name) {
+ elements[name].paddEmpty = true;
+ });
+
+ // Remove these if they have no attributes
+ each(split('span'), function(name) {
+ elements[name].removeEmptyAttrs = true;
+ });
+
+ // Remove these by default
+ // TODO: Reenable in 4.1
+ /*each(split('script style'), function(name) {
+ delete elements[name];
+ });*/
+ } else {
+ setValidElements(settings.valid_elements);
+ }
+
+ addCustomElements(settings.custom_elements);
+ addValidChildren(settings.valid_children);
+ addValidElements(settings.extended_valid_elements);
+
+ // Todo: Remove this when we fix list handling to be valid
+ addValidChildren('+ol[ul|ol],+ul[ul|ol]');
+
+
+ // Some elements are not valid by themselves - require parents
+ each({
+ dd: 'dl',
+ dt: 'dl',
+ li: 'ul ol',
+ td: 'tr',
+ th: 'tr',
+ tr: 'tbody thead tfoot',
+ tbody: 'table',
+ thead: 'table',
+ tfoot: 'table',
+ legend: 'fieldset',
+ area: 'map',
+ param: 'video audio object'
+ }, function(parents, item) {
+ if (elements[item]) {
+ elements[item].parentsRequired = split(parents);
+ }
+ });
+
+
+ // Delete invalid elements
+ if (settings.invalid_elements) {
+ each(explode(settings.invalid_elements), function(item) {
+ if (elements[item]) {
+ delete elements[item];
+ }
+ });
+ }
+
+ // If the user didn't allow span only allow internal spans
+ if (!getElementRule('span')) {
+ addValidElements('span[!data-mce-type|*]');
+ }
+
+ /**
+ * Name/value map object with valid parents and children to those parents.
+ *
+ * @example
+ * children = {
+ * div:{p:{}, h1:{}}
+ * };
+ * @field children
+ * @type Object
+ */
+ self.children = children;
+
+ /**
+ * Name/value map object with valid styles for each element.
+ *
+ * @method getValidStyles
+ * @type Object
+ */
+ self.getValidStyles = function() {
+ return validStyles;
+ };
+
+ /**
+ * Name/value map object with valid styles for each element.
+ *
+ * @method getInvalidStyles
+ * @type Object
+ */
+ self.getInvalidStyles = function() {
+ return invalidStyles;
+ };
+
+ /**
+ * Name/value map object with valid classes for each element.
+ *
+ * @method getValidClasses
+ * @type Object
+ */
+ self.getValidClasses = function() {
+ return validClasses;
+ };
+
+ /**
+ * Returns a map with boolean attributes.
+ *
+ * @method getBoolAttrs
+ * @return {Object} Name/value lookup map for boolean attributes.
+ */
+ self.getBoolAttrs = function() {
+ return boolAttrMap;
+ };
+
+ /**
+ * Returns a map with block elements.
+ *
+ * @method getBlockElements
+ * @return {Object} Name/value lookup map for block elements.
+ */
+ self.getBlockElements = function() {
+ return blockElementsMap;
+ };
+
+ /**
+ * Returns a map with text block elements. Such as: p,h1-h6,div,address
+ *
+ * @method getTextBlockElements
+ * @return {Object} Name/value lookup map for block elements.
+ */
+ self.getTextBlockElements = function() {
+ return textBlockElementsMap;
+ };
+
+ /**
+ * Returns a map of inline text format nodes for example strong/span or ins.
+ *
+ * @method getTextInlineElements
+ * @return {Object} Name/value lookup map for text format elements.
+ */
+ self.getTextInlineElements = function() {
+ return textInlineElementsMap;
+ };
+
+ /**
+ * Returns a map with short ended elements such as BR or IMG.
+ *
+ * @method getShortEndedElements
+ * @return {Object} Name/value lookup map for short ended elements.
+ */
+ self.getShortEndedElements = function() {
+ return shortEndedElementsMap;
+ };
+
+ /**
+ * Returns a map with self closing tags such as
.
+ *
+ * @method getSelfClosingElements
+ * @return {Object} Name/value lookup map for self closing tags elements.
+ */
+ self.getSelfClosingElements = function() {
+ return selfClosingElementsMap;
+ };
+
+ /**
+ * Returns a map with elements that should be treated as contents regardless if it has text
+ * content in them or not such as TD, VIDEO or IMG.
+ *
+ * @method getNonEmptyElements
+ * @return {Object} Name/value lookup map for non empty elements.
+ */
+ self.getNonEmptyElements = function() {
+ return nonEmptyElementsMap;
+ };
+
+ /**
+ * Returns a map with elements that the caret should be moved in front of after enter is
+ * pressed
+ *
+ * @method getMoveCaretBeforeOnEnterElements
+ * @return {Object} Name/value lookup map for elements to place the caret in front of.
+ */
+ self.getMoveCaretBeforeOnEnterElements = function() {
+ return moveCaretBeforeOnEnterElementsMap;
+ };
+
+ /**
+ * Returns a map with elements where white space is to be preserved like PRE or SCRIPT.
+ *
+ * @method getWhiteSpaceElements
+ * @return {Object} Name/value lookup map for white space elements.
+ */
+ self.getWhiteSpaceElements = function() {
+ return whiteSpaceElementsMap;
+ };
+
+ /**
+ * Returns a map with special elements. These are elements that needs to be parsed
+ * in a special way such as script, style, textarea etc. The map object values
+ * are regexps used to find the end of the element.
+ *
+ * @method getSpecialElements
+ * @return {Object} Name/value lookup map for special elements.
+ */
+ self.getSpecialElements = function() {
+ return specialElements;
+ };
+
+ /**
+ * Returns true/false if the specified element and it's child is valid or not
+ * according to the schema.
+ *
+ * @method isValidChild
+ * @param {String} name Element name to check for.
+ * @param {String} child Element child to verify.
+ * @return {Boolean} True/false if the element is a valid child of the specified parent.
+ */
+ self.isValidChild = function(name, child) {
+ var parent = children[name];
+
+ return !!(parent && parent[child]);
+ };
+
+ /**
+ * Returns true/false if the specified element name and optional attribute is
+ * valid according to the schema.
+ *
+ * @method isValid
+ * @param {String} name Name of element to check.
+ * @param {String} attr Optional attribute name to check for.
+ * @return {Boolean} True/false if the element and attribute is valid.
+ */
+ self.isValid = function(name, attr) {
+ var attrPatterns, i, rule = getElementRule(name);
+
+ // Check if it's a valid element
+ if (rule) {
+ if (attr) {
+ // Check if attribute name exists
+ if (rule.attributes[attr]) {
+ return true;
+ }
+
+ // Check if attribute matches a regexp pattern
+ attrPatterns = rule.attributePatterns;
+ if (attrPatterns) {
+ i = attrPatterns.length;
+ while (i--) {
+ if (attrPatterns[i].pattern.test(name)) {
+ return true;
+ }
+ }
+ }
+ } else {
+ return true;
+ }
+ }
+
+ // No match
+ return false;
+ };
+
+ /**
+ * Returns true/false if the specified element is valid or not
+ * according to the schema.
+ *
+ * @method getElementRule
+ * @param {String} name Element name to check for.
+ * @return {Object} Element object or undefined if the element isn't valid.
+ */
+ self.getElementRule = getElementRule;
+
+ /**
+ * Returns an map object of all custom elements.
+ *
+ * @method getCustomElements
+ * @return {Object} Name/value map object of all custom elements.
+ */
+ self.getCustomElements = function() {
+ return customElementsMap;
+ };
+
+ /**
+ * Parses a valid elements string and adds it to the schema. The valid elements
+ * format is for example "element[attr=default|otherattr]".
+ * Existing rules will be replaced with the ones specified, so this extends the schema.
+ *
+ * @method addValidElements
+ * @param {String} valid_elements String in the valid elements format to be parsed.
+ */
+ self.addValidElements = addValidElements;
+
+ /**
+ * Parses a valid elements string and sets it to the schema. The valid elements
+ * format is for example "element[attr=default|otherattr]".
+ * Existing rules will be replaced with the ones specified, so this extends the schema.
+ *
+ * @method setValidElements
+ * @param {String} valid_elements String in the valid elements format to be parsed.
+ */
+ self.setValidElements = setValidElements;
+
+ /**
+ * Adds custom non HTML elements to the schema.
+ *
+ * @method addCustomElements
+ * @param {String} custom_elements Comma separated list of custom elements to add.
+ */
+ self.addCustomElements = addCustomElements;
+
+ /**
+ * Parses a valid children string and adds them to the schema structure. The valid children
+ * format is for example: "element[child1|child2]".
+ *
+ * @method addValidChildren
+ * @param {String} valid_children Valid children elements string to parse
+ */
+ self.addValidChildren = addValidChildren;
+
+ self.elements = elements;
+ };
+});
+
+// Included from: js/tinymce/classes/html/SaxParser.js
+
+/**
+ * SaxParser.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 max-depth:[2, 9] */
+
+/**
+ * This class parses HTML code using pure JavaScript and executes various events for each item it finds. It will
+ * always execute the events in the right order for tag soup code like . It will also remove elements
+ * and attributes that doesn't fit the schema if the validate setting is enabled.
+ *
+ * @example
+ * var parser = new tinymce.html.SaxParser({
+ * validate: true,
+ *
+ * comment: function(text) {
+ * console.log('Comment:', text);
+ * },
+ *
+ * cdata: function(text) {
+ * console.log('CDATA:', text);
+ * },
+ *
+ * text: function(text, raw) {
+ * console.log('Text:', text, 'Raw:', raw);
+ * },
+ *
+ * start: function(name, attrs, empty) {
+ * console.log('Start:', name, attrs, empty);
+ * },
+ *
+ * end: function(name) {
+ * console.log('End:', name);
+ * },
+ *
+ * pi: function(name, text) {
+ * console.log('PI:', name, text);
+ * },
+ *
+ * doctype: function(text) {
+ * console.log('DocType:', text);
+ * }
+ * }, schema);
+ * @class tinymce.html.SaxParser
+ * @version 3.4
+ */
+define("tinymce/html/SaxParser", [
+ "tinymce/html/Schema",
+ "tinymce/html/Entities",
+ "tinymce/util/Tools"
+], function(Schema, Entities, Tools) {
+ var each = Tools.each;
+
+ /**
+ * Returns the index of the end tag for a specific start tag. This can be
+ * used to skip all children of a parent element from being processed.
+ *
+ * @private
+ * @method findEndTag
+ * @param {tinymce.html.Schema} schema Schema instance to use to match short ended elements.
+ * @param {String} html HTML string to find the end tag in.
+ * @param {Number} startIndex Indext to start searching at should be after the start tag.
+ * @return {Number} Index of the end tag.
+ */
+ function findEndTag(schema, html, startIndex) {
+ var count = 1, index, matches, tokenRegExp, shortEndedElements;
+
+ shortEndedElements = schema.getShortEndedElements();
+ tokenRegExp = /<([!?\/])?([A-Za-z0-9\-_\:\.]+)((?:\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\/|\s+)>/g;
+ tokenRegExp.lastIndex = index = startIndex;
+
+ while ((matches = tokenRegExp.exec(html))) {
+ index = tokenRegExp.lastIndex;
+
+ if (matches[1] === '/') { // End element
+ count--;
+ } else if (!matches[1]) { // Start element
+ if (matches[2] in shortEndedElements) {
+ continue;
+ }
+
+ count++;
+ }
+
+ if (count === 0) {
+ break;
+ }
+ }
+
+ return index;
+ }
+
+ /**
+ * Constructs a new SaxParser instance.
+ *
+ * @constructor
+ * @method SaxParser
+ * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
+ * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
+ */
+ function SaxParser(settings, schema) {
+ var self = this;
+
+ function noop() {}
+
+ settings = settings || {};
+ self.schema = schema = schema || new Schema();
+
+ if (settings.fix_self_closing !== false) {
+ settings.fix_self_closing = true;
+ }
+
+ // Add handler functions from settings and setup default handlers
+ each('comment cdata text start end pi doctype'.split(' '), function(name) {
+ if (name) {
+ self[name] = settings[name] || noop;
+ }
+ });
+
+ /**
+ * Parses the specified HTML string and executes the callbacks for each item it finds.
+ *
+ * @example
+ * new SaxParser({...}).parse('text');
+ * @method parse
+ * @param {String} html Html string to sax parse.
+ */
+ self.parse = function(html) {
+ var self = this, matches, index = 0, value, endRegExp, stack = [], attrList, i, text, name;
+ var isInternalElement, removeInternalElements, shortEndedElements, fillAttrsMap, isShortEnded;
+ var validate, elementRule, isValidElement, attr, attribsValue, validAttributesMap, validAttributePatterns;
+ var attributesRequired, attributesDefault, attributesForced;
+ var anyAttributesRequired, selfClosing, tokenRegExp, attrRegExp, specialElements, attrValue, idCount = 0;
+ var decode = Entities.decode, fixSelfClosing, filteredUrlAttrs = Tools.makeMap('src,href,data,background,formaction,poster');
+ var scriptUriRegExp = /((java|vb)script|mhtml):/i, dataUriRegExp = /^data:/i;
+
+ function processEndTag(name) {
+ var pos, i;
+
+ // Find position of parent of the same type
+ pos = stack.length;
+ while (pos--) {
+ if (stack[pos].name === name) {
+ break;
+ }
+ }
+
+ // Found parent
+ if (pos >= 0) {
+ // Close all the open elements
+ for (i = stack.length - 1; i >= pos; i--) {
+ name = stack[i];
+
+ if (name.valid) {
+ self.end(name.name);
+ }
+ }
+
+ // Remove the open elements from the stack
+ stack.length = pos;
+ }
+ }
+
+ function parseAttribute(match, name, value, val2, val3) {
+ var attrRule, i, trimRegExp = /[\s\u0000-\u001F]+/g;
+
+ name = name.toLowerCase();
+ value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
+
+ // Validate name and value pass through all data- attributes
+ if (validate && !isInternalElement && name.indexOf('data-') !== 0) {
+ attrRule = validAttributesMap[name];
+
+ // Find rule by pattern matching
+ if (!attrRule && validAttributePatterns) {
+ i = validAttributePatterns.length;
+ while (i--) {
+ attrRule = validAttributePatterns[i];
+ if (attrRule.pattern.test(name)) {
+ break;
+ }
+ }
+
+ // No rule matched
+ if (i === -1) {
+ attrRule = null;
+ }
+ }
+
+ // No attribute rule found
+ if (!attrRule) {
+ return;
+ }
+
+ // Validate value
+ if (attrRule.validValues && !(value in attrRule.validValues)) {
+ return;
+ }
+ }
+
+ // Block any javascript: urls or non image data uris
+ if (filteredUrlAttrs[name] && !settings.allow_script_urls) {
+ var uri = value.replace(trimRegExp, '');
+
+ try {
+ // Might throw malformed URI sequence
+ uri = decodeURIComponent(uri);
+ } catch (ex) {
+ // Fallback to non UTF-8 decoder
+ uri = unescape(uri);
+ }
+
+ if (scriptUriRegExp.test(uri)) {
+ return;
+ }
+
+ if (!settings.allow_html_data_urls && dataUriRegExp.test(uri) && !/^data:image\//i.test(uri)) {
+ return;
+ }
+ }
+
+ // Add attribute to list and map
+ attrList.map[name] = value;
+ attrList.push({
+ name: name,
+ value: value
+ });
+ }
+
+ // Precompile RegExps and map objects
+ tokenRegExp = new RegExp('<(?:' +
+ '(?:!--([\\w\\W]*?)-->)|' + // Comment
+ '(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|' + // CDATA
+ '(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
+ '(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
+ '(?:\\/([^>]+)>)|' + // End element
+ '(?:([A-Za-z0-9\\-_\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
+ ')', 'g');
+
+ attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
+
+ // Setup lookup tables for empty elements and boolean attributes
+ shortEndedElements = schema.getShortEndedElements();
+ selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
+ fillAttrsMap = schema.getBoolAttrs();
+ validate = settings.validate;
+ removeInternalElements = settings.remove_internals;
+ fixSelfClosing = settings.fix_self_closing;
+ specialElements = schema.getSpecialElements();
+
+ while ((matches = tokenRegExp.exec(html))) {
+ // Text
+ if (index < matches.index) {
+ self.text(decode(html.substr(index, matches.index - index)));
+ }
+
+ if ((value = matches[6])) { // End element
+ value = value.toLowerCase();
+
+ // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
+ if (value.charAt(0) === ':') {
+ value = value.substr(1);
+ }
+
+ processEndTag(value);
+ } else if ((value = matches[7])) { // Start element
+ value = value.toLowerCase();
+
+ // IE will add a ":" in front of elements it doesn't understand like custom elements or HTML5 elements
+ if (value.charAt(0) === ':') {
+ value = value.substr(1);
+ }
+
+ isShortEnded = value in shortEndedElements;
+
+ // Is self closing tag for example an
after an open
+ if (fixSelfClosing && selfClosing[value] && stack.length > 0 && stack[stack.length - 1].name === value) {
+ processEndTag(value);
+ }
+
+ // Validate element
+ if (!validate || (elementRule = schema.getElementRule(value))) {
+ isValidElement = true;
+
+ // Grab attributes map and patters when validation is enabled
+ if (validate) {
+ validAttributesMap = elementRule.attributes;
+ validAttributePatterns = elementRule.attributePatterns;
+ }
+
+ // Parse attributes
+ if ((attribsValue = matches[8])) {
+ isInternalElement = attribsValue.indexOf('data-mce-type') !== -1; // Check if the element is an internal element
+
+ // If the element has internal attributes then remove it if we are told to do so
+ if (isInternalElement && removeInternalElements) {
+ isValidElement = false;
+ }
+
+ attrList = [];
+ attrList.map = {};
+
+ attribsValue.replace(attrRegExp, parseAttribute);
+ } else {
+ attrList = [];
+ attrList.map = {};
+ }
+
+ // Process attributes if validation is enabled
+ if (validate && !isInternalElement) {
+ attributesRequired = elementRule.attributesRequired;
+ attributesDefault = elementRule.attributesDefault;
+ attributesForced = elementRule.attributesForced;
+ anyAttributesRequired = elementRule.removeEmptyAttrs;
+
+ // Check if any attribute exists
+ if (anyAttributesRequired && !attrList.length) {
+ isValidElement = false;
+ }
+
+ // Handle forced attributes
+ if (attributesForced) {
+ i = attributesForced.length;
+ while (i--) {
+ attr = attributesForced[i];
+ name = attr.name;
+ attrValue = attr.value;
+
+ if (attrValue === '{$uid}') {
+ attrValue = 'mce_' + idCount++;
+ }
+
+ attrList.map[name] = attrValue;
+ attrList.push({name: name, value: attrValue});
+ }
+ }
+
+ // Handle default attributes
+ if (attributesDefault) {
+ i = attributesDefault.length;
+ while (i--) {
+ attr = attributesDefault[i];
+ name = attr.name;
+
+ if (!(name in attrList.map)) {
+ attrValue = attr.value;
+
+ if (attrValue === '{$uid}') {
+ attrValue = 'mce_' + idCount++;
+ }
+
+ attrList.map[name] = attrValue;
+ attrList.push({name: name, value: attrValue});
+ }
+ }
+ }
+
+ // Handle required attributes
+ if (attributesRequired) {
+ i = attributesRequired.length;
+ while (i--) {
+ if (attributesRequired[i] in attrList.map) {
+ break;
+ }
+ }
+
+ // None of the required attributes where found
+ if (i === -1) {
+ isValidElement = false;
+ }
+ }
+
+ // Invalidate element if it's marked as bogus
+ if ((attr = attrList.map['data-mce-bogus'])) {
+ if (attr === 'all') {
+ index = findEndTag(schema, html, tokenRegExp.lastIndex);
+ tokenRegExp.lastIndex = index;
+ continue;
+ }
+
+ isValidElement = false;
+ }
+ }
+
+ if (isValidElement) {
+ self.start(value, attrList, isShortEnded);
+ }
+ } else {
+ isValidElement = false;
+ }
+
+ // Treat script, noscript and style a bit different since they may include code that looks like elements
+ if ((endRegExp = specialElements[value])) {
+ endRegExp.lastIndex = index = matches.index + matches[0].length;
+
+ if ((matches = endRegExp.exec(html))) {
+ if (isValidElement) {
+ text = html.substr(index, matches.index - index);
+ }
+
+ index = matches.index + matches[0].length;
+ } else {
+ text = html.substr(index);
+ index = html.length;
+ }
+
+ if (isValidElement) {
+ if (text.length > 0) {
+ self.text(text, true);
+ }
+
+ self.end(value);
+ }
+
+ tokenRegExp.lastIndex = index;
+ continue;
+ }
+
+ // Push value on to stack
+ if (!isShortEnded) {
+ if (!attribsValue || attribsValue.indexOf('/') != attribsValue.length - 1) {
+ stack.push({name: value, valid: isValidElement});
+ } else if (isValidElement) {
+ self.end(value);
+ }
+ }
+ } else if ((value = matches[1])) { // Comment
+ // Padd comment value to avoid browsers from parsing invalid comments as HTML
+ if (value.charAt(0) === '>') {
+ value = ' ' + value;
+ }
+
+ if (!settings.allow_conditional_comments && value.substr(0, 3).toLowerCase() === '[if') {
+ value = ' ' + value;
+ }
+
+ self.comment(value);
+ } else if ((value = matches[2])) { // CDATA
+ self.cdata(value);
+ } else if ((value = matches[3])) { // DOCTYPE
+ self.doctype(value);
+ } else if ((value = matches[4])) { // PI
+ self.pi(value, matches[5]);
+ }
+
+ index = matches.index + matches[0].length;
+ }
+
+ // Text
+ if (index < html.length) {
+ self.text(decode(html.substr(index)));
+ }
+
+ // Close any open elements
+ for (i = stack.length - 1; i >= 0; i--) {
+ value = stack[i];
+
+ if (value.valid) {
+ self.end(value.name);
+ }
+ }
+ };
+ }
+
+ SaxParser.findEndTag = findEndTag;
+
+ return SaxParser;
+});
+
+// Included from: js/tinymce/classes/html/DomParser.js
+
+/**
+ * DomParser.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 parses HTML code into a DOM like structure of nodes it will remove redundant whitespace and make
+ * sure that the node tree is valid according to the specified schema.
+ * So for example:
a
b
c will become
a
b
c
+ *
+ * @example
+ * var parser = new tinymce.html.DomParser({validate: true}, schema);
+ * var rootNode = parser.parse('
content
');
+ *
+ * @class tinymce.html.DomParser
+ * @version 3.4
+ */
+define("tinymce/html/DomParser", [
+ "tinymce/html/Node",
+ "tinymce/html/Schema",
+ "tinymce/html/SaxParser",
+ "tinymce/util/Tools"
+], function(Node, Schema, SaxParser, Tools) {
+ var makeMap = Tools.makeMap, each = Tools.each, explode = Tools.explode, extend = Tools.extend;
+
+ var paddEmptyNode = function (settings, node) {
+ if (settings.padd_empty_with_br) {
+ node.empty().append(new Node('br', '1')).shortEnded = true;
+ } else {
+ node.empty().append(new Node('#text', '3')).value = '\u00a0';
+ }
+ };
+
+ var hasOnlyChild = function (node, name) {
+ return node && node.firstChild === node.lastChild && node.firstChild.name === name;
+ };
+
+ /**
+ * Constructs a new DomParser instance.
+ *
+ * @constructor
+ * @method DomParser
+ * @param {Object} settings Name/value collection of settings. comment, cdata, text, start and end are callbacks.
+ * @param {tinymce.html.Schema} schema HTML Schema class to use when parsing.
+ */
+ return function(settings, schema) {
+ var self = this, nodeFilters = {}, attributeFilters = [], matchedNodes = {}, matchedAttributes = {};
+
+ settings = settings || {};
+ settings.validate = "validate" in settings ? settings.validate : true;
+ settings.root_name = settings.root_name || 'body';
+ self.schema = schema = schema || new Schema();
+
+ function fixInvalidChildren(nodes) {
+ var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i;
+ var nonEmptyElements, whitespaceElements, nonSplitableElements, textBlockElements, specialElements, sibling, nextNode;
+
+ nonSplitableElements = makeMap('tr,td,th,tbody,thead,tfoot,table');
+ nonEmptyElements = schema.getNonEmptyElements();
+ whitespaceElements = schema.getWhiteSpaceElements();
+ textBlockElements = schema.getTextBlockElements();
+ specialElements = schema.getSpecialElements();
+
+ for (ni = 0; ni < nodes.length; ni++) {
+ node = nodes[ni];
+
+ // Already removed or fixed
+ if (!node.parent || node.fixed) {
+ continue;
+ }
+
+ // If the invalid element is a text block and the text block is within a parent LI element
+ // Then unwrap the first text block and convert other sibling text blocks to LI elements similar to Word/Open Office
+ if (textBlockElements[node.name] && node.parent.name == 'li') {
+ // Move sibling text blocks after LI element
+ sibling = node.next;
+ while (sibling) {
+ if (textBlockElements[sibling.name]) {
+ sibling.name = 'li';
+ sibling.fixed = true;
+ node.parent.insert(sibling, node.parent);
+ } else {
+ break;
+ }
+
+ sibling = sibling.next;
+ }
+
+ // Unwrap current text block
+ node.unwrap(node);
+ continue;
+ }
+
+ // Get list of all parent nodes until we find a valid parent to stick the child into
+ parents = [node];
+ for (parent = node.parent; parent && !schema.isValidChild(parent.name, node.name) &&
+ !nonSplitableElements[parent.name]; parent = parent.parent) {
+ parents.push(parent);
+ }
+
+ // Found a suitable parent
+ if (parent && parents.length > 1) {
+ // Reverse the array since it makes looping easier
+ parents.reverse();
+
+ // Clone the related parent and insert that after the moved node
+ newParent = currentNode = self.filterNode(parents[0].clone());
+
+ // Start cloning and moving children on the left side of the target node
+ for (i = 0; i < parents.length - 1; i++) {
+ if (schema.isValidChild(currentNode.name, parents[i].name)) {
+ tempNode = self.filterNode(parents[i].clone());
+ currentNode.append(tempNode);
+ } else {
+ tempNode = currentNode;
+ }
+
+ for (childNode = parents[i].firstChild; childNode && childNode != parents[i + 1];) {
+ nextNode = childNode.next;
+ tempNode.append(childNode);
+ childNode = nextNode;
+ }
+
+ currentNode = tempNode;
+ }
+
+ if (!newParent.isEmpty(nonEmptyElements, whitespaceElements)) {
+ parent.insert(newParent, parents[0], true);
+ parent.insert(node, newParent);
+ } else {
+ parent.insert(node, parents[0], true);
+ }
+
+ // Check if the element is empty by looking through it's contents and special treatment for
+ parent = parents[0];
+ if (parent.isEmpty(nonEmptyElements, whitespaceElements) || hasOnlyChild(parent, 'br')) {
+ parent.empty().remove();
+ }
+ } else if (node.parent) {
+ // If it's an LI try to find a UL/OL for it or wrap it
+ if (node.name === 'li') {
+ sibling = node.prev;
+ if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
+ sibling.append(node);
+ continue;
+ }
+
+ sibling = node.next;
+ if (sibling && (sibling.name === 'ul' || sibling.name === 'ul')) {
+ sibling.insert(node, sibling.firstChild, true);
+ continue;
+ }
+
+ node.wrap(self.filterNode(new Node('ul', 1)));
+ continue;
+ }
+
+ // Try wrapping the element in a DIV
+ if (schema.isValidChild(node.parent.name, 'div') && schema.isValidChild('div', node.name)) {
+ node.wrap(self.filterNode(new Node('div', 1)));
+ } else {
+ // We failed wrapping it, then remove or unwrap it
+ if (specialElements[node.name]) {
+ node.empty().remove();
+ } else {
+ node.unwrap();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Runs the specified node though the element and attributes filters.
+ *
+ * @method filterNode
+ * @param {tinymce.html.Node} Node the node to run filters on.
+ * @return {tinymce.html.Node} The passed in node.
+ */
+ self.filterNode = function(node) {
+ var i, name, list;
+
+ // Run element filters
+ if (name in nodeFilters) {
+ list = matchedNodes[name];
+
+ if (list) {
+ list.push(node);
+ } else {
+ matchedNodes[name] = [node];
+ }
+ }
+
+ // Run attribute filters
+ i = attributeFilters.length;
+ while (i--) {
+ name = attributeFilters[i].name;
+
+ if (name in node.attributes.map) {
+ list = matchedAttributes[name];
+
+ if (list) {
+ list.push(node);
+ } else {
+ matchedAttributes[name] = [node];
+ }
+ }
+ }
+
+ return node;
+ };
+
+ /**
+ * Adds a node filter function to the parser, the parser will collect the specified nodes by name
+ * and then execute the callback ones it has finished parsing the document.
+ *
+ * @example
+ * parser.addNodeFilter('p,h1', function(nodes, name) {
+ * for (var i = 0; i < nodes.length; i++) {
+ * console.log(nodes[i].name);
+ * }
+ * });
+ * @method addNodeFilter
+ * @method {String} name Comma separated list of nodes to collect.
+ * @param {function} callback Callback function to execute once it has collected nodes.
+ */
+ self.addNodeFilter = function(name, callback) {
+ each(explode(name), function(name) {
+ var list = nodeFilters[name];
+
+ if (!list) {
+ nodeFilters[name] = list = [];
+ }
+
+ list.push(callback);
+ });
+ };
+
+ /**
+ * Adds a attribute filter function to the parser, the parser will collect nodes that has the specified attributes
+ * and then execute the callback ones it has finished parsing the document.
+ *
+ * @example
+ * parser.addAttributeFilter('src,href', function(nodes, name) {
+ * for (var i = 0; i < nodes.length; i++) {
+ * console.log(nodes[i].name);
+ * }
+ * });
+ * @method addAttributeFilter
+ * @method {String} name Comma separated list of nodes to collect.
+ * @param {function} callback Callback function to execute once it has collected nodes.
+ */
+ self.addAttributeFilter = function(name, callback) {
+ each(explode(name), function(name) {
+ var i;
+
+ for (i = 0; i < attributeFilters.length; i++) {
+ if (attributeFilters[i].name === name) {
+ attributeFilters[i].callbacks.push(callback);
+ return;
+ }
+ }
+
+ attributeFilters.push({name: name, callbacks: [callback]});
+ });
+ };
+
+ /**
+ * Parses the specified HTML string into a DOM like node tree and returns the result.
+ *
+ * @example
+ * var rootNode = new DomParser({...}).parse('text');
+ * @method parse
+ * @param {String} html Html string to sax parse.
+ * @param {Object} args Optional args object that gets passed to all filter functions.
+ * @return {tinymce.html.Node} Root node containing the tree.
+ */
+ self.parse = function(html, args) {
+ var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate;
+ var blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement;
+ var endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements;
+ var children, nonEmptyElements, rootBlockName;
+
+ args = args || {};
+ matchedNodes = {};
+ matchedAttributes = {};
+ blockElements = extend(makeMap('script,style,head,html,body,title,meta,param'), schema.getBlockElements());
+ nonEmptyElements = schema.getNonEmptyElements();
+ children = schema.children;
+ validate = settings.validate;
+ rootBlockName = "forced_root_block" in args ? args.forced_root_block : settings.forced_root_block;
+
+ whiteSpaceElements = schema.getWhiteSpaceElements();
+ startWhiteSpaceRegExp = /^[ \t\r\n]+/;
+ endWhiteSpaceRegExp = /[ \t\r\n]+$/;
+ allWhiteSpaceRegExp = /[ \t\r\n]+/g;
+ isAllWhiteSpaceRegExp = /^[ \t\r\n]+$/;
+
+ function addRootBlocks() {
+ var node = rootNode.firstChild, next, rootBlockNode;
+
+ // Removes whitespace at beginning and end of block so:
+ //
x
->
x
+ function trim(rootBlockNode) {
+ if (rootBlockNode) {
+ node = rootBlockNode.firstChild;
+ if (node && node.type == 3) {
+ node.value = node.value.replace(startWhiteSpaceRegExp, '');
+ }
+
+ node = rootBlockNode.lastChild;
+ if (node && node.type == 3) {
+ node.value = node.value.replace(endWhiteSpaceRegExp, '');
+ }
+ }
+ }
+
+ // Check if rootBlock is valid within rootNode for example if P is valid in H1 if H1 is the contentEditabe root
+ if (!schema.isValidChild(rootNode.name, rootBlockName.toLowerCase())) {
+ return;
+ }
+
+ while (node) {
+ next = node.next;
+
+ if (node.type == 3 || (node.type == 1 && node.name !== 'p' &&
+ !blockElements[node.name] && !node.attr('data-mce-type'))) {
+ if (!rootBlockNode) {
+ // Create a new root block element
+ rootBlockNode = createNode(rootBlockName, 1);
+ rootBlockNode.attr(settings.forced_root_block_attrs);
+ rootNode.insert(rootBlockNode, node);
+ rootBlockNode.append(node);
+ } else {
+ rootBlockNode.append(node);
+ }
+ } else {
+ trim(rootBlockNode);
+ rootBlockNode = null;
+ }
+
+ node = next;
+ }
+
+ trim(rootBlockNode);
+ }
+
+ function createNode(name, type) {
+ var node = new Node(name, type), list;
+
+ if (name in nodeFilters) {
+ list = matchedNodes[name];
+
+ if (list) {
+ list.push(node);
+ } else {
+ matchedNodes[name] = [node];
+ }
+ }
+
+ return node;
+ }
+
+ function removeWhitespaceBefore(node) {
+ var textNode, textNodeNext, textVal, sibling, blockElements = schema.getBlockElements();
+
+ for (textNode = node.prev; textNode && textNode.type === 3;) {
+ textVal = textNode.value.replace(endWhiteSpaceRegExp, '');
+
+ // Found a text node with non whitespace then trim that and break
+ if (textVal.length > 0) {
+ textNode.value = textVal;
+ return;
+ }
+
+ textNodeNext = textNode.next;
+
+ // Fix for bug #7543 where bogus nodes would produce empty
+ // text nodes and these would be removed if a nested list was before it
+ if (textNodeNext) {
+ if (textNodeNext.type == 3 && textNodeNext.value.length) {
+ textNode = textNode.prev;
+ continue;
+ }
+
+ if (!blockElements[textNodeNext.name] && textNodeNext.name != 'script' && textNodeNext.name != 'style') {
+ textNode = textNode.prev;
+ continue;
+ }
+ }
+
+ sibling = textNode.prev;
+ textNode.remove();
+ textNode = sibling;
+ }
+ }
+
+ function cloneAndExcludeBlocks(input) {
+ var name, output = {};
+
+ for (name in input) {
+ if (name !== 'li' && name != 'p') {
+ output[name] = input[name];
+ }
+ }
+
+ return output;
+ }
+
+ parser = new SaxParser({
+ validate: validate,
+ allow_script_urls: settings.allow_script_urls,
+ allow_conditional_comments: settings.allow_conditional_comments,
+
+ // Exclude P and LI from DOM parsing since it's treated better by the DOM parser
+ self_closing_elements: cloneAndExcludeBlocks(schema.getSelfClosingElements()),
+
+ cdata: function(text) {
+ node.append(createNode('#cdata', 4)).value = text;
+ },
+
+ text: function(text, raw) {
+ var textNode;
+
+ // Trim all redundant whitespace on non white space elements
+ if (!isInWhiteSpacePreservedElement) {
+ text = text.replace(allWhiteSpaceRegExp, ' ');
+
+ if (node.lastChild && blockElements[node.lastChild.name]) {
+ text = text.replace(startWhiteSpaceRegExp, '');
+ }
+ }
+
+ // Do we need to create the node
+ if (text.length !== 0) {
+ textNode = createNode('#text', 3);
+ textNode.raw = !!raw;
+ node.append(textNode).value = text;
+ }
+ },
+
+ comment: function(text) {
+ node.append(createNode('#comment', 8)).value = text;
+ },
+
+ pi: function(name, text) {
+ node.append(createNode(name, 7)).value = text;
+ removeWhitespaceBefore(node);
+ },
+
+ doctype: function(text) {
+ var newNode;
+
+ newNode = node.append(createNode('#doctype', 10));
+ newNode.value = text;
+ removeWhitespaceBefore(node);
+ },
+
+ start: function(name, attrs, empty) {
+ var newNode, attrFiltersLen, elementRule, attrName, parent;
+
+ elementRule = validate ? schema.getElementRule(name) : {};
+ if (elementRule) {
+ newNode = createNode(elementRule.outputName || name, 1);
+ newNode.attributes = attrs;
+ newNode.shortEnded = empty;
+
+ node.append(newNode);
+
+ // Check if node is valid child of the parent node is the child is
+ // unknown we don't collect it since it's probably a custom element
+ parent = children[node.name];
+ if (parent && children[newNode.name] && !parent[newNode.name]) {
+ invalidChildren.push(newNode);
+ }
+
+ attrFiltersLen = attributeFilters.length;
+ while (attrFiltersLen--) {
+ attrName = attributeFilters[attrFiltersLen].name;
+
+ if (attrName in attrs.map) {
+ list = matchedAttributes[attrName];
+
+ if (list) {
+ list.push(newNode);
+ } else {
+ matchedAttributes[attrName] = [newNode];
+ }
+ }
+ }
+
+ // Trim whitespace before block
+ if (blockElements[name]) {
+ removeWhitespaceBefore(newNode);
+ }
+
+ // Change current node if the element wasn't empty i.e not or
+ if (!empty) {
+ node = newNode;
+ }
+
+ // Check if we are inside a whitespace preserved element
+ if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
+ isInWhiteSpacePreservedElement = true;
+ }
+ }
+ },
+
+ end: function(name) {
+ var textNode, elementRule, text, sibling, tempNode;
+
+ elementRule = validate ? schema.getElementRule(name) : {};
+ if (elementRule) {
+ if (blockElements[name]) {
+ if (!isInWhiteSpacePreservedElement) {
+ // Trim whitespace of the first node in a block
+ textNode = node.firstChild;
+ if (textNode && textNode.type === 3) {
+ text = textNode.value.replace(startWhiteSpaceRegExp, '');
+
+ // Any characters left after trim or should we remove it
+ if (text.length > 0) {
+ textNode.value = text;
+ textNode = textNode.next;
+ } else {
+ sibling = textNode.next;
+ textNode.remove();
+ textNode = sibling;
+
+ // Remove any pure whitespace siblings
+ while (textNode && textNode.type === 3) {
+ text = textNode.value;
+ sibling = textNode.next;
+
+ if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
+ textNode.remove();
+ textNode = sibling;
+ }
+
+ textNode = sibling;
+ }
+ }
+ }
+
+ // Trim whitespace of the last node in a block
+ textNode = node.lastChild;
+ if (textNode && textNode.type === 3) {
+ text = textNode.value.replace(endWhiteSpaceRegExp, '');
+
+ // Any characters left after trim or should we remove it
+ if (text.length > 0) {
+ textNode.value = text;
+ textNode = textNode.prev;
+ } else {
+ sibling = textNode.prev;
+ textNode.remove();
+ textNode = sibling;
+
+ // Remove any pure whitespace siblings
+ while (textNode && textNode.type === 3) {
+ text = textNode.value;
+ sibling = textNode.prev;
+
+ if (text.length === 0 || isAllWhiteSpaceRegExp.test(text)) {
+ textNode.remove();
+ textNode = sibling;
+ }
+
+ textNode = sibling;
+ }
+ }
+ }
+ }
+
+ // Trim start white space
+ // Removed due to: #5424
+ /*textNode = node.prev;
+ if (textNode && textNode.type === 3) {
+ text = textNode.value.replace(startWhiteSpaceRegExp, '');
+
+ if (text.length > 0)
+ textNode.value = text;
+ else
+ textNode.remove();
+ }*/
+ }
+
+ // Check if we exited a whitespace preserved element
+ if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
+ isInWhiteSpacePreservedElement = false;
+ }
+
+ // Handle empty nodes
+ if (elementRule.removeEmpty || elementRule.paddEmpty) {
+ if (node.isEmpty(nonEmptyElements, whiteSpaceElements)) {
+ if (elementRule.paddEmpty) {
+ paddEmptyNode(settings, node);
+ } else {
+ // Leave nodes that have a name like
+ if (!node.attributes.map.name && !node.attributes.map.id) {
+ tempNode = node.parent;
+
+ if (blockElements[node.name]) {
+ node.empty().remove();
+ } else {
+ node.unwrap();
+ }
+
+ node = tempNode;
+ return;
+ }
+ }
+ }
+ }
+
+ node = node.parent;
+ }
+ }
+ }, schema);
+
+ rootNode = node = new Node(args.context || settings.root_name, 11);
+
+ parser.parse(html);
+
+ // Fix invalid children or report invalid children in a contextual parsing
+ if (validate && invalidChildren.length) {
+ if (!args.context) {
+ fixInvalidChildren(invalidChildren);
+ } else {
+ args.invalid = true;
+ }
+ }
+
+ // Wrap nodes in the root into block elements if the root is body
+ if (rootBlockName && (rootNode.name == 'body' || args.isRootContent)) {
+ addRootBlocks();
+ }
+
+ // Run filters only when the contents is valid
+ if (!args.invalid) {
+ // Run node filters
+ for (name in matchedNodes) {
+ list = nodeFilters[name];
+ nodes = matchedNodes[name];
+
+ // Remove already removed children
+ fi = nodes.length;
+ while (fi--) {
+ if (!nodes[fi].parent) {
+ nodes.splice(fi, 1);
+ }
+ }
+
+ for (i = 0, l = list.length; i < l; i++) {
+ list[i](nodes, name, args);
+ }
+ }
+
+ // Run attribute filters
+ for (i = 0, l = attributeFilters.length; i < l; i++) {
+ list = attributeFilters[i];
+
+ if (list.name in matchedAttributes) {
+ nodes = matchedAttributes[list.name];
+
+ // Remove already removed children
+ fi = nodes.length;
+ while (fi--) {
+ if (!nodes[fi].parent) {
+ nodes.splice(fi, 1);
+ }
+ }
+
+ for (fi = 0, fl = list.callbacks.length; fi < fl; fi++) {
+ list.callbacks[fi](nodes, list.name, args);
+ }
+ }
+ }
+ }
+
+ return rootNode;
+ };
+
+ // Remove at end of block elements Gecko and WebKit injects BR elements to
+ // make it possible to place the caret inside empty blocks. This logic tries to remove
+ // these elements and keep br elements that where intended to be there intact
+ if (settings.remove_trailing_brs) {
+ self.addNodeFilter('br', function(nodes) {
+ var i, l = nodes.length, node, blockElements = extend({}, schema.getBlockElements());
+ var nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
+ var whiteSpaceElements = schema.getNonEmptyElements();
+ var elementRule, textNode;
+
+ // Remove brs from body element as well
+ blockElements.body = 1;
+
+ // Must loop forwards since it will otherwise remove all brs in
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
text|text2
will become this
text|text2
+ // 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);
+ if (dom.isEmpty(parentBlock)) {
+ emptyBlock(parentBlock);
+ }
+ moveToCaretPosition(newBlock);
+ } 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 (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 validInsertion = function (editor, value, parentNode) {
+ // Should never insert content into bogus elements, since these can
+ // be resize handles or similar
+ if (parentNode.getAttribute('data-mce-bogus') === 'all') {
+ parentNode.parentNode.insertBefore(editor.dom.createFragment(value), parentNode);
+ } else {
+ // Check if parent is empty or only has one BR element then set the innerHTML of that parent
+ var node = parentNode.firstChild;
+ var node2 = parentNode.lastChild;
+ if (!node || (node === node2 && node.nodeName === 'BR')) {///
+ editor.dom.setHTML(parentNode, value);
+ } else {
+ editor.selection.setContent(value);
+ }
+ }
+ };
+
+ 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);
+ validInsertion(editor, value, parentNode);
+ } 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",
+ "tinymce/dom/NodeType"
+], function(Env, Tools, RangeUtils, TreeWalker, InsertContent, NodeType) {
+ // 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) {
+ var editingHost = dom.getParent(selection.getStart(), NodeType.isContentEditableTrue);
+ if (editingHost) {
+ rng = dom.createRng();
+ rng.selectNodeContents(editingHost);
+ 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 (
+ '
'
+ );
+ },
+
+ 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
+ if (/^(IMG|HR)$/.test(target.nodeName) && dom.getContentEditableParent(target) !== "false") {
+ e.preventDefault();
+ selection.select(target);
+ 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:
+ *
+ */
+ 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:
+ */
+ function blockFormSubmitInsideEditor() {
+ editor.on('init', function() {
+ editor.dom.bind(editor.getBody(), 'submit', function(e) {
+ e.preventDefault();
+ });
+ });
+ }
+
+ /**
+ * Sometimes WebKit/Blink generates BR elements with the Apple-interchange-newline class.
+ *
+ * Scenario:
+ * 1) Create a table 2x2.
+ * 2) Select and copy cells A2-B2.
+ * 3) Paste and it will add BR element to table cell.
+ */
+ function removeAppleInterchangeBrs() {
+ parser.addNodeFilter('br', function(nodes) {
+ var i = nodes.length;
+
+ while (i--) {
+ if (nodes[i].attr('class') == 'Apple-interchange-newline') {
+ nodes[i].remove();
+ }
+ }
+ });
+ }
+
+ /**
+ * IE cannot set custom contentType's on drag events, and also does not properly drag/drop between
+ * editors. This uses a special data:text/mce-internal URL to pass data when drag/drop between editors.
+ */
+ function ieInternalDragAndDrop() {
+ editor.on('dragstart', function(e) {
+ setMceInternalContent(e);
+ });
+
+ editor.on('drop', function(e) {
+ if (!isDefaultPrevented(e)) {
+ var internalContent = getMceInternalContent(e);
+
+ if (internalContent && internalContent.id != editor.id) {
+ e.preventDefault();
+
+ var rng = RangeUtils.getCaretRangeFromPoint(e.x, e.y, editor.getDoc());
+ selection.setRng(rng);
+ insertClipboardContents(internalContent.html);
+ }
+ }
+ });
+ }
+
+ function refreshContentEditable() {
+ // No-op since Mozilla seems to have fixed the caret repaint issues
+ }
+
+ function isHidden() {
+ var sel;
+
+ if (!isGecko) {
+ return 0;
+ }
+
+ // Weird, wheres that cursor selection?
+ sel = editor.selection.getSel();
+ return (!sel || !sel.rangeCount || sel.rangeCount === 0);
+ }
+
+ /**
+ * Properly empties the editor if all contents is selected and deleted this to
+ * prevent empty paragraphs from being produced at beginning/end of contents.
+ */
+ function emptyEditorOnDeleteEverything() {
+ function isEverythingSelected(editor) {
+ var caretWalker = new CaretWalker(editor.getBody());
+ var rng = editor.selection.getRng();
+ var startCaretPos = CaretPosition.fromRangeStart(rng);
+ var endCaretPos = CaretPosition.fromRangeEnd(rng);
+ var prev = caretWalker.prev(startCaretPos);
+ var next = caretWalker.next(endCaretPos);
+
+ return !editor.selection.isCollapsed() &&
+ (!prev || (prev.isAtStart() && startCaretPos.isEqual(prev))) &&
+ (!next || (next.isAtEnd() && startCaretPos.isEqual(next)));
+ }
+
+ // Type over case delete and insert this won't cover typeover with a IME but at least it covers the common case
+ editor.on('keypress', function (e) {
+ if (!isDefaultPrevented(e) && !selection.isCollapsed() && e.charCode > 31 && !VK.metaKeyPressed(e)) {
+ if (isEverythingSelected(editor)) {
+ e.preventDefault();
+ editor.setContent(String.fromCharCode(e.charCode));
+ editor.selection.select(editor.getBody(), true);
+ editor.selection.collapse(false);
+ editor.nodeChanged();
+ }
+ }
+ });
+
+ editor.on('keydown', function (e) {
+ var keyCode = e.keyCode;
+
+ if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
+ if (isEverythingSelected(editor)) {
+ e.preventDefault();
+ editor.setContent('');
+ editor.nodeChanged();
+ }
+ }
+ });
+ }
+
+ // All browsers
+ removeBlockQuoteOnBackSpace();
+ emptyEditorWhenDeleting();
+
+ // Windows phone will return a range like [body, 0] on mousedown so
+ // it will always normalize to the wrong location
+ if (!Env.windowsPhone) {
+ normalizeSelection();
+ }
+
+ // WebKit
+ if (isWebKit) {
+ emptyEditorOnDeleteEverything();
+ cleanupStylesWhenDeleting();
+ inputMethodFocus();
+ selectControlElements();
+ setDefaultBlockType();
+ blockFormSubmitInsideEditor();
+ disableBackspaceIntoATable();
+ removeAppleInterchangeBrs();
+
+ //touchClickEvent();
+
+ // iOS
+ if (Env.iOS) {
+ restoreFocusOnKeyDown();
+ bodyHeight();
+ tapLinksAndImages();
+ } else {
+ selectAll();
+ }
+ }
+
+ // IE
+ if (isIE && Env.ie < 11) {
+ removeHrOnBackspace();
+ ensureBodyHasRoleApplication();
+ addNewLinesBeforeBrInPre();
+ removePreSerializedStylesWhenSelectingControls();
+ deleteControlItemOnBackSpace();
+ renderEmptyBlocksFix();
+ keepNoScriptContents();
+ fixCaretSelectionOfDocumentElementOnIe();
+ }
+
+ if (Env.ie >= 11) {
+ bodyHeight();
+ disableBackspaceIntoATable();
+ }
+
+ if (Env.ie) {
+ selectAll();
+ disableAutoUrlDetect();
+ ieInternalDragAndDrop();
+ }
+
+ // Gecko
+ if (isGecko) {
+ emptyEditorOnDeleteEverything();
+ removeHrOnBackspace();
+ focusBody();
+ removeStylesWhenDeletingAcrossBlockElements();
+ setGeckoEditingOptions();
+ addBrAfterLastLinks();
+ showBrokenImageIcon();
+ blockCmdArrowNavigation();
+ disableBackspaceIntoATable();
+ }
+
+ return {
+ refreshContentEditable: refreshContentEditable,
+ isHidden: isHidden
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/EditorObservable.js
+
+/**
+ * EditorObservable.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 contains the event logic for the tinymce.Editor class.
+ *
+ * @mixin tinymce.EditorObservable
+ * @extends tinymce.util.Observable
+ */
+define("tinymce/EditorObservable", [
+ "tinymce/util/Observable",
+ "tinymce/dom/DOMUtils",
+ "tinymce/util/Tools"
+], function(Observable, DOMUtils, Tools) {
+ var DOM = DOMUtils.DOM, customEventRootDelegates;
+
+ /**
+ * Returns the event target so for the specified event. Some events fire
+ * only on document, some fire on documentElement etc. This also handles the
+ * custom event root setting where it returns that element instead of the body.
+ *
+ * @private
+ * @param {tinymce.Editor} editor Editor instance to get event target from.
+ * @param {String} eventName Name of the event for example "click".
+ * @return {Element/Document} HTML Element or document target to bind on.
+ */
+ function getEventTarget(editor, eventName) {
+ if (eventName == 'selectionchange') {
+ return editor.getDoc();
+ }
+
+ // Need to bind mousedown/mouseup etc to document not body in iframe mode
+ // Since the user might click on the HTML element not the BODY
+ if (!editor.inline && /^mouse|touch|click|contextmenu|drop|dragover|dragend/.test(eventName)) {
+ return editor.getDoc().documentElement;
+ }
+
+ // Bind to event root instead of body if it's defined
+ if (editor.settings.event_root) {
+ if (!editor.eventRoot) {
+ editor.eventRoot = DOM.select(editor.settings.event_root)[0];
+ }
+
+ return editor.eventRoot;
+ }
+
+ return editor.getBody();
+ }
+
+ /**
+ * Binds a event delegate for the specified name this delegate will fire
+ * the event to the editor dispatcher.
+ *
+ * @private
+ * @param {tinymce.Editor} editor Editor instance to get event target from.
+ * @param {String} eventName Name of the event for example "click".
+ */
+ function bindEventDelegate(editor, eventName) {
+ var eventRootElm = getEventTarget(editor, eventName), delegate;
+
+ function isListening(editor) {
+ return !editor.hidden && !editor.readonly;
+ }
+
+ if (!editor.delegates) {
+ editor.delegates = {};
+ }
+
+ if (editor.delegates[eventName]) {
+ return;
+ }
+
+ if (editor.settings.event_root) {
+ if (!customEventRootDelegates) {
+ customEventRootDelegates = {};
+ editor.editorManager.on('removeEditor', function() {
+ var name;
+
+ if (!editor.editorManager.activeEditor) {
+ if (customEventRootDelegates) {
+ for (name in customEventRootDelegates) {
+ editor.dom.unbind(getEventTarget(editor, name));
+ }
+
+ customEventRootDelegates = null;
+ }
+ }
+ });
+ }
+
+ if (customEventRootDelegates[eventName]) {
+ return;
+ }
+
+ delegate = function(e) {
+ var target = e.target, editors = editor.editorManager.editors, i = editors.length;
+
+ while (i--) {
+ var body = editors[i].getBody();
+
+ if (body === target || DOM.isChildOf(target, body)) {
+ if (isListening(editors[i])) {
+ editors[i].fire(eventName, e);
+ }
+ }
+ }
+ };
+
+ customEventRootDelegates[eventName] = delegate;
+ DOM.bind(eventRootElm, eventName, delegate);
+ } else {
+ delegate = function(e) {
+ if (isListening(editor)) {
+ editor.fire(eventName, e);
+ }
+ };
+
+ DOM.bind(eventRootElm, eventName, delegate);
+ editor.delegates[eventName] = delegate;
+ }
+ }
+
+ var EditorObservable = {
+ /**
+ * Bind any pending event delegates. This gets executed after the target body/document is created.
+ *
+ * @private
+ */
+ bindPendingEventDelegates: function() {
+ var self = this;
+
+ Tools.each(self._pendingNativeEvents, function(name) {
+ bindEventDelegate(self, name);
+ });
+ },
+
+ /**
+ * Toggles a native event on/off this is called by the EventDispatcher when
+ * the first native event handler is added and when the last native event handler is removed.
+ *
+ * @private
+ */
+ toggleNativeEvent: function(name, state) {
+ var self = this;
+
+ // Never bind focus/blur since the FocusManager fakes those
+ if (name == "focus" || name == "blur") {
+ return;
+ }
+
+ if (state) {
+ if (self.initialized) {
+ bindEventDelegate(self, name);
+ } else {
+ if (!self._pendingNativeEvents) {
+ self._pendingNativeEvents = [name];
+ } else {
+ self._pendingNativeEvents.push(name);
+ }
+ }
+ } else if (self.initialized) {
+ self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
+ delete self.delegates[name];
+ }
+ },
+
+ /**
+ * Unbinds all native event handlers that means delegates, custom events bound using the Events API etc.
+ *
+ * @private
+ */
+ unbindAllNativeEvents: function() {
+ var self = this, name;
+
+ if (self.delegates) {
+ for (name in self.delegates) {
+ self.dom.unbind(getEventTarget(self, name), name, self.delegates[name]);
+ }
+
+ delete self.delegates;
+ }
+
+ if (!self.inline) {
+ self.getBody().onload = null;
+ self.dom.unbind(self.getWin());
+ self.dom.unbind(self.getDoc());
+ }
+
+ self.dom.unbind(self.getBody());
+ self.dom.unbind(self.getContainer());
+ }
+ };
+
+ EditorObservable = Tools.extend({}, Observable, EditorObservable);
+
+ return EditorObservable;
+});
+
+// Included from: js/tinymce/classes/Mode.js
+
+/**
+ * Mode.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
+ */
+
+/**
+ * Mode switcher logic.
+ *
+ * @private
+ * @class tinymce.Mode
+ */
+define("tinymce/Mode", [], function() {
+ function setEditorCommandState(editor, cmd, state) {
+ try {
+ editor.getDoc().execCommand(cmd, false, state);
+ } catch (ex) {
+ // Ignore
+ }
+ }
+
+ function clickBlocker(editor) {
+ var target, handler;
+
+ target = editor.getBody();
+
+ handler = function(e) {
+ if (editor.dom.getParents(e.target, 'a').length > 0) {
+ e.preventDefault();
+ }
+ };
+
+ editor.dom.bind(target, 'click', handler);
+
+ return {
+ unbind: function() {
+ editor.dom.unbind(target, 'click', handler);
+ }
+ };
+ }
+
+ function toggleReadOnly(editor, state) {
+ if (editor._clickBlocker) {
+ editor._clickBlocker.unbind();
+ editor._clickBlocker = null;
+ }
+
+ if (state) {
+ editor._clickBlocker = clickBlocker(editor);
+ editor.selection.controlSelection.hideResizeRect();
+ editor.readonly = true;
+ editor.getBody().contentEditable = false;
+ } else {
+ editor.readonly = false;
+ editor.getBody().contentEditable = true;
+ setEditorCommandState(editor, "StyleWithCSS", false);
+ setEditorCommandState(editor, "enableInlineTableEditing", false);
+ setEditorCommandState(editor, "enableObjectResizing", false);
+ editor.focus();
+ editor.nodeChanged();
+ }
+ }
+
+ function setMode(editor, mode) {
+ var currentMode = editor.readonly ? 'readonly' : 'design';
+
+ if (mode == currentMode) {
+ return;
+ }
+
+ if (editor.initialized) {
+ toggleReadOnly(editor, mode == 'readonly');
+ } else {
+ editor.on('init', function() {
+ toggleReadOnly(editor, mode == 'readonly');
+ });
+ }
+
+ // Event is NOT preventable
+ editor.fire('SwitchMode', {mode: mode});
+ }
+
+ return {
+ setMode: setMode
+ };
+});
+
+// Included from: js/tinymce/classes/Shortcuts.js
+
+/**
+ * Shortcuts.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 all logic for handling of keyboard shortcuts.
+ *
+ * @class tinymce.Shortcuts
+ * @example
+ * editor.shortcuts.add('ctrl+a', function() {});
+ * editor.shortcuts.add('meta+a', function() {}); // "meta" maps to Command on Mac and Ctrl on PC
+ * editor.shortcuts.add('ctrl+alt+a', function() {});
+ * editor.shortcuts.add('access+a', function() {}); // "access" maps to ctrl+alt on Mac and shift+alt on PC
+ */
+define("tinymce/Shortcuts", [
+ "tinymce/util/Tools",
+ "tinymce/Env"
+], function(Tools, Env) {
+ var each = Tools.each, explode = Tools.explode;
+
+ var keyCodeLookup = {
+ "f9": 120,
+ "f10": 121,
+ "f11": 122
+ };
+
+ var modifierNames = Tools.makeMap('alt,ctrl,shift,meta,access');
+
+ return function(editor) {
+ var self = this, shortcuts = {}, pendingPatterns = [];
+
+ function parseShortcut(pattern) {
+ var id, key, shortcut = {};
+
+ // Parse modifiers and keys ctrl+alt+b for example
+ each(explode(pattern, '+'), function(value) {
+ if (value in modifierNames) {
+ shortcut[value] = true;
+ } else {
+ // Allow numeric keycodes like ctrl+219 for ctrl+[
+ if (/^[0-9]{2,}$/.test(value)) {
+ shortcut.keyCode = parseInt(value, 10);
+ } else {
+ shortcut.charCode = value.charCodeAt(0);
+ shortcut.keyCode = keyCodeLookup[value] || value.toUpperCase().charCodeAt(0);
+ }
+ }
+ });
+
+ // Generate unique id for modifier combination and set default state for unused modifiers
+ id = [shortcut.keyCode];
+ for (key in modifierNames) {
+ if (shortcut[key]) {
+ id.push(key);
+ } else {
+ shortcut[key] = false;
+ }
+ }
+ shortcut.id = id.join(',');
+
+ // Handle special access modifier differently depending on Mac/Win
+ if (shortcut.access) {
+ shortcut.alt = true;
+
+ if (Env.mac) {
+ shortcut.ctrl = true;
+ } else {
+ shortcut.shift = true;
+ }
+ }
+
+ // Handle special meta modifier differently depending on Mac/Win
+ if (shortcut.meta) {
+ if (Env.mac) {
+ shortcut.meta = true;
+ } else {
+ shortcut.ctrl = true;
+ shortcut.meta = false;
+ }
+ }
+
+ return shortcut;
+ }
+
+ function createShortcut(pattern, desc, cmdFunc, scope) {
+ var shortcuts;
+
+ shortcuts = Tools.map(explode(pattern, '>'), parseShortcut);
+ shortcuts[shortcuts.length - 1] = Tools.extend(shortcuts[shortcuts.length - 1], {
+ func: cmdFunc,
+ scope: scope || editor
+ });
+
+ return Tools.extend(shortcuts[0], {
+ desc: editor.translate(desc),
+ subpatterns: shortcuts.slice(1)
+ });
+ }
+
+ function hasModifier(e) {
+ return e.altKey || e.ctrlKey || e.metaKey;
+ }
+
+ function isFunctionKey(e) {
+ return e.type === "keydown" && e.keyCode >= 112 && e.keyCode <= 123;
+ }
+
+ function matchShortcut(e, shortcut) {
+ if (!shortcut) {
+ return false;
+ }
+
+ if (shortcut.ctrl != e.ctrlKey || shortcut.meta != e.metaKey) {
+ return false;
+ }
+
+ if (shortcut.alt != e.altKey || shortcut.shift != e.shiftKey) {
+ return false;
+ }
+
+ if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
+ e.preventDefault();
+ return true;
+ }
+
+ return false;
+ }
+
+ function executeShortcutAction(shortcut) {
+ return shortcut.func ? shortcut.func.call(shortcut.scope) : null;
+ }
+
+ editor.on('keyup keypress keydown', function(e) {
+ if ((hasModifier(e) || isFunctionKey(e)) && !e.isDefaultPrevented()) {
+ each(shortcuts, function(shortcut) {
+ if (matchShortcut(e, shortcut)) {
+ pendingPatterns = shortcut.subpatterns.slice(0);
+
+ if (e.type == "keydown") {
+ executeShortcutAction(shortcut);
+ }
+
+ return true;
+ }
+ });
+
+ if (matchShortcut(e, pendingPatterns[0])) {
+ if (pendingPatterns.length === 1) {
+ if (e.type == "keydown") {
+ executeShortcutAction(pendingPatterns[0]);
+ }
+ }
+
+ pendingPatterns.shift();
+ }
+ }
+ });
+
+ /**
+ * Adds a keyboard shortcut for some command or function.
+ *
+ * @method add
+ * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
+ * @param {String} desc Text description for the command.
+ * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
+ * @param {Object} scope Optional scope to execute the function in.
+ * @return {Boolean} true/false state if the shortcut was added or not.
+ */
+ self.add = function(pattern, desc, cmdFunc, scope) {
+ var cmd;
+
+ cmd = cmdFunc;
+
+ if (typeof cmdFunc === 'string') {
+ cmdFunc = function() {
+ editor.execCommand(cmd, false, null);
+ };
+ } else if (Tools.isArray(cmd)) {
+ cmdFunc = function() {
+ editor.execCommand(cmd[0], cmd[1], cmd[2]);
+ };
+ }
+
+ each(explode(Tools.trim(pattern.toLowerCase())), function(pattern) {
+ var shortcut = createShortcut(pattern, desc, cmdFunc, scope);
+ shortcuts[shortcut.id] = shortcut;
+ });
+
+ return true;
+ };
+
+ /**
+ * Remove a keyboard shortcut by pattern.
+ *
+ * @method remove
+ * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
+ * @return {Boolean} true/false state if the shortcut was removed or not.
+ */
+ self.remove = function(pattern) {
+ var shortcut = createShortcut(pattern);
+
+ if (shortcuts[shortcut.id]) {
+ delete shortcuts[shortcut.id];
+ return true;
+ }
+
+ return false;
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/file/Uploader.js
+
+/**
+ * Uploader.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
+ */
+
+/**
+ * Upload blobs or blob infos to the specified URL or handler.
+ *
+ * @private
+ * @class tinymce.file.Uploader
+ * @example
+ * var uploader = new Uploader({
+ * url: '/upload.php',
+ * basePath: '/base/path',
+ * credentials: true,
+ * handler: function(data, success, failure) {
+ * ...
+ * }
+ * });
+ *
+ * uploader.upload(blobInfos).then(function(result) {
+ * ...
+ * });
+ */
+define("tinymce/file/Uploader", [
+ "tinymce/util/Promise",
+ "tinymce/util/Tools",
+ "tinymce/util/Fun"
+], function(Promise, Tools, Fun) {
+ return function(uploadStatus, settings) {
+ var pendingPromises = {};
+
+ function filename(blobInfo) {
+ var ext, extensions;
+
+ extensions = {
+ 'image/jpeg': 'jpg',
+ 'image/jpg': 'jpg',
+ 'image/gif': 'gif',
+ 'image/png': 'png'
+ };
+
+ ext = extensions[blobInfo.blob().type.toLowerCase()] || 'dat';
+
+ return blobInfo.filename() + '.' + ext;
+ }
+
+ function pathJoin(path1, path2) {
+ if (path1) {
+ return path1.replace(/\/$/, '') + '/' + path2.replace(/^\//, '');
+ }
+
+ return path2;
+ }
+
+ function blobInfoToData(blobInfo) {
+ return {
+ id: blobInfo.id,
+ blob: blobInfo.blob,
+ base64: blobInfo.base64,
+ filename: Fun.constant(filename(blobInfo))
+ };
+ }
+
+ function defaultHandler(blobInfo, success, failure, progress) {
+ var xhr, formData;
+
+ xhr = new XMLHttpRequest();
+ xhr.open('POST', settings.url);
+ xhr.withCredentials = settings.credentials;
+
+ xhr.upload.onprogress = function(e) {
+ progress(e.loaded / e.total * 100);
+ };
+
+ xhr.onerror = function() {
+ failure("Image upload failed due to a XHR Transport error. Code: " + xhr.status);
+ };
+
+ xhr.onload = function() {
+ var json;
+
+ if (xhr.status != 200) {
+ failure("HTTP Error: " + xhr.status);
+ return;
+ }
+
+ json = JSON.parse(xhr.responseText);
+
+ if (!json || typeof json.location != "string") {
+ failure("Invalid JSON: " + xhr.responseText);
+ return;
+ }
+
+ success(pathJoin(settings.basePath, json.location));
+ };
+
+ formData = new FormData();
+ formData.append('file', blobInfo.blob(), blobInfo.filename());
+
+ xhr.send(formData);
+ }
+
+ function noUpload() {
+ return new Promise(function(resolve) {
+ resolve([]);
+ });
+ }
+
+ function handlerSuccess(blobInfo, url) {
+ return {
+ url: url,
+ blobInfo: blobInfo,
+ status: true
+ };
+ }
+
+ function handlerFailure(blobInfo, error) {
+ return {
+ url: '',
+ blobInfo: blobInfo,
+ status: false,
+ error: error
+ };
+ }
+
+ function resolvePending(blobUri, result) {
+ Tools.each(pendingPromises[blobUri], function(resolve) {
+ resolve(result);
+ });
+
+ delete pendingPromises[blobUri];
+ }
+
+ function uploadBlobInfo(blobInfo, handler, openNotification) {
+ uploadStatus.markPending(blobInfo.blobUri());
+
+ return new Promise(function(resolve) {
+ var notification, progress;
+
+ var noop = function() {
+ };
+
+ try {
+ var closeNotification = function() {
+ if (notification) {
+ notification.close();
+ progress = noop; // Once it's closed it's closed
+ }
+ };
+
+ var success = function(url) {
+ closeNotification();
+ uploadStatus.markUploaded(blobInfo.blobUri(), url);
+ resolvePending(blobInfo.blobUri(), handlerSuccess(blobInfo, url));
+ resolve(handlerSuccess(blobInfo, url));
+ };
+
+ var failure = function(error) {
+ closeNotification();
+ uploadStatus.removeFailed(blobInfo.blobUri());
+ resolvePending(blobInfo.blobUri(), handlerFailure(blobInfo, error));
+ resolve(handlerFailure(blobInfo, error));
+ };
+
+ progress = function(percent) {
+ if (percent < 0 || percent > 100) {
+ return;
+ }
+
+ if (!notification) {
+ notification = openNotification();
+ }
+
+ notification.progressBar.value(percent);
+ };
+
+ handler(blobInfoToData(blobInfo), success, failure, progress);
+ } catch (ex) {
+ resolve(handlerFailure(blobInfo, ex.message));
+ }
+ });
+ }
+
+ function isDefaultHandler(handler) {
+ return handler === defaultHandler;
+ }
+
+ function pendingUploadBlobInfo(blobInfo) {
+ var blobUri = blobInfo.blobUri();
+
+ return new Promise(function(resolve) {
+ pendingPromises[blobUri] = pendingPromises[blobUri] || [];
+ pendingPromises[blobUri].push(resolve);
+ });
+ }
+
+ function uploadBlobs(blobInfos, openNotification) {
+ blobInfos = Tools.grep(blobInfos, function(blobInfo) {
+ return !uploadStatus.isUploaded(blobInfo.blobUri());
+ });
+
+ return Promise.all(Tools.map(blobInfos, function(blobInfo) {
+ return uploadStatus.isPending(blobInfo.blobUri()) ?
+ pendingUploadBlobInfo(blobInfo) : uploadBlobInfo(blobInfo, settings.handler, openNotification);
+ }));
+ }
+
+ function upload(blobInfos, openNotification) {
+ return (!settings.url && isDefaultHandler(settings.handler)) ? noUpload() : uploadBlobs(blobInfos, openNotification);
+ }
+
+ settings = Tools.extend({
+ credentials: false,
+ // We are adding a notify argument to this (at the moment, until it doesn't work)
+ handler: defaultHandler
+ }, settings);
+
+ return {
+ upload: upload
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/file/Conversions.js
+
+/**
+ * Conversions.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 blob/uris back and forth.
+ *
+ * @private
+ * @class tinymce.file.Conversions
+ */
+define("tinymce/file/Conversions", [
+ "tinymce/util/Promise"
+], function(Promise) {
+ function blobUriToBlob(url) {
+ return new Promise(function(resolve) {
+ var xhr = new XMLHttpRequest();
+
+ xhr.open('GET', url, true);
+ xhr.responseType = 'blob';
+
+ xhr.onload = function() {
+ if (this.status == 200) {
+ resolve(this.response);
+ }
+ };
+
+ xhr.send();
+ });
+ }
+
+ function parseDataUri(uri) {
+ var type, matches;
+
+ uri = decodeURIComponent(uri).split(',');
+
+ matches = /data:([^;]+)/.exec(uri[0]);
+ if (matches) {
+ type = matches[1];
+ }
+
+ return {
+ type: type,
+ data: uri[1]
+ };
+ }
+
+ function dataUriToBlob(uri) {
+ return new Promise(function(resolve) {
+ var str, arr, i;
+
+ uri = parseDataUri(uri);
+
+ // Might throw error if data isn't proper base64
+ try {
+ str = atob(uri.data);
+ } catch (e) {
+ resolve(new Blob([]));
+ return;
+ }
+
+ arr = new Uint8Array(str.length);
+
+ for (i = 0; i < arr.length; i++) {
+ arr[i] = str.charCodeAt(i);
+ }
+
+ resolve(new Blob([arr], {type: uri.type}));
+ });
+ }
+
+ function uriToBlob(url) {
+ if (url.indexOf('blob:') === 0) {
+ return blobUriToBlob(url);
+ }
+
+ if (url.indexOf('data:') === 0) {
+ return dataUriToBlob(url);
+ }
+
+ return null;
+ }
+
+ function blobToDataUri(blob) {
+ return new Promise(function(resolve) {
+ var reader = new FileReader();
+
+ reader.onloadend = function() {
+ resolve(reader.result);
+ };
+
+ reader.readAsDataURL(blob);
+ });
+ }
+
+ return {
+ uriToBlob: uriToBlob,
+ blobToDataUri: blobToDataUri,
+ parseDataUri: parseDataUri
+ };
+});
+
+// Included from: js/tinymce/classes/file/ImageScanner.js
+
+/**
+ * ImageScanner.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
+ */
+
+/**
+ * Finds images with data uris or blob uris. If data uris are found it will convert them into blob uris.
+ *
+ * @private
+ * @class tinymce.file.ImageScanner
+ */
+define("tinymce/file/ImageScanner", [
+ "tinymce/util/Promise",
+ "tinymce/util/Arr",
+ "tinymce/util/Fun",
+ "tinymce/file/Conversions",
+ "tinymce/Env"
+], function(Promise, Arr, Fun, Conversions, Env) {
+ var count = 0;
+
+ var uniqueId = function(prefix) {
+ return (prefix || 'blobid') + (count++);
+ };
+
+ return function(uploadStatus, blobCache) {
+ var cachedPromises = {};
+
+ function findAll(elm, predicate) {
+ var images, promises;
+
+ function imageToBlobInfo(img, resolve) {
+ var base64, blobInfo;
+
+ if (img.src.indexOf('blob:') === 0) {
+ blobInfo = blobCache.getByUri(img.src);
+
+ if (blobInfo) {
+ resolve({
+ image: img,
+ blobInfo: blobInfo
+ });
+ } else {
+ Conversions.uriToBlob(img.src).then(function (blob) {
+ Conversions.blobToDataUri(blob).then(function (dataUri) {
+ base64 = Conversions.parseDataUri(dataUri).data;
+ blobInfo = blobCache.create(uniqueId(), blob, base64);
+ blobCache.add(blobInfo);
+
+ resolve({
+ image: img,
+ blobInfo: blobInfo
+ });
+ });
+ });
+ }
+
+ return;
+ }
+
+ base64 = Conversions.parseDataUri(img.src).data;
+ blobInfo = blobCache.findFirst(function(cachedBlobInfo) {
+ return cachedBlobInfo.base64() === base64;
+ });
+
+ if (blobInfo) {
+ resolve({
+ image: img,
+ blobInfo: blobInfo
+ });
+ } else {
+ Conversions.uriToBlob(img.src).then(function(blob) {
+ blobInfo = blobCache.create(uniqueId(), blob, base64);
+ blobCache.add(blobInfo);
+
+ resolve({
+ image: img,
+ blobInfo: blobInfo
+ });
+ });
+ }
+ }
+
+ if (!predicate) {
+ predicate = Fun.constant(true);
+ }
+
+ images = Arr.filter(elm.getElementsByTagName('img'), function(img) {
+ var src = img.src;
+
+ if (!Env.fileApi) {
+ return false;
+ }
+
+ if (img.hasAttribute('data-mce-bogus')) {
+ return false;
+ }
+
+ if (img.hasAttribute('data-mce-placeholder')) {
+ return false;
+ }
+
+ if (!src || src == Env.transparentSrc) {
+ return false;
+ }
+
+ if (src.indexOf('blob:') === 0) {
+ return !uploadStatus.isUploaded(src);
+ }
+
+ if (src.indexOf('data:') === 0) {
+ return predicate(img);
+ }
+
+ return false;
+ });
+
+ promises = Arr.map(images, function(img) {
+ var newPromise;
+
+ if (cachedPromises[img.src]) {
+ // Since the cached promise will return the cached image
+ // We need to wrap it and resolve with the actual image
+ return new Promise(function(resolve) {
+ cachedPromises[img.src].then(function(imageInfo) {
+ resolve({
+ image: img,
+ blobInfo: imageInfo.blobInfo
+ });
+ });
+ });
+ }
+
+ newPromise = new Promise(function(resolve) {
+ imageToBlobInfo(img, resolve);
+ }).then(function(result) {
+ delete cachedPromises[result.image.src];
+ return result;
+ })['catch'](function(error) {
+ delete cachedPromises[img.src];
+ return error;
+ });
+
+ cachedPromises[img.src] = newPromise;
+
+ return newPromise;
+ });
+
+ return Promise.all(promises);
+ }
+
+ return {
+ findAll: findAll
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/file/BlobCache.js
+
+/**
+ * BlobCache.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
+ */
+
+/**
+ * Hold blob info objects where a blob has extra internal information.
+ *
+ * @private
+ * @class tinymce.file.BlobCache
+ */
+define("tinymce/file/BlobCache", [
+ "tinymce/util/Arr",
+ "tinymce/util/Fun"
+], function(Arr, Fun) {
+ return function() {
+ var cache = [], constant = Fun.constant;
+
+ function create(id, blob, base64, filename) {
+ return {
+ id: constant(id),
+ filename: constant(filename || id),
+ blob: constant(blob),
+ base64: constant(base64),
+ blobUri: constant(URL.createObjectURL(blob))
+ };
+ }
+
+ function add(blobInfo) {
+ if (!get(blobInfo.id())) {
+ cache.push(blobInfo);
+ }
+ }
+
+ function get(id) {
+ return findFirst(function(cachedBlobInfo) {
+ return cachedBlobInfo.id() === id;
+ });
+ }
+
+ function findFirst(predicate) {
+ return Arr.filter(cache, predicate)[0];
+ }
+
+ function getByUri(blobUri) {
+ return findFirst(function(blobInfo) {
+ return blobInfo.blobUri() == blobUri;
+ });
+ }
+
+ function removeByUri(blobUri) {
+ cache = Arr.filter(cache, function(blobInfo) {
+ if (blobInfo.blobUri() === blobUri) {
+ URL.revokeObjectURL(blobInfo.blobUri());
+ return false;
+ }
+
+ return true;
+ });
+ }
+
+ function destroy() {
+ Arr.each(cache, function(cachedBlobInfo) {
+ URL.revokeObjectURL(cachedBlobInfo.blobUri());
+ });
+
+ cache = [];
+ }
+
+ return {
+ create: create,
+ add: add,
+ get: get,
+ getByUri: getByUri,
+ findFirst: findFirst,
+ removeByUri: removeByUri,
+ destroy: destroy
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/file/UploadStatus.js
+
+/**
+ * UploadStatus.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
+ */
+
+/**
+ * Holds the current status of a blob uri, if it's pending or uploaded and what the result urls was.
+ *
+ * @private
+ * @class tinymce.file.UploadStatus
+ */
+define("tinymce/file/UploadStatus", [
+], function() {
+ return function() {
+ var PENDING = 1, UPLOADED = 2;
+ var blobUriStatuses = {};
+
+ function createStatus(status, resultUri) {
+ return {
+ status: status,
+ resultUri: resultUri
+ };
+ }
+
+ function hasBlobUri(blobUri) {
+ return blobUri in blobUriStatuses;
+ }
+
+ function getResultUri(blobUri) {
+ var result = blobUriStatuses[blobUri];
+
+ return result ? result.resultUri : null;
+ }
+
+ function isPending(blobUri) {
+ return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === PENDING : false;
+ }
+
+ function isUploaded(blobUri) {
+ return hasBlobUri(blobUri) ? blobUriStatuses[blobUri].status === UPLOADED : false;
+ }
+
+ function markPending(blobUri) {
+ blobUriStatuses[blobUri] = createStatus(PENDING, null);
+ }
+
+ function markUploaded(blobUri, resultUri) {
+ blobUriStatuses[blobUri] = createStatus(UPLOADED, resultUri);
+ }
+
+ function removeFailed(blobUri) {
+ delete blobUriStatuses[blobUri];
+ }
+
+ function destroy() {
+ blobUriStatuses = {};
+ }
+
+ return {
+ hasBlobUri: hasBlobUri,
+ getResultUri: getResultUri,
+ isPending: isPending,
+ isUploaded: isUploaded,
+ markPending: markPending,
+ markUploaded: markUploaded,
+ removeFailed: removeFailed,
+ destroy: destroy
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/ErrorReporter.js
+
+/**
+ * ErrorReporter.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
+ */
+
+/**
+ * Various error reporting helper functions.
+ *
+ * @class tinymce.ErrorReporter
+ * @private
+ */
+define("tinymce/ErrorReporter", [
+ "tinymce/AddOnManager"
+], function (AddOnManager) {
+ var PluginManager = AddOnManager.PluginManager;
+
+ var resolvePluginName = function (targetUrl, suffix) {
+ for (var name in PluginManager.urls) {
+ var matchUrl = PluginManager.urls[name] + '/plugin' + suffix + '.js';
+ if (matchUrl === targetUrl) {
+ return name;
+ }
+ }
+
+ return null;
+ };
+
+ var pluginUrlToMessage = function (editor, url) {
+ var plugin = resolvePluginName(url, editor.suffix);
+ return plugin ?
+ 'Failed to load plugin: ' + plugin + ' from url ' + url :
+ 'Failed to load plugin url: ' + url;
+ };
+
+ var displayNotification = function (editor, message) {
+ editor.notificationManager.open({
+ type: 'error',
+ text: message
+ });
+ };
+
+ var displayError = function (editor, message) {
+ if (editor._skinLoaded) {
+ displayNotification(editor, message);
+ } else {
+ editor.on('SkinLoaded', function () {
+ displayNotification(editor, message);
+ });
+ }
+ };
+
+ var uploadError = function (editor, message) {
+ displayError(editor, 'Failed to upload image: ' + message);
+ };
+
+ var pluginLoadError = function (editor, url) {
+ displayError(editor, pluginUrlToMessage(editor, url));
+ };
+
+ return {
+ pluginLoadError: pluginLoadError,
+ uploadError: uploadError
+ };
+});
+
+// Included from: js/tinymce/classes/EditorUpload.js
+
+/**
+ * EditorUpload.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 image uploads, updates undo stack and patches over various internal functions.
+ *
+ * @private
+ * @class tinymce.EditorUpload
+ */
+define("tinymce/EditorUpload", [
+ "tinymce/util/Arr",
+ "tinymce/file/Uploader",
+ "tinymce/file/ImageScanner",
+ "tinymce/file/BlobCache",
+ "tinymce/file/UploadStatus",
+ "tinymce/ErrorReporter"
+], function(Arr, Uploader, ImageScanner, BlobCache, UploadStatus, ErrorReporter) {
+ return function(editor) {
+ var blobCache = new BlobCache(), uploader, imageScanner, settings = editor.settings;
+ var uploadStatus = new UploadStatus();
+
+ function aliveGuard(callback) {
+ return function(result) {
+ if (editor.selection) {
+ return callback(result);
+ }
+
+ return [];
+ };
+ }
+
+ function cacheInvalidator() {
+ return '?' + (new Date()).getTime();
+ }
+
+ // Replaces strings without regexps to avoid FF regexp to big issue
+ function replaceString(content, search, replace) {
+ var index = 0;
+
+ do {
+ index = content.indexOf(search, index);
+
+ if (index !== -1) {
+ content = content.substring(0, index) + replace + content.substr(index + search.length);
+ index += replace.length - search.length + 1;
+ }
+ } while (index !== -1);
+
+ return content;
+ }
+
+ function replaceImageUrl(content, targetUrl, replacementUrl) {
+ content = replaceString(content, 'src="' + targetUrl + '"', 'src="' + replacementUrl + '"');
+ content = replaceString(content, 'data-mce-src="' + targetUrl + '"', 'data-mce-src="' + replacementUrl + '"');
+
+ return content;
+ }
+
+ function replaceUrlInUndoStack(targetUrl, replacementUrl) {
+ Arr.each(editor.undoManager.data, function(level) {
+ if (level.type === 'fragmented') {
+ level.fragments = Arr.map(level.fragments, function (fragment) {
+ return replaceImageUrl(fragment, targetUrl, replacementUrl);
+ });
+ } else {
+ level.content = replaceImageUrl(level.content, targetUrl, replacementUrl);
+ }
+ });
+ }
+
+ function openNotification() {
+ return editor.notificationManager.open({
+ text: editor.translate('Image uploading...'),
+ type: 'info',
+ timeout: -1,
+ progressBar: true
+ });
+ }
+
+ function replaceImageUri(image, resultUri) {
+ blobCache.removeByUri(image.src);
+ replaceUrlInUndoStack(image.src, resultUri);
+
+ editor.$(image).attr({
+ src: settings.images_reuse_filename ? resultUri + cacheInvalidator() : resultUri,
+ 'data-mce-src': editor.convertURL(resultUri, 'src')
+ });
+ }
+
+ function uploadImages(callback) {
+ if (!uploader) {
+ uploader = new Uploader(uploadStatus, {
+ url: settings.images_upload_url,
+ basePath: settings.images_upload_base_path,
+ credentials: settings.images_upload_credentials,
+ handler: settings.images_upload_handler
+ });
+ }
+
+ return scanForImages().then(aliveGuard(function(imageInfos) {
+ var blobInfos;
+
+ blobInfos = Arr.map(imageInfos, function(imageInfo) {
+ return imageInfo.blobInfo;
+ });
+
+ return uploader.upload(blobInfos, openNotification).then(aliveGuard(function(result) {
+ result = Arr.map(result, function(uploadInfo, index) {
+ var image = imageInfos[index].image;
+
+ if (uploadInfo.status && editor.settings.images_replace_blob_uris !== false) {
+ replaceImageUri(image, uploadInfo.url);
+ } else if (uploadInfo.error) {
+ ErrorReporter.uploadError(editor, uploadInfo.error);
+ }
+
+ return {
+ element: image,
+ status: uploadInfo.status
+ };
+ });
+
+ if (callback) {
+ callback(result);
+ }
+
+ return result;
+ }));
+ }));
+ }
+
+ function uploadImagesAuto(callback) {
+ if (settings.automatic_uploads !== false) {
+ return uploadImages(callback);
+ }
+ }
+
+ function isValidDataUriImage(imgElm) {
+ return settings.images_dataimg_filter ? settings.images_dataimg_filter(imgElm) : true;
+ }
+
+ function scanForImages() {
+ if (!imageScanner) {
+ imageScanner = new ImageScanner(uploadStatus, blobCache);
+ }
+
+ return imageScanner.findAll(editor.getBody(), isValidDataUriImage).then(aliveGuard(function(result) {
+ Arr.each(result, function(resultItem) {
+ replaceUrlInUndoStack(resultItem.image.src, resultItem.blobInfo.blobUri());
+ resultItem.image.src = resultItem.blobInfo.blobUri();
+ resultItem.image.removeAttribute('data-mce-src');
+ });
+
+ return result;
+ }));
+ }
+
+ function destroy() {
+ blobCache.destroy();
+ uploadStatus.destroy();
+ imageScanner = uploader = null;
+ }
+
+ function replaceBlobUris(content) {
+ return content.replace(/src="(blob:[^"]+)"/g, function(match, blobUri) {
+ var resultUri = uploadStatus.getResultUri(blobUri);
+
+ if (resultUri) {
+ return 'src="' + resultUri + '"';
+ }
+
+ var blobInfo = blobCache.getByUri(blobUri);
+
+ if (!blobInfo) {
+ blobInfo = Arr.reduce(editor.editorManager.editors, function(result, editor) {
+ return result || editor.editorUpload.blobCache.getByUri(blobUri);
+ }, null);
+ }
+
+ if (blobInfo) {
+ return 'src="data:' + blobInfo.blob().type + ';base64,' + blobInfo.base64() + '"';
+ }
+
+ return match;
+ });
+ }
+
+ editor.on('setContent', function() {
+ if (editor.settings.automatic_uploads !== false) {
+ uploadImagesAuto();
+ } else {
+ scanForImages();
+ }
+ });
+
+ editor.on('RawSaveContent', function(e) {
+ e.content = replaceBlobUris(e.content);
+ });
+
+ editor.on('getContent', function(e) {
+ if (e.source_view || e.format == 'raw') {
+ return;
+ }
+
+ e.content = replaceBlobUris(e.content);
+ });
+
+ editor.on('PostRender', function() {
+ editor.parser.addNodeFilter('img', function(images) {
+ Arr.each(images, function(img) {
+ var src = img.attr('src');
+
+ if (blobCache.getByUri(src)) {
+ return;
+ }
+
+ var resultUri = uploadStatus.getResultUri(src);
+ if (resultUri) {
+ img.attr('src', resultUri);
+ }
+ });
+ });
+ });
+
+ return {
+ blobCache: blobCache,
+ uploadImages: uploadImages,
+ uploadImagesAuto: uploadImagesAuto,
+ scanForImages: scanForImages,
+ destroy: destroy
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/caret/FakeCaret.js
+
+/**
+ * FakeCaret.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 rendering a fake visual caret.
+ *
+ * @private
+ * @class tinymce.caret.FakeCaret
+ */
+define("tinymce/caret/FakeCaret", [
+ "tinymce/caret/CaretContainer",
+ "tinymce/caret/CaretPosition",
+ "tinymce/dom/NodeType",
+ "tinymce/dom/RangeUtils",
+ "tinymce/dom/DomQuery",
+ "tinymce/geom/ClientRect",
+ "tinymce/util/Delay"
+], function(CaretContainer, CaretPosition, NodeType, RangeUtils, $, ClientRect, Delay) {
+ var isContentEditableFalse = NodeType.isContentEditableFalse;
+
+ return function(rootNode, isBlock) {
+ var cursorInterval, $lastVisualCaret, caretContainerNode;
+
+ function getAbsoluteClientRect(node, before) {
+ var clientRect = ClientRect.collapse(node.getBoundingClientRect(), before),
+ docElm, scrollX, scrollY, margin, rootRect;
+
+ if (rootNode.tagName == 'BODY') {
+ docElm = rootNode.ownerDocument.documentElement;
+ scrollX = rootNode.scrollLeft || docElm.scrollLeft;
+ scrollY = rootNode.scrollTop || docElm.scrollTop;
+ } else {
+ rootRect = rootNode.getBoundingClientRect();
+ scrollX = rootNode.scrollLeft - rootRect.left;
+ scrollY = rootNode.scrollTop - rootRect.top;
+ }
+
+ clientRect.left += scrollX;
+ clientRect.right += scrollX;
+ clientRect.top += scrollY;
+ clientRect.bottom += scrollY;
+ clientRect.width = 1;
+
+ margin = node.offsetWidth - node.clientWidth;
+
+ if (margin > 0) {
+ if (before) {
+ margin *= -1;
+ }
+
+ clientRect.left += margin;
+ clientRect.right += margin;
+ }
+
+ return clientRect;
+ }
+
+ function trimInlineCaretContainers() {
+ var contentEditableFalseNodes, node, sibling, i, data;
+
+ contentEditableFalseNodes = $('*[contentEditable=false]', rootNode);
+ for (i = 0; i < contentEditableFalseNodes.length; i++) {
+ node = contentEditableFalseNodes[i];
+
+ sibling = node.previousSibling;
+ if (CaretContainer.endsWithCaretContainer(sibling)) {
+ data = sibling.data;
+
+ if (data.length == 1) {
+ sibling.parentNode.removeChild(sibling);
+ } else {
+ sibling.deleteData(data.length - 1, 1);
+ }
+ }
+
+ sibling = node.nextSibling;
+ if (CaretContainer.startsWithCaretContainer(sibling)) {
+ data = sibling.data;
+
+ if (data.length == 1) {
+ sibling.parentNode.removeChild(sibling);
+ } else {
+ sibling.deleteData(0, 1);
+ }
+ }
+ }
+
+ return null;
+ }
+
+ function show(before, node) {
+ var clientRect, rng;
+
+ hide();
+
+ if (isBlock(node)) {
+ caretContainerNode = CaretContainer.insertBlock('p', node, before);
+ clientRect = getAbsoluteClientRect(node, before);
+ $(caretContainerNode).css('top', clientRect.top);
+
+ $lastVisualCaret = $('').css(clientRect).appendTo(rootNode);
+
+ if (before) {
+ $lastVisualCaret.addClass('mce-visual-caret-before');
+ }
+
+ startBlink();
+
+ rng = node.ownerDocument.createRange();
+ rng.setStart(caretContainerNode, 0);
+ rng.setEnd(caretContainerNode, 0);
+ } else {
+ caretContainerNode = CaretContainer.insertInline(node, before);
+ rng = node.ownerDocument.createRange();
+
+ if (isContentEditableFalse(caretContainerNode.nextSibling)) {
+ rng.setStart(caretContainerNode, 0);
+ rng.setEnd(caretContainerNode, 0);
+ } else {
+ rng.setStart(caretContainerNode, 1);
+ rng.setEnd(caretContainerNode, 1);
+ }
+
+ return rng;
+ }
+
+ return rng;
+ }
+
+ function hide() {
+ trimInlineCaretContainers();
+
+ if (caretContainerNode) {
+ CaretContainer.remove(caretContainerNode);
+ caretContainerNode = null;
+ }
+
+ if ($lastVisualCaret) {
+ $lastVisualCaret.remove();
+ $lastVisualCaret = null;
+ }
+
+ clearInterval(cursorInterval);
+ }
+
+ function startBlink() {
+ cursorInterval = Delay.setInterval(function() {
+ $('div.mce-visual-caret', rootNode).toggleClass('mce-visual-caret-hidden');
+ }, 500);
+ }
+
+ function destroy() {
+ Delay.clearInterval(cursorInterval);
+ }
+
+ function getCss() {
+ return (
+ '.mce-visual-caret {' +
+ 'position: absolute;' +
+ 'background-color: black;' +
+ 'background-color: currentcolor;' +
+ '}' +
+ '.mce-visual-caret-hidden {' +
+ 'display: none;' +
+ '}' +
+ '*[data-mce-caret] {' +
+ 'position: absolute;' +
+ 'left: -1000px;' +
+ 'right: auto;' +
+ 'top: 0;' +
+ 'margin: 0;' +
+ 'padding: 0;' +
+ '}'
+ );
+ }
+
+ return {
+ show: show,
+ hide: hide,
+ getCss: getCss,
+ destroy: destroy
+ };
+ };
+});
+
+// Included from: js/tinymce/classes/dom/Dimensions.js
+
+/**
+ * Dimensions.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 measures nodes and returns client rects. The client rects has an
+ * extra node property.
+ *
+ * @private
+ * @class tinymce.dom.Dimensions
+ */
+define("tinymce/dom/Dimensions", [
+ "tinymce/util/Arr",
+ "tinymce/dom/NodeType",
+ "tinymce/geom/ClientRect"
+], function(Arr, NodeType, ClientRect) {
+
+ function getClientRects(node) {
+ function toArrayWithNode(clientRects) {
+ return Arr.map(clientRects, function(clientRect) {
+ clientRect = ClientRect.clone(clientRect);
+ clientRect.node = node;
+
+ return clientRect;
+ });
+ }
+
+ if (Arr.isArray(node)) {
+ return Arr.reduce(node, function(result, node) {
+ return result.concat(getClientRects(node));
+ }, []);
+ }
+
+ if (NodeType.isElement(node)) {
+ return toArrayWithNode(node.getClientRects());
+ }
+
+ if (NodeType.isText(node)) {
+ var rng = node.ownerDocument.createRange();
+
+ rng.setStart(node, 0);
+ rng.setEnd(node, node.data.length);
+
+ return toArrayWithNode(rng.getClientRects());
+ }
+ }
+
+ return {
+ /**
+ * Returns the client rects for a specific node.
+ *
+ * @method getClientRects
+ * @param {Array/DOMNode} node Node or array of nodes to get client rects on.
+ * @param {Array} Array of client rects with a extra node property.
+ */
+ getClientRects: getClientRects
+ };
+});
+
+// Included from: js/tinymce/classes/caret/LineWalker.js
+
+/**
+ * LineWalker.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 lets you walk the document line by line
+ * returing nodes and client rects for each line.
+ *
+ * @private
+ * @class tinymce.caret.LineWalker
+ */
+define("tinymce/caret/LineWalker", [
+ "tinymce/util/Fun",
+ "tinymce/util/Arr",
+ "tinymce/dom/Dimensions",
+ "tinymce/caret/CaretCandidate",
+ "tinymce/caret/CaretUtils",
+ "tinymce/caret/CaretWalker",
+ "tinymce/caret/CaretPosition",
+ "tinymce/geom/ClientRect"
+], function(Fun, Arr, Dimensions, CaretCandidate, CaretUtils, CaretWalker, CaretPosition, ClientRect) {
+ var curry = Fun.curry;
+
+ function findUntil(direction, rootNode, predicateFn, node) {
+ while ((node = CaretUtils.findNode(node, direction, CaretCandidate.isEditableCaretCandidate, rootNode))) {
+ if (predicateFn(node)) {
+ return;
+ }
+ }
+ }
+
+ function walkUntil(direction, isAboveFn, isBeflowFn, rootNode, predicateFn, caretPosition) {
+ var line = 0, node, result = [], targetClientRect;
+
+ function add(node) {
+ var i, clientRect, clientRects;
+
+ clientRects = Dimensions.getClientRects(node);
+ if (direction == -1) {
+ clientRects = clientRects.reverse();
+ }
+
+ for (i = 0; i < clientRects.length; i++) {
+ clientRect = clientRects[i];
+ if (isBeflowFn(clientRect, targetClientRect)) {
+ continue;
+ }
+
+ if (result.length > 0 && isAboveFn(clientRect, Arr.last(result))) {
+ line++;
+ }
+
+ clientRect.line = line;
+
+ if (predicateFn(clientRect)) {
+ return true;
+ }
+
+ result.push(clientRect);
+ }
+ }
+
+ targetClientRect = Arr.last(caretPosition.getClientRects());
+ if (!targetClientRect) {
+ return result;
+ }
+
+ node = caretPosition.getNode();
+ add(node);
+ findUntil(direction, rootNode, add, node);
+
+ return result;
+ }
+
+ function aboveLineNumber(lineNumber, clientRect) {
+ return clientRect.line > lineNumber;
+ }
+
+ function isLine(lineNumber, clientRect) {
+ return clientRect.line === lineNumber;
+ }
+
+ var upUntil = curry(walkUntil, -1, ClientRect.isAbove, ClientRect.isBelow);
+ var downUntil = curry(walkUntil, 1, ClientRect.isBelow, ClientRect.isAbove);
+
+ function positionsUntil(direction, rootNode, predicateFn, node) {
+ var caretWalker = new CaretWalker(rootNode), walkFn, isBelowFn, isAboveFn,
+ caretPosition, result = [], line = 0, clientRect, targetClientRect;
+
+ function getClientRect(caretPosition) {
+ if (direction == 1) {
+ return Arr.last(caretPosition.getClientRects());
+ }
+
+ return Arr.last(caretPosition.getClientRects());
+ }
+
+ if (direction == 1) {
+ walkFn = caretWalker.next;
+ isBelowFn = ClientRect.isBelow;
+ isAboveFn = ClientRect.isAbove;
+ caretPosition = CaretPosition.after(node);
+ } else {
+ walkFn = caretWalker.prev;
+ isBelowFn = ClientRect.isAbove;
+ isAboveFn = ClientRect.isBelow;
+ caretPosition = CaretPosition.before(node);
+ }
+
+ targetClientRect = getClientRect(caretPosition);
+
+ do {
+ if (!caretPosition.isVisible()) {
+ continue;
+ }
+
+ clientRect = getClientRect(caretPosition);
+
+ if (isAboveFn(clientRect, targetClientRect)) {
+ continue;
+ }
+
+ if (result.length > 0 && isBelowFn(clientRect, Arr.last(result))) {
+ line++;
+ }
+
+ clientRect = ClientRect.clone(clientRect);
+ clientRect.position = caretPosition;
+ clientRect.line = line;
+
+ if (predicateFn(clientRect)) {
+ return result;
+ }
+
+ result.push(clientRect);
+ } while ((caretPosition = walkFn(caretPosition)));
+
+ return result;
+ }
+
+ return {
+ upUntil: upUntil,
+ downUntil: downUntil,
+
+ /**
+ * Find client rects with line and caret position until the predicate returns true.
+ *
+ * @method positionsUntil
+ * @param {Number} direction Direction forward/backward 1/-1.
+ * @param {DOMNode} rootNode Root node to walk within.
+ * @param {function} predicateFn Gets the client rect as it's input.
+ * @param {DOMNode} node Node to start walking from.
+ * @return {Array} Array of client rects with line and position properties.
+ */
+ positionsUntil: positionsUntil,
+
+ isAboveLine: curry(aboveLineNumber),
+ isLine: curry(isLine)
+ };
+});
+
+// Included from: js/tinymce/classes/caret/LineUtils.js
+
+/**
+ * LineUtils.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 for working with lines.
+ *
+ * @private
+ * @class tinymce.caret.LineUtils
+ */
+define("tinymce/caret/LineUtils", [
+ "tinymce/util/Fun",
+ "tinymce/util/Arr",
+ "tinymce/dom/NodeType",
+ "tinymce/dom/Dimensions",
+ "tinymce/geom/ClientRect",
+ "tinymce/caret/CaretUtils",
+ "tinymce/caret/CaretCandidate"
+], function(Fun, Arr, NodeType, Dimensions, ClientRect, CaretUtils, CaretCandidate) {
+ var isContentEditableFalse = NodeType.isContentEditableFalse,
+ findNode = CaretUtils.findNode,
+ curry = Fun.curry;
+
+ function distanceToRectLeft(clientRect, clientX) {
+ return Math.abs(clientRect.left - clientX);
+ }
+
+ function distanceToRectRight(clientRect, clientX) {
+ return Math.abs(clientRect.right - clientX);
+ }
+
+ function findClosestClientRect(clientRects, clientX) {
+ function isInside(clientX, clientRect) {
+ return clientX >= clientRect.left && clientX <= clientRect.right;
+ }
+
+ return Arr.reduce(clientRects, function(oldClientRect, clientRect) {
+ var oldDistance, newDistance;
+
+ oldDistance = Math.min(distanceToRectLeft(oldClientRect, clientX), distanceToRectRight(oldClientRect, clientX));
+ newDistance = Math.min(distanceToRectLeft(clientRect, clientX), distanceToRectRight(clientRect, clientX));
+
+ if (isInside(clientX, clientRect)) {
+ return clientRect;
+ }
+
+ if (isInside(clientX, oldClientRect)) {
+ return oldClientRect;
+ }
+
+ // cE=false has higher priority
+ if (newDistance == oldDistance && isContentEditableFalse(clientRect.node)) {
+ return clientRect;
+ }
+
+ if (newDistance < oldDistance) {
+ return clientRect;
+ }
+
+ return oldClientRect;
+ });
+ }
+
+ function walkUntil(direction, rootNode, predicateFn, node) {
+ while ((node = findNode(node, direction, CaretCandidate.isEditableCaretCandidate, rootNode))) {
+ if (predicateFn(node)) {
+ return;
+ }
+ }
+ }
+
+ function findLineNodeRects(rootNode, targetNodeRect) {
+ var clientRects = [];
+
+ function collect(checkPosFn, node) {
+ var lineRects;
+
+ lineRects = Arr.filter(Dimensions.getClientRects(node), function(clientRect) {
+ return !checkPosFn(clientRect, targetNodeRect);
+ });
+
+ clientRects = clientRects.concat(lineRects);
+
+ return lineRects.length === 0;
+ }
+
+ clientRects.push(targetNodeRect);
+ walkUntil(-1, rootNode, curry(collect, ClientRect.isAbove), targetNodeRect.node);
+ walkUntil(1, rootNode, curry(collect, ClientRect.isBelow), targetNodeRect.node);
+
+ return clientRects;
+ }
+
+ function getContentEditableFalseChildren(rootNode) {
+ return Arr.filter(Arr.toArray(rootNode.getElementsByTagName('*')), isContentEditableFalse);
+ }
+
+ function caretInfo(clientRect, clientX) {
+ return {
+ node: clientRect.node,
+ before: distanceToRectLeft(clientRect, clientX) < distanceToRectRight(clientRect, clientX)
+ };
+ }
+
+ function closestCaret(rootNode, clientX, clientY) {
+ var contentEditableFalseNodeRects, closestNodeRect;
+
+ contentEditableFalseNodeRects = Dimensions.getClientRects(getContentEditableFalseChildren(rootNode));
+ contentEditableFalseNodeRects = Arr.filter(contentEditableFalseNodeRects, function(clientRect) {
+ return clientY >= clientRect.top && clientY <= clientRect.bottom;
+ });
+
+ closestNodeRect = findClosestClientRect(contentEditableFalseNodeRects, clientX);
+ if (closestNodeRect) {
+ closestNodeRect = findClosestClientRect(findLineNodeRects(rootNode, closestNodeRect), clientX);
+ if (closestNodeRect && isContentEditableFalse(closestNodeRect.node)) {
+ return caretInfo(closestNodeRect, clientX);
+ }
+ }
+
+ return null;
+ }
+
+ return {
+ findClosestClientRect: findClosestClientRect,
+ findLineNodeRects: findLineNodeRects,
+ closestCaret: closestCaret
+ };
+});
+
+// Included from: js/tinymce/classes/dom/MousePosition.js
+
+/**
+ * MousePosition.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 calculates an absolute coordinate inside the editor body for both local and global mouse events.
+ *
+ * @private
+ * @class tinymce.dom.MousePosition
+ */
+define("tinymce/dom/MousePosition", [
+], function() {
+ var getAbsolutePosition = function (elm) {
+ var doc, docElem, win, clientRect;
+
+ clientRect = elm.getBoundingClientRect();
+ doc = elm.ownerDocument;
+ docElem = doc.documentElement;
+ win = doc.defaultView;
+
+ return {
+ top: clientRect.top + win.pageYOffset - docElem.clientTop,
+ left: clientRect.left + win.pageXOffset - docElem.clientLeft
+ };
+ };
+
+ var getBodyPosition = function (editor) {
+ return editor.inline ? getAbsolutePosition(editor.getBody()) : {left: 0, top: 0};
+ };
+
+ var getScrollPosition = function (editor) {
+ var body = editor.getBody();
+ return editor.inline ? {left: body.scrollLeft, top: body.scrollTop} : {left: 0, top: 0};
+ };
+
+ var getBodyScroll = function (editor) {
+ var body = editor.getBody(), docElm = editor.getDoc().documentElement;
+ var inlineScroll = {left: body.scrollLeft, top: body.scrollTop};
+ var iframeScroll = {left: body.scrollLeft || docElm.scrollLeft, top: body.scrollTop || docElm.scrollTop};
+
+ return editor.inline ? inlineScroll : iframeScroll;
+ };
+
+ var getMousePosition = function (editor, event) {
+ if (event.target.ownerDocument !== editor.getDoc()) {
+ var iframePosition = getAbsolutePosition(editor.getContentAreaContainer());
+ var scrollPosition = getBodyScroll(editor);
+
+ return {
+ left: event.pageX - iframePosition.left + scrollPosition.left,
+ top: event.pageY - iframePosition.top + scrollPosition.top
+ };
+ }
+
+ return {
+ left: event.pageX,
+ top: event.pageY
+ };
+ };
+
+ var calculatePosition = function (bodyPosition, scrollPosition, mousePosition) {
+ return {
+ pageX: (mousePosition.left - bodyPosition.left) + scrollPosition.left,
+ pageY: (mousePosition.top - bodyPosition.top) + scrollPosition.top
+ };
+ };
+
+ var calc = function (editor, event) {
+ return calculatePosition(getBodyPosition(editor), getScrollPosition(editor), getMousePosition(editor, event));
+ };
+
+ return {
+ calc: calc
+ };
+});
+
+// Included from: js/tinymce/classes/DragDropOverrides.js
+
+/**
+ * DragDropOverrides.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 overriding the drag/drop logic of the editor.
+ *
+ * @private
+ * @class tinymce.DragDropOverrides
+ */
+define("tinymce/DragDropOverrides", [
+ "tinymce/dom/NodeType",
+ "tinymce/util/Arr",
+ "tinymce/util/Fun",
+ "tinymce/util/Delay",
+ "tinymce/dom/DOMUtils",
+ "tinymce/dom/MousePosition"
+], function(
+ NodeType, Arr, Fun, Delay, DOMUtils, MousePosition
+) {
+ var isContentEditableFalse = NodeType.isContentEditableFalse,
+ isContentEditableTrue = NodeType.isContentEditableTrue;
+
+ var isDraggable = function (rootElm, elm) {
+ return isContentEditableFalse(elm) && elm !== rootElm;
+ };
+
+ var isValidDropTarget = function (editor, targetElement, dragElement) {
+ if (targetElement === dragElement || editor.dom.isChildOf(targetElement, dragElement)) {
+ return false;
+ }
+
+ if (isContentEditableFalse(targetElement)) {
+ return false;
+ }
+
+ return true;
+ };
+
+ var cloneElement = function (elm) {
+ var cloneElm = elm.cloneNode(true);
+ cloneElm.removeAttribute('data-mce-selected');
+ return cloneElm;
+ };
+
+ var createGhost = function (editor, elm, width, height) {
+ var clonedElm = elm.cloneNode(true);
+
+ editor.dom.setStyles(clonedElm, {width: width, height: height});
+ editor.dom.setAttrib(clonedElm, 'data-mce-selected', null);
+
+ var ghostElm = editor.dom.create('div', {
+ 'class': 'mce-drag-container',
+ 'data-mce-bogus': 'all',
+ unselectable: 'on',
+ contenteditable: 'false'
+ });
+
+ editor.dom.setStyles(ghostElm, {
+ position: 'absolute',
+ opacity: 0.5,
+ overflow: 'hidden',
+ border: 0,
+ padding: 0,
+ margin: 0,
+ width: width,
+ height: height
+ });
+
+ editor.dom.setStyles(clonedElm, {
+ margin: 0,
+ boxSizing: 'border-box'
+ });
+
+ ghostElm.appendChild(clonedElm);
+
+ return ghostElm;
+ };
+
+ var appendGhostToBody = function (ghostElm, bodyElm) {
+ if (ghostElm.parentNode !== bodyElm) {
+ bodyElm.appendChild(ghostElm);
+ }
+ };
+
+ var moveGhost = function (ghostElm, position, width, height, maxX, maxY) {
+ var overflowX = 0, overflowY = 0;
+
+ ghostElm.style.left = position.pageX + 'px';
+ ghostElm.style.top = position.pageY + 'px';
+
+ if (position.pageX + width > maxX) {
+ overflowX = (position.pageX + width) - maxX;
+ }
+
+ if (position.pageY + height > maxY) {
+ overflowY = (position.pageY + height) - maxY;
+ }
+
+ ghostElm.style.width = (width - overflowX) + 'px';
+ ghostElm.style.height = (height - overflowY) + 'px';
+ };
+
+ var removeElement = function (elm) {
+ if (elm && elm.parentNode) {
+ elm.parentNode.removeChild(elm);
+ }
+ };
+
+ var isLeftMouseButtonPressed = function (e) {
+ return e.button === 0;
+ };
+
+ var hasDraggableElement = function (state) {
+ return state.element;
+ };
+
+ var applyRelPos = function (state, position) {
+ return {
+ pageX: position.pageX - state.relX,
+ pageY: position.pageY + 5
+ };
+ };
+
+ var start = function (state, editor) {
+ return function (e) {
+ if (isLeftMouseButtonPressed(e)) {
+ var ceElm = Arr.find(editor.dom.getParents(e.target), Fun.or(isContentEditableFalse, isContentEditableTrue));
+
+ if (isDraggable(editor.getBody(), ceElm)) {
+ var elmPos = editor.dom.getPos(ceElm);
+ var bodyElm = editor.getBody();
+ var docElm = editor.getDoc().documentElement;
+
+ state.element = ceElm;
+ state.screenX = e.screenX;
+ state.screenY = e.screenY;
+ state.maxX = (editor.inline ? bodyElm.scrollWidth : docElm.offsetWidth) - 2;
+ state.maxY = (editor.inline ? bodyElm.scrollHeight : docElm.offsetHeight) - 2;
+ state.relX = e.pageX - elmPos.x;
+ state.relY = e.pageY - elmPos.y;
+ state.width = ceElm.offsetWidth;
+ state.height = ceElm.offsetHeight;
+ state.ghost = createGhost(editor, ceElm, state.width, state.height);
+ }
+ }
+ };
+ };
+
+ var move = function (state, editor) {
+ // Reduces laggy drag behavior on Gecko
+ var throttledPlaceCaretAt = Delay.throttle(function (clientX, clientY) {
+ editor._selectionOverrides.hideFakeCaret();
+ editor.selection.placeCaretAt(clientX, clientY);
+ }, 0);
+
+ return function (e) {
+ var movement = Math.max(Math.abs(e.screenX - state.screenX), Math.abs(e.screenY - state.screenY));
+
+ if (hasDraggableElement(state) && !state.dragging && movement > 10) {
+ var args = editor.fire('dragstart', {target: state.element});
+ if (args.isDefaultPrevented()) {
+ return;
+ }
+
+ state.dragging = true;
+ editor.focus();
+ }
+
+ if (state.dragging) {
+ var targetPos = applyRelPos(state, MousePosition.calc(editor, e));
+
+ appendGhostToBody(state.ghost, editor.getBody());
+ moveGhost(state.ghost, targetPos, state.width, state.height, state.maxX, state.maxY);
+
+ throttledPlaceCaretAt(e.clientX, e.clientY);
+ }
+ };
+ };
+
+ // Returns the raw element instead of the fake cE=false element
+ var getRawTarget = function (selection) {
+ var rng = selection.getSel().getRangeAt(0);
+ var startContainer = rng.startContainer;
+ return startContainer.nodeType === 3 ? startContainer.parentNode : startContainer;
+ };
+
+ var drop = function (state, editor) {
+ return function (e) {
+ if (state.dragging) {
+ if (isValidDropTarget(editor, getRawTarget(editor.selection), state.element)) {
+ var targetClone = cloneElement(state.element);
+
+ var args = editor.fire('drop', {
+ targetClone: targetClone,
+ clientX: e.clientX,
+ clientY: e.clientY
+ });
+
+ if (!args.isDefaultPrevented()) {
+ targetClone = args.targetClone;
+
+ editor.undoManager.transact(function() {
+ removeElement(state.element);
+ editor.insertContent(editor.dom.getOuterHTML(targetClone));
+ editor._selectionOverrides.hideFakeCaret();
+ });
+ }
+ }
+ }
+
+ removeDragState(state);
+ };
+ };
+
+ var stop = function (state, editor) {
+ return function () {
+ removeDragState(state);
+ if (state.dragging) {
+ editor.fire('dragend');
+ }
+ };
+ };
+
+ var removeDragState = function (state) {
+ state.dragging = false;
+ state.element = null;
+ removeElement(state.ghost);
+ };
+
+ var bindFakeDragEvents = function (editor) {
+ var state = {}, pageDom, dragStartHandler, dragHandler, dropHandler, dragEndHandler, rootDocument;
+
+ pageDom = DOMUtils.DOM;
+ rootDocument = document;
+ dragStartHandler = start(state, editor);
+ dragHandler = move(state, editor);
+ dropHandler = drop(state, editor);
+ dragEndHandler = stop(state, editor);
+
+ editor.on('mousedown', dragStartHandler);
+ editor.on('mousemove', dragHandler);
+ editor.on('mouseup', dropHandler);
+
+ pageDom.bind(rootDocument, 'mousemove', dragHandler);
+ pageDom.bind(rootDocument, 'mouseup', dragEndHandler);
+
+ editor.on('remove', function () {
+ pageDom.unbind(rootDocument, 'mousemove', dragHandler);
+ pageDom.unbind(rootDocument, 'mouseup', dragEndHandler);
+ });
+ };
+
+ var blockIeDrop = function (editor) {
+ editor.on('drop', function(e) {
+ // FF doesn't pass out clientX/clientY for drop since this is for IE we just use null instead
+ var realTarget = typeof e.clientX !== 'undefined' ? editor.getDoc().elementFromPoint(e.clientX, e.clientY) : null;
+
+ if (isContentEditableFalse(realTarget) || isContentEditableFalse(editor.dom.getContentEditableParent(realTarget))) {
+ e.preventDefault();
+ }
+ });
+ };
+
+ var init = function (editor) {
+ bindFakeDragEvents(editor);
+ blockIeDrop(editor);
+ };
+
+ return {
+ init: init
+ };
+});
+
+// Included from: js/tinymce/classes/SelectionOverrides.js
+
+/**
+ * SelectionOverrides.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 overriding the selection with keyboard/mouse
+ * around contentEditable=false regions.
+ *
+ * @example
+ * // Disable the default cE=false selection
+ * tinymce.activeEditor.on('ShowCaret BeforeObjectSelected', function(e) {
+ * e.preventDefault();
+ * });
+ *
+ * @private
+ * @class tinymce.SelectionOverrides
+ */
+define("tinymce/SelectionOverrides", [
+ "tinymce/Env",
+ "tinymce/caret/CaretWalker",
+ "tinymce/caret/CaretPosition",
+ "tinymce/caret/CaretContainer",
+ "tinymce/caret/CaretUtils",
+ "tinymce/caret/FakeCaret",
+ "tinymce/caret/LineWalker",
+ "tinymce/caret/LineUtils",
+ "tinymce/dom/NodeType",
+ "tinymce/dom/RangeUtils",
+ "tinymce/geom/ClientRect",
+ "tinymce/util/VK",
+ "tinymce/util/Fun",
+ "tinymce/util/Arr",
+ "tinymce/util/Delay",
+ "tinymce/DragDropOverrides"
+], function(
+ Env, CaretWalker, CaretPosition, CaretContainer, CaretUtils, FakeCaret, LineWalker,
+ LineUtils, NodeType, RangeUtils, ClientRect, VK, Fun, Arr, Delay, DragDropOverrides
+) {
+ var curry = Fun.curry,
+ isContentEditableTrue = NodeType.isContentEditableTrue,
+ isContentEditableFalse = NodeType.isContentEditableFalse,
+ isElement = NodeType.isElement,
+ isAfterContentEditableFalse = CaretUtils.isAfterContentEditableFalse,
+ isBeforeContentEditableFalse = CaretUtils.isBeforeContentEditableFalse,
+ getSelectedNode = RangeUtils.getSelectedNode;
+
+ function getVisualCaretPosition(walkFn, caretPosition) {
+ while ((caretPosition = walkFn(caretPosition))) {
+ if (caretPosition.isVisible()) {
+ return caretPosition;
+ }
+ }
+
+ return caretPosition;
+ }
+
+ function SelectionOverrides(editor) {
+ var rootNode = editor.getBody(), caretWalker = new CaretWalker(rootNode);
+ var getNextVisualCaretPosition = curry(getVisualCaretPosition, caretWalker.next);
+ var getPrevVisualCaretPosition = curry(getVisualCaretPosition, caretWalker.prev),
+ fakeCaret = new FakeCaret(editor.getBody(), isBlock),
+ realSelectionId = 'sel-' + editor.dom.uniqueId(),
+ selectedContentEditableNode, $ = editor.$;
+
+ function isFakeSelectionElement(elm) {
+ return editor.dom.hasClass(elm, 'mce-offscreen-selection');
+ }
+
+ function getRealSelectionElement() {
+ var container = editor.dom.get(realSelectionId);
+ return container ? container.getElementsByTagName('*')[0] : container;
+ }
+
+ function isBlock(node) {
+ return editor.dom.isBlock(node);
+ }
+
+ function setRange(range) {
+ //console.log('setRange', range);
+ if (range) {
+ editor.selection.setRng(range);
+ }
+ }
+
+ function getRange() {
+ return editor.selection.getRng();
+ }
+
+ function scrollIntoView(node, alignToTop) {
+ editor.selection.scrollIntoView(node, alignToTop);
+ }
+
+ function showCaret(direction, node, before) {
+ var e;
+
+ e = editor.fire('ShowCaret', {
+ target: node,
+ direction: direction,
+ before: before
+ });
+
+ if (e.isDefaultPrevented()) {
+ return null;
+ }
+
+ scrollIntoView(node, direction === -1);
+
+ return fakeCaret.show(before, node);
+ }
+
+ function selectNode(node) {
+ var e;
+
+ e = editor.fire('BeforeObjectSelected', {target: node});
+ if (e.isDefaultPrevented()) {
+ return null;
+ }
+
+ return getNodeRange(node);
+ }
+
+ function getNodeRange(node) {
+ var rng = node.ownerDocument.createRange();
+
+ rng.selectNode(node);
+
+ return rng;
+ }
+
+ function isMoveInsideSameBlock(fromCaretPosition, toCaretPosition) {
+ var inSameBlock = CaretUtils.isInSameBlock(fromCaretPosition, toCaretPosition);
+
+ // Handle bogus BR
abc|
+ if (!inSameBlock && NodeType.isBr(fromCaretPosition.getNode())) {
+ return true;
+ }
+
+ return inSameBlock;
+ }
+
+ function getNormalizedRangeEndPoint(direction, range) {
+ range = CaretUtils.normalizeRange(direction, rootNode, range);
+
+ if (direction == -1) {
+ return CaretPosition.fromRangeStart(range);
+ }
+
+ return CaretPosition.fromRangeEnd(range);
+ }
+
+ function isRangeInCaretContainerBlock(range) {
+ return CaretContainer.isCaretContainerBlock(range.startContainer);
+ }
+
+ function moveToCeFalseHorizontally(direction, getNextPosFn, isBeforeContentEditableFalseFn, range) {
+ var node, caretPosition, peekCaretPosition, rangeIsInContainerBlock;
+
+ if (!range.collapsed) {
+ node = getSelectedNode(range);
+ if (isContentEditableFalse(node)) {
+ return showCaret(direction, node, direction == -1);
+ }
+ }
+
+ rangeIsInContainerBlock = isRangeInCaretContainerBlock(range);
+ caretPosition = getNormalizedRangeEndPoint(direction, range);
+
+ if (isBeforeContentEditableFalseFn(caretPosition)) {
+ return selectNode(caretPosition.getNode(direction == -1));
+ }
+
+ caretPosition = getNextPosFn(caretPosition);
+ if (!caretPosition) {
+ if (rangeIsInContainerBlock) {
+ return range;
+ }
+
+ return null;
+ }
+
+ if (isBeforeContentEditableFalseFn(caretPosition)) {
+ return showCaret(direction, caretPosition.getNode(direction == -1), direction == 1);
+ }
+
+ // Peek ahead for handling of ab|c -> abc|
+ peekCaretPosition = getNextPosFn(caretPosition);
+ if (isBeforeContentEditableFalseFn(peekCaretPosition)) {
+ if (isMoveInsideSameBlock(caretPosition, peekCaretPosition)) {
+ return showCaret(direction, peekCaretPosition.getNode(direction == -1), direction == 1);
+ }
+ }
+
+ if (rangeIsInContainerBlock) {
+ return renderRangeCaret(caretPosition.toRange());
+ }
+
+ return null;
+ }
+
+ function moveToCeFalseVertically(direction, walkerFn, range) {
+ var caretPosition, linePositions, nextLinePositions,
+ closestNextLineRect, caretClientRect, clientX,
+ dist1, dist2, contentEditableFalseNode;
+
+ contentEditableFalseNode = getSelectedNode(range);
+ caretPosition = getNormalizedRangeEndPoint(direction, range);
+ linePositions = walkerFn(rootNode, LineWalker.isAboveLine(1), caretPosition);
+ nextLinePositions = Arr.filter(linePositions, LineWalker.isLine(1));
+ caretClientRect = Arr.last(caretPosition.getClientRects());
+
+ if (isBeforeContentEditableFalse(caretPosition)) {
+ contentEditableFalseNode = caretPosition.getNode();
+ }
+
+ if (isAfterContentEditableFalse(caretPosition)) {
+ contentEditableFalseNode = caretPosition.getNode(true);
+ }
+
+ if (!caretClientRect) {
+ return null;
+ }
+
+ clientX = caretClientRect.left;
+
+ closestNextLineRect = LineUtils.findClosestClientRect(nextLinePositions, clientX);
+ if (closestNextLineRect) {
+ if (isContentEditableFalse(closestNextLineRect.node)) {
+ dist1 = Math.abs(clientX - closestNextLineRect.left);
+ dist2 = Math.abs(clientX - closestNextLineRect.right);
+
+ return showCaret(direction, closestNextLineRect.node, dist1 < dist2);
+ }
+ }
+
+ if (contentEditableFalseNode) {
+ var caretPositions = LineWalker.positionsUntil(direction, rootNode, LineWalker.isAboveLine(1), contentEditableFalseNode);
+
+ closestNextLineRect = LineUtils.findClosestClientRect(Arr.filter(caretPositions, LineWalker.isLine(1)), clientX);
+ if (closestNextLineRect) {
+ return renderRangeCaret(closestNextLineRect.position.toRange());
+ }
+
+ closestNextLineRect = Arr.last(Arr.filter(caretPositions, LineWalker.isLine(0)));
+ if (closestNextLineRect) {
+ return renderRangeCaret(closestNextLineRect.position.toRange());
+ }
+ }
+ }
+
+ function exitPreBlock(direction, range) {
+ var pre, caretPos, newBlock;
+
+ function createTextBlock() {
+ var textBlock = editor.dom.create(editor.settings.forced_root_block);
+
+ if (!Env.ie || Env.ie >= 11) {
+ textBlock.innerHTML = ' ';
+ }
+
+ return textBlock;
+ }
+
+ if (range.collapsed && editor.settings.forced_root_block) {
+ pre = editor.dom.getParent(range.startContainer, 'PRE');
+ if (!pre) {
+ return;
+ }
+
+ if (direction == 1) {
+ caretPos = getNextVisualCaretPosition(CaretPosition.fromRangeStart(range));
+ } else {
+ caretPos = getPrevVisualCaretPosition(CaretPosition.fromRangeStart(range));
+ }
+
+ if (!caretPos) {
+ newBlock = createTextBlock();
+
+ if (direction == 1) {
+ editor.$(pre).after(newBlock);
+ } else {
+ editor.$(pre).before(newBlock);
+ }
+
+ editor.selection.select(newBlock, true);
+ editor.selection.collapse();
+ }
+ }
+ }
+
+ function moveH(direction, getNextPosFn, isBeforeContentEditableFalseFn, range) {
+ var newRange;
+
+ newRange = moveToCeFalseHorizontally(direction, getNextPosFn, isBeforeContentEditableFalseFn, range);
+ if (newRange) {
+ return newRange;
+ }
+
+ newRange = exitPreBlock(direction, range);
+ if (newRange) {
+ return newRange;
+ }
+
+ return null;
+ }
+
+ function moveV(direction, walkerFn, range) {
+ var newRange;
+
+ newRange = moveToCeFalseVertically(direction, walkerFn, range);
+ if (newRange) {
+ return newRange;
+ }
+
+ newRange = exitPreBlock(direction, range);
+ if (newRange) {
+ return newRange;
+ }
+
+ return null;
+ }
+
+ function getBlockCaretContainer() {
+ return $('*[data-mce-caret]')[0];
+ }
+
+ function showBlockCaretContainer(blockCaretContainer) {
+ if (blockCaretContainer.hasAttribute('data-mce-caret')) {
+ CaretContainer.showCaretContainerBlock(blockCaretContainer);
+ setRange(getRange()); // Removes control rect on IE
+ scrollIntoView(blockCaretContainer[0]);
+ }
+ }
+
+ function renderCaretAtRange(range) {
+ var caretPosition, ceRoot;
+
+ range = CaretUtils.normalizeRange(1, rootNode, range);
+ caretPosition = CaretPosition.fromRangeStart(range);
+
+ if (isContentEditableFalse(caretPosition.getNode())) {
+ return showCaret(1, caretPosition.getNode(), !caretPosition.isAtEnd());
+ }
+
+ if (isContentEditableFalse(caretPosition.getNode(true))) {
+ return showCaret(1, caretPosition.getNode(true), false);
+ }
+
+ // TODO: Should render caret before/after depending on where you click on the page forces after now
+ ceRoot = editor.dom.getParent(caretPosition.getNode(), Fun.or(isContentEditableFalse, isContentEditableTrue));
+ if (isContentEditableFalse(ceRoot)) {
+ return showCaret(1, ceRoot, false);
+ }
+
+ return null;
+ }
+
+ function renderRangeCaret(range) {
+ var caretRange;
+
+ if (!range || !range.collapsed) {
+ return range;
+ }
+
+ caretRange = renderCaretAtRange(range);
+ if (caretRange) {
+ return caretRange;
+ }
+
+ return range;
+ }
+
+ function deleteContentEditableNode(node) {
+ var nextCaretPosition, prevCaretPosition, prevCeFalseElm, nextElement;
+
+ if (!isContentEditableFalse(node)) {
+ return null;
+ }
+
+ if (isContentEditableFalse(node.previousSibling)) {
+ prevCeFalseElm = node.previousSibling;
+ }
+
+ prevCaretPosition = getPrevVisualCaretPosition(CaretPosition.before(node));
+ if (!prevCaretPosition) {
+ nextCaretPosition = getNextVisualCaretPosition(CaretPosition.after(node));
+ }
+
+ if (nextCaretPosition && isElement(nextCaretPosition.getNode())) {
+ nextElement = nextCaretPosition.getNode();
+ }
+
+ CaretContainer.remove(node.previousSibling);
+ CaretContainer.remove(node.nextSibling);
+ editor.dom.remove(node);
+
+ if (editor.dom.isEmpty(editor.getBody())) {
+ editor.setContent('');
+ editor.focus();
+ return;
+ }
+
+ if (prevCeFalseElm) {
+ return CaretPosition.after(prevCeFalseElm).toRange();
+ }
+
+ if (nextElement) {
+ return CaretPosition.before(nextElement).toRange();
+ }
+
+ if (prevCaretPosition) {
+ return prevCaretPosition.toRange();
+ }
+
+ if (nextCaretPosition) {
+ return nextCaretPosition.toRange();
+ }
+
+ return null;
+ }
+
+ function isTextBlock(node) {
+ var textBlocks = editor.schema.getTextBlockElements();
+ return node.nodeName in textBlocks;
+ }
+
+ function isEmpty(elm) {
+ return editor.dom.isEmpty(elm);
+ }
+
+ function mergeTextBlocks(direction, fromCaretPosition, toCaretPosition) {
+ var dom = editor.dom, fromBlock, toBlock, node, ceTarget;
+
+ fromBlock = dom.getParent(fromCaretPosition.getNode(), dom.isBlock);
+ toBlock = dom.getParent(toCaretPosition.getNode(), dom.isBlock);
+
+ if (direction === -1) {
+ ceTarget = toCaretPosition.getNode(true);
+ if (isAfterContentEditableFalse(toCaretPosition) && isBlock(ceTarget)) {
+ if (isTextBlock(fromBlock)) {
+ if (isEmpty(fromBlock)) {
+ dom.remove(fromBlock);
+ }
+
+ return CaretPosition.after(ceTarget).toRange();
+ }
+
+ return deleteContentEditableNode(toCaretPosition.getNode(true));
+ }
+ } else {
+ ceTarget = fromCaretPosition.getNode();
+ if (isBeforeContentEditableFalse(fromCaretPosition) && isBlock(ceTarget)) {
+ if (isTextBlock(toBlock)) {
+ if (isEmpty(toBlock)) {
+ dom.remove(toBlock);
+ }
+
+ return CaretPosition.before(ceTarget).toRange();
+ }
+
+ return deleteContentEditableNode(fromCaretPosition.getNode());
+ }
+ }
+
+ // Verify that both blocks are text blocks
+ if (fromBlock === toBlock || !isTextBlock(fromBlock) || !isTextBlock(toBlock)) {
+ return null;
+ }
+
+ while ((node = fromBlock.firstChild)) {
+ toBlock.appendChild(node);
+ }
+
+ editor.dom.remove(fromBlock);
+
+ return toCaretPosition.toRange();
+ }
+
+ function backspaceDelete(direction, beforeFn, afterFn, range) {
+ var node, caretPosition, peekCaretPosition, newCaretPosition;
+
+ if (!range.collapsed) {
+ node = getSelectedNode(range);
+ if (isContentEditableFalse(node)) {
+ return renderRangeCaret(deleteContentEditableNode(node));
+ }
+ }
+
+ caretPosition = getNormalizedRangeEndPoint(direction, range);
+
+ if (afterFn(caretPosition) && CaretContainer.isCaretContainerBlock(range.startContainer)) {
+ newCaretPosition = direction == -1 ? caretWalker.prev(caretPosition) : caretWalker.next(caretPosition);
+ return newCaretPosition ? renderRangeCaret(newCaretPosition.toRange()) : range;
+ }
+
+ if (beforeFn(caretPosition)) {
+ return renderRangeCaret(deleteContentEditableNode(caretPosition.getNode(direction == -1)));
+ }
+
+ peekCaretPosition = direction == -1 ? caretWalker.prev(caretPosition) : caretWalker.next(caretPosition);
+ if (beforeFn(peekCaretPosition)) {
+ if (direction === -1) {
+ return mergeTextBlocks(direction, caretPosition, peekCaretPosition);
+ }
+
+ return mergeTextBlocks(direction, peekCaretPosition, caretPosition);
+ }
+ }
+
+ function registerEvents() {
+ var right = curry(moveH, 1, getNextVisualCaretPosition, isBeforeContentEditableFalse);
+ var left = curry(moveH, -1, getPrevVisualCaretPosition, isAfterContentEditableFalse);
+ var deleteForward = curry(backspaceDelete, 1, isBeforeContentEditableFalse, isAfterContentEditableFalse);
+ var backspace = curry(backspaceDelete, -1, isAfterContentEditableFalse, isBeforeContentEditableFalse);
+ var up = curry(moveV, -1, LineWalker.upUntil);
+ var down = curry(moveV, 1, LineWalker.downUntil);
+
+ function override(evt, moveFn) {
+ if (evt.isDefaultPrevented() === false) {
+ var range = moveFn(getRange());
+ if (range) {
+ evt.preventDefault();
+ setRange(range);
+ }
+ }
+ }
+
+ function getContentEditableRoot(node) {
+ var root = editor.getBody();
+
+ while (node && node != root) {
+ if (isContentEditableTrue(node) || isContentEditableFalse(node)) {
+ return node;
+ }
+
+ node = node.parentNode;
+ }
+
+ return null;
+ }
+
+ function isXYWithinRange(clientX, clientY, range) {
+ if (range.collapsed) {
+ return false;
+ }
+
+ return Arr.reduce(range.getClientRects(), function(state, rect) {
+ return state || ClientRect.containsXY(rect, clientX, clientY);
+ }, false);
+ }
+
+ // Some browsers (Chrome) lets you place the caret after a cE=false
+ // Make sure we render the caret container in this case
+ editor.on('mouseup', function() {
+ var range = getRange();
+
+ if (range.collapsed) {
+ setRange(renderCaretAtRange(range));
+ }
+ });
+
+ editor.on('click', function(e) {
+ var contentEditableRoot;
+
+ contentEditableRoot = getContentEditableRoot(e.target);
+ if (contentEditableRoot) {
+ // Prevent clicks on links in a cE=false element
+ if (isContentEditableFalse(contentEditableRoot)) {
+ e.preventDefault();
+ editor.focus();
+ }
+
+ // Removes fake selection if a cE=true is clicked within a cE=false like the toc title
+ if (isContentEditableTrue(contentEditableRoot)) {
+ if (editor.dom.isChildOf(contentEditableRoot, editor.selection.getNode())) {
+ removeContentEditableSelection();
+ }
+ }
+ }
+ });
+
+ editor.on('blur NewBlock', function () {
+ removeContentEditableSelection();
+ hideFakeCaret();
+ });
+
+ function handleTouchSelect(editor) {
+ var moved = false;
+
+ editor.on('touchstart', function () {
+ moved = false;
+ });
+
+ editor.on('touchmove', function () {
+ moved = true;
+ });
+
+ editor.on('touchend', function (e) {
+ var contentEditableRoot = getContentEditableRoot(e.target);
+
+ if (isContentEditableFalse(contentEditableRoot)) {
+ if (!moved) {
+ e.preventDefault();
+ setContentEditableSelection(selectNode(contentEditableRoot));
+ }
+ }
+ });
+ }
+
+ var hasNormalCaretPosition = function (elm) {
+ var caretWalker = new CaretWalker(elm);
+
+ if (!elm.firstChild) {
+ return false;
+ }
+
+ var startPos = CaretPosition.before(elm.firstChild);
+ var newPos = caretWalker.next(startPos);
+
+ return newPos && !isBeforeContentEditableFalse(newPos) && !isAfterContentEditableFalse(newPos);
+ };
+
+ var isInSameBlock = function (node1, node2) {
+ var block1 = editor.dom.getParent(node1, editor.dom.isBlock);
+ var block2 = editor.dom.getParent(node2, editor.dom.isBlock);
+ return block1 === block2;
+ };
+
+ var isContentKey = function (e) {
+ if (e.keyCode >= 112 && e.keyCode <= 123) {
+ return false;
+ }
+
+ return true;
+ };
+
+ // Checks if the target node is in a block and if that block has a caret position better than the
+ // suggested caretNode this is to prevent the caret from being sucked in towards a cE=false block if
+ // they are adjacent on the vertical axis
+ var hasBetterMouseTarget = function (targetNode, caretNode) {
+ var targetBlock = editor.dom.getParent(targetNode, editor.dom.isBlock);
+ var caretBlock = editor.dom.getParent(caretNode, editor.dom.isBlock);
+
+ return targetBlock && !isInSameBlock(targetBlock, caretBlock) && hasNormalCaretPosition(targetBlock);
+ };
+
+ handleTouchSelect(editor);
+
+ editor.on('mousedown', function(e) {
+ var contentEditableRoot;
+
+ contentEditableRoot = getContentEditableRoot(e.target);
+ if (contentEditableRoot) {
+ if (isContentEditableFalse(contentEditableRoot)) {
+ e.preventDefault();
+ setContentEditableSelection(selectNode(contentEditableRoot));
+ } else {
+ if (!isXYWithinRange(e.clientX, e.clientY, editor.selection.getRng())) {
+ editor.selection.placeCaretAt(e.clientX, e.clientY);
+ }
+ }
+ } else {
+ // Remove needs to be called here since the mousedown might alter the selection without calling selection.setRng
+ // and therefore not fire the AfterSetSelectionRange event.
+ removeContentEditableSelection();
+ hideFakeCaret();
+
+ var caretInfo = LineUtils.closestCaret(rootNode, e.clientX, e.clientY);
+ if (caretInfo) {
+ if (!hasBetterMouseTarget(e.target, caretInfo.node)) {
+ e.preventDefault();
+ editor.getBody().focus();
+ setRange(showCaret(1, caretInfo.node, caretInfo.before));
+ }
+ }
+ }
+ });
+
+ editor.on('keydown', function(e) {
+ if (VK.modifierPressed(e)) {
+ return;
+ }
+
+ switch (e.keyCode) {
+ case VK.RIGHT:
+ override(e, right);
+ break;
+
+ case VK.DOWN:
+ override(e, down);
+ break;
+
+ case VK.LEFT:
+ override(e, left);
+ break;
+
+ case VK.UP:
+ override(e, up);
+ break;
+
+ case VK.DELETE:
+ override(e, deleteForward);
+ break;
+
+ case VK.BACKSPACE:
+ override(e, backspace);
+ break;
+
+ default:
+ if (isContentEditableFalse(editor.selection.getNode()) && isContentKey(e)) {
+ e.preventDefault();
+ }
+ break;
+ }
+ });
+
+ function paddEmptyContentEditableArea() {
+ var br, ceRoot = getContentEditableRoot(editor.selection.getNode());
+
+ if (isContentEditableTrue(ceRoot) && isBlock(ceRoot) && editor.dom.isEmpty(ceRoot)) {
+ br = editor.dom.create('br', {"data-mce-bogus": "1"});
+ editor.$(ceRoot).empty().append(br);
+ editor.selection.setRng(CaretPosition.before(br).toRange());
+ }
+ }
+
+ function handleBlockContainer(e) {
+ var blockCaretContainer = getBlockCaretContainer();
+
+ if (!blockCaretContainer) {
+ return;
+ }
+
+ if (e.type == 'compositionstart') {
+ e.preventDefault();
+ e.stopPropagation();
+ showBlockCaretContainer(blockCaretContainer);
+ return;
+ }
+
+ if (CaretContainer.hasContent(blockCaretContainer)) {
+ showBlockCaretContainer(blockCaretContainer);
+ }
+ }
+
+ function handleEmptyBackspaceDelete(e) {
+ var prevent;
+
+ switch (e.keyCode) {
+ case VK.DELETE:
+ prevent = paddEmptyContentEditableArea();
+ break;
+
+ case VK.BACKSPACE:
+ prevent = paddEmptyContentEditableArea();
+ break;
+ }
+
+ if (prevent) {
+ e.preventDefault();
+ }
+ }
+
+ // Must be added to "top" since undoManager needs to be executed after
+ editor.on('keyup compositionstart', function(e) {
+ handleBlockContainer(e);
+ handleEmptyBackspaceDelete(e);
+ }, true);
+
+ editor.on('cut', function() {
+ var node = editor.selection.getNode();
+
+ if (isContentEditableFalse(node)) {
+ Delay.setEditorTimeout(editor, function() {
+ setRange(renderRangeCaret(deleteContentEditableNode(node)));
+ });
+ }
+ });
+
+ editor.on('getSelectionRange', function(e) {
+ var rng = e.range;
+
+ if (selectedContentEditableNode) {
+ if (!selectedContentEditableNode.parentNode) {
+ selectedContentEditableNode = null;
+ return;
+ }
+
+ rng = rng.cloneRange();
+ rng.selectNode(selectedContentEditableNode);
+ e.range = rng;
+ }
+ });
+
+ editor.on('setSelectionRange', function(e) {
+ var rng;
+
+ rng = setContentEditableSelection(e.range);
+ if (rng) {
+ e.range = rng;
+ }
+ });
+
+ editor.on('AfterSetSelectionRange', function(e) {
+ var rng = e.range;
+
+ if (!isRangeInCaretContainer(rng)) {
+ hideFakeCaret();
+ }
+
+ if (!isFakeSelectionElement(rng.startContainer.parentNode)) {
+ removeContentEditableSelection();
+ }
+ });
+
+ editor.on('focus', function() {
+ // Make sure we have a proper fake caret on focus
+ Delay.setEditorTimeout(editor, function() {
+ editor.selection.setRng(renderRangeCaret(editor.selection.getRng()));
+ }, 0);
+ });
+
+ editor.on('copy', function (e) {
+ var clipboardData = e.clipboardData;
+
+ // Make sure we get proper html/text for the fake cE=false selection
+ // Doesn't work at all on Edge since it doesn't have proper clipboardData support
+ if (!e.isDefaultPrevented() && e.clipboardData && !Env.ie) {
+ var realSelectionElement = getRealSelectionElement();
+ if (realSelectionElement) {
+ e.preventDefault();
+ clipboardData.clearData();
+ clipboardData.setData('text/html', realSelectionElement.outerHTML);
+ clipboardData.setData('text/plain', realSelectionElement.outerText);
+ }
+ }
+ });
+
+ DragDropOverrides.init(editor);
+ }
+
+ function addCss() {
+ var styles = editor.contentStyles, rootClass = '.mce-content-body';
+
+ styles.push(fakeCaret.getCss());
+ styles.push(
+ rootClass + ' .mce-offscreen-selection {' +
+ 'position: absolute;' +
+ 'left: -9999999999px;' +
+ 'max-width: 1000000px;' +
+ '}' +
+ rootClass + ' *[contentEditable=false] {' +
+ 'cursor: default;' +
+ '}' +
+ rootClass + ' *[contentEditable=true] {' +
+ 'cursor: text;' +
+ '}'
+ );
+ }
+
+ function isWithinCaretContainer(node) {
+ return (
+ CaretContainer.isCaretContainer(node) ||
+ CaretContainer.startsWithCaretContainer(node) ||
+ CaretContainer.endsWithCaretContainer(node)
+ );
+ }
+
+ function isRangeInCaretContainer(rng) {
+ return isWithinCaretContainer(rng.startContainer) || isWithinCaretContainer(rng.endContainer);
+ }
+
+ function setContentEditableSelection(range) {
+ var node, $ = editor.$, dom = editor.dom, $realSelectionContainer, sel,
+ startContainer, startOffset, endOffset, e, caretPosition, targetClone, origTargetClone;
+
+ if (!range) {
+ return null;
+ }
+
+ if (range.collapsed) {
+ if (!isRangeInCaretContainer(range)) {
+ caretPosition = getNormalizedRangeEndPoint(1, range);
+
+ if (isContentEditableFalse(caretPosition.getNode())) {
+ return showCaret(1, caretPosition.getNode(), !caretPosition.isAtEnd());
+ }
+
+ if (isContentEditableFalse(caretPosition.getNode(true))) {
+ return showCaret(1, caretPosition.getNode(true), false);
+ }
+ }
+
+ return null;
+ }
+
+ startContainer = range.startContainer;
+ startOffset = range.startOffset;
+ endOffset = range.endOffset;
+
+ // Normalizes [] to []
+ if (startContainer.nodeType == 3 && startOffset == 0 && isContentEditableFalse(startContainer.parentNode)) {
+ startContainer = startContainer.parentNode;
+ startOffset = dom.nodeIndex(startContainer);
+ startContainer = startContainer.parentNode;
+ }
+
+ if (startContainer.nodeType != 1) {
+ return null;
+ }
+
+ if (endOffset == startOffset + 1) {
+ node = startContainer.childNodes[startOffset];
+ }
+
+ if (!isContentEditableFalse(node)) {
+ return null;
+ }
+
+ targetClone = origTargetClone = node.cloneNode(true);
+ e = editor.fire('ObjectSelected', {target: node, targetClone: targetClone});
+ if (e.isDefaultPrevented()) {
+ return null;
+ }
+
+ targetClone = e.targetClone;
+ $realSelectionContainer = $('#' + realSelectionId);
+ if ($realSelectionContainer.length === 0) {
+ $realSelectionContainer = $(
+ ''
+ ).attr('id', realSelectionId);
+
+ $realSelectionContainer.appendTo(editor.getBody());
+ }
+
+ range = editor.dom.createRng();
+
+ // WHY is IE making things so hard! Copy on x produces: x
+ // This is a ridiculous hack where we place the selection from a block over the inline element
+ // so that just the inline element is copied as is and not converted.
+ if (targetClone === origTargetClone && Env.ie) {
+ $realSelectionContainer.empty().append('
\u00a0
').append(targetClone);
+ range.setStartAfter($realSelectionContainer[0].firstChild.firstChild);
+ range.setEndAfter(targetClone);
+ } else {
+ $realSelectionContainer.empty().append('\u00a0').append(targetClone).append('\u00a0');
+ range.setStart($realSelectionContainer[0].firstChild, 1);
+ range.setEnd($realSelectionContainer[0].lastChild, 0);
+ }
+
+ $realSelectionContainer.css({
+ top: dom.getPos(node, editor.getBody()).y
+ });
+
+ $realSelectionContainer[0].focus();
+ sel = editor.selection.getSel();
+ sel.removeAllRanges();
+ sel.addRange(range);
+
+ editor.$('*[data-mce-selected]').removeAttr('data-mce-selected');
+ node.setAttribute('data-mce-selected', 1);
+ selectedContentEditableNode = node;
+ hideFakeCaret();
+
+ return range;
+ }
+
+ function removeContentEditableSelection() {
+ if (selectedContentEditableNode) {
+ selectedContentEditableNode.removeAttribute('data-mce-selected');
+ editor.$('#' + realSelectionId).remove();
+ selectedContentEditableNode = null;
+ }
+ }
+
+ function destroy() {
+ fakeCaret.destroy();
+ selectedContentEditableNode = null;
+ }
+
+ function hideFakeCaret() {
+ fakeCaret.hide();
+ }
+
+ if (Env.ceFalse) {
+ registerEvents();
+ addCss();
+ }
+
+ return {
+ showBlockCaretContainer: showBlockCaretContainer,
+ hideFakeCaret: hideFakeCaret,
+ destroy: destroy
+ };
+ }
+
+ return SelectionOverrides;
+});
+
+// Included from: js/tinymce/classes/util/Uuid.js
+
+/**
+ * Uuid.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
+ */
+
+/**
+ * Generates unique ids.
+ *
+ * @class tinymce.util.Uuid
+ * @private
+ */
+define("tinymce/util/Uuid", [
+], function() {
+ var count = 0;
+
+ var seed = function () {
+ var rnd = function () {
+ return Math.round(Math.random() * 0xFFFFFFFF).toString(36);
+ };
+
+ var now = new Date().getTime();
+ return 's' + now.toString(36) + rnd() + rnd() + rnd();
+ };
+
+ var uuid = function (prefix) {
+ return prefix + (count++) + seed();
+ };
+
+ return {
+ uuid: uuid
+ };
+});
+
+// Included from: js/tinymce/classes/ui/Sidebar.js
+
+/**
+ * Sidebar.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 handle sidebar instances for the editor.
+ *
+ * @class tinymce.ui.Sidebar
+ * @private
+ */
+define("tinymce/ui/Sidebar", [
+], function(
+) {
+ var add = function (editor, name, settings) {
+ var sidebars = editor.sidebars ? editor.sidebars : [];
+ sidebars.push({name: name, settings: settings});
+ editor.sidebars = sidebars;
+ };
+
+ return {
+ add: add
+ };
+});
+
+// Included from: js/tinymce/classes/Editor.js
+
+/**
+ * Editor.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 */
+
+/**
+ * Include the base event class documentation.
+ *
+ * @include ../../../tools/docs/tinymce.Event.js
+ */
+
+/**
+ * This class contains the core logic for a TinyMCE editor.
+ *
+ * @class tinymce.Editor
+ * @mixes tinymce.util.Observable
+ * @example
+ * // Add a class to all paragraphs in the editor.
+ * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
+ *
+ * // Gets the current editors selection as text
+ * tinymce.activeEditor.selection.getContent({format: 'text'});
+ *
+ * // Creates a new editor instance
+ * var ed = new tinymce.Editor('textareaid', {
+ * some_setting: 1
+ * }, tinymce.EditorManager);
+ *
+ * // Select each item the user clicks on
+ * ed.on('click', function(e) {
+ * ed.selection.select(e.target);
+ * });
+ *
+ * ed.render();
+ */
+define("tinymce/Editor", [
+ "tinymce/dom/DOMUtils",
+ "tinymce/dom/DomQuery",
+ "tinymce/AddOnManager",
+ "tinymce/NodeChange",
+ "tinymce/html/Node",
+ "tinymce/dom/Serializer",
+ "tinymce/html/Serializer",
+ "tinymce/dom/Selection",
+ "tinymce/Formatter",
+ "tinymce/UndoManager",
+ "tinymce/EnterKey",
+ "tinymce/ForceBlocks",
+ "tinymce/EditorCommands",
+ "tinymce/util/URI",
+ "tinymce/dom/ScriptLoader",
+ "tinymce/dom/EventUtils",
+ "tinymce/WindowManager",
+ "tinymce/NotificationManager",
+ "tinymce/html/Schema",
+ "tinymce/html/DomParser",
+ "tinymce/util/Quirks",
+ "tinymce/Env",
+ "tinymce/util/Tools",
+ "tinymce/util/Delay",
+ "tinymce/EditorObservable",
+ "tinymce/Mode",
+ "tinymce/Shortcuts",
+ "tinymce/EditorUpload",
+ "tinymce/SelectionOverrides",
+ "tinymce/util/Uuid",
+ "tinymce/ui/Sidebar",
+ "tinymce/ErrorReporter"
+], function(
+ DOMUtils, DomQuery, AddOnManager, NodeChange, Node, DomSerializer, Serializer,
+ Selection, Formatter, UndoManager, EnterKey, ForceBlocks, EditorCommands,
+ URI, ScriptLoader, EventUtils, WindowManager, NotificationManager,
+ Schema, DomParser, Quirks, Env, Tools, Delay, EditorObservable, Mode, Shortcuts, EditorUpload,
+ SelectionOverrides, Uuid, Sidebar, ErrorReporter
+) {
+ // Shorten these names
+ var DOM = DOMUtils.DOM, ThemeManager = AddOnManager.ThemeManager, PluginManager = AddOnManager.PluginManager;
+ var extend = Tools.extend, each = Tools.each, explode = Tools.explode;
+ var inArray = Tools.inArray, trim = Tools.trim, resolve = Tools.resolve;
+ var Event = EventUtils.Event;
+ var isGecko = Env.gecko, ie = Env.ie;
+
+ /**
+ * Include documentation for all the events.
+ *
+ * @include ../../../tools/docs/tinymce.Editor.js
+ */
+
+ /**
+ * Constructs a editor instance by id.
+ *
+ * @constructor
+ * @method Editor
+ * @param {String} id Unique id for the editor.
+ * @param {Object} settings Settings for the editor.
+ * @param {tinymce.EditorManager} editorManager EditorManager instance.
+ */
+ function Editor(id, settings, editorManager) {
+ var self = this, documentBaseUrl, baseUri, defaultSettings;
+
+ documentBaseUrl = self.documentBaseUrl = editorManager.documentBaseURL;
+ baseUri = editorManager.baseURI;
+ defaultSettings = editorManager.defaultSettings;
+
+ /**
+ * Name/value collection with editor settings.
+ *
+ * @property settings
+ * @type Object
+ * @example
+ * // Get the value of the theme setting
+ * tinymce.activeEditor.windowManager.alert("You are using the " + tinymce.activeEditor.settings.theme + " theme");
+ */
+ settings = extend({
+ id: id,
+ theme: 'modern',
+ delta_width: 0,
+ delta_height: 0,
+ popup_css: '',
+ plugins: '',
+ document_base_url: documentBaseUrl,
+ add_form_submit_trigger: true,
+ submit_patch: true,
+ add_unload_trigger: true,
+ convert_urls: true,
+ relative_urls: true,
+ remove_script_host: true,
+ object_resizing: true,
+ doctype: '',
+ visual: true,
+ font_size_style_values: 'xx-small,x-small,small,medium,large,x-large,xx-large',
+
+ // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
+ font_size_legacy_values: 'xx-small,small,medium,large,x-large,xx-large,300%',
+ forced_root_block: 'p',
+ hidden_input: true,
+ padd_empty_editor: true,
+ render_ui: true,
+ indentation: '30px',
+ inline_styles: true,
+ convert_fonts_to_spans: true,
+ indent: 'simple',
+ indent_before: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' +
+ 'tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist',
+ indent_after: 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,th,ul,ol,li,dl,dt,dd,area,table,thead,' +
+ 'tfoot,tbody,tr,section,article,hgroup,aside,figure,figcaption,option,optgroup,datalist',
+ validate: true,
+ entity_encoding: 'named',
+ url_converter: self.convertURL,
+ url_converter_scope: self,
+ ie7_compat: true
+ }, defaultSettings, settings);
+
+ // Merge external_plugins
+ if (defaultSettings && defaultSettings.external_plugins && settings.external_plugins) {
+ settings.external_plugins = extend({}, defaultSettings.external_plugins, settings.external_plugins);
+ }
+
+ self.settings = settings;
+ AddOnManager.language = settings.language || 'en';
+ AddOnManager.languageLoad = settings.language_load;
+ AddOnManager.baseURL = editorManager.baseURL;
+
+ /**
+ * Editor instance id, normally the same as the div/textarea that was replaced.
+ *
+ * @property id
+ * @type String
+ */
+ self.id = settings.id = id;
+
+ /**
+ * State to force the editor to return false on a isDirty call.
+ *
+ * @property isNotDirty
+ * @type Boolean
+ * @deprecated Use editor.setDirty instead.
+ */
+ self.setDirty(false);
+
+ /**
+ * Name/Value object containing plugin instances.
+ *
+ * @property plugins
+ * @type Object
+ * @example
+ * // Execute a method inside a plugin directly
+ * tinymce.activeEditor.plugins.someplugin.someMethod();
+ */
+ self.plugins = {};
+
+ /**
+ * URI object to document configured for the TinyMCE instance.
+ *
+ * @property documentBaseURI
+ * @type tinymce.util.URI
+ * @example
+ * // Get relative URL from the location of document_base_url
+ * tinymce.activeEditor.documentBaseURI.toRelative('/somedir/somefile.htm');
+ *
+ * // Get absolute URL from the location of document_base_url
+ * tinymce.activeEditor.documentBaseURI.toAbsolute('somefile.htm');
+ */
+ self.documentBaseURI = new URI(settings.document_base_url || documentBaseUrl, {
+ base_uri: baseUri
+ });
+
+ /**
+ * URI object to current document that holds the TinyMCE editor instance.
+ *
+ * @property baseURI
+ * @type tinymce.util.URI
+ * @example
+ * // Get relative URL from the location of the API
+ * tinymce.activeEditor.baseURI.toRelative('/somedir/somefile.htm');
+ *
+ * // Get absolute URL from the location of the API
+ * tinymce.activeEditor.baseURI.toAbsolute('somefile.htm');
+ */
+ self.baseURI = baseUri;
+
+ /**
+ * Array with CSS files to load into the iframe.
+ *
+ * @property contentCSS
+ * @type Array
+ */
+ self.contentCSS = [];
+
+ /**
+ * Array of CSS styles to add to head of document when the editor loads.
+ *
+ * @property contentStyles
+ * @type Array
+ */
+ self.contentStyles = [];
+
+ // Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
+ self.shortcuts = new Shortcuts(self);
+ self.loadedCSS = {};
+ self.editorCommands = new EditorCommands(self);
+ self.suffix = editorManager.suffix;
+ self.editorManager = editorManager;
+ self.inline = settings.inline;
+ self.settings.content_editable = self.inline;
+
+ if (settings.cache_suffix) {
+ Env.cacheSuffix = settings.cache_suffix.replace(/^[\?\&]+/, '');
+ }
+
+ if (settings.override_viewport === false) {
+ Env.overrideViewPort = false;
+ }
+
+ // Call setup
+ editorManager.fire('SetupEditor', self);
+ self.execCallback('setup', self);
+
+ /**
+ * Dom query instance with default scope to the editor document and default element is the body of the editor.
+ *
+ * @property $
+ * @type tinymce.dom.DomQuery
+ * @example
+ * tinymce.activeEditor.$('p').css('color', 'red');
+ * tinymce.activeEditor.$().append('
new
');
+ */
+ self.$ = DomQuery.overrideDefaults(function() {
+ return {
+ context: self.inline ? self.getBody() : self.getDoc(),
+ element: self.getBody()
+ };
+ });
+ }
+
+ Editor.prototype = {
+ /**
+ * Renders the editor/adds it to the page.
+ *
+ * @method render
+ */
+ render: function() {
+ var self = this, settings = self.settings, id = self.id, suffix = self.suffix;
+
+ function readyHandler() {
+ DOM.unbind(window, 'ready', readyHandler);
+ self.render();
+ }
+
+ // Page is not loaded yet, wait for it
+ if (!Event.domLoaded) {
+ DOM.bind(window, 'ready', readyHandler);
+ return;
+ }
+
+ // Element not found, then skip initialization
+ if (!self.getElement()) {
+ return;
+ }
+
+ // No editable support old iOS versions etc
+ if (!Env.contentEditable) {
+ return;
+ }
+
+ // Hide target element early to prevent content flashing
+ if (!settings.inline) {
+ self.orgVisibility = self.getElement().style.visibility;
+ self.getElement().style.visibility = 'hidden';
+ } else {
+ self.inline = true;
+ }
+
+ var form = self.getElement().form || DOM.getParent(id, 'form');
+ if (form) {
+ self.formElement = form;
+
+ // Add hidden input for non input elements inside form elements
+ if (settings.hidden_input && !/TEXTAREA|INPUT/i.test(self.getElement().nodeName)) {
+ DOM.insertAfter(DOM.create('input', {type: 'hidden', name: id}), id);
+ self.hasHiddenInput = true;
+ }
+
+ // Pass submit/reset from form to editor instance
+ self.formEventDelegate = function(e) {
+ self.fire(e.type, e);
+ };
+
+ DOM.bind(form, 'submit reset', self.formEventDelegate);
+
+ // Reset contents in editor when the form is reset
+ self.on('reset', function() {
+ self.setContent(self.startContent, {format: 'raw'});
+ });
+
+ // Check page uses id="submit" or name="submit" for it's submit button
+ if (settings.submit_patch && !form.submit.nodeType && !form.submit.length && !form._mceOldSubmit) {
+ form._mceOldSubmit = form.submit;
+ form.submit = function() {
+ self.editorManager.triggerSave();
+ self.setDirty(false);
+
+ return form._mceOldSubmit(form);
+ };
+ }
+ }
+
+ /**
+ * Window manager reference, use this to open new windows and dialogs.
+ *
+ * @property windowManager
+ * @type tinymce.WindowManager
+ * @example
+ * // Shows an alert message
+ * tinymce.activeEditor.windowManager.alert('Hello world!');
+ *
+ * // 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
+ * });
+ */
+ self.windowManager = new WindowManager(self);
+
+ /**
+ * Notification manager reference, use this to open new windows and dialogs.
+ *
+ * @property notificationManager
+ * @type tinymce.NotificationManager
+ * @example
+ * // Shows a notification info message.
+ * tinymce.activeEditor.notificationManager.open({text: 'Hello world!', type: 'info'});
+ */
+ self.notificationManager = new NotificationManager(self);
+
+ if (settings.encoding == 'xml') {
+ self.on('GetContent', function(e) {
+ if (e.save) {
+ e.content = DOM.encode(e.content);
+ }
+ });
+ }
+
+ if (settings.add_form_submit_trigger) {
+ self.on('submit', function() {
+ if (self.initialized) {
+ self.save();
+ }
+ });
+ }
+
+ if (settings.add_unload_trigger) {
+ self._beforeUnload = function() {
+ if (self.initialized && !self.destroyed && !self.isHidden()) {
+ self.save({format: 'raw', no_events: true, set_dirty: false});
+ }
+ };
+
+ self.editorManager.on('BeforeUnload', self._beforeUnload);
+ }
+
+ // Load scripts
+ function loadScripts() {
+ var scriptLoader = ScriptLoader.ScriptLoader;
+
+ if (settings.language && settings.language != 'en' && !settings.language_url) {
+ settings.language_url = self.editorManager.baseURL + '/langs/' + settings.language + '.js';
+ }
+
+ if (settings.language_url) {
+ scriptLoader.add(settings.language_url);
+ }
+
+ if (settings.theme && typeof settings.theme != "function" &&
+ settings.theme.charAt(0) != '-' && !ThemeManager.urls[settings.theme]) {
+ var themeUrl = settings.theme_url;
+
+ if (themeUrl) {
+ themeUrl = self.documentBaseURI.toAbsolute(themeUrl);
+ } else {
+ themeUrl = 'themes/' + settings.theme + '/theme' + suffix + '.js';
+ }
+
+ ThemeManager.load(settings.theme, themeUrl);
+ }
+
+ if (Tools.isArray(settings.plugins)) {
+ settings.plugins = settings.plugins.join(' ');
+ }
+
+ each(settings.external_plugins, function(url, name) {
+ PluginManager.load(name, url);
+ settings.plugins += ' ' + name;
+ });
+
+ each(settings.plugins.split(/[ ,]/), function(plugin) {
+ plugin = trim(plugin);
+
+ if (plugin && !PluginManager.urls[plugin]) {
+ if (plugin.charAt(0) == '-') {
+ plugin = plugin.substr(1, plugin.length);
+
+ var dependencies = PluginManager.dependencies(plugin);
+
+ each(dependencies, function(dep) {
+ var defaultSettings = {
+ prefix: 'plugins/',
+ resource: dep,
+ suffix: '/plugin' + suffix + '.js'
+ };
+
+ dep = PluginManager.createUrl(defaultSettings, dep);
+ PluginManager.load(dep.resource, dep);
+ });
+ } else {
+ PluginManager.load(plugin, {
+ prefix: 'plugins/',
+ resource: plugin,
+ suffix: '/plugin' + suffix + '.js'
+ });
+ }
+ }
+ });
+
+ scriptLoader.loadQueue(function() {
+ if (!self.removed) {
+ self.init();
+ }
+ }, self, function (urls) {
+ ErrorReporter.pluginLoadError(self, urls[0]);
+
+ if (!self.removed) {
+ self.init();
+ }
+ });
+ }
+
+ self.editorManager.add(self);
+ loadScripts();
+ },
+
+ /**
+ * Initializes the editor this will be called automatically when
+ * all plugins/themes and language packs are loaded by the rendered method.
+ * This method will setup the iframe and create the theme and plugin instances.
+ *
+ * @method init
+ */
+ init: function() {
+ var self = this, settings = self.settings, elm = self.getElement();
+ var w, h, minHeight, n, o, Theme, url, bodyId, bodyClass, re, i, initializedPlugins = [];
+
+ self.rtl = settings.rtl_ui || self.editorManager.i18n.rtl;
+ self.editorManager.i18n.setCode(settings.language);
+ settings.aria_label = settings.aria_label || DOM.getAttrib(elm, 'aria-label', self.getLang('aria.rich_text_area'));
+
+ self.fire('ScriptsLoaded');
+
+ /**
+ * Reference to the theme instance that was used to generate the UI.
+ *
+ * @property theme
+ * @type tinymce.Theme
+ * @example
+ * // Executes a method on the theme directly
+ * tinymce.activeEditor.theme.someMethod();
+ */
+ if (settings.theme) {
+ if (typeof settings.theme != "function") {
+ settings.theme = settings.theme.replace(/-/, '');
+ Theme = ThemeManager.get(settings.theme);
+ self.theme = new Theme(self, ThemeManager.urls[settings.theme]);
+
+ if (self.theme.init) {
+ self.theme.init(self, ThemeManager.urls[settings.theme] || self.documentBaseUrl.replace(/\/$/, ''), self.$);
+ }
+ } else {
+ self.theme = settings.theme;
+ }
+ }
+
+ function initPlugin(plugin) {
+ var Plugin = PluginManager.get(plugin), pluginUrl, pluginInstance;
+
+ pluginUrl = PluginManager.urls[plugin] || self.documentBaseUrl.replace(/\/$/, '');
+ plugin = trim(plugin);
+ if (Plugin && inArray(initializedPlugins, plugin) === -1) {
+ each(PluginManager.dependencies(plugin), function(dep) {
+ initPlugin(dep);
+ });
+
+ if (self.plugins[plugin]) {
+ return;
+ }
+
+ pluginInstance = new Plugin(self, pluginUrl, self.$);
+
+ self.plugins[plugin] = pluginInstance;
+
+ if (pluginInstance.init) {
+ pluginInstance.init(self, pluginUrl);
+ initializedPlugins.push(plugin);
+ }
+ }
+ }
+
+ // Create all plugins
+ each(settings.plugins.replace(/\-/g, '').split(/[ ,]/), initPlugin);
+
+ // Measure box
+ if (settings.render_ui && self.theme) {
+ self.orgDisplay = elm.style.display;
+
+ if (typeof settings.theme != "function") {
+ w = settings.width || elm.style.width || elm.offsetWidth;
+ h = settings.height || elm.style.height || elm.offsetHeight;
+ minHeight = settings.min_height || 100;
+ re = /^[0-9\.]+(|px)$/i;
+
+ if (re.test('' + w)) {
+ w = Math.max(parseInt(w, 10), 100);
+ }
+
+ if (re.test('' + h)) {
+ h = Math.max(parseInt(h, 10), minHeight);
+ }
+
+ // Render UI
+ o = self.theme.renderUI({
+ targetNode: elm,
+ width: w,
+ height: h,
+ deltaWidth: settings.delta_width,
+ deltaHeight: settings.delta_height
+ });
+
+ // Resize editor
+ if (!settings.content_editable) {
+ h = (o.iframeHeight || h) + (typeof h == 'number' ? (o.deltaHeight || 0) : '');
+ if (h < minHeight) {
+ h = minHeight;
+ }
+ }
+ } else {
+ o = settings.theme(self, elm);
+
+ if (o.editorContainer.nodeType) {
+ o.editorContainer.id = o.editorContainer.id || self.id + "_parent";
+ }
+
+ if (o.iframeContainer.nodeType) {
+ o.iframeContainer.id = o.iframeContainer.id || self.id + "_iframecontainer";
+ }
+
+ // Use specified iframe height or the targets offsetHeight
+ h = o.iframeHeight || elm.offsetHeight;
+ }
+
+ self.editorContainer = o.editorContainer;
+ }
+
+ // Load specified content CSS last
+ if (settings.content_css) {
+ each(explode(settings.content_css), function(u) {
+ self.contentCSS.push(self.documentBaseURI.toAbsolute(u));
+ });
+ }
+
+ // Load specified content CSS last
+ if (settings.content_style) {
+ self.contentStyles.push(settings.content_style);
+ }
+
+ // Content editable mode ends here
+ if (settings.content_editable) {
+ elm = n = o = null; // Fix IE leak
+ return self.initContentBody();
+ }
+
+ self.iframeHTML = settings.doctype + '';
+
+ // We only need to override paths if we have to
+ // IE has a bug where it remove site absolute urls to relative ones if this is specified
+ if (settings.document_base_url != self.documentBaseUrl) {
+ self.iframeHTML += '';
+ }
+
+ // IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
+ if (!Env.caretAfter && settings.ie7_compat) {
+ self.iframeHTML += '';
+ }
+
+ self.iframeHTML += '';
+
+ // Load the CSS by injecting them into the HTML this will reduce "flicker"
+ // However we can't do that on Chrome since # will scroll to the editor for some odd reason see #2427
+ if (!/#$/.test(document.location.href)) {
+ for (i = 0; i < self.contentCSS.length; i++) {
+ var cssUrl = self.contentCSS[i];
+ self.iframeHTML += (
+ ''
+ );
+ self.loadedCSS[cssUrl] = true;
+ }
+ }
+
+ bodyId = settings.body_id || 'tinymce';
+ if (bodyId.indexOf('=') != -1) {
+ bodyId = self.getParam('body_id', '', 'hash');
+ bodyId = bodyId[self.id] || bodyId;
+ }
+
+ bodyClass = settings.body_class || '';
+ if (bodyClass.indexOf('=') != -1) {
+ bodyClass = self.getParam('body_class', '', 'hash');
+ bodyClass = bodyClass[self.id] || '';
+ }
+
+ if (settings.content_security_policy) {
+ self.iframeHTML += '';
+ }
+
+ self.iframeHTML += ' ';
+
+ /*eslint no-script-url:0 */
+ var domainRelaxUrl = 'javascript:(function(){' +
+ 'document.open();document.domain="' + document.domain + '";' +
+ 'var ed = window.parent.tinymce.get("' + self.id + '");document.write(ed.iframeHTML);' +
+ 'document.close();ed.initContentBody(true);})()';
+
+ // Domain relaxing is required since the user has messed around with document.domain
+ if (document.domain != location.hostname) {
+ // Edge seems to be able to handle domain relaxing
+ if (Env.ie && Env.ie < 12) {
+ url = domainRelaxUrl;
+ }
+ }
+
+ // Create iframe
+ // TODO: ACC add the appropriate description on this.
+ var ifr = DOM.create('iframe', {
+ id: self.id + "_ifr",
+ //src: url || 'javascript:""', // Workaround for HTTPS warning in IE6/7
+ frameBorder: '0',
+ allowTransparency: "true",
+ title: self.editorManager.translate(
+ "Rich Text Area. Press ALT-F9 for menu. " +
+ "Press ALT-F10 for toolbar. Press ALT-0 for help"
+ ),
+ style: {
+ width: '100%',
+ height: h,
+ display: 'block' // Important for Gecko to render the iframe correctly
+ }
+ });
+
+ ifr.onload = function() {
+ ifr.onload = null;
+ self.fire("load");
+ };
+
+ DOM.setAttrib(ifr, "src", url || 'javascript:""');
+
+ self.contentAreaContainer = o.iframeContainer;
+ self.iframeElement = ifr;
+
+ n = DOM.add(o.iframeContainer, ifr);
+
+ // Try accessing the document this will fail on IE when document.domain is set to the same as location.hostname
+ // Then we have to force domain relaxing using the domainRelaxUrl approach very ugly!!
+ if (ie) {
+ try {
+ self.getDoc();
+ } catch (e) {
+ n.src = url = domainRelaxUrl;
+ }
+ }
+
+ if (o.editorContainer) {
+ DOM.get(o.editorContainer).style.display = self.orgDisplay;
+ self.hidden = DOM.isHidden(o.editorContainer);
+ }
+
+ self.getElement().style.display = 'none';
+ DOM.setAttrib(self.id, 'aria-hidden', true);
+
+ if (!url) {
+ self.initContentBody();
+ }
+
+ elm = n = o = null; // Cleanup
+ },
+
+ /**
+ * This method get called by the init method once the iframe is loaded.
+ * It will fill the iframe with contents, sets up DOM and selection objects for the iframe.
+ *
+ * @method initContentBody
+ * @private
+ */
+ initContentBody: function(skipWrite) {
+ var self = this, settings = self.settings, targetElm = self.getElement(), doc = self.getDoc(), body, contentCssText;
+
+ // Restore visibility on target element
+ if (!settings.inline) {
+ self.getElement().style.visibility = self.orgVisibility;
+ }
+
+ // Setup iframe body
+ if (!skipWrite && !settings.content_editable) {
+ doc.open();
+ doc.write(self.iframeHTML);
+ doc.close();
+ }
+
+ if (settings.content_editable) {
+ self.on('remove', function() {
+ var bodyEl = this.getBody();
+
+ DOM.removeClass(bodyEl, 'mce-content-body');
+ DOM.removeClass(bodyEl, 'mce-edit-focus');
+ DOM.setAttrib(bodyEl, 'contentEditable', null);
+ });
+
+ DOM.addClass(targetElm, 'mce-content-body');
+ self.contentDocument = doc = settings.content_document || document;
+ self.contentWindow = settings.content_window || window;
+ self.bodyElement = targetElm;
+
+ // Prevent leak in IE
+ settings.content_document = settings.content_window = null;
+
+ // TODO: Fix this
+ settings.root_name = targetElm.nodeName.toLowerCase();
+ }
+
+ // It will not steal focus while setting contentEditable
+ body = self.getBody();
+ body.disabled = true;
+ self.readonly = settings.readonly;
+
+ if (!self.readonly) {
+ if (self.inline && DOM.getStyle(body, 'position', true) == 'static') {
+ body.style.position = 'relative';
+ }
+
+ body.contentEditable = self.getParam('content_editable_state', true);
+ }
+
+ body.disabled = false;
+
+ self.editorUpload = new EditorUpload(self);
+
+ /**
+ * Schema instance, enables you to validate elements and its children.
+ *
+ * @property schema
+ * @type tinymce.html.Schema
+ */
+ self.schema = new Schema(settings);
+
+ /**
+ * DOM instance for the editor.
+ *
+ * @property dom
+ * @type tinymce.dom.DOMUtils
+ * @example
+ * // Adds a class to all paragraphs within the editor
+ * tinymce.activeEditor.dom.addClass(tinymce.activeEditor.dom.select('p'), 'someclass');
+ */
+ self.dom = new DOMUtils(doc, {
+ keep_values: true,
+ url_converter: self.convertURL,
+ url_converter_scope: self,
+ hex_colors: settings.force_hex_style_colors,
+ class_filter: settings.class_filter,
+ update_styles: true,
+ root_element: self.inline ? self.getBody() : null,
+ collect: settings.content_editable,
+ schema: self.schema,
+ onSetAttrib: function(e) {
+ self.fire('SetAttrib', e);
+ }
+ });
+
+ /**
+ * HTML parser will be used when contents is inserted into the editor.
+ *
+ * @property parser
+ * @type tinymce.html.DomParser
+ */
+ self.parser = new DomParser(settings, self.schema);
+
+ // Convert src and href into data-mce-src, data-mce-href and data-mce-style
+ self.parser.addAttributeFilter('src,href,style,tabindex', function(nodes, name) {
+ var i = nodes.length, node, dom = self.dom, value, internalName;
+
+ while (i--) {
+ node = nodes[i];
+ value = node.attr(name);
+ internalName = 'data-mce-' + name;
+
+ // Add internal attribute if we need to we don't on a refresh of the document
+ if (!node.attributes.map[internalName]) {
+ // Don't duplicate these since they won't get modified by any browser
+ if (value.indexOf('data:') === 0 || value.indexOf('blob:') === 0) {
+ continue;
+ }
+
+ if (name === "style") {
+ value = dom.serializeStyle(dom.parseStyle(value), node.name);
+
+ if (!value.length) {
+ value = null;
+ }
+
+ node.attr(internalName, value);
+ node.attr(name, value);
+ } else if (name === "tabindex") {
+ node.attr(internalName, value);
+ node.attr(name, null);
+ } else {
+ node.attr(internalName, self.convertURL(value, name, node.name));
+ }
+ }
+ }
+ });
+
+ // Keep scripts from executing
+ self.parser.addNodeFilter('script', function(nodes) {
+ var i = nodes.length, node, type;
+
+ while (i--) {
+ node = nodes[i];
+ type = node.attr('type') || 'no/type';
+ if (type.indexOf('mce-') !== 0) {
+ node.attr('type', 'mce-' + type);
+ }
+ }
+ });
+
+ self.parser.addNodeFilter('#cdata', function(nodes) {
+ var i = nodes.length, node;
+
+ while (i--) {
+ node = nodes[i];
+ node.type = 8;
+ node.name = '#comment';
+ node.value = '[CDATA[' + node.value + ']]';
+ }
+ });
+
+ self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes) {
+ var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
+
+ while (i--) {
+ node = nodes[i];
+
+ if (node.isEmpty(nonEmptyElements) && node.getAll('br').length === 0) {
+ node.append(new Node('br', 1)).shortEnded = true;
+ }
+ }
+ });
+
+ /**
+ * DOM serializer for the editor. Will be used when contents is extracted from the editor.
+ *
+ * @property serializer
+ * @type tinymce.dom.Serializer
+ * @example
+ * // Serializes the first paragraph in the editor into a string
+ * tinymce.activeEditor.serializer.serialize(tinymce.activeEditor.dom.select('p')[0]);
+ */
+ self.serializer = new DomSerializer(settings, self);
+
+ /**
+ * Selection instance for the editor.
+ *
+ * @property selection
+ * @type tinymce.dom.Selection
+ * @example
+ * // Sets some contents to the current selection in the editor
+ * tinymce.activeEditor.selection.setContent('Some contents');
+ *
+ * // Gets the current selection
+ * alert(tinymce.activeEditor.selection.getContent());
+ *
+ * // Selects the first paragraph found
+ * tinymce.activeEditor.selection.select(tinymce.activeEditor.dom.select('p')[0]);
+ */
+ self.selection = new Selection(self.dom, self.getWin(), self.serializer, self);
+
+ /**
+ * Formatter instance.
+ *
+ * @property formatter
+ * @type tinymce.Formatter
+ */
+ self.formatter = new Formatter(self);
+
+ /**
+ * Undo manager instance, responsible for handling undo levels.
+ *
+ * @property undoManager
+ * @type tinymce.UndoManager
+ * @example
+ * // Undoes the last modification to the editor
+ * tinymce.activeEditor.undoManager.undo();
+ */
+ self.undoManager = new UndoManager(self);
+
+ self.forceBlocks = new ForceBlocks(self);
+ self.enterKey = new EnterKey(self);
+ self._nodeChangeDispatcher = new NodeChange(self);
+ self._selectionOverrides = new SelectionOverrides(self);
+
+ self.fire('PreInit');
+
+ if (!settings.browser_spellcheck && !settings.gecko_spellcheck) {
+ doc.body.spellcheck = false; // Gecko
+ DOM.setAttrib(body, "spellcheck", "false");
+ }
+
+ self.quirks = new Quirks(self);
+ self.fire('PostRender');
+
+ if (settings.directionality) {
+ body.dir = settings.directionality;
+ }
+
+ if (settings.nowrap) {
+ body.style.whiteSpace = "nowrap";
+ }
+
+ if (settings.protect) {
+ self.on('BeforeSetContent', function(e) {
+ each(settings.protect, function(pattern) {
+ e.content = e.content.replace(pattern, function(str) {
+ return '';
+ });
+ });
+ });
+ }
+
+ self.on('SetContent', function() {
+ self.addVisual(self.getBody());
+ });
+
+ // Remove empty contents
+ if (settings.padd_empty_editor) {
+ self.on('PostProcess', function(e) {
+ e.content = e.content.replace(/^(
]*>( | |\s|\u00a0| |)<\/p>[\r\n]*| [\r\n]*)$/, '');
+ });
+ }
+
+ self.load({initial: true, format: 'html'});
+ self.startContent = self.getContent({format: 'raw'});
+
+ /**
+ * Is set to true after the editor instance has been initialized
+ *
+ * @property initialized
+ * @type Boolean
+ * @example
+ * function isEditorInitialized(editor) {
+ * return editor && editor.initialized;
+ * }
+ */
+ self.initialized = true;
+ self.bindPendingEventDelegates();
+
+ self.fire('init');
+ self.focus(true);
+ self.nodeChanged({initial: true});
+ self.execCallback('init_instance_callback', self);
+
+ self.on('compositionstart compositionend', function(e) {
+ self.composing = e.type === 'compositionstart';
+ });
+
+ // Add editor specific CSS styles
+ if (self.contentStyles.length > 0) {
+ contentCssText = '';
+
+ each(self.contentStyles, function(style) {
+ contentCssText += style + "\r\n";
+ });
+
+ self.dom.addStyle(contentCssText);
+ }
+
+ // Load specified content CSS last
+ each(self.contentCSS, function(cssUrl) {
+ if (!self.loadedCSS[cssUrl]) {
+ self.dom.loadCSS(cssUrl);
+ self.loadedCSS[cssUrl] = true;
+ }
+ });
+
+ // Handle auto focus
+ if (settings.auto_focus) {
+ Delay.setEditorTimeout(self, function() {
+ var editor;
+
+ if (settings.auto_focus === true) {
+ editor = self;
+ } else {
+ editor = self.editorManager.get(settings.auto_focus);
+ }
+
+ if (!editor.destroyed) {
+ editor.focus();
+ }
+ }, 100);
+ }
+
+ // Clean up references for IE
+ targetElm = doc = body = null;
+ },
+
+ /**
+ * Focuses/activates the editor. This will set this editor as the activeEditor in the tinymce collection
+ * it will also place DOM focus inside the editor.
+ *
+ * @method focus
+ * @param {Boolean} skipFocus Skip DOM focus. Just set is as the active editor.
+ */
+ focus: function(skipFocus) {
+ var self = this, selection = self.selection, contentEditable = self.settings.content_editable, rng;
+ var controlElm, doc = self.getDoc(), body = self.getBody(), contentEditableHost;
+
+ function getContentEditableHost(node) {
+ return self.dom.getParent(node, function(node) {
+ return self.dom.getContentEditable(node) === "true";
+ });
+ }
+
+ if (!skipFocus) {
+ // Get selected control element
+ rng = selection.getRng();
+ if (rng.item) {
+ controlElm = rng.item(0);
+ }
+
+ self.quirks.refreshContentEditable();
+
+ // Move focus to contentEditable=true child if needed
+ contentEditableHost = getContentEditableHost(selection.getNode());
+ if (self.$.contains(body, contentEditableHost)) {
+ contentEditableHost.focus();
+ selection.normalize();
+ self.editorManager.setActive(self);
+ return;
+ }
+
+ // Focus the window iframe
+ if (!contentEditable) {
+ // WebKit needs this call to fire focusin event properly see #5948
+ // But Opera pre Blink engine will produce an empty selection so skip Opera
+ if (!Env.opera) {
+ self.getBody().focus();
+ }
+
+ self.getWin().focus();
+ }
+
+ // Focus the body as well since it's contentEditable
+ if (isGecko || contentEditable) {
+ // Check for setActive since it doesn't scroll to the element
+ if (body.setActive) {
+ // IE 11 sometimes throws "Invalid function" then fallback to focus
+ try {
+ body.setActive();
+ } catch (ex) {
+ body.focus();
+ }
+ } else {
+ body.focus();
+ }
+
+ if (contentEditable) {
+ selection.normalize();
+ }
+ }
+
+ // Restore selected control element
+ // This is needed when for example an image is selected within a
+ // layer a call to focus will then remove the control selection
+ if (controlElm && controlElm.ownerDocument == doc) {
+ rng = doc.body.createControlRange();
+ rng.addElement(controlElm);
+ rng.select();
+ }
+ }
+
+ self.editorManager.setActive(self);
+ },
+
+ /**
+ * Executes a legacy callback. This method is useful to call old 2.x option callbacks.
+ * There new event model is a better way to add callback so this method might be removed in the future.
+ *
+ * @method execCallback
+ * @param {String} name Name of the callback to execute.
+ * @return {Object} Return value passed from callback function.
+ */
+ execCallback: function(name) {
+ var self = this, callback = self.settings[name], scope;
+
+ if (!callback) {
+ return;
+ }
+
+ // Look through lookup
+ if (self.callbackLookup && (scope = self.callbackLookup[name])) {
+ callback = scope.func;
+ scope = scope.scope;
+ }
+
+ if (typeof callback === 'string') {
+ scope = callback.replace(/\.\w+$/, '');
+ scope = scope ? resolve(scope) : 0;
+ callback = resolve(callback);
+ self.callbackLookup = self.callbackLookup || {};
+ self.callbackLookup[name] = {func: callback, scope: scope};
+ }
+
+ return callback.apply(scope || self, Array.prototype.slice.call(arguments, 1));
+ },
+
+ /**
+ * Translates the specified string by replacing variables with language pack items it will also check if there is
+ * a key matching the input.
+ *
+ * @method translate
+ * @param {String} text String to translate by the language pack data.
+ * @return {String} Translated string.
+ */
+ translate: function(text) {
+ var lang = this.settings.language || 'en', i18n = this.editorManager.i18n;
+
+ if (!text) {
+ return '';
+ }
+
+ text = i18n.data[lang + '.' + text] || text.replace(/\{\#([^\}]+)\}/g, function(a, b) {
+ return i18n.data[lang + '.' + b] || '{#' + b + '}';
+ });
+
+ return this.editorManager.translate(text);
+ },
+
+ /**
+ * Returns a language pack item by name/key.
+ *
+ * @method getLang
+ * @param {String} name Name/key to get from the language pack.
+ * @param {String} defaultVal Optional default value to retrieve.
+ */
+ getLang: function(name, defaultVal) {
+ return (
+ this.editorManager.i18n.data[(this.settings.language || 'en') + '.' + name] ||
+ (defaultVal !== undefined ? defaultVal : '{#' + name + '}')
+ );
+ },
+
+ /**
+ * Returns a configuration parameter by name.
+ *
+ * @method getParam
+ * @param {String} name Configruation parameter to retrieve.
+ * @param {String} defaultVal Optional default value to return.
+ * @param {String} type Optional type parameter.
+ * @return {String} Configuration parameter value or default value.
+ * @example
+ * // Returns a specific config value from the currently active editor
+ * var someval = tinymce.activeEditor.getParam('myvalue');
+ *
+ * // Returns a specific config value from a specific editor instance by id
+ * var someval2 = tinymce.get('my_editor').getParam('myvalue');
+ */
+ getParam: function(name, defaultVal, type) {
+ var value = name in this.settings ? this.settings[name] : defaultVal, output;
+
+ if (type === 'hash') {
+ output = {};
+
+ if (typeof value === 'string') {
+ each(value.indexOf('=') > 0 ? value.split(/[;,](?![^=;,]*(?:[;,]|$))/) : value.split(','), function(value) {
+ value = value.split('=');
+
+ if (value.length > 1) {
+ output[trim(value[0])] = trim(value[1]);
+ } else {
+ output[trim(value[0])] = trim(value);
+ }
+ });
+ } else {
+ output = value;
+ }
+
+ return output;
+ }
+
+ return value;
+ },
+
+ /**
+ * Dispatches out a onNodeChange event to all observers. This method should be called when you
+ * need to update the UI states or element path etc.
+ *
+ * @method nodeChanged
+ * @param {Object} args Optional args to pass to NodeChange event handlers.
+ */
+ nodeChanged: function(args) {
+ this._nodeChangeDispatcher.nodeChanged(args);
+ },
+
+ /**
+ * Adds a button that later gets created by the theme in the editors toolbars.
+ *
+ * @method addButton
+ * @param {String} name Button name to add.
+ * @param {Object} settings Settings object with title, cmd etc.
+ * @example
+ * // Adds a custom button to the editor that inserts contents when clicked
+ * tinymce.init({
+ * ...
+ *
+ * toolbar: 'example'
+ *
+ * setup: function(ed) {
+ * ed.addButton('example', {
+ * title: 'My title',
+ * image: '../js/tinymce/plugins/example/img/example.gif',
+ * onclick: function() {
+ * ed.insertContent('Hello world!!');
+ * }
+ * });
+ * }
+ * });
+ */
+ addButton: function(name, settings) {
+ var self = this;
+
+ if (settings.cmd) {
+ settings.onclick = function() {
+ self.execCommand(settings.cmd);
+ };
+ }
+
+ if (!settings.text && !settings.icon) {
+ settings.icon = name;
+ }
+
+ self.buttons = self.buttons || {};
+ settings.tooltip = settings.tooltip || settings.title;
+ self.buttons[name] = settings;
+ },
+
+ /**
+ * Adds a sidebar for the editor instance.
+ *
+ * @method addSidebar
+ * @param {String} name Sidebar name to add.
+ * @param {Object} settings Settings object with icon, onshow etc.
+ * @example
+ * // Adds a custom sidebar that when clicked logs the panel element
+ * tinymce.init({
+ * ...
+ * setup: function(ed) {
+ * ed.addSidebar('example', {
+ * tooltip: 'My sidebar',
+ * icon: 'my-side-bar',
+ * onshow: function(api) {
+ * console.log(api.element());
+ * }
+ * });
+ * }
+ * });
+ */
+ addSidebar: function (name, settings) {
+ return Sidebar.add(this, name, settings);
+ },
+
+ /**
+ * Adds a menu item to be used in the menus of the theme. There might be multiple instances
+ * of this menu item for example it might be used in the main menus of the theme but also in
+ * the context menu so make sure that it's self contained and supports multiple instances.
+ *
+ * @method addMenuItem
+ * @param {String} name Menu item name to add.
+ * @param {Object} settings Settings object with title, cmd etc.
+ * @example
+ * // Adds a custom menu item to the editor that inserts contents when clicked
+ * // The context option allows you to add the menu item to an existing default menu
+ * tinymce.init({
+ * ...
+ *
+ * setup: function(ed) {
+ * ed.addMenuItem('example', {
+ * text: 'My menu item',
+ * context: 'tools',
+ * onclick: function() {
+ * ed.insertContent('Hello world!!');
+ * }
+ * });
+ * }
+ * });
+ */
+ addMenuItem: function(name, settings) {
+ var self = this;
+
+ if (settings.cmd) {
+ settings.onclick = function() {
+ self.execCommand(settings.cmd);
+ };
+ }
+
+ self.menuItems = self.menuItems || {};
+ self.menuItems[name] = settings;
+ },
+
+ /**
+ * Adds a contextual toolbar to be rendered when the selector matches.
+ *
+ * @method addContextToolbar
+ * @param {function/string} predicate Predicate that needs to return true if provided strings get converted into CSS predicates.
+ * @param {String/Array} items String or array with items to add to the context toolbar.
+ */
+ addContextToolbar: function(predicate, items) {
+ var self = this, selector;
+
+ self.contextToolbars = self.contextToolbars || [];
+
+ // Convert selector to predicate
+ if (typeof predicate == "string") {
+ selector = predicate;
+ predicate = function(elm) {
+ return self.dom.is(elm, selector);
+ };
+ }
+
+ self.contextToolbars.push({
+ id: Uuid.uuid('mcet'),
+ predicate: predicate,
+ items: items
+ });
+ },
+
+ /**
+ * Adds a custom command to the editor, you can also override existing commands with this method.
+ * The command that you add can be executed with execCommand.
+ *
+ * @method addCommand
+ * @param {String} name Command name to add/override.
+ * @param {addCommandCallback} callback Function to execute when the command occurs.
+ * @param {Object} scope Optional scope to execute the function in.
+ * @example
+ * // Adds a custom command that later can be executed using execCommand
+ * tinymce.init({
+ * ...
+ *
+ * setup: function(ed) {
+ * // Register example command
+ * ed.addCommand('mycommand', function(ui, v) {
+ * ed.windowManager.alert('Hello world!! Selection: ' + ed.selection.getContent({format: 'text'}));
+ * });
+ * }
+ * });
+ */
+ addCommand: function(name, callback, scope) {
+ /**
+ * Callback function that gets called when a command is executed.
+ *
+ * @callback addCommandCallback
+ * @param {Boolean} ui Display UI state true/false.
+ * @param {Object} value Optional value for command.
+ * @return {Boolean} True/false state if the command was handled or not.
+ */
+ this.editorCommands.addCommand(name, callback, scope);
+ },
+
+ /**
+ * Adds a custom query state command to the editor, you can also override existing commands with this method.
+ * The command that you add can be executed with queryCommandState function.
+ *
+ * @method addQueryStateHandler
+ * @param {String} name Command name to add/override.
+ * @param {addQueryStateHandlerCallback} callback Function to execute when the command state retrieval occurs.
+ * @param {Object} scope Optional scope to execute the function in.
+ */
+ addQueryStateHandler: function(name, callback, scope) {
+ /**
+ * Callback function that gets called when a queryCommandState is executed.
+ *
+ * @callback addQueryStateHandlerCallback
+ * @return {Boolean} True/false state if the command is enabled or not like is it bold.
+ */
+ this.editorCommands.addQueryStateHandler(name, callback, scope);
+ },
+
+ /**
+ * Adds a custom query value command to the editor, you can also override existing commands with this method.
+ * The command that you add can be executed with queryCommandValue function.
+ *
+ * @method addQueryValueHandler
+ * @param {String} name Command name to add/override.
+ * @param {addQueryValueHandlerCallback} callback Function to execute when the command value retrieval occurs.
+ * @param {Object} scope Optional scope to execute the function in.
+ */
+ addQueryValueHandler: function(name, callback, scope) {
+ /**
+ * Callback function that gets called when a queryCommandValue is executed.
+ *
+ * @callback addQueryValueHandlerCallback
+ * @return {Object} Value of the command or undefined.
+ */
+ this.editorCommands.addQueryValueHandler(name, callback, scope);
+ },
+
+ /**
+ * Adds a keyboard shortcut for some command or function.
+ *
+ * @method addShortcut
+ * @param {String} pattern Shortcut pattern. Like for example: ctrl+alt+o.
+ * @param {String} desc Text description for the command.
+ * @param {String/Function} cmdFunc Command name string or function to execute when the key is pressed.
+ * @param {Object} sc Optional scope to execute the function in.
+ * @return {Boolean} true/false state if the shortcut was added or not.
+ */
+ addShortcut: function(pattern, desc, cmdFunc, scope) {
+ this.shortcuts.add(pattern, desc, cmdFunc, scope);
+ },
+
+ /**
+ * Executes a command on the current instance. These commands can be TinyMCE internal commands prefixed with "mce" or
+ * they can be build in browser commands such as "Bold". A compleate list of browser commands is available on MSDN or Mozilla.org.
+ * This function will dispatch the execCommand function on each plugin, theme or the execcommand_callback option if none of these
+ * return true it will handle the command as a internal browser command.
+ *
+ * @method execCommand
+ * @param {String} cmd Command name to execute, for example mceLink or Bold.
+ * @param {Boolean} ui True/false state if a UI (dialog) should be presented or not.
+ * @param {mixed} value Optional command value, this can be anything.
+ * @param {Object} args Optional arguments object.
+ */
+ execCommand: function(cmd, ui, value, args) {
+ return this.editorCommands.execCommand(cmd, ui, value, args);
+ },
+
+ /**
+ * Returns a command specific state, for example if bold is enabled or not.
+ *
+ * @method queryCommandState
+ * @param {string} cmd Command to query state from.
+ * @return {Boolean} Command specific state, for example if bold is enabled or not.
+ */
+ queryCommandState: function(cmd) {
+ return this.editorCommands.queryCommandState(cmd);
+ },
+
+ /**
+ * Returns a command specific value, for example the current font size.
+ *
+ * @method queryCommandValue
+ * @param {string} cmd Command to query value from.
+ * @return {Object} Command specific value, for example the current font size.
+ */
+ queryCommandValue: function(cmd) {
+ return this.editorCommands.queryCommandValue(cmd);
+ },
+
+ /**
+ * Returns true/false if the command is supported or not.
+ *
+ * @method queryCommandSupported
+ * @param {String} cmd Command that we check support for.
+ * @return {Boolean} true/false if the command is supported or not.
+ */
+ queryCommandSupported: function(cmd) {
+ return this.editorCommands.queryCommandSupported(cmd);
+ },
+
+ /**
+ * Shows the editor and hides any textarea/div that the editor is supposed to replace.
+ *
+ * @method show
+ */
+ show: function() {
+ var self = this;
+
+ if (self.hidden) {
+ self.hidden = false;
+
+ if (self.inline) {
+ self.getBody().contentEditable = true;
+ } else {
+ DOM.show(self.getContainer());
+ DOM.hide(self.id);
+ }
+
+ self.load();
+ self.fire('show');
+ }
+ },
+
+ /**
+ * Hides the editor and shows any textarea/div that the editor is supposed to replace.
+ *
+ * @method hide
+ */
+ hide: function() {
+ var self = this, doc = self.getDoc();
+
+ if (!self.hidden) {
+ // Fixed bug where IE has a blinking cursor left from the editor
+ if (ie && doc && !self.inline) {
+ doc.execCommand('SelectAll');
+ }
+
+ // We must save before we hide so Safari doesn't crash
+ self.save();
+
+ if (self.inline) {
+ self.getBody().contentEditable = false;
+
+ // Make sure the editor gets blurred
+ if (self == self.editorManager.focusedEditor) {
+ self.editorManager.focusedEditor = null;
+ }
+ } else {
+ DOM.hide(self.getContainer());
+ DOM.setStyle(self.id, 'display', self.orgDisplay);
+ }
+
+ self.hidden = true;
+ self.fire('hide');
+ }
+ },
+
+ /**
+ * Returns true/false if the editor is hidden or not.
+ *
+ * @method isHidden
+ * @return {Boolean} True/false if the editor is hidden or not.
+ */
+ isHidden: function() {
+ return !!this.hidden;
+ },
+
+ /**
+ * Sets the progress state, this will display a throbber/progess for the editor.
+ * This is ideal for asynchronous operations like an AJAX save call.
+ *
+ * @method setProgressState
+ * @param {Boolean} state Boolean state if the progress should be shown or hidden.
+ * @param {Number} time Optional time to wait before the progress gets shown.
+ * @return {Boolean} Same as the input state.
+ * @example
+ * // Show progress for the active editor
+ * tinymce.activeEditor.setProgressState(true);
+ *
+ * // Hide progress for the active editor
+ * tinymce.activeEditor.setProgressState(false);
+ *
+ * // Show progress after 3 seconds
+ * tinymce.activeEditor.setProgressState(true, 3000);
+ */
+ setProgressState: function(state, time) {
+ this.fire('ProgressState', {state: state, time: time});
+ },
+
+ /**
+ * Loads contents from the textarea or div element that got converted into an editor instance.
+ * This method will move the contents from that textarea or div into the editor by using setContent
+ * so all events etc that method has will get dispatched as well.
+ *
+ * @method load
+ * @param {Object} args Optional content object, this gets passed around through the whole load process.
+ * @return {String} HTML string that got set into the editor.
+ */
+ load: function(args) {
+ var self = this, elm = self.getElement(), html;
+
+ if (elm) {
+ args = args || {};
+ args.load = true;
+
+ html = self.setContent(elm.value !== undefined ? elm.value : elm.innerHTML, args);
+ args.element = elm;
+
+ if (!args.no_events) {
+ self.fire('LoadContent', args);
+ }
+
+ args.element = elm = null;
+
+ return html;
+ }
+ },
+
+ /**
+ * Saves the contents from a editor out to the textarea or div element that got converted into an editor instance.
+ * This method will move the HTML contents from the editor into that textarea or div by getContent
+ * so all events etc that method has will get dispatched as well.
+ *
+ * @method save
+ * @param {Object} args Optional content object, this gets passed around through the whole save process.
+ * @return {String} HTML string that got set into the textarea/div.
+ */
+ save: function(args) {
+ var self = this, elm = self.getElement(), html, form;
+
+ if (!elm || !self.initialized) {
+ return;
+ }
+
+ args = args || {};
+ args.save = true;
+
+ args.element = elm;
+ html = args.content = self.getContent(args);
+
+ if (!args.no_events) {
+ self.fire('SaveContent', args);
+ }
+
+ // Always run this internal event
+ if (args.format == 'raw') {
+ self.fire('RawSaveContent', args);
+ }
+
+ html = args.content;
+
+ if (!/TEXTAREA|INPUT/i.test(elm.nodeName)) {
+ // Update DIV element when not in inline mode
+ if (!self.inline) {
+ elm.innerHTML = html;
+ }
+
+ // Update hidden form element
+ if ((form = DOM.getParent(self.id, 'form'))) {
+ each(form.elements, function(elm) {
+ if (elm.name == self.id) {
+ elm.value = html;
+ return false;
+ }
+ });
+ }
+ } else {
+ elm.value = html;
+ }
+
+ args.element = elm = null;
+
+ if (args.set_dirty !== false) {
+ self.setDirty(false);
+ }
+
+ return html;
+ },
+
+ /**
+ * Sets the specified content to the editor instance, this will cleanup the content before it gets set using
+ * the different cleanup rules options.
+ *
+ * @method setContent
+ * @param {String} content Content to set to editor, normally HTML contents but can be other formats as well.
+ * @param {Object} args Optional content object, this gets passed around through the whole set process.
+ * @return {String} HTML string that got set into the editor.
+ * @example
+ * // Sets the HTML contents of the activeEditor editor
+ * tinymce.activeEditor.setContent('some html');
+ *
+ * // Sets the raw contents of the activeEditor editor
+ * tinymce.activeEditor.setContent('some html', {format: 'raw'});
+ *
+ * // Sets the content of a specific editor (my_editor in this example)
+ * tinymce.get('my_editor').setContent(data);
+ *
+ * // Sets the bbcode contents of the activeEditor editor if the bbcode plugin was added
+ * tinymce.activeEditor.setContent('[b]some[/b] html', {format: 'bbcode'});
+ */
+ setContent: function(content, args) {
+ var self = this, body = self.getBody(), forcedRootBlockName, padd;
+
+ // Setup args object
+ args = args || {};
+ args.format = args.format || 'html';
+ args.set = true;
+ args.content = content;
+
+ // Do preprocessing
+ if (!args.no_events) {
+ self.fire('BeforeSetContent', args);
+ }
+
+ content = args.content;
+
+ // Padd empty content in Gecko and Safari. Commands will otherwise fail on the content
+ // It will also be impossible to place the caret in the editor unless there is a BR element present
+ if (content.length === 0 || /^\s+$/.test(content)) {
+ padd = ie && ie < 11 ? '' : ' ';
+
+ // Todo: There is a lot more root elements that need special padding
+ // so separate this and add all of them at some point.
+ if (body.nodeName == 'TABLE') {
+ content = '