aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--CHANGES.txt146
-rw-r--r--COPYRIGHT.txt1
-rw-r--r--actions/admin/site/regenerate_secret.php11
-rw-r--r--actions/admin/site/update_advanced.php6
-rw-r--r--actions/admin/site/update_basic.php2
-rw-r--r--actions/avatar/remove.php52
-rw-r--r--actions/comments/delete.php5
-rw-r--r--actions/friends/collections/add.php2
-rw-r--r--actions/login.php5
-rw-r--r--actions/profile/edit.php9
-rw-r--r--actions/register.php2
-rw-r--r--documentation/info/manifest.xml2
-rw-r--r--engine/classes/ElggAccess.php4
-rw-r--r--engine/classes/ElggAnnotation.php5
-rw-r--r--engine/classes/ElggAttributeLoader.php77
-rw-r--r--engine/classes/ElggAutoP.php59
-rw-r--r--engine/classes/ElggBatch.php71
-rw-r--r--engine/classes/ElggCache.php4
-rw-r--r--engine/classes/ElggCrypto.php208
-rw-r--r--engine/classes/ElggData.php20
-rw-r--r--engine/classes/ElggDiskFilestore.php40
-rw-r--r--engine/classes/ElggEntity.php46
-rw-r--r--engine/classes/ElggExtender.php2
-rw-r--r--engine/classes/ElggFile.php21
-rw-r--r--engine/classes/ElggFileCache.php6
-rw-r--r--engine/classes/ElggGroup.php25
-rw-r--r--engine/classes/ElggLRUCache.php181
-rw-r--r--engine/classes/ElggMemcache.php2
-rw-r--r--engine/classes/ElggMenuBuilder.php29
-rw-r--r--engine/classes/ElggMetadata.php4
-rw-r--r--engine/classes/ElggObject.php19
-rw-r--r--engine/classes/ElggPlugin.php33
-rw-r--r--engine/classes/ElggPluginPackage.php6
-rw-r--r--engine/classes/ElggPriorityList.php30
-rw-r--r--engine/classes/ElggRelationship.php5
-rw-r--r--engine/classes/ElggSite.php20
-rw-r--r--engine/classes/ElggStaticVariableCache.php6
-rw-r--r--engine/classes/ElggTranslit.php55
-rw-r--r--engine/classes/ElggUser.php23
-rw-r--r--engine/classes/ElggVolatileMetadataCache.php92
-rw-r--r--engine/classes/ElggWidget.php12
-rw-r--r--engine/classes/ElggXMLElement.php24
-rw-r--r--engine/classes/ODDMetaData.php14
-rw-r--r--engine/classes/ODDRelationship.php8
-rw-r--r--engine/handlers/cache_handler.php13
-rw-r--r--engine/lib/access.php11
-rw-r--r--engine/lib/actions.php117
-rw-r--r--engine/lib/admin.php37
-rw-r--r--engine/lib/annotations.php89
-rw-r--r--engine/lib/cache.php11
-rw-r--r--engine/lib/calendar.php2
-rw-r--r--engine/lib/configuration.php29
-rw-r--r--engine/lib/cron.php4
-rw-r--r--engine/lib/database.php95
-rw-r--r--engine/lib/deprecated-1.7.php2
-rw-r--r--engine/lib/deprecated-1.8.php66
-rw-r--r--engine/lib/elgglib.php78
-rw-r--r--engine/lib/entities.php216
-rw-r--r--engine/lib/export.php29
-rw-r--r--engine/lib/extender.php20
-rw-r--r--engine/lib/filestore.php6
-rw-r--r--engine/lib/group.php6
-rw-r--r--engine/lib/input.php15
-rw-r--r--engine/lib/languages.php48
-rw-r--r--engine/lib/location.php4
-rw-r--r--engine/lib/mb_wrapper.php2
-rw-r--r--engine/lib/memcache.php20
-rw-r--r--engine/lib/metadata.php62
-rw-r--r--engine/lib/metastrings.php37
-rw-r--r--engine/lib/navigation.php21
-rw-r--r--engine/lib/notification.php38
-rw-r--r--engine/lib/objects.php6
-rw-r--r--engine/lib/opendd.php4
-rw-r--r--engine/lib/output.php57
-rw-r--r--engine/lib/pageowner.php7
-rw-r--r--engine/lib/plugins.php90
-rw-r--r--engine/lib/relationships.php34
-rw-r--r--engine/lib/river.php23
-rw-r--r--engine/lib/sessions.php17
-rw-r--r--engine/lib/sites.php12
-rw-r--r--engine/lib/statistics.php11
-rw-r--r--engine/lib/system_log.php22
-rw-r--r--engine/lib/tags.php7
-rw-r--r--engine/lib/upgrade.php21
-rw-r--r--engine/lib/upgrades/2009102801.php5
-rw-r--r--engine/lib/upgrades/2010033101.php2
-rw-r--r--engine/lib/upgrades/2010061501.php6
-rw-r--r--engine/lib/upgrades/2010071001.php5
-rw-r--r--engine/lib/upgrades/2010071002.php5
-rw-r--r--engine/lib/upgrades/2011052801.php5
-rw-r--r--engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php2
-rw-r--r--engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php24
-rw-r--r--engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php28
-rw-r--r--engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php12
-rw-r--r--engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php16
-rw-r--r--engine/lib/upgrades/create_upgrade.php5
-rw-r--r--engine/lib/user_settings.php7
-rw-r--r--engine/lib/users.php100
-rw-r--r--engine/lib/views.php56
-rw-r--r--engine/lib/web_services.php27
-rw-r--r--engine/lib/widgets.php3
-rw-r--r--engine/lib/xml.php2
-rw-r--r--engine/schema/mysql.sql2
-rw-r--r--engine/tests/api/access_collections.php3
-rw-r--r--engine/tests/api/annotations.php80
-rw-r--r--engine/tests/api/entity_getter_functions.php68
-rw-r--r--engine/tests/api/helpers.php103
-rw-r--r--engine/tests/api/metadata.php16
-rw-r--r--engine/tests/api/metadata_cache.php7
-rw-r--r--engine/tests/api/metastrings.php33
-rw-r--r--engine/tests/api/plugins.php4
-rw-r--r--engine/tests/objects/entities.php2
-rw-r--r--engine/tests/objects/objects.php2
-rw-r--r--engine/tests/objects/users.php21
-rw-r--r--engine/tests/regression/trac_bugs.php190
-rw-r--r--engine/tests/test_files/plugin_18/manifest.xml2
-rw-r--r--engine/tests/test_files/xxe/external_entity.txt1
-rw-r--r--engine/tests/test_files/xxe/request.xml8
-rw-r--r--htaccess_dist46
-rw-r--r--install/ElggInstaller.php14
-rw-r--r--install/cli/sample_installer.php40
-rw-r--r--install/languages/en.php3
-rw-r--r--js/lib/elgglib.js4
-rw-r--r--js/lib/languages.js12
-rw-r--r--js/lib/security.js33
-rw-r--r--js/lib/session.js32
-rw-r--r--js/lib/ui.river.js26
-rw-r--r--js/lib/ui.userpicker.js4
-rw-r--r--js/tests/ElggLibTest.js11
-rw-r--r--js/tests/README5
-rw-r--r--languages/en.php29
-rw-r--r--mod/blog/actions/blog/save.php33
-rw-r--r--mod/blog/lib/blog.php8
-rw-r--r--mod/blog/start.php28
-rw-r--r--mod/blog/views/default/blog/sidebar/archives.php2
-rw-r--r--mod/blog/views/default/forms/blog/save.php2
-rw-r--r--mod/blog/views/default/river/object/blog/create.php6
-rw-r--r--mod/bookmarks/languages/en.php2
-rw-r--r--mod/bookmarks/pages/bookmarks/all.php3
-rw-r--r--mod/bookmarks/pages/bookmarks/friends.php2
-rw-r--r--mod/bookmarks/pages/bookmarks/owner.php3
-rw-r--r--mod/bookmarks/start.php21
-rw-r--r--mod/categories/pages/categories/listing.php4
-rw-r--r--mod/custom_index/index.php2
-rw-r--r--mod/developers/languages/en.php3
-rw-r--r--mod/developers/manifest.xml2
-rw-r--r--mod/developers/start.php9
-rw-r--r--mod/developers/views/default/theme_preview/components/image_block.php12
-rw-r--r--mod/developers/views/default/theme_preview/components/list.php38
-rw-r--r--mod/developers/views/default/theme_preview/components/messages.php10
-rw-r--r--mod/developers/views/default/theme_preview/components/table.php22
-rw-r--r--mod/developers/views/default/theme_preview/icons/avatars.php72
-rw-r--r--mod/developers/views/default/theme_preview/icons/sprites.php120
-rw-r--r--mod/developers/views/default/theme_preview/modules/modules.php44
-rw-r--r--mod/developers/views/default/theme_preview/navigation/breadcrumbs.php20
-rw-r--r--mod/developers/views/default/theme_preview/navigation/default.php22
-rw-r--r--mod/developers/views/default/theme_preview/navigation/extras.php34
-rw-r--r--mod/developers/views/default/theme_preview/navigation/filter.php26
-rw-r--r--mod/developers/views/default/theme_preview/navigation/horizontal.php24
-rw-r--r--mod/developers/views/default/theme_preview/navigation/owner_block.php26
-rw-r--r--mod/developers/views/default/theme_preview/navigation/page.php38
-rw-r--r--mod/developers/views/default/theme_preview/navigation/pagination.php14
-rw-r--r--mod/developers/views/default/theme_preview/navigation/site.php36
-rw-r--r--mod/developers/views/default/theme_preview/navigation/tabs.php18
-rw-r--r--mod/developers/views/default/theme_preview/typography/headings.php10
-rw-r--r--mod/developers/views/default/theme_preview/typography/misc.php32
-rw-r--r--mod/developers/views/default/theme_preview/typography/paragraph.php36
-rw-r--r--mod/embed/start.php26
-rw-r--r--mod/embed/views/default/js/embed/embed.php13
-rw-r--r--mod/externalpages/start.php8
-rw-r--r--mod/file/actions/file/upload.php23
-rw-r--r--mod/file/pages/file/friends.php2
-rw-r--r--mod/file/pages/file/owner.php7
-rw-r--r--mod/file/pages/file/search.php4
-rw-r--r--mod/file/pages/file/world.php7
-rw-r--r--mod/groups/actions/groups/edit.php30
-rw-r--r--mod/groups/actions/groups/membership/invite.php73
-rw-r--r--mod/groups/lib/discussion.php5
-rw-r--r--mod/groups/lib/groups.php44
-rw-r--r--mod/groups/start.php10
-rw-r--r--mod/groups/views/default/forms/groups/edit.php13
-rw-r--r--mod/groups/views/default/groups/css.php4
-rw-r--r--mod/groups/views/default/groups/profile/summary.php9
-rw-r--r--mod/groups/views/default/groups/sidebar/featured.php2
-rw-r--r--mod/groups/views/default/groups/sidebar/members.php2
-rw-r--r--mod/groups/views/default/groups/sidebar/my_status.php4
-rw-r--r--mod/groups/views/default/object/groupforumtopic.php5
-rw-r--r--mod/htmlawed/start.php4
-rw-r--r--mod/htmlawed/tests/tags.php78
-rwxr-xr-xmod/htmlawed/vendors/htmLawed/htmLawed.php82
-rwxr-xr-xmod/htmlawed/vendors/htmLawed/htmLawedTest.php89
-rwxr-xr-x[-rw-r--r--]mod/htmlawed/vendors/htmLawed/htmLawed_README.htm4336
-rwxr-xr-xmod/htmlawed/vendors/htmLawed/htmLawed_README.txt3433
-rwxr-xr-xmod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt66
-rw-r--r--mod/logbrowser/views/default/forms/logbrowser/refine.php4
-rw-r--r--mod/logbrowser/views/default/logbrowser/refine.php2
-rw-r--r--mod/logbrowser/views/default/logbrowser/table.php2
-rw-r--r--mod/logrotate/languages/en.php3
-rw-r--r--mod/logrotate/start.php51
-rw-r--r--mod/logrotate/views/default/plugins/logrotate/settings.php1
-rw-r--r--mod/members/pages/members/search.php8
-rw-r--r--mod/messageboard/pages/messageboard/owner.php1
-rw-r--r--mod/messages/start.php53
-rw-r--r--mod/notifications/actions/groupsave.php4
-rw-r--r--mod/notifications/groups.php4
-rw-r--r--mod/notifications/index.php4
-rw-r--r--mod/notifications/views/default/forms/notificationsettings/groupsave.php4
-rw-r--r--mod/notifications/views/default/notifications/subscriptions/forminternals.php4
-rw-r--r--mod/oauth_api/manifest.xml25
-rw-r--r--mod/oauth_api/start.php24
-rw-r--r--mod/oauth_api/vendors/oauth/LICENSE21
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/INSTALL53
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/init.php127
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl2
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl2
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl13
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl21
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl41
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/hello.php65
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/index.php37
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/logon.php55
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/oauth.php77
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/register.php28
-rw-r--r--mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php71
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php226
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthException.php50
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequest.php801
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php274
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php209
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php262
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthRequester.php508
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthServer.php232
-rw-r--r--mod/oauth_api/vendors/oauth/library/OAuthStore.php86
-rw-r--r--mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php129
-rw-r--r--mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php143
-rw-r--r--mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php304
-rw-r--r--mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt101
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php69
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php115
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php95
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php80
-rw-r--r--mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php136
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php149
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php265
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php1879
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/mysql/install.php32
-rw-r--r--mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql219
-rw-r--r--mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds78
-rw-r--r--mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds73
-rw-r--r--mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds81
-rw-r--r--mod/oauth_api/vendors/oauth/test/oauth_test.php188
-rw-r--r--mod/pages/actions/annotations/page/delete.php20
-rw-r--r--mod/pages/actions/pages/delete.php26
-rw-r--r--mod/pages/languages/en.php7
-rw-r--r--mod/pages/lib/pages.php7
-rw-r--r--mod/pages/pages/pages/edit.php13
-rw-r--r--mod/pages/pages/pages/friends.php2
-rw-r--r--mod/pages/pages/pages/history.php4
-rw-r--r--mod/pages/pages/pages/owner.php6
-rw-r--r--mod/pages/pages/pages/world.php4
-rw-r--r--mod/pages/start.php48
-rw-r--r--mod/pages/upgrades/2012061800.php49
-rw-r--r--mod/pages/views/default/annotation/page.php18
-rw-r--r--mod/pages/views/default/object/page_top.php26
-rw-r--r--mod/pages/views/default/pages/sidebar/history.php1
-rw-r--r--mod/profile/icondirect.php6
-rw-r--r--mod/profile/views/default/profile/details.php16
-rw-r--r--mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php2
-rw-r--r--mod/reportedcontent/views/default/object/reported_content.php20
-rw-r--r--mod/reportedcontent/views/default/widgets/reportedcontent/content.php4
-rw-r--r--mod/search/README.txt2
-rw-r--r--mod/search/pages/search/index.php12
-rw-r--r--mod/search/search_hooks.php148
-rw-r--r--mod/search/views/default/search/comments/entity.php11
-rw-r--r--mod/search/views/default/search/list.php14
-rw-r--r--mod/search/views/rss/search/comments/entity.php11
-rw-r--r--mod/thewire/pages/thewire/everyone.php2
-rw-r--r--mod/thewire/pages/thewire/friends.php2
-rw-r--r--mod/thewire/pages/thewire/owner.php4
-rw-r--r--mod/thewire/views/default/thewire/profile_status.php4
-rw-r--r--mod/tinymce/vendor/tinymce/changelog.txt1414
-rw-r--r--mod/tinymce/vendor/tinymce/examples/full.html4
-rw-r--r--mod/tinymce/vendor/tinymce/examples/lists/media_list.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/langs/en.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advimage/js/image.js8
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advlink/js/advlink.js21
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin_src.js36
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin_src.js58
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin_src.js6
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/langs/en.js4
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin_src.js17
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin_src.js41
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/emotions/emotions.htm2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin_src.js10
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin_src.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin_src.js166
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin_src.js38
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/js/media.js101
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin_src.js556
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin_src.js20
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js8
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/css/props.css1
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin_src.js22
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/js/props.js80
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/langs/en_dlg.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/props.htm7
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/readme.txt19
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin_src.js128
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/cell.js4
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/row.js17
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/table.js23
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/row.htm30
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/css/visualblocks.css21
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin.js1
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin_src.js63
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js20
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/color_picker.htm8
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template_src.js224
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/img/icons.gifbin11790 -> 11982 bytes
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js25
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js674
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/image.js6
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/link.js12
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/source_editor.js32
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/langs/en_dlg.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css5
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/ui.css7
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/dialog.css7
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/ui.css6
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css5
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css7
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/source_editor.htm2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_popup.js2
-rw-r--r--mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_src.js9038
-rw-r--r--mod/tinymce/views/default/js/tinymce.php14
-rw-r--r--mod/twitter/graphics/thewire_speech_bubble.gifbin560 -> 0 bytes
-rw-r--r--mod/twitter/graphics/twitter16px.pngbin724 -> 0 bytes
-rw-r--r--mod/twitter/languages/en.php17
-rw-r--r--mod/twitter/manifest.xml16
-rw-r--r--mod/twitter/start.php14
-rw-r--r--mod/twitter/views/default/twitter/css.php63
-rw-r--r--mod/twitter/views/default/widgets/twitter/content.php42
-rw-r--r--mod/twitter/views/default/widgets/twitter/edit.php24
-rw-r--r--mod/twitter_api/languages/en.php4
-rw-r--r--mod/twitter_api/lib/twitter_api.php41
-rw-r--r--mod/twitter_api/manifest.xml10
-rw-r--r--mod/twitter_api/pages/twitter_api/interstitial.php4
-rw-r--r--mod/twitter_api/start.php33
-rw-r--r--mod/twitter_api/vendors/twitteroauth/OAuth.php390
-rw-r--r--mod/twitter_api/vendors/twitteroauth/README117
-rw-r--r--mod/twitter_api/vendors/twitteroauth/twitterOAuth.php16
-rw-r--r--mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php7
-rw-r--r--mod/twitter_api/views/default/plugins/twitter_api/settings.php15
-rw-r--r--pages/account/forgotten_password.php11
-rw-r--r--pages/account/login.php14
-rw-r--r--pages/account/register.php11
-rw-r--r--pages/account/reset_password.php11
-rw-r--r--pages/avatar/edit.php5
-rw-r--r--pages/river.php1
-rw-r--r--pages/settings/account.php3
-rw-r--r--pages/settings/statistics.php3
-rw-r--r--pages/settings/tools.php5
-rw-r--r--upgrade.php2
-rw-r--r--version.php6
-rw-r--r--views/default/admin/settings/advanced/site_secret.php11
-rw-r--r--views/default/css/admin.php29
-rw-r--r--views/default/css/elements/forms.php5
-rw-r--r--views/default/css/elements/navigation.php7
-rw-r--r--views/default/css/ie.php8
-rw-r--r--views/default/css/ie7.php12
-rw-r--r--views/default/forms/admin/site/regenerate_secret.php24
-rw-r--r--views/default/forms/plugins/settings/save.php6
-rw-r--r--views/default/forms/profile/edit.php13
-rw-r--r--views/default/icon/default.php30
-rw-r--r--views/default/input/pulldown.php2
-rw-r--r--views/default/input/userpicker.php8
-rw-r--r--views/default/js/elgg.php2
-rw-r--r--views/default/js/languages.php24
-rw-r--r--views/default/js/walled_garden.php28
-rw-r--r--views/default/object/default.php1
-rw-r--r--views/default/object/elements/full.php4
-rw-r--r--views/default/object/elements/summary.php2
-rw-r--r--views/default/output/access.php2
-rw-r--r--views/default/output/tag.php17
-rw-r--r--views/default/output/tagcloud.php2
-rw-r--r--views/default/output/tags.php19
-rw-r--r--views/default/page/walled_garden.php15
-rw-r--r--views/default/river/elements/summary.php3
409 files changed, 16632 insertions, 18737 deletions
diff --git a/.gitignore b/.gitignore
index c90f6b85b..2303faa0c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,7 +40,7 @@
!/mod/uservalidationbyemail/
!/mod/zaudio/
-# ignore IDE/hidden/OS cache files
+# ignore IDE/hidden/testing/OS cache files
.*
*~
/nbproject
@@ -51,6 +51,7 @@ Session.vim
tmtags
Thumbs.db
Desktop.ini
+/JsTestDriver-*.jar
# don't ignore travis config
!/.travis.yml
diff --git a/CHANGES.txt b/CHANGES.txt
index 13c30ae3e..f6974a3ae 100644
--- a/CHANGES.txt
+++ b/CHANGES.txt
@@ -1,3 +1,149 @@
+Version 1.8.18
+(January 11, 2014 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Juho Jaakkola
+ * Steve Clay
+
+ Bugfixes:
+ * Fixes notify_user() broken in 1.8.17
+
+
+Version 1.8.17
+(January 1, 2014 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Brett Profitt
+ * Cash Costello
+ * Ed Lyons
+ * Evan Winslow
+ * Jeroen Dalsem
+ * Jerome Bakker
+ * Juho Jaakkola
+ * Matt Beckett
+ * Paweł Sroka
+ * Sem
+ * Steve Clay
+
+ Security Fixes:
+ * Specially-crafted request could return the contents of sensitive files.
+ * Reflected XSS attack was possible against 1.8 systems.
+ * The cryptographic key used for various purposes may have been generated with weak entropy, particularly on Windows.
+
+ Bugfixes:
+ * URLs with non-ASCII usernames again work
+ * Floated images are now properly cleared in content areas
+ * The activity page title now matches the document title
+ * Search again supports multiple comments on the same entity
+ * Blog archive sidebar now reverse chronological
+ * URLs with matching parens can now be auto-linked
+ * Log browser links for users now work
+ * Disabling over 50 objects should no longer result in an infinite loop
+ * Radio/checkbox inputs no longer have border radius (for IE10)
+ * User picker: the Only Friends checkbox again works
+ * Group bookmarklet no longer shown to non-members
+ * Widget reordering fixed when moving across columns
+ * Refuse to deactivate plugins needed as dependencies
+
+ Enhancements:
+ * Group member listings are ordered by name
+ * The system_log table can now store IPv6 addresses
+ * Web services auth_gettoken() now accepts email address
+ * List functions: no need to specify pagination for unlimited queries
+ * Htmlawed was upgraded to 1.1.16
+
+
+Version 1.8.16
+(June 25, 2013 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Brett Profitt
+ * Cash Costello
+ * Jeff Tilson
+ * Jerome Bakker
+ * Paweł Sroka
+ * Steve Clay
+
+ Security Fixes:
+ * Fixed avatar removal bug (thanks to Jerome Bakker for the first report of this)
+
+ Bugfixes:
+ * Fixed infinite loop when deleting/disabling an entity with > 50 annotations
+ * Fixed deleting log tables in log rotate plugin
+ * Added full text index for groups if missing
+ * Added workaround for IE8 and jumping user avatar
+ * Fixed pagination for members pages
+ * Fixed several internal cache issues
+ * Plus many more bug fixes
+
+
+Version 1.8.15
+(April 23, 2013 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Cash Costello
+ * Ismayil Khayredinov
+ * Jeff Tilson
+ * Juho Jaakkola
+ * Matt Beckett
+ * Paweł Sroka
+ * Sem
+ * Steve Clay
+ * Tom Voorneveld
+
+ Bugfixes:
+ * Not displaying http:// on profiles when website isn't set
+ * Fixed pagination display issue for small screens
+ * Not hiding subpages of top level pages that have been deleted
+ * Stop corrupting JavaScript views with elgg deprecation messages
+ * Fixed out of memory error due to query cache
+ * Fixed bug preventing users authorizing Twitter account access
+ * Fixed friends access level for editing pages
+ * Fixed uploading files within the embed dialog
+
+ Enhancements:
+ * Added browser caching of language JS files
+ * Adding nofollow on user posted URLs for spam deterrence (thanks to Hellekin)
+ * Auto-registering views for simplecache when their URL is requested
+ * Display helpful message for those who have site URL configuration issues
+ * Can revert to a previous revision with pages plugin
+ * Site owners can turn off posting wire messages to Twitter
+ * Search results are sorted by relevance
+
+ Dropped Plugins:
+ * Twitter widget due to changes in Twitter API and terms of service
+ * OAuth API plugin due to conflicts with the Twitter API plugin
+
+
+Version 1.8.14
+(March 12, 2013 from https://github.com/Elgg/Elgg/tree/1.8)
+ Contributing Developers:
+ * Aday Talavera
+ * Brett Profitt
+ * Cash Costello
+ * Ed Lyons
+ * German Bortoli
+ * Hellekin Wolf
+ * iionly
+ * Jerome Bakker
+ * Luciano Lima
+ * Matt Beckett
+ * Paweł Sroka
+ * Sem
+ * Steve Clay
+
+ Security Fixes:
+ * Fixed a XSS vulnerability when accepting URLs on user profiles
+ * Fixed bug that exposed subject lines of messages in inbox
+ * Added requirement for CSRF token for login
+
+ Bugfixes:
+ * Strip html tags from tag input
+ * Fixed several display issues for IE7
+ * Fixed several issues with blog drafts
+ * Fixed repeated token timeout errors
+ * Fixed JavaScript localization for non-English languages
+
+ Enhancements:
+ * Web services fall back to json if the viewtype is invalid
+
+
Version 1.8.13
(January 29, 2013 from https://github.com/Elgg/Elgg/tree/1.8)
Contributing Developers:
diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt
index 76781f25a..262515386 100644
--- a/COPYRIGHT.txt
+++ b/COPYRIGHT.txt
@@ -6,6 +6,7 @@ The MITRE Corportation (jricher@mitre.org)
Curverider Ltd (info@elgg.com)
Individuals:
+Steve Clay (steve@mrclay.org)
Cash Costello (cash.costello@gmail.com)
Brett Profitt (brett.profitt@gmail.com)
Dave Tosh (davidgtosh@gmail.com)
diff --git a/actions/admin/site/regenerate_secret.php b/actions/admin/site/regenerate_secret.php
new file mode 100644
index 000000000..3112fb5f3
--- /dev/null
+++ b/actions/admin/site/regenerate_secret.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Generate a new site secret
+ */
+
+init_site_secret();
+elgg_reset_system_cache();
+
+system_message(elgg_echo('admin:site:secret_regenerated'));
+
+forward(REFERER);
diff --git a/actions/admin/site/update_advanced.php b/actions/admin/site/update_advanced.php
index 0fd8d1f35..4888b0a8d 100644
--- a/actions/admin/site/update_advanced.php
+++ b/actions/admin/site/update_advanced.php
@@ -14,10 +14,10 @@ if ($site = elgg_get_site_entity()) {
throw new InstallationException(elgg_echo('InvalidParameterException:NonElggSite'));
}
- $site->url = get_input('wwwroot');
+ $site->url = rtrim(get_input('wwwroot', '', false), '/') . '/';
- datalist_set('path', sanitise_filepath(get_input('path')));
- $dataroot = sanitise_filepath(get_input('dataroot'));
+ datalist_set('path', sanitise_filepath(get_input('path', '', false)));
+ $dataroot = sanitise_filepath(get_input('dataroot', '', false));
// check for relative paths
if (stripos(PHP_OS, 'win') === 0) {
diff --git a/actions/admin/site/update_basic.php b/actions/admin/site/update_basic.php
index 97d258b65..9765182cc 100644
--- a/actions/admin/site/update_basic.php
+++ b/actions/admin/site/update_basic.php
@@ -16,7 +16,7 @@ if ($site = elgg_get_site_entity()) {
}
$site->description = get_input('sitedescription');
- $site->name = get_input('sitename');
+ $site->name = strip_tags(get_input('sitename'));
$site->email = get_input('siteemail');
$site->save();
diff --git a/actions/avatar/remove.php b/actions/avatar/remove.php
index cd38e456a..9cb40a760 100644
--- a/actions/avatar/remove.php
+++ b/actions/avatar/remove.php
@@ -3,32 +3,34 @@
* Avatar remove action
*/
-$guid = get_input('guid');
-$user = get_entity($guid);
-if ($user) {
- // Delete all icons from diskspace
- $icon_sizes = elgg_get_config('icon_sizes');
- foreach ($icon_sizes as $name => $size_info) {
- $file = new ElggFile();
- $file->owner_guid = $guid;
- $file->setFilename("profile/{$guid}{$name}.jpg");
- $filepath = $file->getFilenameOnFilestore();
- if (!$file->delete()) {
- elgg_log("Avatar file remove failed. Remove $filepath manually, please.", 'WARNING');
- }
- }
-
- // Remove crop coords
- unset($user->x1);
- unset($user->x2);
- unset($user->y1);
- unset($user->y2);
-
- // Remove icon
- unset($user->icontime);
- system_message(elgg_echo('avatar:remove:success'));
-} else {
+$user_guid = get_input('guid');
+$user = get_user($user_guid);
+
+if (!$user || !$user->canEdit()) {
register_error(elgg_echo('avatar:remove:fail'));
+ forward(REFERER);
}
+// Delete all icons from diskspace
+$icon_sizes = elgg_get_config('icon_sizes');
+foreach ($icon_sizes as $name => $size_info) {
+ $file = new ElggFile();
+ $file->owner_guid = $user_guid;
+ $file->setFilename("profile/{$user_guid}{$name}.jpg");
+ $filepath = $file->getFilenameOnFilestore();
+ if (!$file->delete()) {
+ elgg_log("Avatar file remove failed. Remove $filepath manually, please.", 'WARNING');
+ }
+}
+
+// Remove crop coords
+unset($user->x1);
+unset($user->x2);
+unset($user->y1);
+unset($user->y2);
+
+// Remove icon
+unset($user->icontime);
+
+system_message(elgg_echo('avatar:remove:success'));
forward(REFERER);
diff --git a/actions/comments/delete.php b/actions/comments/delete.php
index f2c058ff4..c6b481da4 100644
--- a/actions/comments/delete.php
+++ b/actions/comments/delete.php
@@ -5,11 +5,6 @@
* @package Elgg
*/
-// Ensure we're logged in
-if (!elgg_is_logged_in()) {
- forward();
-}
-
// Make sure we can get the comment in question
$annotation_id = (int) get_input('annotation_id');
$comment = elgg_get_annotation_from_id($annotation_id);
diff --git a/actions/friends/collections/add.php b/actions/friends/collections/add.php
index 9dc17b37e..e63a149f7 100644
--- a/actions/friends/collections/add.php
+++ b/actions/friends/collections/add.php
@@ -6,7 +6,7 @@
* @subpackage Friends.Collections
*/
-$collection_name = get_input('collection_name');
+$collection_name = htmlspecialchars(get_input('collection_name', '', false), ENT_QUOTES, 'UTF-8');
$friends = get_input('friends_collection');
if (!$collection_name) {
diff --git a/actions/login.php b/actions/login.php
index 1e5e92ede..bd7f91299 100644
--- a/actions/login.php
+++ b/actions/login.php
@@ -9,7 +9,6 @@
// set forward url
if (!empty($_SESSION['last_forward_from'])) {
$forward_url = $_SESSION['last_forward_from'];
- unset($_SESSION['last_forward_from']);
} elseif (get_input('returntoreferer')) {
$forward_url = REFERER;
} else {
@@ -62,5 +61,9 @@ if ($user->language) {
$message = elgg_echo('loginok');
}
+if (isset($_SESSION['last_forward_from'])) {
+ unset($_SESSION['last_forward_from']);
+}
+
system_message($message);
forward($forward_url);
diff --git a/actions/profile/edit.php b/actions/profile/edit.php
index 89bf2bc0b..e1f066e82 100644
--- a/actions/profile/edit.php
+++ b/actions/profile/edit.php
@@ -4,6 +4,8 @@
*
*/
+elgg_make_sticky_form('profile:edit');
+
$guid = get_input('guid');
$owner = get_entity($guid);
@@ -48,6 +50,10 @@ foreach ($profile_fields as $shortname => $valuetype) {
forward(REFERER);
}
+ if ($value && $valuetype == 'url' && !preg_match('~^https?\://~i', $value)) {
+ $value = "http://$value";
+ }
+
if ($valuetype == 'tags') {
$value = string_to_tag_array($value);
}
@@ -76,7 +82,7 @@ if (sizeof($input) > 0) {
);
elgg_delete_metadata($options);
- if(!is_null($value) && ($value !== '')){
+ if (!is_null($value) && ($value !== '')) {
// only create metadata for non empty values (0 is allowed) to prevent metadata records with empty string values #4858
if (isset($accesslevel[$shortname])) {
@@ -103,6 +109,7 @@ if (sizeof($input) > 0) {
// Notify of profile update
elgg_trigger_event('profileupdate', $owner->type, $owner);
+ elgg_clear_sticky_form('profile:edit');
system_message(elgg_echo("profile:saved"));
}
diff --git a/actions/register.php b/actions/register.php
index 810ceaf27..73926232c 100644
--- a/actions/register.php
+++ b/actions/register.php
@@ -45,7 +45,9 @@ if (elgg_get_config('allow_registration')) {
// @todo should registration be allowed no matter what the plugins return?
if (!elgg_trigger_plugin_hook('register', 'user', $params, TRUE)) {
+ $ia = elgg_set_ignore_access(true);
$new_user->delete();
+ elgg_set_ignore_access($ia);
// @todo this is a generic messages. We could have plugins
// throw a RegistrationException, but that is very odd
// for the plugin hooks system.
diff --git a/documentation/info/manifest.xml b/documentation/info/manifest.xml
index 494158481..4fd4be8ce 100644
--- a/documentation/info/manifest.xml
+++ b/documentation/info/manifest.xml
@@ -7,7 +7,7 @@
<description>This is a longer, more interesting description of my plugin, its features, and other important information.</description>
<website>http://www.elgg.org/</website>
<repository>https://github.com/Elgg/Elgg</repository>
- <bugtracker>http://trac.elgg.org</bugtracker>
+ <bugtracker>https://github.com/Elgg/Elgg/issues</bugtracker>
<donations>http://elgg.org/supporter.php</donations>
<copyright>(C) Elgg 2011</copyright>
<license>GNU General Public License version 2</license>
diff --git a/engine/classes/ElggAccess.php b/engine/classes/ElggAccess.php
index 6f8d9bb4b..0aed477fc 100644
--- a/engine/classes/ElggAccess.php
+++ b/engine/classes/ElggAccess.php
@@ -16,6 +16,7 @@ class ElggAccess {
*/
private $ignore_access;
+ // @codingStandardsIgnoreStart
/**
* Get current ignore access setting.
*
@@ -26,6 +27,7 @@ class ElggAccess {
elgg_deprecated_notice('ElggAccess::get_ignore_access() is deprecated by ElggAccess::getIgnoreAccess()', 1.8);
return $this->getIgnoreAccess();
}
+ // @codingStandardsIgnoreEnd
/**
* Get current ignore access setting.
@@ -36,6 +38,7 @@ class ElggAccess {
return $this->ignore_access;
}
+ // @codingStandardsIgnoreStart
/**
* Set ignore access.
*
@@ -49,6 +52,7 @@ class ElggAccess {
elgg_deprecated_notice('ElggAccess::set_ignore_access() is deprecated by ElggAccess::setIgnoreAccess()', 1.8);
return $this->setIgnoreAccess($ignore);
}
+ // @codingStandardsIgnoreEnd
/**
* Set ignore access.
diff --git a/engine/classes/ElggAnnotation.php b/engine/classes/ElggAnnotation.php
index 511b5151f..175e7049d 100644
--- a/engine/classes/ElggAnnotation.php
+++ b/engine/classes/ElggAnnotation.php
@@ -11,6 +11,9 @@
* @package Elgg.Core
* @subpackage DataModel.Annotations
* @link http://docs.elgg.org/DataModel/Annotations
+ *
+ * @property string $value_type
+ * @property string $enabled
*/
class ElggAnnotation extends ElggExtender {
@@ -56,6 +59,8 @@ class ElggAnnotation extends ElggExtender {
* Save this instance
*
* @return int an object id
+ *
+ * @throws IOException
*/
function save() {
if ($this->id > 0) {
diff --git a/engine/classes/ElggAttributeLoader.php b/engine/classes/ElggAttributeLoader.php
index 602bb8bae..ffc80b02d 100644
--- a/engine/classes/ElggAttributeLoader.php
+++ b/engine/classes/ElggAttributeLoader.php
@@ -4,6 +4,9 @@
* Loads ElggEntity attributes from DB or validates those passed in via constructor
*
* @access private
+ *
+ * @package Elgg.Core
+ * @subpackage DataModel
*/
class ElggAttributeLoader {
@@ -21,7 +24,7 @@ class ElggAttributeLoader {
'time_created',
'time_updated',
'last_action',
- 'enabled'
+ 'enabled',
);
/**
@@ -65,9 +68,11 @@ class ElggAttributeLoader {
public $full_loader = '';
/**
- * @param string $class class of object being loaded
- * @param string $required_type entity type this is being used to populate
- * @param array $initialized_attrs attributes after initializeAttributes() has been run
+ * Constructor
+ *
+ * @param string $class class of object being loaded
+ * @param string $required_type entity type this is being used to populate
+ * @param array $initialized_attrs attributes after initializeAttributes() has been run
* @throws InvalidArgumentException
*/
public function __construct($class, $required_type, array $initialized_attrs) {
@@ -87,14 +92,33 @@ class ElggAttributeLoader {
$this->secondary_attr_names = array_diff($all_attr_names, self::$primary_attr_names);
}
+ /**
+ * Get primary attributes missing that are missing
+ *
+ * @param stdClass $row Database row
+ * @return array
+ */
protected function isMissingPrimaries($row) {
return array_diff(self::$primary_attr_names, array_keys($row)) !== array();
}
+ /**
+ * Get secondary attributes that are missing
+ *
+ * @param stdClass $row Database row
+ * @return array
+ */
protected function isMissingSecondaries($row) {
return array_diff($this->secondary_attr_names, array_keys($row)) !== array();
}
+ /**
+ * Check that the type is correct
+ *
+ * @param stdClass $row Database row
+ * @return void
+ * @throws InvalidClassException
+ */
protected function checkType($row) {
if ($row['type'] !== $this->required_type) {
$msg = elgg_echo('InvalidClassException:NotValidElggStar', array($row['guid'], $this->class));
@@ -148,11 +172,11 @@ class ElggAttributeLoader {
if (!is_callable($this->primary_loader)) {
throw new LogicException('Primary attribute loader must be callable');
}
- if (!$this->requires_access_control) {
+ if ($this->requires_access_control) {
+ $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
+ } else {
$ignoring_access = elgg_set_ignore_access();
- }
- $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
- if (!$this->requires_access_control) {
+ $fetched = (array) call_user_func($this->primary_loader, $row['guid']);
elgg_set_ignore_access($ignoring_access);
}
if (!$fetched) {
@@ -176,6 +200,8 @@ class ElggAttributeLoader {
// saved, these are stored w/ type "site", but with no sites_entity row. These
// are probably only created in the unit tests.
// @todo Don't save vanilla ElggEntities with type "site"
+
+ $row = $this->filterAddedColumns($row);
$row['guid'] = (int) $row['guid'];
return $row;
}
@@ -185,15 +211,38 @@ class ElggAttributeLoader {
}
}
- // loading complete: re-check missing and check type
- if (($was_missing_primaries && $this->isMissingPrimaries($row))
- || ($was_missing_secondaries && $this->isMissingSecondaries($row))) {
- throw new LogicException('Attribute loaders failed to return proper attributes');
- }
+ $row = $this->filterAddedColumns($row);
+
+ // Note: If there are still missing attributes, we're running on a 1.7 or earlier schema. We let
+ // this pass so the upgrades can run.
- // guid needs to be an int http://trac.elgg.org/ticket/4111
+ // guid needs to be an int https://github.com/elgg/elgg/issues/4111
$row['guid'] = (int) $row['guid'];
return $row;
}
+
+ /**
+ * Filter out keys returned by the query which should not appear in the entity's attributes
+ *
+ * @param array $row All columns from the query
+ * @return array Columns acceptable for the entity's attributes
+ */
+ protected function filterAddedColumns($row) {
+ // make an array with keys as acceptable attribute names
+ $acceptable_attrs = self::$primary_attr_names;
+ array_splice($acceptable_attrs, count($acceptable_attrs), 0, $this->secondary_attr_names);
+ $acceptable_attrs = array_combine($acceptable_attrs, $acceptable_attrs);
+
+ // @todo remove these when #4584 is in place
+ $acceptable_attrs['tables_split'] = true;
+ $acceptable_attrs['tables_loaded'] = true;
+
+ foreach ($row as $key => $val) {
+ if (!isset($acceptable_attrs[$key])) {
+ unset($row[$key]);
+ }
+ }
+ return $row;
+ }
}
diff --git a/engine/classes/ElggAutoP.php b/engine/classes/ElggAutoP.php
index 89d77e583..05842d1b2 100644
--- a/engine/classes/ElggAutoP.php
+++ b/engine/classes/ElggAutoP.php
@@ -7,6 +7,9 @@
*
* In DIV elements, Ps are only added when there would be at
* least two of them.
+ *
+ * @package Elgg.Core
+ * @subpackage Output
*/
class ElggAutoP {
@@ -51,8 +54,12 @@ class ElggAutoP {
protected $_alterList = 'article aside blockquote body details div footer header
section';
+ /** @var string */
protected $_unique = '';
+ /**
+ * Constructor
+ */
public function __construct() {
$this->_blocks = preg_split('@\\s+@', $this->_blocks);
$this->_descendList = preg_split('@\\s+@', $this->_descendList);
@@ -98,25 +105,34 @@ class ElggAutoP {
$html = str_replace('&', $this->_unique . 'AMP', $html);
$this->_doc = new DOMDocument();
-
+
// parse to DOM, suppressing loadHTML warnings
// http://www.php.net/manual/en/domdocument.loadhtml.php#95463
libxml_use_internal_errors(true);
+ // Do not load entities. May be unnecessary, better safe than sorry
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
if (!$this->_doc->loadHTML("<html><meta http-equiv='content-type' "
. "content='text/html; charset={$this->encoding}'><body>{$html}</body>"
. "</html>")) {
+
+ libxml_disable_entity_loader($disable_load_entities);
return false;
}
+ libxml_disable_entity_loader($disable_load_entities);
+
$this->_xpath = new DOMXPath($this->_doc);
// start processing recursively at the BODY element
$nodeList = $this->_xpath->query('//body[1]');
- $this->_addParagraphs($nodeList->item(0));
+ $this->addParagraphs($nodeList->item(0));
// serialize back to HTML
$html = $this->_doc->saveHTML();
+ // Note: we create <autop> elements, which will later be converted to paragraphs
+
// split AUTOPs into multiples at /\n\n+/
$html = preg_replace('/(' . $this->_unique . 'NL){2,}/', '</autop><autop>', $html);
$html = str_replace(array($this->_unique . 'BR', $this->_unique . 'NL', '<br>'),
@@ -126,14 +142,22 @@ class ElggAutoP {
// re-parse so we can handle new AUTOP elements
+ // Do not load entities. May be unnecessary, better safe than sorry
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
if (!$this->_doc->loadHTML($html)) {
+ libxml_disable_entity_loader($disable_load_entities);
return false;
}
+
+ libxml_disable_entity_loader($disable_load_entities);
+
// must re-create XPath object after DOM load
$this->_xpath = new DOMXPath($this->_doc);
// strip AUTOPs that only have comments/whitespace
foreach ($this->_xpath->query('//autop') as $autop) {
+ /* @var DOMElement $autop */
$hasContent = false;
if (trim($autop->textContent) !== '') {
$hasContent = true;
@@ -146,17 +170,19 @@ class ElggAutoP {
}
}
if (!$hasContent) {
- // strip w/ preg_replace later (faster than moving nodes out)
+ // mark to be later replaced w/ preg_replace (faster than moving nodes out)
$autop->setAttribute("r", "1");
}
}
- // remove a single AUTOP inside certain elements
+ // If a DIV contains a single AUTOP, remove it
foreach ($this->_xpath->query('//div') as $el) {
+ /* @var DOMElement $el */
$autops = $this->_xpath->query('./autop', $el);
if ($autops->length === 1) {
- // strip w/ preg_replace later (faster than moving nodes out)
- $autops->item(0)->setAttribute("r", "1");
+ $firstAutop = $autops->item(0);
+ /* @var DOMElement $firstAutop */
+ $firstAutop->setAttribute("r", "1");
}
}
@@ -182,15 +208,16 @@ class ElggAutoP {
/**
* Add P and BR elements as necessary
*
- * @param DOMElement $el
+ * @param DOMElement $el DOM element
+ * @return void
*/
- protected function _addParagraphs(DOMElement $el) {
- // no need to recurse, just queue up
+ protected function addParagraphs(DOMElement $el) {
+ // no need to call recursively, just queue up
$elsToProcess = array($el);
$inlinesToProcess = array();
while ($el = array_shift($elsToProcess)) {
// if true, we can alter all child nodes, if not, we'll just call
- // _addParagraphs on each element in the descendInto list
+ // addParagraphs on each element in the descendInto list
$alterInline = in_array($el->nodeName, $this->_alterList);
// inside affected elements, we want to trim leading whitespace from
@@ -216,16 +243,16 @@ class ElggAutoP {
$isElement = ($node->nodeType === XML_ELEMENT_NODE);
if ($isElement) {
- $elName = $node->nodeName;
+ $isBlock = in_array($node->nodeName, $this->_blocks);
+ } else {
+ $isBlock = false;
}
- $isBlock = ($isElement && in_array($elName, $this->_blocks));
if ($alterInline) {
- $isInline = $isElement && ! $isBlock;
$isText = ($node->nodeType === XML_TEXT_NODE);
$isLastInline = (! $node->nextSibling
- || ($node->nextSibling->nodeType === XML_ELEMENT_NODE
- && in_array($node->nextSibling->nodeName, $this->_blocks)));
+ || ($node->nextSibling->nodeType === XML_ELEMENT_NODE
+ && in_array($node->nextSibling->nodeName, $this->_blocks)));
if ($isElement) {
$isFollowingBr = ($node->nodeName === 'br');
}
@@ -258,7 +285,7 @@ class ElggAutoP {
if ($isBlock) {
if (in_array($node->nodeName, $this->_descendList)) {
$elsToProcess[] = $node;
- //$this->_addParagraphs($node);
+ //$this->addParagraphs($node);
}
}
$openP = true;
diff --git a/engine/classes/ElggBatch.php b/engine/classes/ElggBatch.php
index c1a77a0d9..d810ea066 100644
--- a/engine/classes/ElggBatch.php
+++ b/engine/classes/ElggBatch.php
@@ -150,6 +150,20 @@ class ElggBatch
private $incrementOffset = true;
/**
+ * Entities that could not be instantiated during a fetch
+ *
+ * @var stdClass[]
+ */
+ private $incompleteEntities = array();
+
+ /**
+ * Total number of incomplete entities fetched
+ *
+ * @var int
+ */
+ private $totalIncompletes = 0;
+
+ /**
* Batches operations on any elgg_get_*() or compatible function that supports
* an options array.
*
@@ -222,16 +236,22 @@ class ElggBatch
}
/**
+ * Tell the process that an entity was incomplete during a fetch
+ *
+ * @param stdClass $row
+ *
+ * @access private
+ */
+ public function reportIncompleteEntity(stdClass $row) {
+ $this->incompleteEntities[] = $row;
+ }
+
+ /**
* Fetches the next chunk of results
*
* @return bool
*/
private function getNextResultsChunk() {
- // reset memory caches after first chunk load
- if ($this->chunkIndex > 0) {
- global $DB_QUERY_CACHE, $ENTITY_CACHE;
- $DB_QUERY_CACHE = $ENTITY_CACHE = array();
- }
// always reset results.
$this->results = array();
@@ -265,27 +285,47 @@ class ElggBatch
if ($this->incrementOffset) {
$offset = $this->offset + $this->retrievedResults;
} else {
- $offset = $this->offset;
+ $offset = $this->offset + $this->totalIncompletes;
}
$current_options = array(
'limit' => $limit,
- 'offset' => $offset
+ 'offset' => $offset,
+ '__ElggBatch' => $this,
);
$options = array_merge($this->options, $current_options);
- $getter = $this->getter;
- if (is_string($getter)) {
- $this->results = $getter($options);
- } else {
- $this->results = call_user_func_array($getter, array($options));
+ $this->incompleteEntities = array();
+ $this->results = call_user_func_array($this->getter, array($options));
+
+ $num_results = count($this->results);
+ $num_incomplete = count($this->incompleteEntities);
+
+ $this->totalIncompletes += $num_incomplete;
+
+ if ($this->incompleteEntities) {
+ // pad the front of the results with nulls representing the incompletes
+ array_splice($this->results, 0, 0, array_pad(array(), $num_incomplete, null));
+ // ...and skip past them
+ reset($this->results);
+ for ($i = 0; $i < $num_incomplete; $i++) {
+ next($this->results);
+ }
}
if ($this->results) {
$this->chunkIndex++;
- $this->resultIndex = 0;
- $this->retrievedResults += count($this->results);
+
+ // let the system know we've jumped past the nulls
+ $this->resultIndex = $num_incomplete;
+
+ $this->retrievedResults += ($num_results + $num_incomplete);
+ if ($num_results == 0) {
+ // This fetch was *all* incompletes! We need to fetch until we can either
+ // offer at least one row to iterate over, or give up.
+ return $this->getNextResultsChunk();
+ }
return true;
} else {
return false;
@@ -296,7 +336,8 @@ class ElggBatch
* Increment the offset from the original options array? Setting to
* false is required for callbacks that delete rows.
*
- * @param bool $increment
+ * @param bool $increment Set to false when deleting data
+ * @return void
*/
public function setIncrementOffset($increment = true) {
$this->incrementOffset = (bool) $increment;
diff --git a/engine/classes/ElggCache.php b/engine/classes/ElggCache.php
index 4317f4be9..909eab39b 100644
--- a/engine/classes/ElggCache.php
+++ b/engine/classes/ElggCache.php
@@ -21,6 +21,7 @@ abstract class ElggCache implements ArrayAccess {
$this->variables = array();
}
+ // @codingStandardsIgnoreStart
/**
* Set a cache variable.
*
@@ -35,6 +36,7 @@ abstract class ElggCache implements ArrayAccess {
elgg_deprecated_notice('ElggCache::set_variable() is deprecated by ElggCache::setVariable()', 1.8);
$this->setVariable($variable, $value);
}
+ // @codingStandardsIgnoreEnd
/**
* Set a cache variable.
@@ -52,6 +54,7 @@ abstract class ElggCache implements ArrayAccess {
$this->variables[$variable] = $value;
}
+ // @codingStandardsIgnoreStart
/**
* Get variables for this cache.
*
@@ -65,6 +68,7 @@ abstract class ElggCache implements ArrayAccess {
elgg_deprecated_notice('ElggCache::get_variable() is deprecated by ElggCache::getVariable()', 1.8);
return $this->getVariable($variable);
}
+ // @codingStandardsIgnoreEnd
/**
* Get variables for this cache.
diff --git a/engine/classes/ElggCrypto.php b/engine/classes/ElggCrypto.php
new file mode 100644
index 000000000..317d371e4
--- /dev/null
+++ b/engine/classes/ElggCrypto.php
@@ -0,0 +1,208 @@
+<?php
+/**
+ * ElggCrypto
+ *
+ * @package Elgg.Core
+ * @subpackage Crypto
+ *
+ * @access private
+ */
+class ElggCrypto {
+
+ /**
+ * Character set for temp passwords (no risk of embedded profanity/glyphs that look similar)
+ */
+ const CHARS_PASSWORD = 'bcdfghjklmnpqrstvwxyz2346789';
+
+ /**
+ * Generate a string of highly randomized bytes (over the full 8-bit range).
+ *
+ * @param int $length Number of bytes needed
+ * @return string Random bytes
+ *
+ * @author George Argyros <argyros.george@gmail.com>
+ * @copyright 2012, George Argyros. All rights reserved.
+ * @license Modified BSD
+ * @link https://github.com/GeorgeArgyros/Secure-random-bytes-in-PHP/blob/master/srand.php Original
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * * Neither the name of the <organization> nor the
+ * names of its contributors may be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL GEORGE ARGYROS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ public function getRandomBytes($length) {
+ /**
+ * Our primary choice for a cryptographic strong randomness function is
+ * openssl_random_pseudo_bytes.
+ */
+ $SSLstr = '4'; // http://xkcd.com/221/
+ if (function_exists('openssl_random_pseudo_bytes')
+ && (version_compare(PHP_VERSION, '5.3.4') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) {
+ $SSLstr = openssl_random_pseudo_bytes($length, $strong);
+ if ($strong) {
+ return $SSLstr;
+ }
+ }
+
+ /**
+ * If mcrypt extension is available then we use it to gather entropy from
+ * the operating system's PRNG. This is better than reading /dev/urandom
+ * directly since it avoids reading larger blocks of data than needed.
+ * Older versions of mcrypt_create_iv may be broken or take too much time
+ * to finish so we only use this function with PHP 5.3.7 and above.
+ * @see https://bugs.php.net/bug.php?id=55169
+ */
+ if (function_exists('mcrypt_create_iv')
+ && (version_compare(PHP_VERSION, '5.3.7') >= 0 || substr(PHP_OS, 0, 3) !== 'WIN')) {
+ $str = mcrypt_create_iv($length, MCRYPT_DEV_URANDOM);
+ if ($str !== false) {
+ return $str;
+ }
+ }
+
+ /**
+ * No build-in crypto randomness function found. We collect any entropy
+ * available in the PHP core PRNGs along with some filesystem info and memory
+ * stats. To make this data cryptographically strong we add data either from
+ * /dev/urandom or if its unavailable, we gather entropy by measuring the
+ * time needed to compute a number of SHA-1 hashes.
+ */
+ $str = '';
+ $bits_per_round = 2; // bits of entropy collected in each clock drift round
+ $msec_per_round = 400; // expected running time of each round in microseconds
+ $hash_len = 20; // SHA-1 Hash length
+ $total = $length; // total bytes of entropy to collect
+
+ $handle = @fopen('/dev/urandom', 'rb');
+ if ($handle && function_exists('stream_set_read_buffer')) {
+ @stream_set_read_buffer($handle, 0);
+ }
+
+ do {
+ $bytes = ($total > $hash_len) ? $hash_len : $total;
+ $total -= $bytes;
+
+ //collect any entropy available from the PHP system and filesystem
+ $entropy = rand() . uniqid(mt_rand(), true) . $SSLstr;
+ $entropy .= implode('', @fstat(@fopen(__FILE__, 'r')));
+ $entropy .= memory_get_usage() . getmypid();
+ $entropy .= serialize($_ENV) . serialize($_SERVER);
+ if (function_exists('posix_times')) {
+ $entropy .= serialize(posix_times());
+ }
+ if (function_exists('zend_thread_id')) {
+ $entropy .= zend_thread_id();
+ }
+
+ if ($handle) {
+ $entropy .= @fread($handle, $bytes);
+ } else {
+ // Measure the time that the operations will take on average
+ for ($i = 0; $i < 3; $i++) {
+ $c1 = microtime(true);
+ $var = sha1(mt_rand());
+ for ($j = 0; $j < 50; $j++) {
+ $var = sha1($var);
+ }
+ $c2 = microtime(true);
+ $entropy .= $c1 . $c2;
+ }
+
+ // Based on the above measurement determine the total rounds
+ // in order to bound the total running time.
+ $rounds = (int) ($msec_per_round * 50 / (int) (($c2 - $c1) * 1000000));
+
+ // Take the additional measurements. On average we can expect
+ // at least $bits_per_round bits of entropy from each measurement.
+ $iter = $bytes * (int) (ceil(8 / $bits_per_round));
+
+ for ($i = 0; $i < $iter; $i++) {
+ $c1 = microtime();
+ $var = sha1(mt_rand());
+ for ($j = 0; $j < $rounds; $j++) {
+ $var = sha1($var);
+ }
+ $c2 = microtime();
+ $entropy .= $c1 . $c2;
+ }
+ }
+
+ // We assume sha1 is a deterministic extractor for the $entropy variable.
+ $str .= sha1($entropy, true);
+
+ } while ($length > strlen($str));
+
+ if ($handle) {
+ @fclose($handle);
+ }
+
+ return substr($str, 0, $length);
+ }
+
+ /**
+ * Generate a random string of specified length.
+ *
+ * Uses supplied character list for generating the new string.
+ * If no character list provided - uses Base64 URL character set.
+ *
+ * @param int $length Desired length of the string
+ * @param string|null $chars Characters to be chosen from randomly. If not given, the Base64 URL
+ * charset will be used.
+ *
+ * @return string The random string
+ *
+ * @throws InvalidArgumentException
+ *
+ * @copyright Copyright (c) 2005-2013 Zend Technologies USA Inc. (http://www.zend.com)
+ * @license http://framework.zend.com/license/new-bsd New BSD License
+ *
+ * @see https://github.com/zendframework/zf2/blob/master/library/Zend/Math/Rand.php#L179
+ */
+ public static function getRandomString($length, $chars = null) {
+ if ($length < 1) {
+ throw new InvalidArgumentException('Length should be >= 1');
+ }
+
+ if (empty($chars)) {
+ $numBytes = ceil($length * 0.75);
+ $bytes = self::getRandomBytes($numBytes);
+ $string = substr(rtrim(base64_encode($bytes), '='), 0, $length);
+
+ // Base64 URL
+ return strtr($string, '+/', '-_');
+ }
+
+ $listLen = strlen($chars);
+
+ if ($listLen == 1) {
+ return str_repeat($chars, $length);
+ }
+
+ $bytes = self::getRandomBytes($length);
+ $pos = 0;
+ $result = '';
+ for ($i = 0; $i < $length; $i++) {
+ $pos = ($pos + ord($bytes[$i])) % $listLen;
+ $result .= $chars[$pos];
+ }
+
+ return $result;
+ }
+}
diff --git a/engine/classes/ElggData.php b/engine/classes/ElggData.php
index 3470ee1cf..4f843cde4 100644
--- a/engine/classes/ElggData.php
+++ b/engine/classes/ElggData.php
@@ -5,6 +5,9 @@
*
* @package Elgg.Core
* @subpackage DataModel
+ *
+ * @property int $owner_guid
+ * @property int $time_created
*/
abstract class ElggData implements
Loggable, // Can events related to this object class be logged
@@ -23,6 +26,7 @@ abstract class ElggData implements
*/
protected $attributes = array();
+ // @codingStandardsIgnoreStart
/**
* Initialise the attributes array.
*
@@ -33,16 +37,15 @@ abstract class ElggData implements
* Passing false returns false. Core constructors always pass false.
* Does nothing either way since attributes are initialized by the time
* this is called.
- * @return false|void False is
+ * @return void
* @deprecated 1.8 Use initializeAttributes()
*/
protected function initialise_attributes($pre18_api = true) {
if ($pre18_api) {
elgg_deprecated_notice('initialise_attributes() is deprecated by initializeAttributes()', 1.8);
- } else {
- return false;
}
}
+ // @codingStandardsIgnoreEnd
/**
* Initialize the attributes array.
@@ -111,7 +114,7 @@ abstract class ElggData implements
* @param string $name The attribute to set
* @param mixed $value The value to set it to
*
- * @return The success of your set funtion?
+ * @return bool The success of your set function?
*/
abstract protected function set($name, $value);
@@ -195,7 +198,7 @@ abstract class ElggData implements
*
* @see Iterator::current()
*
- * @return void
+ * @return mixed
*/
public function current() {
return current($this->attributes);
@@ -206,7 +209,7 @@ abstract class ElggData implements
*
* @see Iterator::key()
*
- * @return void
+ * @return string
*/
public function key() {
return key($this->attributes);
@@ -228,7 +231,7 @@ abstract class ElggData implements
*
* @see Iterator::valid()
*
- * @return void
+ * @return bool
*/
public function valid() {
return $this->valid;
@@ -266,12 +269,13 @@ abstract class ElggData implements
*
* @param mixed $key Name
*
- * @return void
+ * @return mixed
*/
public function offsetGet($key) {
if (array_key_exists($key, $this->attributes)) {
return $this->attributes[$key];
}
+ return null;
}
/**
diff --git a/engine/classes/ElggDiskFilestore.php b/engine/classes/ElggDiskFilestore.php
index 7aace43ba..6e2354012 100644
--- a/engine/classes/ElggDiskFilestore.php
+++ b/engine/classes/ElggDiskFilestore.php
@@ -60,6 +60,7 @@ class ElggDiskFilestore extends ElggFilestore {
$path = substr($fullname, 0, $ls);
$name = substr($fullname, $ls);
+ // @todo $name is unused, remove it or do we need to fix something?
// Try and create the directory
try {
@@ -108,7 +109,7 @@ class ElggDiskFilestore extends ElggFilestore {
*
* @param resource $f File pointer resource
* @param int $length The number of bytes to read
- * @param inf $offset The number of bytes to start after
+ * @param int $offset The number of bytes to start after
*
* @return mixed Contents of file or false on fail.
*/
@@ -193,11 +194,14 @@ class ElggDiskFilestore extends ElggFilestore {
}
/**
- * Returns the filename as saved on disk for an ElggFile object
+ * Get the filename as saved on disk for an ElggFile object
+ *
+ * Returns an empty string if no filename set
*
* @param ElggFile $file File object
*
* @return string The full path of where the file is stored
+ * @throws InvalidParameterException
*/
public function getFilenameOnFilestore(ElggFile $file) {
$owner_guid = $file->getOwnerGuid();
@@ -211,7 +215,12 @@ class ElggDiskFilestore extends ElggFilestore {
throw new InvalidParameterException($msg);
}
- return $this->dir_root . $this->makefileMatrix($owner_guid) . $file->getFilename();
+ $filename = $file->getFilename();
+ if (!$filename) {
+ return '';
+ }
+
+ return $this->dir_root . $this->makeFileMatrix($owner_guid) . $filename;
}
/**
@@ -219,7 +228,7 @@ class ElggDiskFilestore extends ElggFilestore {
*
* @param ElggFile $file File object
*
- * @return mixed
+ * @return string
*/
public function grabFile(ElggFile $file) {
return file_get_contents($file->getFilenameOnFilestore());
@@ -233,6 +242,9 @@ class ElggDiskFilestore extends ElggFilestore {
* @return bool
*/
public function exists(ElggFile $file) {
+ if (!$file->getFilename()) {
+ return false;
+ }
return file_exists($this->getFilenameOnFilestore($file));
}
@@ -246,12 +258,13 @@ class ElggDiskFilestore extends ElggFilestore {
*/
public function getSize($prefix = '', $container_guid) {
if ($container_guid) {
- return get_dir_size($this->dir_root . $this->makefileMatrix($container_guid) . $prefix);
+ return get_dir_size($this->dir_root . $this->makeFileMatrix($container_guid) . $prefix);
} else {
return false;
}
}
+ // @codingStandardsIgnoreStart
/**
* Create a directory $dirroot
*
@@ -266,6 +279,7 @@ class ElggDiskFilestore extends ElggFilestore {
return $this->makeDirectoryRoot($dirroot);
}
+ // @codingStandardsIgnoreEnd
/**
* Create a directory $dirroot
@@ -285,6 +299,7 @@ class ElggDiskFilestore extends ElggFilestore {
return true;
}
+ // @codingStandardsIgnoreStart
/**
* Multibyte string tokeniser.
*
@@ -315,30 +330,31 @@ class ElggDiskFilestore extends ElggFilestore {
} else {
return str_split($string);
}
-
- return false;
}
+ // @codingStandardsIgnoreEnd
+ // @codingStandardsIgnoreStart
/**
* Construct a file path matrix for an entity.
*
* @param int $identifier The guide of the entity to store the data under.
*
- * @return str The path where the entity's data will be stored.
+ * @return string The path where the entity's data will be stored.
* @deprecated 1.8 Use ElggDiskFilestore::makeFileMatrix()
*/
protected function make_file_matrix($identifier) {
elgg_deprecated_notice('ElggDiskFilestore::make_file_matrix() is deprecated by ::makeFileMatrix()', 1.8);
- return $this->makefileMatrix($identifier);
+ return $this->makeFileMatrix($identifier);
}
+ // @codingStandardsIgnoreEnd
/**
* Construct a file path matrix for an entity.
*
* @param int $guid The guide of the entity to store the data under.
*
- * @return str The path where the entity's data will be stored.
+ * @return string The path where the entity's data will be stored.
*/
protected function makeFileMatrix($guid) {
$entity = get_entity($guid);
@@ -352,6 +368,7 @@ class ElggDiskFilestore extends ElggFilestore {
return "$time_created/$entity->guid/";
}
+ // @codingStandardsIgnoreStart
/**
* Construct a filename matrix.
*
@@ -363,13 +380,14 @@ class ElggDiskFilestore extends ElggFilestore {
*
* @param int $guid The entity to contrust a matrix for
*
- * @return str The
+ * @return string The
*/
protected function user_file_matrix($guid) {
elgg_deprecated_notice('ElggDiskFilestore::user_file_matrix() is deprecated by ::makeFileMatrix()', 1.8);
return $this->makeFileMatrix($guid);
}
+ // @codingStandardsIgnoreEnd
/**
* Returns a list of attributes to save to the database when saving
diff --git a/engine/classes/ElggEntity.php b/engine/classes/ElggEntity.php
index 929abceb2..a563f6fad 100644
--- a/engine/classes/ElggEntity.php
+++ b/engine/classes/ElggEntity.php
@@ -24,7 +24,7 @@
*
* @package Elgg.Core
* @subpackage DataModel.Entities
- *
+ *
* @property string $type object, user, group, or site (read-only after save)
* @property string $subtype Further clarifies the nature of the entity (read-only after save)
* @property int $guid The unique identifier for this entity (read only)
@@ -34,6 +34,7 @@
* @property int $access_id Specifies the visibility level of this entity
* @property int $time_created A UNIX timestamp of when the entity was created (read-only, set on first save)
* @property int $time_updated A UNIX timestamp of when the entity was last updated (automatically updated on save)
+ * @property-read string $enabled
*/
abstract class ElggEntity extends ElggData implements
Notable, // Calendar interface
@@ -351,8 +352,8 @@ abstract class ElggEntity extends ElggData implements
'limit' => 0
);
// @todo in 1.9 make this return false if can't add metadata
- // http://trac.elgg.org/ticket/4520
- //
+ // https://github.com/elgg/elgg/issues/4520
+ //
// need to remove access restrictions right now to delete
// because this is the expected behavior
$ia = elgg_set_ignore_access(true);
@@ -374,12 +375,11 @@ abstract class ElggEntity extends ElggData implements
}
return $result;
- }
-
- // unsaved entity. store in temp array
- // returning single entries instead of an array of 1 element is decided in
- // getMetaData(), just like pulling from the db.
- else {
+ } else {
+ // unsaved entity. store in temp array
+ // returning single entries instead of an array of 1 element is decided in
+ // getMetaData(), just like pulling from the db.
+ //
// if overwrite, delete first
if (!$multiple || !isset($this->temp_metadata[$name])) {
$this->temp_metadata[$name] = array();
@@ -940,7 +940,7 @@ abstract class ElggEntity extends ElggData implements
* @param ElggMetadata $metadata The piece of metadata to specifically check
* @param int $user_guid The user GUID, optionally (default: logged in user)
*
- * @return true|false
+ * @return bool
*/
function canEditMetadata($metadata = null, $user_guid = 0) {
return can_edit_entity_metadata($this->getGUID(), $user_guid, $metadata);
@@ -964,7 +964,7 @@ abstract class ElggEntity extends ElggData implements
*
* @tip Can be overridden by registering for the permissions_check:comment,
* <entity type> plugin hook.
- *
+ *
* @param int $user_guid User guid (default is logged in user)
*
* @return bool
@@ -1270,15 +1270,23 @@ abstract class ElggEntity extends ElggData implements
public function save() {
$guid = $this->getGUID();
if ($guid > 0) {
- cache_entity($this);
- return update_entity(
+ // See #5600. This ensures the lower level can_edit_entity() check will use a
+ // fresh entity from the DB so it sees the persisted owner_guid
+ _elgg_disable_caching_for_entity($guid);
+
+ $ret = update_entity(
$guid,
$this->get('owner_guid'),
$this->get('access_id'),
$this->get('container_guid'),
$this->get('time_created')
);
+
+ _elgg_enable_caching_for_entity($guid);
+ _elgg_cache_entity($this);
+
+ return $ret;
} else {
// Create a new entity (nb: using attribute array directly
// 'cos set function does something special!)
@@ -1320,7 +1328,7 @@ abstract class ElggEntity extends ElggData implements
$this->attributes['subtype'] = get_subtype_id($this->attributes['type'],
$this->attributes['subtype']);
- cache_entity($this);
+ _elgg_cache_entity($this);
return $this->attributes['guid'];
}
@@ -1357,12 +1365,12 @@ abstract class ElggEntity extends ElggData implements
$this->attributes['tables_loaded']++;
}
- // guid needs to be an int http://trac.elgg.org/ticket/4111
+ // guid needs to be an int https://github.com/elgg/elgg/issues/4111
$this->attributes['guid'] = (int)$this->attributes['guid'];
// Cache object handle
if ($this->attributes['guid']) {
- cache_entity($this);
+ _elgg_cache_entity($this);
}
return true;
@@ -1668,9 +1676,11 @@ abstract class ElggEntity extends ElggData implements
/**
* Import data from an parsed ODD xml data array.
*
- * @param array $data XML data
+ * @param ODD $data XML data
*
* @return true
+ *
+ * @throws InvalidParameterException
*/
public function import(ODD $data) {
if (!($data instanceof ODDEntity)) {
@@ -1732,8 +1742,6 @@ abstract class ElggEntity extends ElggData implements
* @return array
*/
public function getTags($tag_names = NULL) {
- global $CONFIG;
-
if ($tag_names && !is_array($tag_names)) {
$tag_names = array($tag_names);
}
diff --git a/engine/classes/ElggExtender.php b/engine/classes/ElggExtender.php
index d94bad837..25aba354f 100644
--- a/engine/classes/ElggExtender.php
+++ b/engine/classes/ElggExtender.php
@@ -171,7 +171,7 @@ abstract class ElggExtender extends ElggData {
public function export() {
$uuid = get_uuid_from_object($this);
- $meta = new ODDMetadata($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'],
+ $meta = new ODDMetaData($uuid, guid_to_uuid($this->entity_guid), $this->attributes['name'],
$this->attributes['value'], $this->attributes['type'], guid_to_uuid($this->owner_guid));
$meta->setAttribute('published', date("r", $this->time_created));
diff --git a/engine/classes/ElggFile.php b/engine/classes/ElggFile.php
index f21621ffd..23080834b 100644
--- a/engine/classes/ElggFile.php
+++ b/engine/classes/ElggFile.php
@@ -93,6 +93,7 @@ class ElggFile extends ElggObject {
$container_guid = $this->container_guid;
}
$fs = $this->getFilestore();
+ // @todo add getSize() to ElggFilestore
return $fs->getSize($prefix, $container_guid);
}
@@ -127,9 +128,11 @@ class ElggFile extends ElggObject {
* @param mixed $default A default. Useful to pass what the browser thinks it is.
* @since 1.7.12
*
+ * @note If $file is provided, this may be called statically
+ *
* @return mixed Detected type on success, false on failure.
*/
- static function detectMimeType($file = null, $default = null) {
+ public function detectMimeType($file = null, $default = null) {
if (!$file) {
if (isset($this) && $this->filename) {
$file = $this->filename;
@@ -178,6 +181,8 @@ class ElggFile extends ElggObject {
* @param string $mode Either read/write/append
*
* @return resource File handler
+ *
+ * @throws IOException|InvalidParameterException
*/
public function open($mode) {
if (!$this->getFilename()) {
@@ -270,9 +275,14 @@ class ElggFile extends ElggObject {
*/
public function delete() {
$fs = $this->getFilestore();
- if ($fs->delete($this)) {
- return parent::delete();
+
+ $result = $fs->delete($this);
+
+ if ($this->getGUID() && $result) {
+ $result = parent::delete();
}
+
+ return $result;
}
/**
@@ -285,6 +295,7 @@ class ElggFile extends ElggObject {
public function seek($position) {
$fs = $this->getFilestore();
+ // @todo add seek() to ElggFilestore
return $fs->seek($this->handle, $position);
}
@@ -347,6 +358,8 @@ class ElggFile extends ElggObject {
* a filestore as recorded in metadata or the system default.
*
* @return ElggFilestore
+ *
+ * @throws ClassNotFoundException
*/
protected function getFilestore() {
// Short circuit if already set.
@@ -359,7 +372,6 @@ class ElggFile extends ElggObject {
// need to get all filestore::* metadata because the rest are "parameters" that
// get passed to filestore::setParameters()
if ($this->guid) {
- $db_prefix = elgg_get_config('dbprefix');
$options = array(
'guid' => $this->guid,
'where' => array("n.string LIKE 'filestore::%'"),
@@ -388,6 +400,7 @@ class ElggFile extends ElggObject {
$this->filestore = new $filestore();
$this->filestore->setParameters($parameters);
+ // @todo explain why $parameters will always be set here (PhpStorm complains)
}
// this means the entity hasn't been saved so fallback to default
diff --git a/engine/classes/ElggFileCache.php b/engine/classes/ElggFileCache.php
index 34178d452..94143f777 100644
--- a/engine/classes/ElggFileCache.php
+++ b/engine/classes/ElggFileCache.php
@@ -13,6 +13,8 @@ class ElggFileCache extends ElggCache {
* @param string $cache_path The cache path.
* @param int $max_age Maximum age in seconds, 0 if no limit.
* @param int $max_size Maximum size of cache in seconds, 0 if no limit.
+ *
+ * @throws ConfigurationException
*/
function __construct($cache_path, $max_age = 0, $max_size = 0) {
$this->setVariable("cache_path", $cache_path);
@@ -24,6 +26,7 @@ class ElggFileCache extends ElggCache {
}
}
+ // @codingStandardsIgnoreStart
/**
* Create and return a handle to a file.
*
@@ -39,6 +42,7 @@ class ElggFileCache extends ElggCache {
return $this->createFile($filename, $rw);
}
+ // @codingStandardsIgnoreEnd
/**
* Create and return a handle to a file.
@@ -70,6 +74,7 @@ class ElggFileCache extends ElggCache {
return fopen($path . $filename, $rw);
}
+ // @codingStandardsIgnoreStart
/**
* Create a sanitised filename for the file.
*
@@ -84,6 +89,7 @@ class ElggFileCache extends ElggCache {
return $filename;
}
+ // @codingStandardsIgnoreEnd
/**
* Create a sanitised filename for the file.
diff --git a/engine/classes/ElggGroup.php b/engine/classes/ElggGroup.php
index ea257f368..7e69b7a84 100644
--- a/engine/classes/ElggGroup.php
+++ b/engine/classes/ElggGroup.php
@@ -32,7 +32,7 @@ class ElggGroup extends ElggEntity
* @param mixed $guid If an int, load that GUID.
* If an entity table db row, then will load the rest of the data.
*
- * @throws Exception if there was a problem creating the group.
+ * @throws IOException|InvalidParameterException if there was a problem creating the group.
*/
function __construct($guid = null) {
$this->initializeAttributes();
@@ -48,21 +48,18 @@ class ElggGroup extends ElggEntity
$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
throw new IOException($msg);
}
-
- // Is $guid is an ElggGroup? Use a copy constructor
} else if ($guid instanceof ElggGroup) {
+ // $guid is an ElggGroup so this is a copy constructor
elgg_deprecated_notice('This type of usage of the ElggGroup constructor was deprecated. Please use the clone method.', 1.7);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is this is an ElggEntity but not an ElggGroup = ERROR!
} else if ($guid instanceof ElggEntity) {
+ // @todo why separate from else
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggGroup'));
-
- // Is it a GUID
} else if (is_numeric($guid)) {
+ // $guid is a GUID so load entity
if (!$this->load($guid)) {
throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
}
@@ -220,6 +217,7 @@ class ElggGroup extends ElggEntity
* @return array|false
*/
public function getObjects($subtype = "", $limit = 10, $offset = 0) {
+ // @todo are we deprecating this method, too?
return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
}
@@ -233,6 +231,7 @@ class ElggGroup extends ElggEntity
* @return array|false
*/
public function getFriendsObjects($subtype = "", $limit = 10, $offset = 0) {
+ // @todo are we deprecating this method, too?
return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", $limit, $offset, false);
}
@@ -244,6 +243,7 @@ class ElggGroup extends ElggEntity
* @return array|false
*/
public function countObjects($subtype = "") {
+ // @todo are we deprecating this method, too?
return get_objects_in_group($this->getGUID(), $subtype, 0, 0, "", 10, 0, true);
}
@@ -284,7 +284,7 @@ class ElggGroup extends ElggEntity
*
* @return bool
*/
- public function isMember($user = 0) {
+ public function isMember($user = null) {
if (!($user instanceof ElggUser)) {
$user = elgg_get_logged_in_user_entity();
}
@@ -335,7 +335,7 @@ class ElggGroup extends ElggEntity
$this->attributes = $attrs;
$this->attributes['tables_loaded'] = 2;
- cache_entity($this);
+ _elgg_cache_entity($this);
return true;
}
@@ -352,7 +352,12 @@ class ElggGroup extends ElggEntity
}
// Now save specific stuff
- return create_group_entity($this->get('guid'), $this->get('name'), $this->get('description'));
+
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_group_entity($this->get('guid'), $this->get('name'), $this->get('description'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
}
// EXPORTABLE INTERFACE ////////////////////////////////////////////////////////////
diff --git a/engine/classes/ElggLRUCache.php b/engine/classes/ElggLRUCache.php
new file mode 100644
index 000000000..f51af2ed7
--- /dev/null
+++ b/engine/classes/ElggLRUCache.php
@@ -0,0 +1,181 @@
+<?php
+
+/**
+ * Least Recently Used Cache
+ *
+ * A fixed sized cache that removes the element used last when it reaches its
+ * size limit.
+ *
+ * Based on https://github.com/cash/LRUCache
+ *
+ * @access private
+ *
+ * @package Elgg.Core
+ * @subpackage Cache
+ */
+class ElggLRUCache implements ArrayAccess {
+ /** @var int */
+ protected $maximumSize;
+
+ /**
+ * The front of the array contains the LRU element
+ *
+ * @var array
+ */
+ protected $data = array();
+
+ /**
+ * Create a LRU Cache
+ *
+ * @param int $size The size of the cache
+ * @throws InvalidArgumentException
+ */
+ public function __construct($size) {
+ if (!is_int($size) || $size <= 0) {
+ throw new InvalidArgumentException();
+ }
+ $this->maximumSize = $size;
+ }
+
+ /**
+ * Get the value cached with this key
+ *
+ * @param int|string $key The key. Strings that are ints are cast to ints.
+ * @param mixed $default The value to be returned if key not found. (Optional)
+ * @return mixed
+ */
+ public function get($key, $default = null) {
+ if (isset($this->data[$key])) {
+ $this->recordAccess($key);
+ return $this->data[$key];
+ } else {
+ return $default;
+ }
+ }
+
+ /**
+ * Add something to the cache
+ *
+ * @param int|string $key The key. Strings that are ints are cast to ints.
+ * @param mixed $value The value to cache
+ * @return void
+ */
+ public function set($key, $value) {
+ if (isset($this->data[$key])) {
+ $this->data[$key] = $value;
+ $this->recordAccess($key);
+ } else {
+ $this->data[$key] = $value;
+ if ($this->size() > $this->maximumSize) {
+ // remove least recently used element (front of array)
+ reset($this->data);
+ unset($this->data[key($this->data)]);
+ }
+ }
+ }
+
+ /**
+ * Get the number of elements in the cache
+ *
+ * @return int
+ */
+ public function size() {
+ return count($this->data);
+ }
+
+ /**
+ * Does the cache contain an element with this key
+ *
+ * @param int|string $key The key
+ * @return boolean
+ */
+ public function containsKey($key) {
+ return isset($this->data[$key]);
+ }
+
+ /**
+ * Remove the element with this key.
+ *
+ * @param int|string $key The key
+ * @return mixed Value or null if not set
+ */
+ public function remove($key) {
+ if (isset($this->data[$key])) {
+ $value = $this->data[$key];
+ unset($this->data[$key]);
+ return $value;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Clear the cache
+ *
+ * @return void
+ */
+ public function clear() {
+ $this->data = array();
+ }
+
+ /**
+ * Moves the element from current position to end of array
+ *
+ * @param int|string $key The key
+ * @return void
+ */
+ protected function recordAccess($key) {
+ $value = $this->data[$key];
+ unset($this->data[$key]);
+ $this->data[$key] = $value;
+ }
+
+ /**
+ * Assigns a value for the specified key
+ *
+ * @see ArrayAccess::offsetSet()
+ *
+ * @param int|string $key The key to assign the value to.
+ * @param mixed $value The value to set.
+ * @return void
+ */
+ public function offsetSet($key, $value) {
+ $this->set($key, $value);
+ }
+
+ /**
+ * Get the value for specified key
+ *
+ * @see ArrayAccess::offsetGet()
+ *
+ * @param int|string $key The key to retrieve.
+ * @return mixed
+ */
+ public function offsetGet($key) {
+ return $this->get($key);
+ }
+
+ /**
+ * Unsets a key.
+ *
+ * @see ArrayAccess::offsetUnset()
+ *
+ * @param int|string $key The key to unset.
+ * @return void
+ */
+ public function offsetUnset($key) {
+ $this->remove($key);
+ }
+
+ /**
+ * Does key exist?
+ *
+ * @see ArrayAccess::offsetExists()
+ *
+ * @param int|string $key A key to check for.
+ * @return boolean
+ */
+ public function offsetExists($key) {
+ return $this->containsKey($key);
+ }
+}
diff --git a/engine/classes/ElggMemcache.php b/engine/classes/ElggMemcache.php
index d9539b9cb..91d50ab89 100644
--- a/engine/classes/ElggMemcache.php
+++ b/engine/classes/ElggMemcache.php
@@ -32,6 +32,8 @@ class ElggMemcache extends ElggSharedMemoryCache {
*
* @param string $namespace The namespace for this cache to write to -
* note, namespaces of the same name are shared!
+ *
+ * @throws ConfigurationException
*/
function __construct($namespace = 'default') {
global $CONFIG;
diff --git a/engine/classes/ElggMenuBuilder.php b/engine/classes/ElggMenuBuilder.php
index d9a704dd9..b463143d8 100644
--- a/engine/classes/ElggMenuBuilder.php
+++ b/engine/classes/ElggMenuBuilder.php
@@ -8,6 +8,9 @@
*/
class ElggMenuBuilder {
+ /**
+ * @var ElggMenuItem[]
+ */
protected $menu = array();
protected $selected = null;
@@ -15,7 +18,7 @@ class ElggMenuBuilder {
/**
* ElggMenuBuilder constructor
*
- * @param array $menu Array of ElggMenuItem objects
+ * @param ElggMenuItem[] $menu Array of ElggMenuItem objects
*/
public function __construct(array $menu) {
$this->menu = $menu;
@@ -107,6 +110,7 @@ class ElggMenuBuilder {
$children = array();
// divide base nodes from children
foreach ($section as $menu_item) {
+ /* @var ElggMenuItem $menu_item */
$parent_name = $menu_item->getParentName();
if (!$parent_name) {
$parents[$menu_item->getName()] = $menu_item;
@@ -118,13 +122,16 @@ class ElggMenuBuilder {
// attach children to parents
$iteration = 0;
$current_gen = $parents;
+ $next_gen = null;
while (count($children) && $iteration < 5) {
foreach ($children as $index => $menu_item) {
$parent_name = $menu_item->getParentName();
if (array_key_exists($parent_name, $current_gen)) {
$next_gen[$menu_item->getName()] = $menu_item;
- $current_gen[$parent_name]->addChild($menu_item);
- $menu_item->setParent($current_gen[$parent_name]);
+ if (!in_array($menu_item, $current_gen[$parent_name]->getData('children'))) {
+ $current_gen[$parent_name]->addChild($menu_item);
+ $menu_item->setParent($current_gen[$parent_name]);
+ }
unset($children[$index]);
}
}
@@ -216,12 +223,12 @@ class ElggMenuBuilder {
array_push($stack, $root);
while (!empty($stack)) {
$node = array_pop($stack);
+ /* @var ElggMenuItem $node */
$node->sortChildren($sort_callback);
$children = $node->getChildren();
if ($children) {
$stack = array_merge($stack, $children);
}
- $p = count($stack);
}
}
}
@@ -230,8 +237,8 @@ class ElggMenuBuilder {
/**
* Compare two menu items by their display text
*
- * @param ElggMenuItem $a
- * @param ElggMenuItem $b
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
* @return bool
*/
public static function compareByText($a, $b) {
@@ -248,8 +255,8 @@ class ElggMenuBuilder {
/**
* Compare two menu items by their identifiers
*
- * @param ElggMenuItem $a
- * @param ElggMenuItem $b
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
* @return bool
*/
public static function compareByName($a, $b) {
@@ -266,9 +273,11 @@ class ElggMenuBuilder {
/**
* Compare two menu items by their priority
*
- * @param ElggMenuItem $a
- * @param ElggMenuItem $b
+ * @param ElggMenuItem $a Menu item
+ * @param ElggMenuItem $b Menu item
* @return bool
+ *
+ * @todo change name to compareByPriority
*/
public static function compareByWeight($a, $b) {
$aw = $a->getWeight();
diff --git a/engine/classes/ElggMetadata.php b/engine/classes/ElggMetadata.php
index 7f45dc3ea..3a8e2d817 100644
--- a/engine/classes/ElggMetadata.php
+++ b/engine/classes/ElggMetadata.php
@@ -6,6 +6,10 @@
*
* @package Elgg.Core
* @subpackage Metadata
+ *
+ * @property string $value_type
+ * @property int $owner_guid
+ * @property string $enabled
*/
class ElggMetadata extends ElggExtender {
diff --git a/engine/classes/ElggObject.php b/engine/classes/ElggObject.php
index 6263f84f6..aeaa3ba5c 100644
--- a/engine/classes/ElggObject.php
+++ b/engine/classes/ElggObject.php
@@ -66,21 +66,18 @@ class ElggObject extends ElggEntity {
$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
throw new IOException($msg);
}
-
- // Is $guid is an ElggObject? Use a copy constructor
} else if ($guid instanceof ElggObject) {
+ // $guid is an ElggObject so this is a copy constructor
elgg_deprecated_notice('This type of usage of the ElggObject constructor was deprecated. Please use the clone method.', 1.7);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is this is an ElggEntity but not an ElggObject = ERROR!
} else if ($guid instanceof ElggEntity) {
+ // @todo remove - do not need separate exception
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggObject'));
-
- // Is it a GUID
} else if (is_numeric($guid)) {
+ // $guid is a GUID so load
if (!$this->load($guid)) {
throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
}
@@ -110,7 +107,7 @@ class ElggObject extends ElggEntity {
$this->attributes = $attrs;
$this->attributes['tables_loaded'] = 2;
- cache_entity($this);
+ _elgg_cache_entity($this);
return true;
}
@@ -129,8 +126,12 @@ class ElggObject extends ElggEntity {
}
// Save ElggObject-specific attributes
- return create_object_entity($this->get('guid'), $this->get('title'),
- $this->get('description'));
+
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_object_entity($this->get('guid'), $this->get('title'), $this->get('description'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
}
/**
diff --git a/engine/classes/ElggPlugin.php b/engine/classes/ElggPlugin.php
index 8f71b79a8..545b9a53c 100644
--- a/engine/classes/ElggPlugin.php
+++ b/engine/classes/ElggPlugin.php
@@ -145,7 +145,7 @@ class ElggPlugin extends ElggObject {
/**
* Sets the location of this plugin.
*
- * @param path $id The path to the plugin's dir.
+ * @param string $id The path to the plugin's dir.
* @return bool
*/
public function setID($id) {
@@ -299,17 +299,15 @@ class ElggPlugin extends ElggObject {
$private_settings = get_data($q);
- if ($private_settings) {
- $return = array();
+ $return = array();
+ if ($private_settings) {
foreach ($private_settings as $setting) {
$return[$setting->name] = $setting->value;
}
-
- return $return;
}
- return false;
+ return $return;
}
/**
@@ -350,11 +348,14 @@ class ElggPlugin extends ElggObject {
*/
public function unsetAllSettings() {
$db_prefix = get_config('dbprefix');
- $ps_prefix = elgg_namespace_plugin_private_setting('setting', '');
+
+ $us_prefix = elgg_namespace_plugin_private_setting('user_setting', '', $this->getID());
+ $is_prefix = elgg_namespace_plugin_private_setting('internal', '', $this->getID());
$q = "DELETE FROM {$db_prefix}private_settings
WHERE entity_guid = $this->guid
- AND name NOT LIKE '$ps_prefix%'";
+ AND name NOT LIKE '$us_prefix%'
+ AND name NOT LIKE '$is_prefix%'";
return delete_data($q);
}
@@ -420,20 +421,18 @@ class ElggPlugin extends ElggObject {
$private_settings = get_data($q);
- if ($private_settings) {
- $return = array();
+ $return = array();
+ if ($private_settings) {
foreach ($private_settings as $setting) {
$name = substr($setting->name, $ps_prefix_len);
$value = $setting->value;
$return[$name] = $value;
}
-
- return $return;
}
- return false;
+ return $return;
}
/**
@@ -546,7 +545,7 @@ class ElggPlugin extends ElggObject {
* Returns if the plugin is complete, meaning has all required files
* and Elgg can read them and they make sense.
*
- * @todo bad name? This could be confused with isValid() from ElggPackage.
+ * @todo bad name? This could be confused with isValid() from ElggPluginPackage.
*
* @return bool
*/
@@ -597,6 +596,8 @@ class ElggPlugin extends ElggObject {
* Checks if this plugin can be activated on the current
* Elgg installation.
*
+ * @todo remove $site_guid param or implement it
+ *
* @param mixed $site_guid Optional site guid
* @return bool
*/
@@ -647,8 +648,8 @@ class ElggPlugin extends ElggObject {
// Note: this will not run re-run the init hooks!
if ($return) {
if ($this->canReadFile('activate.php')) {
- $flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES
- | ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS;
+ $flags = ELGG_PLUGIN_INCLUDE_START | ELGG_PLUGIN_REGISTER_CLASSES |
+ ELGG_PLUGIN_REGISTER_LANGUAGES | ELGG_PLUGIN_REGISTER_VIEWS;
$this->start($flags);
diff --git a/engine/classes/ElggPluginPackage.php b/engine/classes/ElggPluginPackage.php
index 2dc4bdb3d..37eb4bf4d 100644
--- a/engine/classes/ElggPluginPackage.php
+++ b/engine/classes/ElggPluginPackage.php
@@ -100,7 +100,6 @@ class ElggPluginPackage {
* @param string $plugin The ID (directory name) or full path of the plugin.
* @param bool $validate Automatically run isValid()?
*
- * @return true
* @throws PluginException
*/
public function __construct($plugin, $validate = true) {
@@ -213,6 +212,7 @@ class ElggPluginPackage {
return false;
}
+ // Note: $conflicts and $requires are not unused. They're called dynamically
$conflicts = $this->getManifest()->getConflicts();
$requires = $this->getManifest()->getRequires();
$provides = $this->getManifest()->getProvides();
@@ -294,6 +294,7 @@ class ElggPluginPackage {
return true;
}
+ $this->errorMsg = elgg_echo('unknown_error');
return false;
}
@@ -330,8 +331,10 @@ class ElggPluginPackage {
* @return bool|array
*/
public function checkDependencies($full_report = false) {
+ // Note: $conflicts and $requires are not unused. They're called dynamically
$requires = $this->getManifest()->getRequires();
$conflicts = $this->getManifest()->getConflicts();
+
$enabled_plugins = elgg_get_plugins('active');
$this_id = $this->getID();
$report = array();
@@ -368,6 +371,7 @@ class ElggPluginPackage {
$check_types = array('requires', 'conflicts');
if ($full_report) {
+ // Note: $suggests is not unused. It's called dynamically
$suggests = $this->getManifest()->getSuggests();
$check_types[] = 'suggests';
}
diff --git a/engine/classes/ElggPriorityList.php b/engine/classes/ElggPriorityList.php
index 8a3b836a8..416df885c 100644
--- a/engine/classes/ElggPriorityList.php
+++ b/engine/classes/ElggPriorityList.php
@@ -89,7 +89,7 @@
* return true;
* }
*
- * @package Elgg.Core
+ * @package Elgg.Core
* @subpackage Helpers
*/
class ElggPriorityList
@@ -126,7 +126,9 @@ class ElggPriorityList
* maintains its priority and the new element is to the next available
* slot, taking into consideration all previously registered elements.
* Negative elements are accepted.
+ * @param bool $exact unused
* @return int The priority of the added element.
+ * @todo remove $exact or implement it. Note we use variable name strict below.
*/
public function add($element, $priority = null, $exact = false) {
if ($priority !== null && !is_numeric($priority)) {
@@ -146,7 +148,8 @@ class ElggPriorityList
* @warning The element must have the same attributes / values. If using $strict, it must have
* the same types. array(10) will fail in strict against array('10') (str vs int).
*
- * @param type $element
+ * @param mixed $element The element to remove from the list
+ * @param bool $strict Whether to check the type of the element match
* @return bool
*/
public function remove($element, $strict = false) {
@@ -162,10 +165,10 @@ class ElggPriorityList
/**
* Move an existing element to a new priority.
*
- * @param mixed $current_priority
- * @param int $new_priority
- *
- * @return int The new priority.
+ * @param mixed $element The element to move
+ * @param int $new_priority The new priority for the element
+ * @param bool $strict Whether to check the type of the element match
+ * @return bool
*/
public function move($element, $new_priority, $strict = false) {
$new_priority = (int) $new_priority;
@@ -200,12 +203,12 @@ class ElggPriorityList
*
* If no user function is provided the elements are sorted by priority registered.
*
- * The callback function should accept the array of elements as the first argument and should
- * return a sorted array.
+ * The callback function should accept the array of elements as the first
+ * argument and should return a sorted array.
*
* This function can be called multiple times.
*
- * @param type $callback
+ * @param callback $callback The callback for sorting. Numeric sorting is the default.
* @return bool
*/
public function sort($callback = null) {
@@ -268,7 +271,7 @@ class ElggPriorityList
/**
* Returns the element at $priority.
*
- * @param int $priority
+ * @param int $priority The priority
* @return mixed The element or false on fail.
*/
public function getElement($priority) {
@@ -351,7 +354,12 @@ class ElggPriorityList
return ($key !== NULL && $key !== FALSE);
}
- // Countable
+ /**
+ * Countable interface
+ *
+ * @see Countable::count()
+ * @return int
+ */
public function count() {
return count($this->elements);
}
diff --git a/engine/classes/ElggRelationship.php b/engine/classes/ElggRelationship.php
index efc0f7eff..d2e88882a 100644
--- a/engine/classes/ElggRelationship.php
+++ b/engine/classes/ElggRelationship.php
@@ -71,6 +71,7 @@ class ElggRelationship extends ElggData implements
* Save the relationship
*
* @return int the relationship id
+ * @throws IOException
*/
public function save() {
if ($this->id > 0) {
@@ -145,7 +146,7 @@ class ElggRelationship extends ElggData implements
* @param ODD $data ODD data
* @return bool
- * @throws ImportException
+ * @throws ImportException|InvalidParameterException
*/
public function import(ODD $data) {
if (!($data instanceof ODDRelationship)) {
@@ -179,6 +180,8 @@ class ElggRelationship extends ElggData implements
return true;
}
}
+
+ return false;
}
// SYSTEM LOG INTERFACE ////////////////////////////////////////////////////////////
diff --git a/engine/classes/ElggSite.php b/engine/classes/ElggSite.php
index 1fe49b85c..dd996fe98 100644
--- a/engine/classes/ElggSite.php
+++ b/engine/classes/ElggSite.php
@@ -77,28 +77,24 @@ class ElggSite extends ElggEntity {
$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
throw new IOException($msg);
}
-
- // Is $guid is an ElggSite? Use a copy constructor
} else if ($guid instanceof ElggSite) {
+ // $guid is an ElggSite so this is a copy constructor
elgg_deprecated_notice('This type of usage of the ElggSite constructor was deprecated. Please use the clone method.', 1.7);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is this is an ElggEntity but not an ElggSite = ERROR!
} else if ($guid instanceof ElggEntity) {
+ // @todo remove and just use else clause
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggSite'));
-
- // See if this is a URL
} else if (strpos($guid, "http") !== false) {
+ // url so retrieve by url
$guid = get_site_by_url($guid);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is it a GUID
} else if (is_numeric($guid)) {
+ // $guid is a GUID so load
if (!$this->load($guid)) {
throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
}
@@ -128,7 +124,7 @@ class ElggSite extends ElggEntity {
$this->attributes = $attrs;
$this->attributes['tables_loaded'] = 2;
- cache_entity($this);
+ _elgg_cache_entity($this);
return true;
}
@@ -381,7 +377,9 @@ class ElggSite extends ElggEntity {
elgg_register_plugin_hook_handler('index', 'system', 'elgg_walled_garden_index', 1);
if (!$this->isPublicPage()) {
- $_SESSION['last_forward_from'] = current_page_url();
+ if (!elgg_is_xhr()) {
+ $_SESSION['last_forward_from'] = current_page_url();
+ }
register_error(elgg_echo('loggedinrequired'));
forward();
}
@@ -443,8 +441,6 @@ class ElggSite extends ElggEntity {
// include a hook for plugin authors to include public pages
$plugins = elgg_trigger_plugin_hook('public_pages', 'walled_garden', NULL, array());
- // lookup admin-specific public pages
-
// allow public pages
foreach (array_merge($defaults, $plugins) as $public) {
$pattern = "`^{$CONFIG->url}$public/*$`i";
diff --git a/engine/classes/ElggStaticVariableCache.php b/engine/classes/ElggStaticVariableCache.php
index 17d849400..9c14fdfba 100644
--- a/engine/classes/ElggStaticVariableCache.php
+++ b/engine/classes/ElggStaticVariableCache.php
@@ -11,7 +11,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {
/**
* The cache.
*
- * @var unknown_type
+ * @var array
*/
private static $__cache;
@@ -22,7 +22,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {
* memory, optionally with a given namespace (to avoid overlap).
*
* @param string $namespace The namespace for this cache to write to.
- * @note namespaces of the same name are shared!
+ * @warning namespaces of the same name are shared!
*/
function __construct($namespace = 'default') {
$this->setNamespace($namespace);
@@ -80,7 +80,7 @@ class ElggStaticVariableCache extends ElggSharedMemoryCache {
}
/**
- * This was probably meant to delete everything?
+ * Clears the cache for a particular namespace
*
* @return void
*/
diff --git a/engine/classes/ElggTranslit.php b/engine/classes/ElggTranslit.php
index 676c59fc8..b4bf87797 100644
--- a/engine/classes/ElggTranslit.php
+++ b/engine/classes/ElggTranslit.php
@@ -20,11 +20,10 @@
* and is licensed under the LGPL. For more information, see
* <http://www.doctrine-project.org>.
*
- * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
- * @author Jonathan H. Wage <jonwage@gmail.com>
- *
- * @author Steve Clay <steve@mrclay.org>
- * @package Elgg.Core
+ * @package Elgg.Core
+ * @author Konsta Vesterinen <kvesteri@cc.hut.fi>
+ * @author Jonathan H. Wage <jonwage@gmail.com>
+ * @author Steve Clay <steve@mrclay.org>
*
* @access private Plugin authors should not use this directly
*/
@@ -32,8 +31,9 @@ class ElggTranslit {
/**
* Create a version of a string for embedding in a URL
- * @param string $string a UTF-8 string
- * @param string $separator
+ *
+ * @param string $string A UTF-8 string
+ * @param string $separator The character to separate words with
* @return string
*/
static public function urlize($string, $separator = '-') {
@@ -49,24 +49,29 @@ class ElggTranslit {
// Internationalization, AND 日本語!
$string = self::transliterateAscii($string);
- // more translation
+ // allow HTML tags in titles
+ $string = preg_replace('~<([a-zA-Z][^>]*)>~', ' $1 ', $string);
+
+ // more substitutions
+ // @todo put these somewhere else
$string = strtr($string, array(
- // Euro/GBP
- "\xE2\x82\xAC" /* € */ => 'E', "\xC2\xA3" /* £ */ => 'GBP',
+ // currency
+ "\xE2\x82\xAC" /* € */ => ' E ',
+ "\xC2\xA3" /* £ */ => ' GBP ',
));
// remove all ASCII except 0-9a-zA-Z, hyphen, underscore, and whitespace
// note: "x" modifier did not work with this pattern.
$string = preg_replace('~['
- . '\x00-\x08' # control chars
- . '\x0b\x0c' # vert tab, form feed
- . '\x0e-\x1f' # control chars
- . '\x21-\x2c' # ! ... ,
- . '\x2e\x2f' # . slash
- . '\x3a-\x40' # : ... @
- . '\x5b-\x5e' # [ ... ^
- . '\x60' # `
- . '\x7b-\x7f' # { ... DEL
+ . '\x00-\x08' // control chars
+ . '\x0b\x0c' // vert tab, form feed
+ . '\x0e-\x1f' // control chars
+ . '\x21-\x2c' // ! ... ,
+ . '\x2e\x2f' // . slash
+ . '\x3a-\x40' // : ... @
+ . '\x5b-\x5e' // [ ... ^
+ . '\x60' // `
+ . '\x7b-\x7f' // { ... DEL
. ']~', '', $string);
$string = strtr($string, '', '');
@@ -80,10 +85,10 @@ class ElggTranslit {
// note: we cannot use [^0-9a-zA-Z] because that matches multibyte chars.
// note: "x" modifier did not work with this pattern.
$pattern = '~['
- . '\x00-\x2f' # controls ... slash
- . '\x3a-\x40' # : ... @
- . '\x5b-\x60' # [ ... `
- . '\x7b-\x7f' # { ... DEL
+ . '\x00-\x2f' // controls ... slash
+ . '\x3a-\x40' // : ... @
+ . '\x5b-\x60' // [ ... `
+ . '\x7b-\x7f' // { ... DEL
. ']+~x';
// ['internationalization', 'and', '日本語']
@@ -98,6 +103,7 @@ class ElggTranslit {
/**
* Transliterate Western multibyte chars to ASCII
+ *
* @param string $utf8 a UTF-8 string
* @return string
*/
@@ -247,6 +253,7 @@ class ElggTranslit {
/**
* Tests that "normalizer_normalize" exists and works
+ *
* @return bool
*/
static public function hasNormalizerSupport() {
@@ -255,7 +262,7 @@ class ElggTranslit {
$form_c = "\xC3\x85"; // 'LATIN CAPITAL LETTER A WITH RING ABOVE' (U+00C5)
$form_d = "A\xCC\x8A"; // A followed by 'COMBINING RING ABOVE' (U+030A)
$ret = (function_exists('normalizer_normalize')
- && $form_c === normalizer_normalize($form_d));
+ && $form_c === normalizer_normalize($form_d));
}
return $ret;
}
diff --git a/engine/classes/ElggUser.php b/engine/classes/ElggUser.php
index 6c1cdc1de..6163f9b62 100644
--- a/engine/classes/ElggUser.php
+++ b/engine/classes/ElggUser.php
@@ -40,6 +40,9 @@ class ElggUser extends ElggEntity
$this->attributes['code'] = NULL;
$this->attributes['banned'] = "no";
$this->attributes['admin'] = 'no';
+ $this->attributes['prev_last_action'] = NULL;
+ $this->attributes['last_login'] = NULL;
+ $this->attributes['prev_last_login'] = NULL;
$this->attributes['tables_split'] = 2;
}
@@ -65,30 +68,26 @@ class ElggUser extends ElggEntity
$msg = elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid->guid));
throw new IOException($msg);
}
-
- // See if this is a username
} else if (is_string($guid)) {
+ // $guid is a username
$user = get_user_by_username($guid);
if ($user) {
foreach ($user->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
}
-
- // Is $guid is an ElggUser? Use a copy constructor
} else if ($guid instanceof ElggUser) {
+ // $guid is an ElggUser so this is a copy constructor
elgg_deprecated_notice('This type of usage of the ElggUser constructor was deprecated. Please use the clone method.', 1.7);
foreach ($guid->attributes as $key => $value) {
$this->attributes[$key] = $value;
}
-
- // Is this is an ElggEntity but not an ElggUser = ERROR!
} else if ($guid instanceof ElggEntity) {
+ // @todo why have a special case here
throw new InvalidParameterException(elgg_echo('InvalidParameterException:NonElggUser'));
-
- // Is it a GUID
} else if (is_numeric($guid)) {
+ // $guid is a GUID so load entity
if (!$this->load($guid)) {
throw new IOException(elgg_echo('IOException:FailedToLoadGUID', array(get_class(), $guid)));
}
@@ -116,7 +115,7 @@ class ElggUser extends ElggEntity
$this->attributes = $attrs;
$this->attributes['tables_loaded'] = 2;
- cache_entity($this);
+ _elgg_cache_entity($this);
return true;
}
@@ -133,9 +132,13 @@ class ElggUser extends ElggEntity
}
// Now save specific stuff
- return create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'),
+ _elgg_disable_caching_for_entity($this->guid);
+ $ret = create_user_entity($this->get('guid'), $this->get('name'), $this->get('username'),
$this->get('password'), $this->get('salt'), $this->get('email'), $this->get('language'),
$this->get('code'));
+ _elgg_enable_caching_for_entity($this->guid);
+
+ return $ret;
}
/**
diff --git a/engine/classes/ElggVolatileMetadataCache.php b/engine/classes/ElggVolatileMetadataCache.php
index 8a33c198d..4acda7cee 100644
--- a/engine/classes/ElggVolatileMetadataCache.php
+++ b/engine/classes/ElggVolatileMetadataCache.php
@@ -33,9 +33,11 @@ class ElggVolatileMetadataCache {
protected $ignoreAccess = null;
/**
- * @param int $entity_guid
- *
- * @param array $values
+ * Cache metadata for an entity
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param array $values The metadata values to cache
+ * @return void
*/
public function saveAll($entity_guid, array $values) {
if (!$this->getIgnoreAccess()) {
@@ -45,8 +47,9 @@ class ElggVolatileMetadataCache {
}
/**
- * @param int $entity_guid
- *
+ * Get the metadata for an entity
+ *
+ * @param int $entity_guid The GUID of the entity
* @return array
*/
public function loadAll($entity_guid) {
@@ -61,15 +64,17 @@ class ElggVolatileMetadataCache {
* Declare that there may be fetch-able metadata names in storage that this
* cache doesn't know about
*
- * @param int $entity_guid
+ * @param int $entity_guid The GUID of the entity
+ * @return void
*/
public function markOutOfSync($entity_guid) {
unset($this->isSynchronized[$entity_guid]);
}
/**
- * @param $entity_guid
- *
+ * Have all the metadata for this entity been cached?
+ *
+ * @param int $entity_guid The GUID of the entity
* @return bool
*/
public function isSynchronized($entity_guid) {
@@ -77,13 +82,15 @@ class ElggVolatileMetadataCache {
}
/**
- * @param int $entity_guid
- *
- * @param string $name
- *
- * @param array|int|string|null $value null means it is known that there is no
- * fetch-able metadata under this name
- * @param bool $allow_multiple
+ * Cache a piece of metadata
+ *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @param array|int|string|null $value The metadata value. null means it is
+ * known that there is no fetch-able
+ * metadata under this name
+ * @param bool $allow_multiple Can the metadata be an array
+ * @return void
*/
public function save($entity_guid, $name, $value, $allow_multiple = false) {
if ($this->getIgnoreAccess()) {
@@ -115,10 +122,8 @@ class ElggVolatileMetadataCache {
* function's return value should be trusted (otherwise a null return value
* is ambiguous).
*
- * @param int $entity_guid
- *
- * @param string $name
- *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
* @return array|string|int|null null = value does not exist
*/
public function load($entity_guid, $name) {
@@ -133,9 +138,9 @@ class ElggVolatileMetadataCache {
* Forget about this metadata entry. We don't want to try to guess what the
* next fetch from storage will return
*
- * @param int $entity_guid
- *
- * @param string $name
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
+ * @return void
*/
public function markUnknown($entity_guid, $name) {
unset($this->values[$entity_guid][$name]);
@@ -145,10 +150,8 @@ class ElggVolatileMetadataCache {
/**
* If true, load() will return an accurate value for this name
*
- * @param int $entity_guid
- *
- * @param string $name
- *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
* @return bool
*/
public function isKnown($entity_guid, $name) {
@@ -163,10 +166,8 @@ class ElggVolatileMetadataCache {
/**
* Declare that metadata under this name is known to be not fetch-able from storage
*
- * @param int $entity_guid
- *
- * @param string $name
- *
+ * @param int $entity_guid The GUID of the entity
+ * @param string $name The metadata name
* @return array
*/
public function markEmpty($entity_guid, $name) {
@@ -176,7 +177,8 @@ class ElggVolatileMetadataCache {
/**
* Forget about all metadata for an entity
*
- * @param int $entity_guid
+ * @param int $entity_guid The GUID of the entity
+ * @return void
*/
public function clear($entity_guid) {
$this->values[$entity_guid] = array();
@@ -185,6 +187,8 @@ class ElggVolatileMetadataCache {
/**
* Clear entire cache and mark all entities as out of sync
+ *
+ * @return void
*/
public function flush() {
$this->values = array();
@@ -197,7 +201,8 @@ class ElggVolatileMetadataCache {
*
* This setting makes this component a little more loosely-coupled.
*
- * @param bool $ignore
+ * @param bool $ignore Whether to ignore access or not
+ * @return void
*/
public function setIgnoreAccess($ignore) {
$this->ignoreAccess = (bool) $ignore;
@@ -205,12 +210,16 @@ class ElggVolatileMetadataCache {
/**
* Tell the cache to call elgg_get_ignore_access() to determing access status.
+ *
+ * @return void
*/
public function unsetIgnoreAccess() {
$this->ignoreAccess = null;
}
/**
+ * Get the ignore access value
+ *
* @return bool
*/
protected function getIgnoreAccess() {
@@ -225,12 +234,10 @@ class ElggVolatileMetadataCache {
* Invalidate based on options passed to the global *_metadata functions
*
* @param string $action Action performed on metadata. "delete", "disable", or "enable"
- *
- * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
- *
- * "guid" if given, invalidation will be limited to this entity
- *
- * "metadata_name" if given, invalidation will be limited to metadata with this name
+ * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
+ * "guid" if given, invalidation will be limited to this entity
+ * "metadata_name" if given, invalidation will be limited to metadata with this name
+ * @return void
*/
public function invalidateByOptions($action, array $options) {
// remove as little as possible, optimizing for common cases
@@ -254,7 +261,10 @@ class ElggVolatileMetadataCache {
}
/**
- * @param int|array $guids
+ * Populate the cache from a set of entities
+ *
+ * @param int|array $guids Array of or single GUIDs
+ * @return void
*/
public function populateFromEntities($guids) {
if (empty($guids)) {
@@ -318,9 +328,7 @@ class ElggVolatileMetadataCache {
* cache if RAM usage becomes an issue.
*
* @param array $guids GUIDs of entities to examine
- *
- * @param int $limit Limit in characters of all metadata (with ints casted to strings)
- *
+ * @param int $limit Limit in characters of all metadata (with ints casted to strings)
* @return array
*/
public function filterMetadataHeavyEntities(array $guids, $limit = 1024000) {
diff --git a/engine/classes/ElggWidget.php b/engine/classes/ElggWidget.php
index 99708f66a..66191bf47 100644
--- a/engine/classes/ElggWidget.php
+++ b/engine/classes/ElggWidget.php
@@ -7,6 +7,11 @@
*
* @package Elgg.Core
* @subpackage Widgets
+ *
+ * @property-read string $handler internal, do not use
+ * @property-read string $column internal, do not use
+ * @property-read string $order internal, do not use
+ * @property-read string $context internal, do not use
*/
class ElggWidget extends ElggObject {
@@ -141,10 +146,15 @@ class ElggWidget extends ElggObject {
}
}
+ $bottom_rank = count($widgets);
+ if ($column == $this->column) {
+ $bottom_rank--;
+ }
+
if ($rank == 0) {
// top of the column
$this->order = reset($widgets)->order - 10;
- } elseif ($rank == (count($widgets) - 1)) {
+ } elseif ($rank == $bottom_rank) {
// bottom of the column of active widgets
$this->order = end($widgets)->order + 10;
} else {
diff --git a/engine/classes/ElggXMLElement.php b/engine/classes/ElggXMLElement.php
index 65a13912c..cbd3fc5ce 100644
--- a/engine/classes/ElggXMLElement.php
+++ b/engine/classes/ElggXMLElement.php
@@ -20,7 +20,12 @@ class ElggXMLElement {
if ($xml instanceof SimpleXMLElement) {
$this->_element = $xml;
} else {
+ // do not load entities
+ $disable_load_entities = libxml_disable_entity_loader(true);
+
$this->_element = new SimpleXMLElement($xml);
+
+ libxml_disable_entity_loader($disable_load_entities);
}
}
@@ -32,7 +37,7 @@ class ElggXMLElement {
}
/**
- * @return array:string The attributes
+ * @return string[] The attributes
*/
public function getAttributes() {
//include namespace declarations as attributes
@@ -64,7 +69,7 @@ class ElggXMLElement {
}
/**
- * @return array:ElggXMLElement Child elements
+ * @return ElggXMLElement[] Child elements
*/
public function getChildren() {
$children = $this->_element->children();
@@ -76,6 +81,12 @@ class ElggXMLElement {
return $result;
}
+ /**
+ * Override ->
+ *
+ * @param string $name Property name
+ * @return mixed
+ */
function __get($name) {
switch ($name) {
case 'name':
@@ -94,6 +105,12 @@ class ElggXMLElement {
return null;
}
+ /**
+ * Override isset
+ *
+ * @param string $name Property name
+ * @return boolean
+ */
function __isset($name) {
switch ($name) {
case 'name':
@@ -111,5 +128,4 @@ class ElggXMLElement {
}
return false;
}
-
-} \ No newline at end of file
+}
diff --git a/engine/classes/ODDMetaData.php b/engine/classes/ODDMetaData.php
index 58862e0fb..09b653582 100644
--- a/engine/classes/ODDMetaData.php
+++ b/engine/classes/ODDMetaData.php
@@ -10,12 +10,12 @@ class ODDMetaData extends ODD {
/**
* New ODD metadata
*
- * @param unknown_type $uuid Unique ID
- * @param unknown_type $entity_uuid Another unique ID
- * @param unknown_type $name Name
- * @param unknown_type $value Value
- * @param unknown_type $type Type
- * @param unknown_type $owner_uuid Owner ID
+ * @param string $uuid Unique ID
+ * @param string $entity_uuid Another unique ID
+ * @param string $name Name
+ * @param string $value Value
+ * @param string $type Type
+ * @param string $owner_uuid Owner ID
*/
function __construct($uuid, $entity_uuid, $name, $value, $type = "", $owner_uuid = "") {
parent::__construct();
@@ -31,7 +31,7 @@ class ODDMetaData extends ODD {
/**
* Returns 'metadata'
*
- * @return 'metadata'
+ * @return string 'metadata'
*/
protected function getTagName() {
return "metadata";
diff --git a/engine/classes/ODDRelationship.php b/engine/classes/ODDRelationship.php
index 2906b1c73..8b1fe217b 100644
--- a/engine/classes/ODDRelationship.php
+++ b/engine/classes/ODDRelationship.php
@@ -10,9 +10,9 @@ class ODDRelationship extends ODD {
/**
* New ODD Relationship
*
- * @param unknown_type $uuid1 First UUID
- * @param unknown_type $type Type of telationship
- * @param unknown_type $uuid2 Second UUId
+ * @param string $uuid1 First UUID
+ * @param string $type Type of telationship
+ * @param string $uuid2 Second UUId
*/
function __construct($uuid1, $type, $uuid2) {
parent::__construct();
@@ -25,7 +25,7 @@ class ODDRelationship extends ODD {
/**
* Returns 'relationship'
*
- * @return 'relationship'
+ * @return string 'relationship'
*/
protected function getTagName() {
return "relationship";
diff --git a/engine/handlers/cache_handler.php b/engine/handlers/cache_handler.php
index 7706c2c92..36fc665bb 100644
--- a/engine/handlers/cache_handler.php
+++ b/engine/handlers/cache_handler.php
@@ -88,15 +88,18 @@ header("ETag: \"$etag\"");
$filename = $dataroot . 'views_simplecache/' . md5($viewtype . $view);
if (file_exists($filename)) {
- $contents = file_get_contents($filename);
+ readfile($filename);
} else {
// someone trying to access a non-cached file or a race condition with cache flushing
mysql_close($mysql_dblink);
require_once(dirname(dirname(__FILE__)) . "/start.php");
- elgg_regenerate_simplecache();
+
+ global $CONFIG;
+ if (!in_array($view, $CONFIG->views->simplecache)) {
+ header("HTTP/1.1 404 Not Found");
+ exit;
+ }
elgg_set_viewtype($viewtype);
- $contents = elgg_view($view);
+ echo elgg_view($view);
}
-
-echo $contents;
diff --git a/engine/lib/access.php b/engine/lib/access.php
index f7d3bf7ea..de0693ea8 100644
--- a/engine/lib/access.php
+++ b/engine/lib/access.php
@@ -1015,6 +1015,10 @@ function access_init() {
*
* Returns true to override the access system or null if no change is needed.
*
+ * @param string $hook
+ * @param string $type
+ * @param bool $value
+ * @param array $params
* @return true|null
* @access private
*/
@@ -1047,6 +1051,13 @@ function elgg_override_permissions($hook, $type, $value, $params) {
/**
* Runs unit tests for the entities object.
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $value
+ * @param array $params
+ * @return array
+ *
* @access private
*/
function access_test($hook, $type, $value, $params) {
diff --git a/engine/lib/actions.php b/engine/lib/actions.php
index 53b185dea..8047914ac 100644
--- a/engine/lib/actions.php
+++ b/engine/lib/actions.php
@@ -65,18 +65,16 @@ function action($action, $forwarder = "") {
// @todo REMOVE THESE ONCE #1509 IS IN PLACE.
// Allow users to disable plugins without a token in order to
// remove plugins that are incompatible.
- // Login and logout are for convenience.
+ // Logout for convenience.
// file/download (see #2010)
$exceptions = array(
'admin/plugins/disable',
'logout',
- 'login',
'file/download',
);
if (!in_array($action, $exceptions)) {
- // All actions require a token.
- action_gatekeeper();
+ action_gatekeeper($action);
}
$forwarder = str_replace(elgg_get_site_url(), "", $forwarder);
@@ -189,6 +187,26 @@ function elgg_unregister_action($action) {
}
/**
+ * Is the token timestamp within acceptable range?
+ *
+ * @param int $ts timestamp from the CSRF token
+ *
+ * @return bool
+ */
+function _elgg_validate_token_timestamp($ts) {
+ $action_token_timeout = elgg_get_config('action_token_timeout');
+ // default is 2 hours
+ $timeout = ($action_token_timeout !== null) ? $action_token_timeout : 2;
+
+ $hour = 60 * 60;
+ $timeout = $timeout * $hour;
+ $now = time();
+
+ // Validate time to ensure its not crazy
+ return ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout));
+}
+
+/**
* Validate an action token.
*
* Calls to actions will automatically validate tokens. If tokens are not
@@ -206,8 +224,6 @@ function elgg_unregister_action($action) {
* @access private
*/
function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL) {
- global $CONFIG;
-
if (!$token) {
$token = get_input('__elgg_token');
}
@@ -216,29 +232,18 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)
$ts = get_input('__elgg_ts');
}
- if (!isset($CONFIG->action_token_timeout)) {
- // default to 2 hours
- $timeout = 2;
- } else {
- $timeout = $CONFIG->action_token_timeout;
- }
-
$session_id = session_id();
if (($token) && ($ts) && ($session_id)) {
// generate token, check with input and forward if invalid
- $generated_token = generate_action_token($ts);
+ $required_token = generate_action_token($ts);
// Validate token
- if ($token == $generated_token) {
- $hour = 60 * 60;
- $timeout = $timeout * $hour;
- $now = time();
-
- // Validate time to ensure its not crazy
- if ($timeout == 0 || ($ts > $now - $timeout) && ($ts < $now + $timeout)) {
+ if ($token == $required_token) {
+
+ if (_elgg_validate_token_timestamp($ts)) {
// We have already got this far, so unless anything
- // else says something to the contry we assume we're ok
+ // else says something to the contrary we assume we're ok
$returnval = true;
$returnval = elgg_trigger_plugin_hook('action_gatekeeper:permissions:check', 'all', array(
@@ -252,10 +257,20 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)
register_error(elgg_echo('actiongatekeeper:pluginprevents'));
}
} else if ($visibleerrors) {
- register_error(elgg_echo('actiongatekeeper:timeerror'));
+ // this is necessary because of #5133
+ if (elgg_is_xhr()) {
+ register_error(elgg_echo('js:security:token_refresh_failed', array(elgg_get_site_url())));
+ } else {
+ register_error(elgg_echo('actiongatekeeper:timeerror'));
+ }
}
} else if ($visibleerrors) {
- register_error(elgg_echo('actiongatekeeper:tokeninvalid'));
+ // this is necessary because of #5133
+ if (elgg_is_xhr()) {
+ register_error(elgg_echo('js:security:token_refresh_failed', array(elgg_get_site_url())));
+ } else {
+ register_error(elgg_echo('actiongatekeeper:tokeninvalid'));
+ }
}
} else {
if (! empty($_SERVER['CONTENT_LENGTH']) && empty($_POST)) {
@@ -284,12 +299,33 @@ function validate_action_token($visibleerrors = TRUE, $token = NULL, $ts = NULL)
* This function verifies form input for security features (like a generated token),
* and forwards if they are invalid.
*
+ * @param string $action The action being performed
+ *
* @return mixed True if valid or redirects.
* @access private
*/
-function action_gatekeeper() {
- if (validate_action_token()) {
- return TRUE;
+function action_gatekeeper($action) {
+ if ($action === 'login') {
+ if (validate_action_token(false)) {
+ return true;
+ }
+
+ $token = get_input('__elgg_token');
+ $ts = (int)get_input('__elgg_ts');
+ if ($token && _elgg_validate_token_timestamp($ts)) {
+ // The tokens are present and the time looks valid: this is probably a mismatch due to the
+ // login form being on a different domain.
+ register_error(elgg_echo('actiongatekeeper:crosssitelogin'));
+
+
+ forward('login', 'csrf');
+ }
+
+ // let the validator send an appropriate msg
+ validate_action_token();
+
+ } elseif (validate_action_token()) {
+ return true;
}
forward(REFERER, 'csrf');
@@ -328,16 +364,19 @@ function generate_action_token($timestamp) {
}
/**
- * Initialise the site secret hash.
+ * Initialise the site secret (32 bytes: "z" to indicate format + 186-bit key in Base64 URL).
*
* Used during installation and saves as a datalist.
*
+ * Note: Old secrets were hex encoded.
+ *
* @return mixed The site secret hash or false
* @access private
* @todo Move to better file.
*/
function init_site_secret() {
- $secret = md5(rand() . microtime());
+ $secret = 'z' . ElggCrypto::getRandomString(31);
+
if (datalist_set('__site_secret__', $secret)) {
return $secret;
}
@@ -364,6 +403,26 @@ function get_site_secret() {
}
/**
+ * Get the strength of the site secret
+ *
+ * @return string "strong", "moderate", or "weak"
+ * @access private
+ */
+function _elgg_get_site_secret_strength() {
+ $secret = get_site_secret();
+ if ($secret[0] !== 'z') {
+ $rand_max = getrandmax();
+ if ($rand_max < pow(2, 16)) {
+ return 'weak';
+ }
+ if ($rand_max < pow(2, 32)) {
+ return 'moderate';
+ }
+ }
+ return 'strong';
+}
+
+/**
* Check if an action is registered and its script exists.
*
* @param string $action Action name
diff --git a/engine/lib/admin.php b/engine/lib/admin.php
index 35ab5599d..f36f29668 100644
--- a/engine/lib/admin.php
+++ b/engine/lib/admin.php
@@ -134,11 +134,11 @@ function elgg_delete_admin_notice($id) {
}
/**
- * List all admin messages.
+ * Get admin notices. An admin must be logged in since the notices are private.
*
* @param int $limit Limit
*
- * @return array List of admin notices
+ * @return array Array of admin notices
* @since 1.8.0
*/
function elgg_get_admin_notices($limit = 10) {
@@ -158,11 +158,13 @@ function elgg_get_admin_notices($limit = 10) {
* @since 1.8.0
*/
function elgg_admin_notice_exists($id) {
+ $old_ia = elgg_set_ignore_access(true);
$notice = elgg_get_entities_from_metadata(array(
'type' => 'object',
'subtype' => 'admin_notice',
'metadata_name_value_pair' => array('name' => 'admin_notice_id', 'value' => $id)
));
+ elgg_set_ignore_access($old_ia);
return ($notice) ? TRUE : FALSE;
}
@@ -234,6 +236,7 @@ function admin_init() {
elgg_register_action('admin/site/update_advanced', '', 'admin');
elgg_register_action('admin/site/flush_cache', '', 'admin');
elgg_register_action('admin/site/unlock_upgrade', '', 'admin');
+ elgg_register_action('admin/site/regenerate_secret', '', 'admin');
elgg_register_action('admin/menu/save', '', 'admin');
@@ -289,6 +292,7 @@ function admin_init() {
elgg_register_admin_menu_item('configure', 'settings', null, 100);
elgg_register_admin_menu_item('configure', 'basic', 'settings', 10);
elgg_register_admin_menu_item('configure', 'advanced', 'settings', 20);
+ elgg_register_admin_menu_item('configure', 'advanced/site_secret', 'settings', 25);
elgg_register_admin_menu_item('configure', 'menu_items', 'appearance', 30);
elgg_register_admin_menu_item('configure', 'profile_fields', 'appearance', 40);
// default widgets is added via an event handler elgg_default_widgets_init() in widgets.php
@@ -346,7 +350,7 @@ function elgg_admin_add_plugin_settings_menu() {
$active_plugins = elgg_get_plugins('active');
if (!$active_plugins) {
// nothing added because no items
- return FALSE;
+ return;
}
foreach ($active_plugins as $plugin) {
@@ -380,6 +384,7 @@ function elgg_admin_add_plugin_settings_menu() {
*/
function elgg_admin_sort_page_menu($hook, $type, $return, $params) {
$configure_items = $return['configure'];
+ /* @var ElggMenuItem[] $configure_items */
foreach ($configure_items as $menu_item) {
if ($menu_item->getName() == 'settings') {
$settings = $menu_item;
@@ -387,6 +392,7 @@ function elgg_admin_sort_page_menu($hook, $type, $return, $params) {
}
// keep the basic and advanced settings at the top
+ /* @var ElggMenuItem $settings */
$children = $settings->getChildren();
$site_settings = array_splice($children, 0, 2);
usort($children, array('ElggMenuBuilder', 'compareByText'));
@@ -466,14 +472,18 @@ function admin_page_handler($page) {
$vars = array('page' => $page);
// special page for plugin settings since we create the form for them
- if ($page[0] == 'plugin_settings' && isset($page[1]) &&
- (elgg_view_exists("settings/{$page[1]}/edit") || elgg_view_exists("plugins/{$page[1]}/settings"))) {
+ if ($page[0] == 'plugin_settings') {
+ if (isset($page[1]) && (elgg_view_exists("settings/{$page[1]}/edit") ||
+ elgg_view_exists("plugins/{$page[1]}/settings"))) {
- $view = 'admin/plugin_settings';
- $plugin = elgg_get_plugin_from_id($page[1]);
- $vars['plugin'] = $plugin;
+ $view = 'admin/plugin_settings';
+ $plugin = elgg_get_plugin_from_id($page[1]);
+ $vars['plugin'] = $plugin;
- $title = elgg_echo("admin:{$page[0]}");
+ $title = elgg_echo("admin:{$page[0]}");
+ } else {
+ forward('', '404');
+ }
} else {
$view = 'admin/' . implode('/', $page);
$title = elgg_echo("admin:{$page[0]}");
@@ -552,7 +562,7 @@ function admin_plugin_screenshot_page_handler($pages) {
* * COPYRIGHT.txt
* * LICENSE.txt
*
- * @param type $page
+ * @param array $pages
* @return bool
* @access private
*/
@@ -615,7 +625,11 @@ function admin_markdown_page_handler($pages) {
/**
* Adds default admin widgets to the admin dashboard.
*
- * @return void
+ * @param string $event
+ * @param string $type
+ * @param ElggUser $user
+ *
+ * @return null|true
* @access private
*/
function elgg_add_admin_widgets($event, $type, $user) {
@@ -637,6 +651,7 @@ function elgg_add_admin_widgets($event, $type, $user) {
$guid = elgg_create_widget($user->getGUID(), $handler, 'admin');
if ($guid) {
$widget = get_entity($guid);
+ /* @var ElggWidget $widget */
$widget->move($column, $position);
}
}
diff --git a/engine/lib/annotations.php b/engine/lib/annotations.php
index 3b9f84703..5e9b530de 100644
--- a/engine/lib/annotations.php
+++ b/engine/lib/annotations.php
@@ -17,6 +17,7 @@
*/
function row_to_elggannotation($row) {
if (!($row instanceof stdClass)) {
+ // @todo should throw in this case?
return $row;
}
@@ -30,7 +31,7 @@ function row_to_elggannotation($row) {
*
* @param int $id The id of the annotation object being retrieved.
*
- * @return false|ElggAnnotation
+ * @return ElggAnnotation|false
*/
function elgg_get_annotation_from_id($id) {
return elgg_get_metastring_based_object_from_id($id, 'annotations');
@@ -195,10 +196,22 @@ function update_annotation($annotation_id, $name, $value, $value_type, $owner_gu
* for the proper use of the "calculation" option.
*
*
- * @return mixed
+ * @return ElggAnnotation[]|mixed
* @since 1.8.0
*/
function elgg_get_annotations(array $options = array()) {
+
+ // @todo remove support for count shortcut - see #4393
+ if (isset($options['__egefac']) && $options['__egefac']) {
+ unset($options['__egefac']);
+ } else {
+ // support shortcut of 'count' => true for 'annotation_calculation' => 'count'
+ if (isset($options['count']) && $options['count']) {
+ $options['annotation_calculation'] = 'count';
+ unset($options['count']);
+ }
+ }
+
$options['metastring_type'] = 'annotations';
return elgg_get_metastring_based_objects($options);
}
@@ -211,7 +224,7 @@ function elgg_get_annotations(array $options = array()) {
* annotation_name(s), annotation_value(s), or guid(s) must be set.
*
* @param array $options An options array. {@See elgg_get_annotations()}
- * @return mixed Null if the metadata name is invalid. Bool on success or fail.
+ * @return bool|null true on success, false on failure, null if no annotations to delete.
* @since 1.8.0
*/
function elgg_delete_annotations(array $options) {
@@ -229,16 +242,20 @@ function elgg_delete_annotations(array $options) {
* @warning Unlike elgg_get_annotations() this will not accept an empty options array!
*
* @param array $options An options array. {@See elgg_get_annotations()}
- * @return mixed
+ * @return bool|null true on success, false on failure, null if no annotations disabled.
* @since 1.8.0
*/
function elgg_disable_annotations(array $options) {
if (!elgg_is_valid_options_for_batch_operation($options, 'annotations')) {
return false;
}
+
+ // if we can see hidden (disabled) we need to use the offset
+ // otherwise we risk an infinite loop if there are more than 50
+ $inc_offset = access_get_show_hidden_status();
$options['metastring_type'] = 'annotations';
- return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false);
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset);
}
/**
@@ -246,8 +263,11 @@ function elgg_disable_annotations(array $options) {
*
* @warning Unlike elgg_get_annotations() this will not accept an empty options array!
*
+ * @warning In order to enable annotations, you must first use
+ * {@link access_show_hidden_entities()}.
+ *
* @param array $options An options array. {@See elgg_get_annotations()}
- * @return mixed
+ * @return bool|null true on success, false on failure, null if no metadata enabled.
* @since 1.8.0
*/
function elgg_enable_annotations(array $options) {
@@ -403,8 +423,8 @@ function elgg_list_entities_from_annotations($options = array()) {
function elgg_get_entities_from_annotation_calculation($options) {
$db_prefix = elgg_get_config('dbprefix');
$defaults = array(
- 'calculation' => 'sum',
- 'order_by' => 'annotation_calculation desc'
+ 'calculation' => 'sum',
+ 'order_by' => 'annotation_calculation desc'
);
$options = array_merge($defaults, $options);
@@ -424,6 +444,10 @@ function elgg_get_entities_from_annotation_calculation($options) {
$options['callback'] = 'entity_row_to_elggstar';
+ // see #4393
+ // @todo remove after the 'count' shortcut is removed from elgg_get_annotations()
+ $options['__egefac'] = true;
+
return elgg_get_annotations($options);
}
@@ -437,23 +461,30 @@ function elgg_get_entities_from_annotation_calculation($options) {
* @return string
*/
function elgg_list_entities_from_annotation_calculation($options) {
+ $defaults = array(
+ 'calculation' => 'sum',
+ 'order_by' => 'annotation_calculation desc'
+ );
+ $options = array_merge($defaults, $options);
+
return elgg_list_entities($options, 'elgg_get_entities_from_annotation_calculation');
}
/**
- * Handler called by trigger_plugin_hook on the "export" event.
+ * Export the annotations for the specified entity
*
* @param string $hook 'export'
- * @param string $entity_type 'all'
+ * @param string $type 'all'
* @param mixed $returnvalue Default return value
- * @param mixed $params List of params to export
+ * @param mixed $params Parameters determining what annotations to export
*
* @elgg_plugin_hook export all
*
- * @return mixed
+ * @return array
+ * @throws InvalidParameterException
* @access private
*/
-function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $params) {
+function export_annotation_plugin_hook($hook, $type, $returnvalue, $params) {
// Sanity check values
if ((!is_array($params)) && (!isset($params['guid']))) {
throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport'));
@@ -464,12 +495,12 @@ function export_annotation_plugin_hook($hook, $entity_type, $returnvalue, $param
}
$guid = (int)$params['guid'];
- $name = $params['name'];
+ $options = array('guid' => $guid, 'limit' => 0);
+ if (isset($params['name'])) {
+ $options['annotation_name'] = $params['name'];
+ }
- $result = elgg_get_annotations(array(
- 'guid' => $guid,
- 'limit' => 0
- ));
+ $result = elgg_get_annotations($options);
if ($result) {
foreach ($result as $r) {
@@ -514,15 +545,16 @@ function elgg_annotation_exists($entity_guid, $annotation_type, $owner_guid = NU
return FALSE;
}
- $entity_guid = (int)$entity_guid;
- $annotation_type = sanitise_string($annotation_type);
+ $entity_guid = sanitize_int($entity_guid);
+ $owner_guid = sanitize_int($owner_guid);
+ $annotation_type = sanitize_string($annotation_type);
- $sql = "select a.id" .
- " FROM {$CONFIG->dbprefix}annotations a, {$CONFIG->dbprefix}metastrings m " .
- " WHERE a.owner_guid={$owner_guid} AND a.entity_guid={$entity_guid} " .
- " AND a.name_id=m.id AND m.string='{$annotation_type}'";
+ $sql = "SELECT a.id FROM {$CONFIG->dbprefix}annotations a" .
+ " JOIN {$CONFIG->dbprefix}metastrings m ON a.name_id = m.id" .
+ " WHERE a.owner_guid = $owner_guid AND a.entity_guid = $entity_guid" .
+ " AND m.string = '$annotation_type'";
- if ($check_annotation = get_data_row($sql)) {
+ if (get_data_row($sql)) {
return TRUE;
}
@@ -541,6 +573,7 @@ function elgg_comment_url_handler(ElggAnnotation $comment) {
if ($entity) {
return $entity->getURL() . '#item-annotation-' . $comment->id;
}
+ return "";
}
/**
@@ -557,6 +590,12 @@ function elgg_register_annotation_url_handler($extender_name = "all", $function_
/**
* Register annotation unit tests
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $value
+ * @param array $params
+ * @return array
* @access private
*/
function annotations_test($hook, $type, $value, $params) {
diff --git a/engine/lib/cache.php b/engine/lib/cache.php
index be1c43e14..3116c1a9b 100644
--- a/engine/lib/cache.php
+++ b/engine/lib/cache.php
@@ -125,7 +125,7 @@ function elgg_get_filepath_cache() {
* @access private
*/
function elgg_filepath_cache_reset() {
- return elgg_reset_system_cache();
+ elgg_reset_system_cache();
}
/**
* @access private
@@ -143,13 +143,13 @@ function elgg_filepath_cache_load($type) {
* @access private
*/
function elgg_enable_filepath_cache() {
- return elgg_enable_system_cache();
+ elgg_enable_system_cache();
}
/**
* @access private
*/
function elgg_disable_filepath_cache() {
- return elgg_disable_system_cache();
+ elgg_disable_system_cache();
}
/* Simplecache */
@@ -208,6 +208,7 @@ function elgg_get_simplecache_url($type, $view) {
global $CONFIG;
$lastcache = (int)$CONFIG->lastcache;
$viewtype = elgg_get_viewtype();
+ elgg_register_simplecache_view("$type/$view");// see #5302
if (elgg_is_simplecache_enabled()) {
$url = elgg_get_site_url() . "cache/$type/$viewtype/$view.$lastcache.$type";
} else {
@@ -222,7 +223,7 @@ function elgg_get_simplecache_url($type, $view) {
/**
* Regenerates the simple cache.
*
- * @warning This does not invalidate the cache, but actively resets it.
+ * @warning This does not invalidate the cache, but actively rebuilds it.
*
* @param string $viewtype Optional viewtype to regenerate. Defaults to all valid viewtypes.
*
@@ -444,7 +445,7 @@ function _elgg_cache_init() {
if ($CONFIG->system_cache_enabled && !$CONFIG->i18n_loaded_from_cache) {
reload_all_translations();
foreach ($CONFIG->translations as $lang => $map) {
- elgg_save_system_cache("$lang.php", serialize($map));
+ elgg_save_system_cache("$lang.lang", serialize($map));
}
}
}
diff --git a/engine/lib/calendar.php b/engine/lib/calendar.php
index 9a06c5292..e6f95934c 100644
--- a/engine/lib/calendar.php
+++ b/engine/lib/calendar.php
@@ -39,6 +39,8 @@ function get_day_end($day = null, $month = null, $year = null) {
/**
* Return the notable entities for a given time period.
*
+ * @todo this function also accepts an array(type => subtypes) for 3rd arg. Should we document this?
+ *
* @param int $start_time The start time as a unix timestamp.
* @param int $end_time The end time as a unix timestamp.
* @param string $type The type of entity (eg "user", "object" etc)
diff --git a/engine/lib/configuration.php b/engine/lib/configuration.php
index b10e51130..55e5bbd36 100644
--- a/engine/lib/configuration.php
+++ b/engine/lib/configuration.php
@@ -36,6 +36,7 @@ function elgg_get_site_url($site_guid = 0) {
if (!$site instanceof ElggSite) {
return false;
}
+ /* @var ElggSite $site */
return $site->url;
}
@@ -138,7 +139,7 @@ function elgg_set_config($name, $value) {
/**
* Save a configuration setting
*
- * @param string $name Configuration name (cannot be greater than 32 characters)
+ * @param string $name Configuration name (cannot be greater than 255 characters)
* @param mixed $value Configuration value. Should be string for installation setting
* @param int $site_guid NULL for installation setting, 0 for default site
*
@@ -173,7 +174,7 @@ function elgg_save_config($name, $value, $site_guid = 0) {
/**
* Check that installation has completed and the database is populated.
*
- * @throws InstallationException
+ * @throws InstallationException|DatabaseException
* @return void
* @access private
*/
@@ -181,7 +182,7 @@ function verify_installation() {
global $CONFIG;
if (isset($CONFIG->installed)) {
- return $CONFIG->installed;
+ return;
}
try {
@@ -227,9 +228,9 @@ function datalist_get($name) {
$name = trim($name);
- // cannot store anything longer than 32 characters in db, so catch here
- if (elgg_strlen($name) > 32) {
- elgg_log("The name length for configuration variables cannot be greater than 32", "ERROR");
+ // cannot store anything longer than 255 characters in db, so catch here
+ if (elgg_strlen($name) > 255) {
+ elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR");
return false;
}
@@ -286,7 +287,7 @@ function datalist_get($name) {
function datalist_set($name, $value) {
global $CONFIG, $DATALIST_CACHE;
- // cannot store anything longer than 32 characters in db, so catch before we set
+ // cannot store anything longer than 255 characters in db, so catch before we set
if (elgg_strlen($name) > 255) {
elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR");
return false;
@@ -332,7 +333,7 @@ function datalist_set($name, $value) {
* This will cause the run once function to be run on all installations. To perform
* additional upgrades, create new functions for each release.
*
- * @warning The function name cannot be longer than 32 characters long due to
+ * @warning The function name cannot be longer than 255 characters long due to
* the current schema for the datalist table.
*
* @internal A datalist entry $functioname is created with the value of time().
@@ -407,7 +408,7 @@ function unset_config($name, $site_guid = 0) {
* @param string $value Its value
* @param int $site_guid Optionally, the GUID of the site (current site is assumed by default)
*
- * @return 0
+ * @return bool
* @todo The config table doens't have numeric primary keys so insert_data returns 0.
* @todo Use "INSERT ... ON DUPLICATE KEY UPDATE" instead of trying to delete then add.
* @see unset_config()
@@ -419,9 +420,9 @@ function set_config($name, $value, $site_guid = 0) {
$name = trim($name);
- // cannot store anything longer than 32 characters in db, so catch before we set
- if (elgg_strlen($name) > 32) {
- elgg_log("The name length for configuration variables cannot be greater than 32", "ERROR");
+ // cannot store anything longer than 255 characters in db, so catch before we set
+ if (elgg_strlen($name) > 255) {
+ elgg_log("The name length for configuration variables cannot be greater than 255", "ERROR");
return false;
}
@@ -485,9 +486,9 @@ function get_config($name, $site_guid = 0) {
// @todo these haven't really been implemented in Elgg 1.8. Complete in 1.9.
// show dep message
if ($new_name) {
- // $msg = "Config value $name has been renamed as $new_name";
+ // $msg = "Config value $name has been renamed as $new_name";
$name = $new_name;
- // elgg_deprecated_notice($msg, $dep_version);
+ // elgg_deprecated_notice($msg, $dep_version);
}
// decide from where to return the value
diff --git a/engine/lib/cron.php b/engine/lib/cron.php
index f7a032f4a..4f3d05b93 100644
--- a/engine/lib/cron.php
+++ b/engine/lib/cron.php
@@ -26,11 +26,10 @@ function cron_init() {
* @param array $page Pages
*
* @return bool
+ * @throws CronException
* @access private
*/
function cron_page_handler($page) {
- global $CONFIG;
-
if (!isset($page[0])) {
forward();
}
@@ -51,7 +50,6 @@ function cron_page_handler($page) {
$params['time'] = time();
// Data to return to
- $std_out = "";
$old_stdout = "";
ob_start();
diff --git a/engine/lib/database.php b/engine/lib/database.php
index 7d90b30b8..a7949788d 100644
--- a/engine/lib/database.php
+++ b/engine/lib/database.php
@@ -12,15 +12,19 @@
/**
* Query cache for all queries.
*
- * Each query and its results are stored in this array as:
+ * Each query and its results are stored in this cache as:
* <code>
- * $DB_QUERY_CACHE[$query] => array(result1, result2, ... resultN)
+ * $DB_QUERY_CACHE[query hash] => array(result1, result2, ... resultN)
* </code>
+ * @see elgg_query_runner() for details on the hash.
*
- * @global array $DB_QUERY_CACHE
+ * @warning Elgg used to set this as an empty array to turn off the cache
+ *
+ * @global ElggLRUCache|null $DB_QUERY_CACHE
+ * @access private
*/
global $DB_QUERY_CACHE;
-$DB_QUERY_CACHE = array();
+$DB_QUERY_CACHE = null;
/**
* Queries to be executed upon shutdown.
@@ -38,6 +42,7 @@ $DB_QUERY_CACHE = array();
* </code>
*
* @global array $DB_DELAYED_QUERIES
+ * @access private
*/
global $DB_DELAYED_QUERIES;
$DB_DELAYED_QUERIES = array();
@@ -48,7 +53,8 @@ $DB_DELAYED_QUERIES = array();
* Each database link created with establish_db_link($name) is stored in
* $dblink as $dblink[$name] => resource. Use get_db_link($name) to retrieve it.
*
- * @global array $dblink
+ * @global resource[] $dblink
+ * @access private
*/
global $dblink;
$dblink = array();
@@ -59,6 +65,7 @@ $dblink = array();
* Each call to the database increments this counter.
*
* @global integer $dbcalls
+ * @access private
*/
global $dbcalls;
$dbcalls = 0;
@@ -72,11 +79,12 @@ $dbcalls = 0;
* resource. eg "read", "write", or "readwrite".
*
* @return void
+ * @throws DatabaseException
* @access private
*/
function establish_db_link($dblinkname = "readwrite") {
// Get configuration, and globalise database link
- global $CONFIG, $dblink, $DB_QUERY_CACHE, $dbcalls;
+ global $CONFIG, $dblink, $DB_QUERY_CACHE;
if ($dblinkname != "readwrite" && isset($CONFIG->db[$dblinkname])) {
if (is_array($CONFIG->db[$dblinkname])) {
@@ -120,7 +128,8 @@ function establish_db_link($dblinkname = "readwrite") {
// Set up cache if global not initialized and query cache not turned off
if ((!$DB_QUERY_CACHE) && (!$db_cache_off)) {
- $DB_QUERY_CACHE = new ElggStaticVariableCache('db_query_cache');
+ // @todo if we keep this cache in 1.9, expose the size as a config parameter
+ $DB_QUERY_CACHE = new ElggLRUCache(200);
}
}
@@ -134,7 +143,7 @@ function establish_db_link($dblinkname = "readwrite") {
* @access private
*/
function setup_db_connections() {
- global $CONFIG, $dblink;
+ global $CONFIG;
if (!empty($CONFIG->db->split)) {
establish_db_link('read');
@@ -197,7 +206,7 @@ function db_delayedexecution_shutdown_hook() {
*
* @param string $dblinktype The type of link we want: "read", "write" or "readwrite".
*
- * @return object Database link
+ * @return resource Database link
* @access private
*/
function get_db_link($dblinktype) {
@@ -216,7 +225,7 @@ function get_db_link($dblinktype) {
/**
* Execute an EXPLAIN for $query.
*
- * @param str $query The query to explain
+ * @param string $query The query to explain
* @param mixed $link The database link resource to user.
*
* @return mixed An object of the query's result, or FALSE
@@ -240,14 +249,14 @@ function explain_query($query, $link) {
* {@link $dbcalls} is incremented and the query is saved into the {@link $DB_QUERY_CACHE}.
*
* @param string $query The query
- * @param link $dblink The DB link
+ * @param resource $dblink The DB link
*
- * @return The result of mysql_query()
+ * @return resource result of mysql_query()
* @throws DatabaseException
* @access private
*/
function execute_query($query, $dblink) {
- global $CONFIG, $dbcalls;
+ global $dbcalls;
if ($query == NULL) {
throw new DatabaseException(elgg_echo('DatabaseException:InvalidQuery'));
@@ -275,7 +284,7 @@ function execute_query($query, $dblink) {
* the raw result from {@link mysql_query()}.
*
* @param string $query The query to execute
- * @param resource $dblink The database link to use or the link type (read | write)
+ * @param resource|string $dblink The database link to use or the link type (read | write)
* @param string $handler A callback function to pass the results array to
*
* @return true
@@ -386,20 +395,18 @@ function get_data_row($query, $callback = "") {
* @access private
*/
function elgg_query_runner($query, $callback = null, $single = false) {
- global $CONFIG, $DB_QUERY_CACHE;
+ global $DB_QUERY_CACHE;
// Since we want to cache results of running the callback, we need to
// need to namespace the query with the callback and single result request.
- // http://trac.elgg.org/ticket/4049
+ // https://github.com/elgg/elgg/issues/4049
$hash = (string)$callback . (int)$single . $query;
// Is cached?
if ($DB_QUERY_CACHE) {
- $cached_query = $DB_QUERY_CACHE[$hash];
-
- if ($cached_query !== FALSE) {
+ if (isset($DB_QUERY_CACHE[$hash])) {
elgg_log("DB query $query results returned from cache (hash: $hash)", 'NOTICE');
- return $cached_query;
+ return $DB_QUERY_CACHE[$hash];
}
}
@@ -410,7 +417,7 @@ function elgg_query_runner($query, $callback = null, $single = false) {
// test for callback once instead of on each iteration.
// @todo check profiling to see if this needs to be broken out into
- // explicit cases instead of checking in the interation.
+ // explicit cases instead of checking in the iteration.
$is_callable = is_callable($callback);
while ($row = mysql_fetch_object($result)) {
if ($is_callable) {
@@ -451,18 +458,12 @@ function elgg_query_runner($query, $callback = null, $single = false) {
* @access private
*/
function insert_data($query) {
- global $CONFIG, $DB_QUERY_CACHE;
elgg_log("DB query $query", 'NOTICE');
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE->clear();
- }
-
- elgg_log("Query cache invalidated", 'NOTICE');
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
return mysql_insert_id($dblink);
@@ -472,7 +473,7 @@ function insert_data($query) {
}
/**
- * Update a row in the database.
+ * Update the database.
*
* @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
*
@@ -482,17 +483,12 @@ function insert_data($query) {
* @access private
*/
function update_data($query) {
- global $CONFIG, $DB_QUERY_CACHE;
elgg_log("DB query $query", 'NOTICE');
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE->clear();
- elgg_log("Query cache invalidated", 'NOTICE');
- }
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
return TRUE;
@@ -502,7 +498,7 @@ function update_data($query) {
}
/**
- * Remove a row from the database.
+ * Remove data from the database.
*
* @note Altering the DB invalidates all queries in {@link $DB_QUERY_CACHE}.
*
@@ -512,17 +508,12 @@ function update_data($query) {
* @access private
*/
function delete_data($query) {
- global $CONFIG, $DB_QUERY_CACHE;
elgg_log("DB query $query", 'NOTICE');
$dblink = get_db_link('write');
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE->clear();
- elgg_log("Query cache invalidated", 'NOTICE');
- }
+ _elgg_invalidate_query_cache();
if (execute_query("$query", $dblink)) {
return mysql_affected_rows($dblink);
@@ -531,6 +522,22 @@ function delete_data($query) {
return FALSE;
}
+/**
+ * Invalidate the query cache
+ *
+ * @access private
+ */
+function _elgg_invalidate_query_cache() {
+ global $DB_QUERY_CACHE;
+ if ($DB_QUERY_CACHE instanceof ElggLRUCache) {
+ $DB_QUERY_CACHE->clear();
+ elgg_log("Query cache invalidated", 'NOTICE');
+ } elseif ($DB_QUERY_CACHE) {
+ // In case someone sets the cache to an array and primes it with data
+ $DB_QUERY_CACHE = array();
+ elgg_log("Query cache invalidated", 'NOTICE');
+ }
+}
/**
* Return tables matching the database prefix {@link $CONFIG->dbprefix}% in the currently
@@ -638,7 +645,7 @@ function run_sql_script($scriptlocation) {
$statement = str_replace("prefix_", $CONFIG->dbprefix, $statement);
if (!empty($statement)) {
try {
- $result = update_data($statement);
+ update_data($statement);
} catch (DatabaseException $e) {
$errors[] = $e->getMessage();
}
@@ -661,7 +668,7 @@ function run_sql_script($scriptlocation) {
/**
* Format a query string for logging
- *
+ *
* @param string $query Query string
* @return string
* @access private
diff --git a/engine/lib/deprecated-1.7.php b/engine/lib/deprecated-1.7.php
index 519eea89d..ee95b5611 100644
--- a/engine/lib/deprecated-1.7.php
+++ b/engine/lib/deprecated-1.7.php
@@ -1137,6 +1137,7 @@ function make_register_object($register_name, $register_value, $children_array =
* @param int $guid GUID
*
* @return 1
+ * @deprecated 1.7
*/
function delete_object_entity($guid) {
system_message(elgg_echo('deprecatedfunction', array('delete_user_entity')));
@@ -1154,6 +1155,7 @@ function delete_object_entity($guid) {
* @param int $guid User GUID
*
* @return 1
+ * @deprecated 1.7
*/
function delete_user_entity($guid) {
system_message(elgg_echo('deprecatedfunction', array('delete_user_entity')));
diff --git a/engine/lib/deprecated-1.8.php b/engine/lib/deprecated-1.8.php
index 4b9d41543..91068d047 100644
--- a/engine/lib/deprecated-1.8.php
+++ b/engine/lib/deprecated-1.8.php
@@ -87,7 +87,7 @@ function list_entities_from_access_id($access_id, $entity_type = "", $entity_sub
elgg_deprecated_notice("All list_entities* functions were deprecated in 1.8. Use elgg_list_entities* instead.", 1.8);
echo elgg_list_entities_from_access_id(array('access_id' => $access_id,
- 'types' => $entity_type, 'subtypes' => $entity_subtype, 'owner_guids' => $owner_guid,
+ 'type' => $entity_type, 'subtype' => $entity_subtype, 'owner_guids' => $owner_guid,
'limit' => $limit, 'full_view' => $fullview, 'list_type_toggle' => $listtypetoggle,
'pagination' => $pagination,));
}
@@ -1314,8 +1314,8 @@ function list_entities_from_metadata($meta_name, $meta_value = "", $entity_type
$options = array(
'metadata_name' => $meta_name,
'metadata_value' => $meta_value,
- 'types' => $entity_type,
- 'subtypes' => $entity_subtype,
+ 'type' => $entity_type,
+ 'subtype' => $entity_subtype,
'limit' => $limit,
'offset' => $offset,
'count' => TRUE,
@@ -2120,8 +2120,8 @@ $fullview = true, $listtypetoggle = false, $pagination = true, $order_by = '') {
'relationship' => $relationship,
'relationship_guid' => $relationship_guid,
'inverse_relationship' => $inverse_relationship,
- 'types' => $type,
- 'subtypes' => $subtype,
+ 'type' => $type,
+ 'subtype' => $subtype,
'owner_guid' => $owner_guid,
'order_by' => $order_by,
'limit' => $limit,
@@ -2566,9 +2566,9 @@ $owner_guid = "", $owner_relationship = "") {
'relationship' => $owner_relationship,
'relationship_guid' => $owner_guid[0],
'inverse_relationship' => FALSE,
- 'types' => 'user',
- 'subtypes' => $subtype,
- 'limit' => 9999))
+ 'type' => 'user',
+ 'subtype' => $subtype,
+ 'limit' => false))
) {
$friendsarray = array();
@@ -2721,8 +2721,8 @@ function get_site_collections($site_guid, $subtype = "", $limit = 10, $offset =
'relationship' => 'member_of_site',
'relationship_guid' => $site_guid,
'inverse_relationship' => TRUE,
- 'types' => 'collection',
- 'subtypes' => $subtype,
+ 'type' => 'collection',
+ 'subtype' => $subtype,
'limit' => $limit,
'offset' => $offset
));
@@ -3414,6 +3414,7 @@ function list_annotations($entity_guid, $name = "", $limit = 25, $asc = true) {
* @param unknown_type $timeupper
* @param unknown_type $calculation
* @internal Don't use this at all.
+ * @deprecated 1.8 Use elgg_get_annotations()
*/
function elgg_deprecated_annotation_calculation($entity_guid = 0, $entity_type = "", $entity_subtype = "",
$name = "", $value = "", $value_type = "", $owner_guid = 0, $timelower = 0,
@@ -4667,6 +4668,7 @@ function display_widget(ElggObject $widget) {
*
* @param ElggEntity $entity
* @return int Number of comments
+ * @deprecated 1.8 Use ElggEntity->countComments()
*/
function elgg_count_comments($entity) {
elgg_deprecated_notice('elgg_count_comments() is deprecated by ElggEntity->countComments()', 1.8);
@@ -4772,3 +4774,47 @@ function default_page_handler($page, $handler) {
return FALSE;
}
+
+/**
+ * Invalidate this class's entry in the cache.
+ *
+ * @param int $guid The entity guid
+ *
+ * @return void
+ * @access private
+ * @deprecated 1.8
+ */
+function invalidate_cache_for_entity($guid) {
+ elgg_deprecated_notice('invalidate_cache_for_entity() is a private function and should not be used.', 1.8);
+ _elgg_invalidate_cache_for_entity($guid);
+}
+
+/**
+ * Cache an entity.
+ *
+ * Stores an entity in $ENTITY_CACHE;
+ *
+ * @param ElggEntity $entity Entity to cache
+ *
+ * @return void
+ * @access private
+ * @deprecated 1.8
+ */
+function cache_entity(ElggEntity $entity) {
+ elgg_deprecated_notice('cache_entity() is a private function and should not be used.', 1.8);
+ _elgg_cache_entity($entity);
+}
+
+/**
+ * Retrieve a entity from the cache.
+ *
+ * @param int $guid The guid
+ *
+ * @return ElggEntity|bool false if entity not cached, or not fully loaded
+ * @access private
+ * @deprecated 1.8
+ */
+function retrieve_cached_entity($guid) {
+ elgg_deprecated_notice('retrieve_cached_entity() is a private function and should not be used.', 1.8);
+ return _elgg_retrieve_cached_entity($guid);
+}
diff --git a/engine/lib/elgglib.php b/engine/lib/elgglib.php
index 540605876..34111c69d 100644
--- a/engine/lib/elgglib.php
+++ b/engine/lib/elgglib.php
@@ -93,10 +93,17 @@ function elgg_register_library($name, $location) {
* @return void
* @throws InvalidParameterException
* @since 1.8.0
+ * @todo return boolean in 1.9 to indicate whether the library has been loaded
*/
function elgg_load_library($name) {
global $CONFIG;
+ static $loaded_libraries = array();
+
+ if (in_array($name, $loaded_libraries)) {
+ return;
+ }
+
if (!isset($CONFIG->libraries)) {
$CONFIG->libraries = array();
}
@@ -113,6 +120,8 @@ function elgg_load_library($name) {
);
throw new InvalidParameterException($error);
}
+
+ $loaded_libraries[] = $name;
}
/**
@@ -124,12 +133,11 @@ function elgg_load_library($name) {
* @param string $location URL to forward to browser to. Can be path relative to the network's URL.
* @param string $reason Short explanation for why we're forwarding
*
- * @return False False if headers have been sent. Terminates execution if forwarding.
+ * @return false False if headers have been sent. Terminates execution if forwarding.
+ * @throws SecurityException
*/
function forward($location = "", $reason = 'system') {
- global $CONFIG;
-
- if (!headers_sent()) {
+ if (!headers_sent($file, $line)) {
if ($location === REFERER) {
$location = $_SERVER['HTTP_REFERER'];
}
@@ -148,7 +156,7 @@ function forward($location = "", $reason = 'system') {
exit;
}
} else {
- throw new SecurityException(elgg_echo('SecurityException:ForwardFailedToRedirect'));
+ throw new SecurityException(elgg_echo('SecurityException:ForwardFailedToRedirect', array($file, $line)));
}
}
@@ -384,7 +392,7 @@ function elgg_load_external_file($type, $name) {
$item->url = '';
$item->location = '';
- $priority = $CONFIG->externals[$type]->add($item);
+ $CONFIG->externals[$type]->add($item);
$CONFIG->externals_map[$type][$name] = $item;
}
}
@@ -528,7 +536,7 @@ function sanitise_filepath($path, $append_slash = TRUE) {
* @param string $register Types of message: "error", "success" (default: success)
* @param bool $count Count the number of messages (default: false)
*
- * @return true|false|array Either the array of messages, or a response regarding
+ * @return bool|array Either the array of messages, or a response regarding
* whether the message addition was successful.
* @todo Clean up. Separate registering messages and retrieving them.
*/
@@ -562,7 +570,7 @@ function system_messages($message = null, $register = "success", $count = false)
return sizeof($_SESSION['msg'][$register]);
} else {
$count = 0;
- foreach ($_SESSION['msg'] as $register => $submessages) {
+ foreach ($_SESSION['msg'] as $submessages) {
$count += sizeof($submessages);
}
return $count;
@@ -738,7 +746,7 @@ function elgg_unregister_event_handler($event, $object_type, $callback) {
* @tip When referring to events, the preferred syntax is "event, type".
*
* @internal Only rarely should events be changed, added, or removed in core.
- * When making changes to events, be sure to first create a ticket in trac.
+ * When making changes to events, be sure to first create a ticket on Github.
*
* @internal @tip Think of $object_type as the primary namespace element, and
* $event as the secondary namespace.
@@ -839,7 +847,7 @@ function elgg_trigger_event($event, $object_type, $object = null) {
*
* @param string $hook The name of the hook
* @param string $type The type of the hook
- * @param callback $callback The name of a valid function or an array with object and method
+ * @param callable $callback The name of a valid function or an array with object and method
* @param int $priority The priority - 500 is default, lower numbers called first
*
* @return bool
@@ -885,7 +893,7 @@ function elgg_register_plugin_hook_handler($hook, $type, $callback, $priority =
*
* @param string $hook The name of the hook
* @param string $entity_type The name of the type of entity (eg "user", "object" etc)
- * @param callback $callback The PHP callback to be removed
+ * @param callable $callback The PHP callback to be removed
*
* @return void
* @since 1.8.0
@@ -1060,6 +1068,7 @@ function _elgg_php_exception_handler($exception) {
* @param array $vars An array that points to the active symbol table where error occurred
*
* @return true
+ * @throws Exception
* @access private
* @todo Replace error_log calls with elgg_log calls.
*/
@@ -1185,6 +1194,11 @@ function elgg_dump($value, $to_screen = TRUE, $level = 'NOTICE') {
$to_screen = FALSE;
}
+ // Do not want to write to JS or CSS pages
+ if (elgg_in_context('js') || elgg_in_context('css')) {
+ $to_screen = FALSE;
+ }
+
if ($to_screen == TRUE) {
echo '<pre>';
print_r($value);
@@ -1292,8 +1306,6 @@ function elgg_deprecated_notice($msg, $dep_version, $backtrace_level = 1) {
* @return string The current page URL.
*/
function current_page_url() {
- global $CONFIG;
-
$url = parse_url(elgg_get_site_url());
$page = $url['scheme'] . "://";
@@ -1338,7 +1350,7 @@ function full_url() {
"" : (":" . $_SERVER["SERVER_PORT"]);
// This is here to prevent XSS in poorly written browsers used by 80% of the population.
- // {@trac [5813]}
+ // https://github.com/Elgg/Elgg/commit/0c947e80f512cb0a482b1864fd0a6965c8a0cd4a
$quotes = array('\'', '"');
$encoded = array('%27', '%22');
@@ -1354,7 +1366,7 @@ function full_url() {
* @param array $parts Associative array of URL components like parse_url() returns
* @param bool $html_encode HTML Encode the url?
*
- * @return str Full URL
+ * @return string Full URL
* @since 1.7.0
*/
function elgg_http_build_url(array $parts, $html_encode = TRUE) {
@@ -1385,10 +1397,10 @@ function elgg_http_build_url(array $parts, $html_encode = TRUE) {
* add tokens to the action. The form view automatically handles
* tokens.
*
- * @param str $url Full action URL
- * @param bool $html_encode HTML encode the url? (default: false)
+ * @param string $url Full action URL
+ * @param bool $html_encode HTML encode the url? (default: false)
*
- * @return str URL with action tokens
+ * @return string URL with action tokens
* @since 1.7.0
* @link http://docs.elgg.org/Tutorials/Actions
*/
@@ -1440,17 +1452,17 @@ function elgg_http_remove_url_query_element($url, $element) {
}
$url_array['query'] = http_build_query($query);
- $string = elgg_http_build_url($url_array);
+ $string = elgg_http_build_url($url_array, false);
return $string;
}
/**
* Adds an element or elements to a URL's query string.
*
- * @param str $url The URL
- * @param array $elements Key/value pairs to add to the URL
+ * @param string $url The URL
+ * @param array $elements Key/value pairs to add to the URL
*
- * @return str The new URL with the query strings added
+ * @return string The new URL with the query strings added
* @since 1.7.0
*/
function elgg_http_add_url_query_elements($url, array $elements) {
@@ -1487,8 +1499,6 @@ function elgg_http_add_url_query_elements($url, array $elements) {
* @since 1.8.0
*/
function elgg_http_url_is_identical($url1, $url2, $ignore_params = array('offset', 'limit')) {
- global $CONFIG;
-
// if the server portion is missing but it starts with / then add the url in.
// @todo use elgg_normalize_url()
if (elgg_substr($url1, 0, 1) == '/') {
@@ -1627,7 +1637,7 @@ $sort_type = SORT_LOCALE_STRING) {
$sort = array();
- foreach ($array as $k => $v) {
+ foreach ($array as $v) {
if (isset($v[$element])) {
$sort[] = strtolower($v[$element]);
} else {
@@ -1646,7 +1656,7 @@ $sort_type = SORT_LOCALE_STRING) {
*
* @param string $ini_get_arg The INI setting
*
- * @return true|false Depending on whether it's on or off
+ * @return bool Depending on whether it's on or off
*/
function ini_get_bool($ini_get_arg) {
$temp = strtolower(ini_get($ini_get_arg));
@@ -1662,7 +1672,7 @@ function ini_get_bool($ini_get_arg) {
*
* @tip Use this for arithmetic when determining if a file can be uploaded.
*
- * @param str $setting The php.ini setting
+ * @param string $setting The php.ini setting
*
* @return int
* @since 1.7.0
@@ -1677,8 +1687,10 @@ function elgg_get_ini_setting_in_bytes($setting) {
switch($last) {
case 'g':
$val *= 1024;
+ // fallthrough intentional
case 'm':
$val *= 1024;
+ // fallthrough intentional
case 'k':
$val *= 1024;
}
@@ -1835,7 +1847,7 @@ function elgg_ajax_page_handler($page) {
*
* @param array $page The page array
*
- * @return void
+ * @return bool
* @elgg_pagehandler css
* @access private
*/
@@ -1899,6 +1911,7 @@ function elgg_cacheable_view_page_handler($page, $type) {
echo $return;
return true;
}
+ return false;
}
/**
@@ -2220,7 +2233,7 @@ function elgg_init() {
* @param array $params empty
*
* @elgg_plugin_hook unit_tests system
- * @return void
+ * @return array
* @access private
*/
function elgg_api_test($hook, $type, $value, $params) {
@@ -2232,7 +2245,10 @@ function elgg_api_test($hook, $type, $value, $params) {
}
/**#@+
- * Controlls access levels on ElggEntity entities, metadata, and annotations.
+ * Controls access levels on ElggEntity entities, metadata, and annotations.
+ *
+ * @warning ACCESS_DEFAULT is a place holder for the input/access view. Do not
+ * use it when saving an entity.
*
* @var int
*/
@@ -2266,7 +2282,7 @@ define('ELGG_ENTITIES_NO_VALUE', 0);
* referring page.
*
* @see forward
- * @var unknown_type
+ * @var int -1
*/
define('REFERRER', -1);
diff --git a/engine/lib/entities.php b/engine/lib/entities.php
index ce736ce05..4fcf1c657 100644
--- a/engine/lib/entities.php
+++ b/engine/lib/entities.php
@@ -17,6 +17,15 @@ global $ENTITY_CACHE;
$ENTITY_CACHE = array();
/**
+ * GUIDs of entities banned from the entity cache (during this request)
+ *
+ * @global array $ENTITY_CACHE_DISABLED_GUIDS
+ * @access private
+ */
+global $ENTITY_CACHE_DISABLED_GUIDS;
+$ENTITY_CACHE_DISABLED_GUIDS = array();
+
+/**
* Cache subtypes and related class names.
*
* @global array|null $SUBTYPE_CACHE array once populated from DB, initially null
@@ -26,14 +35,42 @@ global $SUBTYPE_CACHE;
$SUBTYPE_CACHE = null;
/**
+ * Remove this entity from the entity cache and make sure it is not re-added
+ *
+ * @param int $guid The entity guid
+ *
+ * @access private
+ * @todo this is a workaround until #5604 can be implemented
+ */
+function _elgg_disable_caching_for_entity($guid) {
+ global $ENTITY_CACHE_DISABLED_GUIDS;
+
+ _elgg_invalidate_cache_for_entity($guid);
+ $ENTITY_CACHE_DISABLED_GUIDS[$guid] = true;
+}
+
+/**
+ * Allow this entity to be stored in the entity cache
+ *
+ * @param int $guid The entity guid
+ *
+ * @access private
+ */
+function _elgg_enable_caching_for_entity($guid) {
+ global $ENTITY_CACHE_DISABLED_GUIDS;
+
+ unset($ENTITY_CACHE_DISABLED_GUIDS[$guid]);
+}
+
+/**
* Invalidate this class's entry in the cache.
*
* @param int $guid The entity guid
*
- * @return null
+ * @return void
* @access private
*/
-function invalidate_cache_for_entity($guid) {
+function _elgg_invalidate_cache_for_entity($guid) {
global $ENTITY_CACHE;
$guid = (int)$guid;
@@ -50,14 +87,14 @@ function invalidate_cache_for_entity($guid) {
*
* @param ElggEntity $entity Entity to cache
*
- * @return null
- * @see retrieve_cached_entity()
- * @see invalidate_cache_for_entity()
+ * @return void
+ * @see _elgg_retrieve_cached_entity()
+ * @see _elgg_invalidate_cache_for_entity()
* @access private
- * TODO(evan): Use an ElggCache object
+ * @todo Use an ElggCache object
*/
-function cache_entity(ElggEntity $entity) {
- global $ENTITY_CACHE;
+function _elgg_cache_entity(ElggEntity $entity) {
+ global $ENTITY_CACHE, $ENTITY_CACHE_DISABLED_GUIDS;
// Don't cache non-plugin entities while access control is off, otherwise they could be
// exposed to users who shouldn't see them when control is re-enabled.
@@ -65,8 +102,13 @@ function cache_entity(ElggEntity $entity) {
return;
}
+ $guid = $entity->getGUID();
+ if (isset($ENTITY_CACHE_DISABLED_GUIDS[$guid])) {
+ return;
+ }
+
// Don't store too many or we'll have memory problems
- // TODO(evan): Pick a less arbitrary limit
+ // @todo Pick a less arbitrary limit
if (count($ENTITY_CACHE) > 256) {
$random_guid = array_rand($ENTITY_CACHE);
@@ -79,7 +121,7 @@ function cache_entity(ElggEntity $entity) {
elgg_get_metadata_cache()->clear($random_guid);
}
- $ENTITY_CACHE[$entity->guid] = $entity;
+ $ENTITY_CACHE[$guid] = $entity;
}
/**
@@ -88,11 +130,11 @@ function cache_entity(ElggEntity $entity) {
* @param int $guid The guid
*
* @return ElggEntity|bool false if entity not cached, or not fully loaded
- * @see cache_entity()
- * @see invalidate_cache_for_entity()
+ * @see _elgg_cache_entity()
+ * @see _elgg_invalidate_cache_for_entity()
* @access private
*/
-function retrieve_cached_entity($guid) {
+function _elgg_retrieve_cached_entity($guid) {
global $ENTITY_CACHE;
if (isset($ENTITY_CACHE[$guid])) {
@@ -105,31 +147,6 @@ function retrieve_cached_entity($guid) {
}
/**
- * As retrieve_cached_entity, but returns the result as a stdClass
- * (compatible with load functions that expect a database row.)
- *
- * @param int $guid The guid
- *
- * @return mixed
- * @todo unused
- * @access private
- */
-function retrieve_cached_entity_row($guid) {
- $obj = retrieve_cached_entity($guid);
- if ($obj) {
- $tmp = new stdClass;
-
- foreach ($obj as $k => $v) {
- $tmp->$k = $v;
- }
-
- return $tmp;
- }
-
- return false;
-}
-
-/**
* Return the id for a given subtype.
*
* ElggEntity objects have a type and a subtype. Subtypes
@@ -432,7 +449,7 @@ function update_subtype($type, $subtype, $class = '') {
* @param int $time_created The time creation timestamp
*
* @return bool
- * @link http://docs.elgg.org/DataModel/Entities
+ * @throws InvalidParameterException
* @access private
*/
function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $time_created = null) {
@@ -455,6 +472,10 @@ function update_entity($guid, $owner_guid, $access_id, $container_guid = null, $
$time_created = (int) $time_created;
}
+ if ($access_id == ACCESS_DEFAULT) {
+ throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
+ }
+
if ($entity && $entity->canEdit()) {
if (elgg_trigger_event('update', $entity->type, $entity)) {
$ret = update_data("UPDATE {$CONFIG->dbprefix}entities
@@ -531,6 +552,7 @@ function can_write_to_container($user_guid = 0, $container_guid = 0, $type = 'al
// If still not approved, see if the user is a member of the group
// @todo this should be moved to the groups plugin/library
if (!$return && $user && $container instanceof ElggGroup) {
+ /* @var ElggGroup $container */
if ($container->isMember($user)) {
$return = true;
}
@@ -580,7 +602,6 @@ $container_guid = 0) {
$type = sanitise_string($type);
$subtype_id = add_subtype($type, $subtype);
$owner_guid = (int)$owner_guid;
- $access_id = (int)$access_id;
$time = time();
if ($site_guid == 0) {
$site_guid = $CONFIG->site_guid;
@@ -589,6 +610,10 @@ $container_guid = 0) {
if ($container_guid == 0) {
$container_guid = $owner_guid;
}
+ $access_id = (int)$access_id;
+ if ($access_id == ACCESS_DEFAULT) {
+ throw new InvalidParameterException('ACCESS_DEFAULT is not a valid access level. See its documentation in elgglib.h');
+ }
$user_guid = elgg_get_logged_in_user_guid();
if (!can_write_to_container($user_guid, $owner_guid, $type, $subtype)) {
@@ -736,7 +761,7 @@ function get_entity($guid) {
// @todo We need a single Memcache instance with a shared pool of namespace wrappers. This function would pull an instance from the pool.
static $shared_cache;
- // We could also use: if (!(int) $guid) { return FALSE },
+ // We could also use: if (!(int) $guid) { return FALSE },
// but that evaluates to a false positive for $guid = TRUE.
// This is a bit slower, but more thorough.
if (!is_numeric($guid) || $guid === 0 || $guid === '0') {
@@ -744,7 +769,7 @@ function get_entity($guid) {
}
// Check local cache first
- $new_entity = retrieve_cached_entity($guid);
+ $new_entity = _elgg_retrieve_cached_entity($guid);
if ($new_entity) {
return $new_entity;
}
@@ -766,7 +791,7 @@ function get_entity($guid) {
if ($shared_cache) {
$cached_entity = $shared_cache->load($guid);
- // @todo store ACLs in memcache http://trac.elgg.org/ticket/3018#comment:3
+ // @todo store ACLs in memcache https://github.com/elgg/elgg/issues/3018#issuecomment-13662617
if ($cached_entity) {
// @todo use ACL and cached entity access_id to determine if user can see it
return $cached_entity;
@@ -781,7 +806,7 @@ function get_entity($guid) {
}
if ($new_entity) {
- cache_entity($new_entity);
+ _elgg_cache_entity($new_entity);
}
return $new_entity;
}
@@ -908,6 +933,8 @@ function elgg_get_entities(array $options = array()) {
'joins' => array(),
'callback' => 'entity_row_to_elggstar',
+
+ '__ElggBatch' => null,
);
$options = array_merge($defaults, $options);
@@ -1025,7 +1052,7 @@ function elgg_get_entities(array $options = array()) {
}
if ($options['callback'] === 'entity_row_to_elggstar') {
- $dt = _elgg_fetch_entities_from_sql($query);
+ $dt = _elgg_fetch_entities_from_sql($query, $options['__ElggBatch']);
} else {
$dt = get_data($query, $options['callback']);
}
@@ -1036,7 +1063,7 @@ function elgg_get_entities(array $options = array()) {
foreach ($dt as $item) {
// A custom callback could result in items that aren't ElggEntity's, so check for them
if ($item instanceof ElggEntity) {
- cache_entity($item);
+ _elgg_cache_entity($item);
// plugins usually have only settings
if (!$item instanceof ElggPlugin) {
$guids[] = $item->guid;
@@ -1060,13 +1087,14 @@ function elgg_get_entities(array $options = array()) {
/**
* Return entities from an SQL query generated by elgg_get_entities.
*
- * @param string $sql
+ * @param string $sql
+ * @param ElggBatch $batch
* @return ElggEntity[]
*
* @access private
* @throws LogicException
*/
-function _elgg_fetch_entities_from_sql($sql) {
+function _elgg_fetch_entities_from_sql($sql, ElggBatch $batch = null) {
static $plugin_subtype;
if (null === $plugin_subtype) {
$plugin_subtype = get_subtype_id('object', 'plugin');
@@ -1101,7 +1129,7 @@ function _elgg_fetch_entities_from_sql($sql) {
if (empty($row->guid) || empty($row->type)) {
throw new LogicException('Entity row missing guid or type');
}
- if ($entity = retrieve_cached_entity($row->guid)) {
+ if ($entity = _elgg_retrieve_cached_entity($row->guid)) {
$rows[$i] = $entity;
continue;
}
@@ -1119,16 +1147,17 @@ function _elgg_fetch_entities_from_sql($sql) {
// Do secondary queries and merge rows
if ($lookup_types) {
$dbprefix = elgg_get_config('dbprefix');
- }
- foreach ($lookup_types as $type => $guids) {
- $set = "(" . implode(',', $guids) . ")";
- $sql = "SELECT * FROM {$dbprefix}{$type}s_entity WHERE guid IN $set";
- $secondary_rows = get_data($sql);
- if ($secondary_rows) {
- foreach ($secondary_rows as $secondary_row) {
- $key = $guid_to_key[$secondary_row->guid];
- // cast to arrays to merge then cast back
- $rows[$key] = (object)array_merge((array)$rows[$key], (array)$secondary_row);
+
+ foreach ($lookup_types as $type => $guids) {
+ $set = "(" . implode(',', $guids) . ")";
+ $sql = "SELECT * FROM {$dbprefix}{$type}s_entity WHERE guid IN $set";
+ $secondary_rows = get_data($sql);
+ if ($secondary_rows) {
+ foreach ($secondary_rows as $secondary_row) {
+ $key = $guid_to_key[$secondary_row->guid];
+ // cast to arrays to merge then cast back
+ $rows[$key] = (object)array_merge((array)$rows[$key], (array)$secondary_row);
+ }
}
}
}
@@ -1142,6 +1171,11 @@ function _elgg_fetch_entities_from_sql($sql) {
} catch (IncompleteEntityException $e) {
// don't let incomplete entities throw fatal errors
unset($rows[$i]);
+
+ // report incompletes to the batch process that spawned this query
+ if ($batch) {
+ $batch->reportIncompleteEntity($row);
+ }
}
}
}
@@ -1217,13 +1251,24 @@ function elgg_get_entity_type_subtype_where_sql($table, $types, $subtypes, $pair
$subtype_ids = array();
if ($subtypes) {
foreach ($subtypes as $subtype) {
- // check that the subtype is valid (with ELGG_ENTITIES_NO_VALUE being a valid subtype)
- if (ELGG_ENTITIES_NO_VALUE === $subtype || $subtype_id = get_subtype_id($type, $subtype)) {
- $subtype_ids[] = (ELGG_ENTITIES_NO_VALUE === $subtype) ? ELGG_ENTITIES_NO_VALUE : $subtype_id;
- } else {
- $valid_subtypes_count--;
- elgg_log("Type-subtype '$type:$subtype' does not exist!", 'NOTICE');
+ // check that the subtype is valid
+ if (!$subtype && ELGG_ENTITIES_NO_VALUE === $subtype) {
+ // subtype value is 0
+ $subtype_ids[] = ELGG_ENTITIES_NO_VALUE;
+ } elseif (!$subtype) {
+ // subtype is ignored.
+ // this handles ELGG_ENTITIES_ANY_VALUE, '', and anything falsy that isn't 0
continue;
+ } else {
+ $subtype_id = get_subtype_id($type, $subtype);
+
+ if ($subtype_id) {
+ $subtype_ids[] = $subtype_id;
+ } else {
+ $valid_subtypes_count--;
+ elgg_log("Type-subtype '$type:$subtype' does not exist!", 'NOTICE');
+ continue;
+ }
}
}
@@ -1428,8 +1473,10 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti
global $autofeed;
$autofeed = true;
+ $offset_key = isset($options['offset_key']) ? $options['offset_key'] : 'offset';
+
$defaults = array(
- 'offset' => (int) max(get_input('offset', 0), 0),
+ 'offset' => (int) max(get_input($offset_key, 0), 0),
'limit' => (int) max(get_input('limit', 10), 0),
'full_view' => TRUE,
'list_type_toggle' => FALSE,
@@ -1459,11 +1506,13 @@ function elgg_list_entities(array $options = array(), $getter = 'elgg_get_entiti
*
* @tip Use this to generate a list of archives by month for when entities were added or updated.
*
+ * @todo document how to pass in array for $subtype
+ *
* @warning Months are returned in the form YYYYMM.
*
* @param string $type The type of entity
* @param string $subtype The subtype of entity
- * @param int $container_guid The container GUID that the entinties belong to
+ * @param int $container_guid The container GUID that the entities belong to
* @param int $site_guid The site GUID
* @param string $order_by Order_by SQL order by clause
*
@@ -1613,7 +1662,7 @@ function disable_entity($guid, $reason = "", $recursive = true) {
$entity->disableMetadata();
$entity->disableAnnotations();
- invalidate_cache_for_entity($guid);
+ _elgg_invalidate_cache_for_entity($guid);
$res = update_data("UPDATE {$CONFIG->dbprefix}entities
SET enabled = 'no'
@@ -1629,8 +1678,8 @@ function disable_entity($guid, $reason = "", $recursive = true) {
/**
* Enable an entity.
*
- * @warning In order to enable an entity using ElggEntity::enable(),
- * you must first use {@link access_show_hidden_entities()}.
+ * @warning In order to enable an entity, you must first use
+ * {@link access_show_hidden_entities()}.
*
* @param int $guid GUID of entity to enable
* @param bool $recursive Recursively enable all entities disabled with the entity?
@@ -1711,7 +1760,7 @@ function delete_entity($guid, $recursive = true) {
// delete cache
if (isset($ENTITY_CACHE[$guid])) {
- invalidate_cache_for_entity($guid);
+ _elgg_invalidate_cache_for_entity($guid);
}
// If memcache is available then delete this entry from the cache
@@ -1758,6 +1807,10 @@ function delete_entity($guid, $recursive = true) {
elgg_set_ignore_access($ia);
}
+ $entity_disable_override = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $ia = elgg_set_ignore_access(true);
+
// Now delete the entity itself
$entity->deleteMetadata();
$entity->deleteOwnedMetadata();
@@ -1765,6 +1818,9 @@ function delete_entity($guid, $recursive = true) {
$entity->deleteOwnedAnnotations();
$entity->deleteRelationships();
+ access_show_hidden_entities($entity_disable_override);
+ elgg_set_ignore_access($ia);
+
elgg_delete_river(array('subject_guid' => $guid));
elgg_delete_river(array('object_guid' => $guid));
remove_all_private_settings($guid);
@@ -2072,7 +2128,7 @@ function can_edit_entity_metadata($entity_guid, $user_guid = 0, $metadata = null
$return = null;
- if ($metadata->owner_guid == 0) {
+ if ($metadata && ($metadata->owner_guid == 0)) {
$return = true;
}
if (is_null($return)) {
@@ -2413,6 +2469,7 @@ function elgg_instanceof($entity, $type = NULL, $subtype = NULL, $class = NULL)
$return = ($entity instanceof ElggEntity);
if ($type) {
+ /* @var ElggEntity $entity */
$return = $return && ($entity->getType() == $type);
}
@@ -2472,11 +2529,18 @@ function update_entity_last_action($guid, $posted = NULL) {
function entities_gc() {
global $CONFIG;
- $tables = array ('sites_entity', 'objects_entity', 'groups_entity', 'users_entity');
+ $tables = array(
+ 'site' => 'sites_entity',
+ 'object' => 'objects_entity',
+ 'group' => 'groups_entity',
+ 'user' => 'users_entity'
+ );
- foreach ($tables as $table) {
- delete_data("DELETE from {$CONFIG->dbprefix}{$table}
- where guid NOT IN (SELECT guid from {$CONFIG->dbprefix}entities)");
+ foreach ($tables as $type => $table) {
+ delete_data("DELETE FROM {$CONFIG->dbprefix}{$table}
+ WHERE guid NOT IN (SELECT guid FROM {$CONFIG->dbprefix}entities)");
+ delete_data("DELETE FROM {$CONFIG->dbprefix}entities
+ WHERE type = '$type' AND guid NOT IN (SELECT guid FROM {$CONFIG->dbprefix}{$table})");
}
}
diff --git a/engine/lib/export.php b/engine/lib/export.php
index ae9be95ce..ecc894e63 100644
--- a/engine/lib/export.php
+++ b/engine/lib/export.php
@@ -11,7 +11,7 @@
*
* @param mixed $object The object either an ElggEntity, ElggRelationship or ElggExtender
*
- * @return the UUID or false
+ * @return string|false the UUID or false
*/
function get_uuid_from_object($object) {
if ($object instanceof ElggEntity) {
@@ -40,8 +40,6 @@ function get_uuid_from_object($object) {
* @return string
*/
function guid_to_uuid($guid) {
- global $CONFIG;
-
return elgg_get_site_url() . "export/opendd/$guid/";
}
@@ -53,8 +51,6 @@ function guid_to_uuid($guid) {
* @return bool
*/
function is_uuid_this_domain($uuid) {
- global $CONFIG;
-
if (strpos($uuid, elgg_get_site_url()) === 0) {
return true;
}
@@ -67,7 +63,7 @@ function is_uuid_this_domain($uuid) {
*
* @param string $uuid A unique ID
*
- * @return mixed
+ * @return ElggEntity|false
*/
function get_entity_from_uuid($uuid) {
$uuid = sanitise_string($uuid);
@@ -117,18 +113,19 @@ function _process_element(ODD $odd) {
global $IMPORTED_DATA, $IMPORTED_OBJECT_COUNTER;
// See if anyone handles this element, return true if it is.
+ $to_be_serialised = null;
if ($odd) {
$handled = elgg_trigger_plugin_hook("import", "all", array("element" => $odd), $to_be_serialised);
- }
- // If not, then see if any of its sub elements are handled
- if ($handled) {
- // Increment validation counter
- $IMPORTED_OBJECT_COUNTER ++;
- // Return the constructed object
- $IMPORTED_DATA[] = $handled;
+ // If not, then see if any of its sub elements are handled
+ if ($handled) {
+ // Increment validation counter
+ $IMPORTED_OBJECT_COUNTER ++;
+ // Return the constructed object
+ $IMPORTED_DATA[] = $handled;
- return true;
+ return true;
+ }
}
return false;
@@ -167,7 +164,7 @@ function exportAsArray($guid) {
*
* @param int $guid The GUID.
*
- * @return xml
+ * @return string XML
* @see ElggEntity for an example of its usage.
* @access private
*/
@@ -184,7 +181,7 @@ function export($guid) {
* @param string $xml XML string
*
* @return bool
- * @throws Exception if there was a problem importing the data.
+ * @throws ImportException if there was a problem importing the data.
* @access private
*/
function import($xml) {
diff --git a/engine/lib/extender.php b/engine/lib/extender.php
index 538f601e1..8323bd3ce 100644
--- a/engine/lib/extender.php
+++ b/engine/lib/extender.php
@@ -86,6 +86,7 @@ function oddmetadata_to_elggextender(ElggEntity $entity, ODDMetaData $element) {
* @return null
* @elgg_plugin_hook_handler volatile metadata
* @todo investigate more.
+ * @throws ImportException
* @access private
*/
function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params) {
@@ -94,6 +95,7 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params)
$tmp = NULL;
if ($element instanceof ODDMetaData) {
+ /* @var ODDMetaData $element */
// Recall entity
$entity_uuid = $element->getAttribute('entity_uuid');
$entity = get_entity_from_uuid($entity_uuid);
@@ -124,14 +126,20 @@ function import_extender_plugin_hook($hook, $entity_type, $returnvalue, $params)
* @return bool
*/
function can_edit_extender($extender_id, $type, $user_guid = 0) {
- if (!elgg_is_logged_in()) {
- return false;
+ // @todo Since Elgg 1.0, Elgg has returned false from can_edit_extender()
+ // if no user was logged in. This breaks the access override. This is a
+ // temporary work around. This function needs to be rewritten in Elgg 1.9
+ if (!elgg_check_access_overrides($user_guid)) {
+ if (!elgg_is_logged_in()) {
+ return false;
+ }
}
$user_guid = (int)$user_guid;
- $user = get_entity($user_guid);
+ $user = get_user($user_guid);
if (!$user) {
$user = elgg_get_logged_in_user_entity();
+ $user_guid = elgg_get_logged_in_user_guid();
}
$functionname = "elgg_get_{$type}_from_id";
@@ -147,16 +155,16 @@ function can_edit_extender($extender_id, $type, $user_guid = 0) {
/* @var ElggExtender $extender */
// If the owner is the specified user, great! They can edit.
- if ($extender->getOwnerGUID() == $user->getGUID()) {
+ if ($extender->getOwnerGUID() == $user_guid) {
return true;
}
// If the user can edit the entity this is attached to, great! They can edit.
- if (can_edit_entity($extender->entity_guid, $user->getGUID())) {
+ if (can_edit_entity($extender->entity_guid, $user_guid)) {
return true;
}
- // Trigger plugin hooks
+ // Trigger plugin hook - note that $user may be null
$params = array('entity' => $extender->getEntity(), 'user' => $user);
return elgg_trigger_plugin_hook('permissions_check', $type, $params, false);
}
diff --git a/engine/lib/filestore.php b/engine/lib/filestore.php
index 93a127257..a3c7ba439 100644
--- a/engine/lib/filestore.php
+++ b/engine/lib/filestore.php
@@ -308,8 +308,6 @@ function get_image_resize_parameters($width, $height, $options) {
function file_delete($guid) {
if ($file = get_entity($guid)) {
if ($file->canEdit()) {
- $container = get_entity($file->container_guid);
-
$thumbnail = $file->thumbnail;
$smallthumb = $file->smallthumb;
$largethumb = $file->largethumb;
@@ -383,7 +381,7 @@ function file_get_general_file_type($mimetype) {
/**
* Delete a directory and all its contents
*
- * @param str $directory Directory to delete
+ * @param string $directory Directory to delete
*
* @return bool
*/
@@ -500,7 +498,7 @@ function filestore_init() {
/**
* Unit tests for files
*
- * @param sting $hook unit_test
+ * @param string $hook unit_test
* @param string $type system
* @param mixed $value Array of tests
* @param mixed $params Params
diff --git a/engine/lib/group.php b/engine/lib/group.php
index 5a38e1ea6..6ded8a825 100644
--- a/engine/lib/group.php
+++ b/engine/lib/group.php
@@ -170,7 +170,7 @@ function get_group_members($group_guid, $limit = 10, $offset = 0, $site_guid = 0
'relationship' => 'member',
'relationship_guid' => $group_guid,
'inverse_relationship' => TRUE,
- 'types' => 'user',
+ 'type' => 'user',
'limit' => $limit,
'offset' => $offset,
'count' => $count,
@@ -240,9 +240,11 @@ function leave_group($group_guid, $user_guid) {
*/
function get_users_membership($user_guid) {
$options = array(
+ 'type' => 'group',
'relationship' => 'member',
'relationship_guid' => $user_guid,
- 'inverse_relationship' => FALSE
+ 'inverse_relationship' => false,
+ 'limit' => false,
);
return elgg_get_entities_from_relationship($options);
}
diff --git a/engine/lib/input.php b/engine/lib/input.php
index 6d1646e1a..80b0b8766 100644
--- a/engine/lib/input.php
+++ b/engine/lib/input.php
@@ -60,8 +60,8 @@ function get_input($variable, $default = NULL, $filter_result = TRUE) {
*
* Note: this function does not handle nested arrays (ex: form input of param[m][n])
*
- * @param string $variable The name of the variable
- * @param string $value The value of the variable
+ * @param string $variable The name of the variable
+ * @param string|string[] $value The value of the variable
*
* @return void
*/
@@ -226,6 +226,8 @@ function elgg_clear_sticky_value($form_name, $variable) {
/**
* Page handler for autocomplete endpoint.
*
+ * @todo split this into functions/objects, this is way too big
+ *
* /livesearch?q=<query>
*
* Other options include:
@@ -233,6 +235,7 @@ function elgg_clear_sticky_value($form_name, $variable) {
* match_owner int 0/1
* limit int default is 10
*
+ * @param array $page
* @return string JSON string is returned and then exit
* @access private
*/
@@ -265,10 +268,8 @@ function input_livesearch_page_handler($page) {
}
if (get_input('match_owner', false)) {
- $owner_guid = $user->getGUID();
$owner_where = 'AND e.owner_guid = ' . $user->getGUID();
} else {
- $owner_guid = null;
$owner_where = '';
}
@@ -289,7 +290,9 @@ function input_livesearch_page_handler($page) {
if ($entities = get_data($query)) {
foreach ($entities as $entity) {
+ // @todo use elgg_get_entities (don't query in a loop!)
$entity = get_entity($entity->guid);
+ /* @var ElggUser $entity */
if (!$entity) {
continue;
}
@@ -338,7 +341,9 @@ function input_livesearch_page_handler($page) {
";
if ($entities = get_data($query)) {
foreach ($entities as $entity) {
+ // @todo use elgg_get_entities (don't query in a loop!)
$entity = get_entity($entity->guid);
+ /* @var ElggGroup $entity */
if (!$entity) {
continue;
}
@@ -385,7 +390,9 @@ function input_livesearch_page_handler($page) {
if ($entities = get_data($query)) {
foreach ($entities as $entity) {
+ // @todo use elgg_get_entities (don't query in a loop!)
$entity = get_entity($entity->guid);
+ /* @var ElggUser $entity */
if (!$entity) {
continue;
}
diff --git a/engine/lib/languages.php b/engine/lib/languages.php
index 98006f7cd..61ba91ddb 100644
--- a/engine/lib/languages.php
+++ b/engine/lib/languages.php
@@ -77,7 +77,7 @@ function elgg_echo($message_key, $args = array(), $language = "") {
* @param string $country_code Standard country code (eg 'en', 'nl', 'es')
* @param array $language_array Formatted array of strings
*
- * @return true|false Depending on success
+ * @return bool Depending on success
*/
function add_translation($country_code, $language_array) {
global $CONFIG;
@@ -104,8 +104,6 @@ function add_translation($country_code, $language_array) {
* @return string The language code for the site/user or "en" if not set
*/
function get_current_language() {
- global $CONFIG;
-
$language = get_language();
if (!$language) {
@@ -141,6 +139,9 @@ function get_language() {
return false;
}
+/**
+ * @access private
+ */
function _elgg_load_translations() {
global $CONFIG;
@@ -148,7 +149,7 @@ function _elgg_load_translations() {
$loaded = true;
$languages = array_unique(array('en', get_current_language()));
foreach ($languages as $language) {
- $data = elgg_load_system_cache("$language.php");
+ $data = elgg_load_system_cache("$language.lang");
if ($data) {
add_translation($language, unserialize($data));
} else {
@@ -177,7 +178,7 @@ function _elgg_load_translations() {
* @param bool $load_all If true all languages are loaded, if
* false only the current language + en are loaded
*
- * @return void
+ * @return bool success
*/
function register_translations($path, $load_all = false) {
global $CONFIG;
@@ -229,23 +230,37 @@ function register_translations($path, $load_all = false) {
/**
* Reload all translations from all registered paths.
*
- * This is only called by functions which need to know all possible translations, namely the
- * statistic gathering ones.
+ * This is only called by functions which need to know all possible translations.
*
* @todo Better on demand loading based on language_paths array
*
- * @return bool
+ * @return void
*/
function reload_all_translations() {
global $CONFIG;
static $LANG_RELOAD_ALL_RUN;
if ($LANG_RELOAD_ALL_RUN) {
- return null;
+ return;
}
- foreach ($CONFIG->language_paths as $path => $dummy) {
- register_translations($path, true);
+ if ($CONFIG->i18n_loaded_from_cache) {
+ $cache = elgg_get_system_cache();
+ $cache_dir = $cache->getVariable("cache_path");
+ $filenames = elgg_get_file_list($cache_dir, array(), array(), array(".lang"));
+ foreach ($filenames as $filename) {
+ if (preg_match('/([a-z]+)\.[^.]+$/', $filename, $matches)) {
+ $language = $matches[1];
+ $data = elgg_load_system_cache("$language.lang");
+ if ($data) {
+ add_translation($language, unserialize($data));
+ }
+ }
+ }
+ } else {
+ foreach ($CONFIG->language_paths as $path => $dummy) {
+ register_translations($path, true);
+ }
}
$LANG_RELOAD_ALL_RUN = true;
@@ -337,14 +352,3 @@ function get_missing_language_keys($language) {
return false;
}
-
-/**
- * Initialize the language library
- * @access private
- */
-function elgg_languages_init() {
- $lang = get_current_language();
- elgg_register_simplecache_view("js/languages/$lang");
-}
-
-elgg_register_event_handler('init', 'system', 'elgg_languages_init');
diff --git a/engine/lib/location.php b/engine/lib/location.php
index 5b889509b..1534c7d7b 100644
--- a/engine/lib/location.php
+++ b/engine/lib/location.php
@@ -101,7 +101,7 @@ function elgg_get_entities_from_location(array $options = array()) {
$long_min = $long - $long_distance;
$long_max = $long + $long_distance;
- $where = array();
+ $wheres = array();
$wheres[] = "lat_name.string='geo:lat'";
$wheres[] = "lat_value.string >= $lat_min";
$wheres[] = "lat_value.string <= $lat_max";
@@ -139,7 +139,7 @@ function elgg_get_entities_from_location(array $options = array()) {
/**
* Returns a viewable list of entities from location
*
- * @param array $options
+ * @param array $options Options array
*
* @see elgg_list_entities()
* @see elgg_get_entities_from_location()
diff --git a/engine/lib/mb_wrapper.php b/engine/lib/mb_wrapper.php
index c2f5503e0..68fa69005 100644
--- a/engine/lib/mb_wrapper.php
+++ b/engine/lib/mb_wrapper.php
@@ -11,7 +11,7 @@ if (is_callable('mb_internal_encoding')) {
* NOTE: This differs from parse_str() by returning the results
* instead of placing them in the local scope!
*
- * @param str $str The string
+ * @param string $str The string
*
* @return array
* @since 1.7.0
diff --git a/engine/lib/memcache.php b/engine/lib/memcache.php
index f79fba4a9..79b87e850 100644
--- a/engine/lib/memcache.php
+++ b/engine/lib/memcache.php
@@ -35,3 +35,23 @@ function is_memcache_available() {
return $memcache_available;
}
+
+/**
+ * Invalidate an entity in memcache
+ *
+ * @param int $entity_guid The GUID of the entity to invalidate
+ *
+ * @return void
+ * @access private
+ */
+function _elgg_invalidate_memcache_for_entity($entity_guid) {
+ static $newentity_cache;
+
+ if ((!$newentity_cache) && (is_memcache_available())) {
+ $newentity_cache = new ElggMemcache('new_entity_cache');
+ }
+
+ if ($newentity_cache) {
+ $newentity_cache->delete($entity_guid);
+ }
+} \ No newline at end of file
diff --git a/engine/lib/metadata.php b/engine/lib/metadata.php
index f76c20f24..fdb1b85f6 100644
--- a/engine/lib/metadata.php
+++ b/engine/lib/metadata.php
@@ -191,19 +191,19 @@ function update_metadata($id, $name, $value, $value_type, $owner_guid, $access_i
}
// Add the metastring
- $value = add_metastring($value);
- if (!$value) {
+ $value_id = add_metastring($value);
+ if (!$value_id) {
return false;
}
- $name = add_metastring($name);
- if (!$name) {
+ $name_id = add_metastring($name);
+ if (!$name_id) {
return false;
}
// If ok then add it
$query = "UPDATE {$CONFIG->dbprefix}metadata"
- . " set name_id='$name', value_id='$value', value_type='$value_type', access_id=$access_id,"
+ . " set name_id='$name_id', value_id='$value_id', value_type='$value_type', access_id=$access_id,"
. " owner_guid=$owner_guid where id=$id";
$result = update_data($query);
@@ -277,10 +277,18 @@ $access_id = ACCESS_PRIVATE, $allow_multiple = false) {
* all metadata that match the query instead of returning
* ElggMetadata objects.
*
- * @return mixed
+ * @return ElggMetadata[]|mixed
* @since 1.8.0
*/
function elgg_get_metadata(array $options = array()) {
+
+ // @todo remove support for count shortcut - see #4393
+ // support shortcut of 'count' => true for 'metadata_calculation' => 'count'
+ if (isset($options['count']) && $options['count']) {
+ $options['metadata_calculation'] = 'count';
+ unset($options['count']);
+ }
+
$options['metastring_type'] = 'metadata';
return elgg_get_metastring_based_objects($options);
}
@@ -292,21 +300,22 @@ function elgg_get_metadata(array $options = array()) {
* This requires at least one constraint: metadata_owner_guid(s),
* metadata_name(s), metadata_value(s), or guid(s) must be set.
*
- * @warning This returns null on no ops.
- *
* @param array $options An options array. {@see elgg_get_metadata()}
- * @return mixed Null if the metadata name is invalid. Bool on success or fail.
+ * @return bool|null true on success, false on failure, null if no metadata to delete.
* @since 1.8.0
*/
function elgg_delete_metadata(array $options) {
if (!elgg_is_valid_options_for_batch_operation($options, 'metadata')) {
return false;
}
+ $options['metastring_type'] = 'metadata';
+ $result = elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false);
+ // This moved last in case an object's constructor sets metadata. Currently the batch
+ // delete process has to create the entity to delete its metadata. See #5214
elgg_get_metadata_cache()->invalidateByOptions('delete', $options);
- $options['metastring_type'] = 'metadata';
- return elgg_batch_metastring_based_objects($options, 'elgg_batch_delete_callback', false);
+ return $result;
}
/**
@@ -314,10 +323,8 @@ function elgg_delete_metadata(array $options) {
*
* @warning Unlike elgg_get_metadata() this will not accept an empty options array!
*
- * @warning This returns null on no ops.
- *
* @param array $options An options array. {@See elgg_get_metadata()}
- * @return mixed
+ * @return bool|null true on success, false on failure, null if no metadata disabled.
* @since 1.8.0
*/
function elgg_disable_metadata(array $options) {
@@ -326,9 +333,13 @@ function elgg_disable_metadata(array $options) {
}
elgg_get_metadata_cache()->invalidateByOptions('disable', $options);
+
+ // if we can see hidden (disabled) we need to use the offset
+ // otherwise we risk an infinite loop if there are more than 50
+ $inc_offset = access_get_show_hidden_status();
$options['metastring_type'] = 'metadata';
- return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', false);
+ return elgg_batch_metastring_based_objects($options, 'elgg_batch_disable_callback', $inc_offset);
}
/**
@@ -336,10 +347,11 @@ function elgg_disable_metadata(array $options) {
*
* @warning Unlike elgg_get_metadata() this will not accept an empty options array!
*
- * @warning This returns null on no ops.
+ * @warning In order to enable metadata, you must first use
+ * {@link access_show_hidden_entities()}.
*
* @param array $options An options array. {@See elgg_get_metadata()}
- * @return mixed
+ * @return bool|null true on success, false on failure, null if no metadata enabled.
* @since 1.8.0
*/
function elgg_enable_metadata(array $options) {
@@ -394,9 +406,11 @@ function elgg_enable_metadata(array $options) {
* 'operand' => '=',
* 'case_sensitive' => TRUE
* )
- * Currently if multiple values are sent via
+ * Currently if multiple values are sent via
* an array (value => array('value1', 'value2')
* the pair's operand will be forced to "IN".
+ * If passing "IN" as the operand and a string as the value,
+ * the value must be a properly quoted and escaped string.
*
* metadata_name_value_pairs_operator => NULL|STR The operator to use for combining
* (name = value) OPERATOR (name = value); default AND
@@ -412,7 +426,7 @@ function elgg_enable_metadata(array $options) {
*
* metadata_owner_guids => NULL|ARR guids for metadata owners
*
- * @return mixed If count, int. If not count, array. false on errors.
+ * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors.
* @since 1.7.0
*/
function elgg_get_entities_from_metadata(array $options = array()) {
@@ -461,7 +475,7 @@ function elgg_get_entities_from_metadata(array $options = array()) {
* @param array|null $order_by_metadata Array of names / direction
* @param array|null $owner_guids Array of owner GUIDs
*
- * @return FALSE|array False on fail, array('joins', 'wheres')
+ * @return false|array False on fail, array('joins', 'wheres')
* @since 1.7.0
* @access private
*/
@@ -608,6 +622,8 @@ $owner_guids = NULL) {
// if the operand is IN don't quote it because quoting should be done already.
if (is_numeric($pair['value'])) {
$value = sanitise_string($pair['value']);
+ } else if (is_bool($pair['value'])) {
+ $value = (int) $pair['value'];
} else if (is_array($pair['value'])) {
$values_array = array();
@@ -774,10 +790,10 @@ function string_to_tag_array($string) {
$ar = explode(",", $string);
$ar = array_map('trim', $ar);
$ar = array_filter($ar, 'is_not_null');
+ $ar = array_map('strip_tags', $ar);
return $ar;
}
return false;
-
}
/**
@@ -909,8 +925,8 @@ function elgg_get_metadata_cache() {
* Invalidate the metadata cache based on options passed to various *_metadata functions
*
* @param string $action Action performed on metadata. "delete", "disable", or "enable"
- *
- * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
+ * @param array $options Options passed to elgg_(delete|disable|enable)_metadata
+ * @return void
*/
function elgg_invalidate_metadata_cache($action, array $options) {
// remove as little as possible, optimizing for common cases
diff --git a/engine/lib/metastrings.php b/engine/lib/metastrings.php
index cf6dd4d98..57d876c06 100644
--- a/engine/lib/metastrings.php
+++ b/engine/lib/metastrings.php
@@ -67,7 +67,7 @@ function get_metastring_id($string, $case_sensitive = TRUE) {
}
$row = FALSE;
- $metaStrings = get_data($query, "entity_row_to_elggstar");
+ $metaStrings = get_data($query);
if (is_array($metaStrings)) {
if (sizeof($metaStrings) > 1) {
$ids = array();
@@ -389,11 +389,6 @@ function elgg_get_metastring_based_objects($options) {
$selects = $options['selects'];
- // allow count shortcut
- if ($options['count']) {
- $options['metastring_calculation'] = 'count';
- }
-
// For performance reasons we don't want the joins required for metadata / annotations
// unless we're going through one of their callbacks.
// this means we expect the functions passing different callbacks to pass their required joins.
@@ -426,9 +421,11 @@ function elgg_get_metastring_based_objects($options) {
if ($metastring_clauses) {
$wheres = array_merge($wheres, $metastring_clauses['wheres']);
$joins = array_merge($joins, $metastring_clauses['joins']);
+ } else {
+ $wheres[] = get_access_sql_suffix('n_table');
}
- if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE) {
+ if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
$selects = array_unique($selects);
// evalutate selects
$select_str = '';
@@ -439,6 +436,9 @@ function elgg_get_metastring_based_objects($options) {
}
$query = "SELECT DISTINCT n_table.*{$select_str} FROM {$db_prefix}$type n_table";
+ } elseif ($options['count']) {
+ // count is over the entities
+ $query = "SELECT count(DISTINCT e.guid) as calculation FROM {$db_prefix}$type n_table";
} else {
$query = "SELECT {$options['metastring_calculation']}(v.string) as calculation FROM {$db_prefix}$type n_table";
}
@@ -467,7 +467,7 @@ function elgg_get_metastring_based_objects($options) {
$defaults['order_by']);
}
- if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE) {
+ if ($options['metastring_calculation'] === ELGG_ENTITIES_NO_VALUE && !$options['count']) {
if (isset($options['group_by'])) {
$options['group_by'] = sanitise_string($options['group_by']);
$query .= " GROUP BY {$options['group_by']}";
@@ -515,21 +515,16 @@ function elgg_get_metastring_sql($table, $names = null, $values = null,
&& !$ids
&& (!$pairs && $pairs !== 0)) {
- return '';
+ return array();
}
$db_prefix = elgg_get_config('dbprefix');
- // join counter for incremental joins.
- $i = 1;
-
// binary forces byte-to-byte comparision of strings, making
// it case- and diacritical-mark- sensitive.
// only supported on values.
$binary = ($case_sensitive) ? ' BINARY ' : '';
- $access = get_access_sql_suffix($table);
-
$return = array (
'joins' => array (),
'wheres' => array()
@@ -594,13 +589,15 @@ function elgg_get_metastring_sql($table, $names = null, $values = null,
}
if ($names_where && $values_where) {
- $wheres[] = "($names_where AND $values_where AND $access)";
+ $wheres[] = "($names_where AND $values_where)";
} elseif ($names_where) {
- $wheres[] = "($names_where AND $access)";
+ $wheres[] = $names_where;
} elseif ($values_where) {
- $wheres[] = "($values_where AND $access)";
+ $wheres[] = $values_where;
}
+ $wheres[] = get_access_sql_suffix($table);
+
if ($where = implode(' AND ', $wheres)) {
$return['wheres'][] = "($where)";
}
@@ -663,9 +660,10 @@ function elgg_normalize_metastrings_options(array $options = array()) {
*
* @param int $id The object's ID
* @param string $enabled Value to set to: yes or no
- * @param string $type The type of table to use: metadata or anntations
+ * @param string $type The type of table to use: metadata or annotations
*
* @return bool
+ * @throws InvalidParameterException
* @since 1.8.0
* @access private
*/
@@ -740,7 +738,7 @@ function elgg_batch_metastring_based_objects(array $options, $callback, $inc_off
*
* @param int $id The metastring-based object's ID
* @param string $type The type: annotation or metadata
- * @return mixed
+ * @return ElggMetadata|ElggAnnotation
*
* @since 1.8.0
* @access private
@@ -806,6 +804,7 @@ function elgg_delete_metastring_based_object_by_id($id, $type) {
}
if ($metabyname_memcache) {
+ // @todo why name_id? is that even populated?
$metabyname_memcache->delete("{$obj->entity_guid}:{$obj->name_id}");
}
}
diff --git a/engine/lib/navigation.php b/engine/lib/navigation.php
index 86624cd7c..ab9cc05e8 100644
--- a/engine/lib/navigation.php
+++ b/engine/lib/navigation.php
@@ -126,6 +126,7 @@ function elgg_unregister_menu_item($menu_name, $item_name) {
}
foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) {
+ /* @var ElggMenuItem $menu_object */
if ($menu_object->getName() == $item_name) {
unset($CONFIG->menus[$menu_name][$index]);
return true;
@@ -151,7 +152,8 @@ function elgg_is_menu_item_registered($menu_name, $item_name) {
return false;
}
- foreach ($CONFIG->menus[$menu_name] as $index => $menu_object) {
+ foreach ($CONFIG->menus[$menu_name] as $menu_object) {
+ /* @var ElggMenuItem $menu_object */
if ($menu_object->getName() == $item_name) {
return true;
}
@@ -216,7 +218,7 @@ function elgg_push_breadcrumb($title, $link = NULL) {
}
// avoid key collisions.
- $CONFIG->breadcrumbs[] = array('title' => $title, 'link' => $link);
+ $CONFIG->breadcrumbs[] = array('title' => elgg_get_excerpt($title, 100), 'link' => $link);
}
/**
@@ -311,8 +313,8 @@ function elgg_site_menu_setup($hook, $type, $return, $params) {
// check if we have anything selected
$selected = false;
- foreach ($return as $section_name => $section) {
- foreach ($section as $key => $item) {
+ foreach ($return as $section) {
+ foreach ($section as $item) {
if ($item->getSelected()) {
$selected = true;
break 2;
@@ -321,7 +323,8 @@ function elgg_site_menu_setup($hook, $type, $return, $params) {
}
if (!$selected) {
- // nothing selected, match name to context
+ // nothing selected, match name to context or match url
+ $current_url = current_page_url();
foreach ($return as $section_name => $section) {
foreach ($section as $key => $item) {
// only highlight internal links
@@ -330,6 +333,10 @@ function elgg_site_menu_setup($hook, $type, $return, $params) {
$return[$section_name][$key]->setSelected(true);
break 2;
}
+ if ($item->getHref() == $current_url) {
+ $return[$section_name][$key]->setSelected(true);
+ break 2;
+ }
}
}
}
@@ -345,6 +352,7 @@ function elgg_site_menu_setup($hook, $type, $return, $params) {
function elgg_river_menu_setup($hook, $type, $return, $params) {
if (elgg_is_logged_in()) {
$item = $params['item'];
+ /* @var ElggRiverItem $item */
$object = $item->getObjectEntity();
// comments and non-objects cannot be commented on or liked
if (!elgg_in_context('widgets') && $item->annotation_id == 0) {
@@ -388,6 +396,7 @@ function elgg_entity_menu_setup($hook, $type, $return, $params) {
}
$entity = $params['entity'];
+ /* @var ElggEntity $entity */
$handler = elgg_extract('handler', $params, false);
// access
@@ -433,6 +442,7 @@ function elgg_entity_menu_setup($hook, $type, $return, $params) {
function elgg_widget_menu_setup($hook, $type, $return, $params) {
$widget = $params['entity'];
+ /* @var ElggWidget $widget */
$show_edit = elgg_extract('show_edit', $params, true);
$collapse = array(
@@ -481,6 +491,7 @@ function elgg_widget_menu_setup($hook, $type, $return, $params) {
*/
function elgg_annotation_menu_setup($hook, $type, $return, $params) {
$annotation = $params['annotation'];
+ /* @var ElggAnnotation $annotation */
if ($annotation->name == 'generic_comment' && $annotation->canEdit()) {
$url = elgg_http_add_url_query_elements('action/comments/delete', array(
diff --git a/engine/lib/notification.php b/engine/lib/notification.php
index 9e3c075a8..be0c359d4 100644
--- a/engine/lib/notification.php
+++ b/engine/lib/notification.php
@@ -86,7 +86,7 @@ function unregister_notification_handler($method) {
* @throws NotificationException
*/
function notify_user($to, $from, $subject, $message, array $params = NULL, $methods_override = "") {
- global $NOTIFICATION_HANDLERS, $CONFIG;
+ global $NOTIFICATION_HANDLERS;
// Sanitise
if (!is_array($to)) {
@@ -110,12 +110,15 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth
// Are we overriding delivery?
$methods = $methods_override;
if (!$methods) {
- $tmp = (array)get_user_notification_settings($guid);
+ $tmp = get_user_notification_settings($guid);
$methods = array();
- foreach ($tmp as $k => $v) {
- // Add method if method is turned on for user!
- if ($v) {
- $methods[] = $k;
+ // $tmp may be false. don't cast
+ if (is_object($tmp)) {
+ foreach ($tmp as $k => $v) {
+ // Add method if method is turned on for user!
+ if ($v) {
+ $methods[] = $k;
+ }
}
}
}
@@ -165,7 +168,7 @@ function notify_user($to, $from, $subject, $message, array $params = NULL, $meth
*
* @param int $user_guid The user id
*
- * @return stdClass
+ * @return stdClass|false
*/
function get_user_notification_settings($user_guid = 0) {
$user_guid = (int)$user_guid;
@@ -174,7 +177,8 @@ function get_user_notification_settings($user_guid = 0) {
$user_guid = elgg_get_logged_in_user_guid();
}
- // @todo: holy crap, really?
+ // @todo: there should be a better way now that metadata is cached. E.g. just query for MD names, then
+ // query user object directly
$all_metadata = elgg_get_metadata(array(
'guid' => $user_guid,
'limit' => 0
@@ -237,6 +241,7 @@ function set_user_notification_setting($user_guid, $method, $value) {
* @param array $params Optional parameters (none taken in this instance)
*
* @return bool
+ * @throws NotificationException
* @access private
*/
function email_notify_handler(ElggEntity $from, ElggUser $to, $subject, $message,
@@ -263,7 +268,7 @@ array $params = NULL) {
$to = $to->email;
// From
- $site = get_entity($CONFIG->site_guid);
+ $site = elgg_get_site_entity();
// If there's an email address, use it - but only if its not from a user.
if (!($from instanceof ElggUser) && $from->email) {
$from = $from->email;
@@ -288,6 +293,7 @@ array $params = NULL) {
* @param array $params Optional parameters (none used in this function)
*
* @return bool
+ * @throws NotificationException
* @since 1.7.2
*/
function elgg_send_email($from, $to, $subject, $body, array $params = NULL) {
@@ -344,6 +350,8 @@ function elgg_send_email($from, $to, $subject, $body, array $params = NULL) {
// Sanitise subject by stripping line endings
$subject = preg_replace("/(\r\n|\r|\n)/", " ", $subject);
+ // this is because Elgg encodes everything and matches what is done with body
+ $subject = html_entity_decode($subject, ENT_COMPAT, 'UTF-8'); // Decode any html entities
if (is_callable('mb_encode_mimeheader')) {
$subject = mb_encode_mimeheader($subject, "UTF-8", "B");
}
@@ -422,7 +430,7 @@ function register_notification_object($entity_type, $object_subtype, $language_n
* @param int $user_guid The GUID of the user who wants to follow a user's content
* @param int $author_guid The GUID of the user whose content the user wants to follow
*
- * @return true|false Depending on success
+ * @return bool Depending on success
*/
function register_notification_interest($user_guid, $author_guid) {
return add_entity_relationship($user_guid, 'notify', $author_guid);
@@ -434,7 +442,7 @@ function register_notification_interest($user_guid, $author_guid) {
* @param int $user_guid The GUID of the user who is following a user's content
* @param int $author_guid The GUID of the user whose content the user wants to unfollow
*
- * @return true|false Depending on success
+ * @return bool Depending on success
*/
function remove_notification_interest($user_guid, $author_guid) {
return remove_entity_relationship($user_guid, 'notify', $author_guid);
@@ -450,12 +458,13 @@ function remove_notification_interest($user_guid, $author_guid) {
* @param string $object_type mixed
* @param mixed $object The object created
*
- * @return void
+ * @return bool
* @access private
*/
function object_notifications($event, $object_type, $object) {
// We only want to trigger notification events for ElggEntities
if ($object instanceof ElggEntity) {
+ /* @var ElggEntity $object */
// Get config data
global $CONFIG, $SESSION, $NOTIFICATION_HANDLERS;
@@ -492,9 +501,10 @@ function object_notifications($event, $object_type, $object) {
'relationship' => 'notify' . $method,
'relationship_guid' => $object->container_guid,
'inverse_relationship' => TRUE,
- 'types' => 'user',
- 'limit' => 99999
+ 'type' => 'user',
+ 'limit' => false
));
+ /* @var ElggUser[] $interested_users */
if ($interested_users && is_array($interested_users)) {
foreach ($interested_users as $user) {
diff --git a/engine/lib/objects.php b/engine/lib/objects.php
index e5e8f67c4..ff3cc733f 100644
--- a/engine/lib/objects.php
+++ b/engine/lib/objects.php
@@ -93,16 +93,16 @@ function get_object_sites($object_guid, $limit = 10, $offset = 0) {
return elgg_get_entities_from_relationship(array(
'relationship' => 'member_of_site',
'relationship_guid' => $object_guid,
- 'types' => 'site',
+ 'type' => 'site',
'limit' => $limit,
- 'offset' => $offset
+ 'offset' => $offset,
));
}
/**
* Runs unit tests for ElggObject
*
- * @param sting $hook unit_test
+ * @param string $hook unit_test
* @param string $type system
* @param mixed $value Array of tests
* @param mixed $params Params
diff --git a/engine/lib/opendd.php b/engine/lib/opendd.php
index f00ea6aab..7d635a295 100644
--- a/engine/lib/opendd.php
+++ b/engine/lib/opendd.php
@@ -7,6 +7,8 @@
* @version 0.4
*/
+// @codingStandardsIgnoreStart
+
/**
* Attempt to construct an ODD object out of a XmlElement or sub-elements.
*
@@ -103,3 +105,5 @@ function ODD_Import($xml) {
function ODD_Export(ODDDocument $document) {
return "$document";
}
+
+// @codingStandardsIgnoreEnd
diff --git a/engine/lib/output.php b/engine/lib/output.php
index 9295f2173..de4f911fb 100644
--- a/engine/lib/output.php
+++ b/engine/lib/output.php
@@ -12,29 +12,34 @@
*
* @param string $text The input string
*
- * @return string The output stirng with formatted links
- **/
+ * @return string The output string with formatted links
+ */
function parse_urls($text) {
+
+ // URI specification: http://www.ietf.org/rfc/rfc3986.txt
+ // This varies from the specification in the following ways:
+ // * Supports non-ascii characters
+ // * Does not allow parentheses and single quotes
+ // * Cuts off commas, exclamation points, and periods off as last character
+
// @todo this causes problems with <attr = "val">
// must be in <attr="val"> format (no space).
// By default htmlawed rewrites tags to this format.
// if PHP supported conditional negative lookbehinds we could use this:
// $r = preg_replace_callback('/(?<!=)(?<![ ])?(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i',
- //
- // we can put , in the list of excluded char but need to keep . because of domain names.
- // it is removed in the callback.
- $r = preg_replace_callback('/(?<!=)(?<!["\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\'\!\(\),]+)/i',
+ $r = preg_replace_callback('/(?<![=\/"\'])((ht|f)tps?:\/\/[^\s\r\n\t<>"\']+)/i',
create_function(
'$matches',
'
$url = $matches[1];
- $period = \'\';
- if (substr($url, -1, 1) == \'.\') {
- $period = \'.\';
- $url = trim($url, \'.\');
+ $punc = "";
+ $last = substr($url, -1, 1);
+ if (in_array($last, array(".", "!", ",", "(", ")"))) {
+ $punc = $last;
+ $url = rtrim($url, ".!,()");
}
$urltext = str_replace("/", "/<wbr />", $url);
- return "<a href=\"$url\">$urltext</a>$period";
+ return "<a href=\"$url\" rel=\"nofollow\">$urltext</a>$punc";
'
), $text);
@@ -224,7 +229,6 @@ function elgg_normalize_url($url) {
$php_5_3_0_to_5_3_2 = version_compare(PHP_VERSION, '5.3.0', '>=') &&
version_compare(PHP_VERSION, '5.3.3', '<');
- $validated = false;
if ($php_5_2_13_and_below || $php_5_3_0_to_5_3_2) {
$tmp_address = str_replace("-", "", $url);
$validated = filter_var($tmp_address, FILTER_VALIDATE_URL);
@@ -285,11 +289,9 @@ function elgg_get_friendly_title($title) {
return $result;
}
- // handle some special cases
- $title = str_replace('&amp;', 'and', $title);
- // quotes and angle brackets stored in the database as html encoded
- $title = htmlspecialchars_decode($title);
-
+ // titles are often stored HTML encoded
+ $title = html_entity_decode($title, ENT_QUOTES, 'UTF-8');
+
$title = ElggTranslit::urlize($title);
return $title;
@@ -419,9 +421,28 @@ function _elgg_html_decode($string) {
}
/**
+ * Prepares query string for output to prevent CSRF attacks.
+ *
+ * @param string $string
+ * @return string
+ *
+ * @access private
+ */
+function _elgg_get_display_query($string) {
+ //encode <,>,&, quotes and characters above 127
+ if (function_exists('mb_convert_encoding')) {
+ $display_query = mb_convert_encoding($string, 'HTML-ENTITIES', 'UTF-8');
+ } else {
+ // if no mbstring extension, we just strip characters
+ $display_query = preg_replace("/[^\x01-\x7F]/", "", $string);
+ }
+ return htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false);
+}
+
+/**
* Unit tests for Output
*
- * @param sting $hook unit_test
+ * @param string $hook unit_test
* @param string $type system
* @param mixed $value Array of tests
* @param mixed $params Params
diff --git a/engine/lib/pageowner.php b/engine/lib/pageowner.php
index 94765feee..bd63d08c6 100644
--- a/engine/lib/pageowner.php
+++ b/engine/lib/pageowner.php
@@ -29,7 +29,9 @@ function elgg_get_page_owner_guid($guid = 0) {
// return guid of page owner entity
$guid = elgg_trigger_plugin_hook('page_owner', 'system', NULL, 0);
- $page_owner_guid = $guid;
+ if ($guid) {
+ $page_owner_guid = $guid;
+ }
return $guid;
}
@@ -39,7 +41,7 @@ function elgg_get_page_owner_guid($guid = 0) {
*
* @note Access is disabled when getting the page owner entity.
*
- * @return ElggEntity|false The current page owner or false if none.
+ * @return ElggUser|ElggGroup|false The current page owner or false if none.
*
* @since 1.8.0
*/
@@ -113,6 +115,7 @@ function default_page_owner_handler($hook, $entity_type, $returnvalue, $params)
}
if ($user = get_user_by_username($username)) {
+ elgg_set_ignore_access($ia);
return $user->getGUID();
}
}
diff --git a/engine/lib/plugins.php b/engine/lib/plugins.php
index 94aff277e..d5d3db466 100644
--- a/engine/lib/plugins.php
+++ b/engine/lib/plugins.php
@@ -91,7 +91,9 @@ function elgg_get_plugin_ids_in_dir($dir = null) {
* @access private
*/
function elgg_generate_plugin_entities() {
+ // @todo $site unused, can remove?
$site = get_config('site');
+
$dir = elgg_get_plugins_path();
$db_prefix = elgg_get_config('dbprefix');
@@ -107,6 +109,7 @@ function elgg_generate_plugin_entities() {
$old_access = access_get_show_hidden_status();
access_show_hidden_entities(true);
$known_plugins = elgg_get_entities_from_relationship($options);
+ /* @var ElggPlugin[] $known_plugins */
if (!$known_plugins) {
$known_plugins = array();
@@ -138,7 +141,7 @@ function elgg_generate_plugin_entities() {
$index = $id_map[$plugin_id];
$plugin = $known_plugins[$index];
// was this plugin deleted and its entity disabled?
- if ($plugin->enabled != 'yes') {
+ if (!$plugin->isEnabled()) {
$plugin->enable();
$plugin->deactivate();
$plugin->setPriority('last');
@@ -192,7 +195,7 @@ function _elgg_cache_plugin_by_id(ElggPlugin $plugin) {
* Returns an ElggPlugin object with the path $path.
*
* @param string $plugin_id The id (dir name) of the plugin. NOT the guid.
- * @return mixed ElggPlugin or false.
+ * @return ElggPlugin|false
* @since 1.8.0
*/
function elgg_get_plugin_from_id($plugin_id) {
@@ -260,6 +263,8 @@ function elgg_get_max_plugin_priority() {
$data = get_data($q);
if ($data) {
$max = $data[0]->max;
+ } else {
+ $max = 1;
}
// can't have a priority of 0.
@@ -306,13 +311,11 @@ function elgg_is_active_plugin($plugin_id, $site_guid = null) {
* @access private
*/
function elgg_load_plugins() {
- global $CONFIG;
-
$plugins_path = elgg_get_plugins_path();
- $start_flags = ELGG_PLUGIN_INCLUDE_START
- | ELGG_PLUGIN_REGISTER_VIEWS
- | ELGG_PLUGIN_REGISTER_LANGUAGES
- | ELGG_PLUGIN_REGISTER_CLASSES;
+ $start_flags = ELGG_PLUGIN_INCLUDE_START |
+ ELGG_PLUGIN_REGISTER_VIEWS |
+ ELGG_PLUGIN_REGISTER_LANGUAGES |
+ ELGG_PLUGIN_REGISTER_CLASSES;
if (!$plugins_path) {
return false;
@@ -360,7 +363,7 @@ function elgg_load_plugins() {
*
* @param string $status The status of the plugins. active, inactive, or all.
* @param mixed $site_guid Optional site guid
- * @return array
+ * @return ElggPlugin[]
* @since 1.8.0
* @access private
*/
@@ -441,6 +444,7 @@ function elgg_set_plugin_priorities(array $order) {
// though we do start with 1
$order = array_values($order);
+ $missing_plugins = array();
foreach ($plugins as $plugin) {
$plugin_id = $plugin->getID();
@@ -639,19 +643,18 @@ function elgg_get_plugins_provides($type = null, $name = null) {
* @access private
*/
function elgg_check_plugins_provides($type, $name, $version = null, $comparison = 'ge') {
- if (!$provided = elgg_get_plugins_provides($type, $name)) {
+ $provided = elgg_get_plugins_provides($type, $name);
+ if (!$provided) {
return array(
'status' => false,
'version' => ''
);
}
- if ($provided) {
- if ($version) {
- $status = version_compare($provided['version'], $version, $comparison);
- } else {
- $status = true;
- }
+ if ($version) {
+ $status = version_compare($provided['version'], $version, $comparison);
+ } else {
+ $status = true;
}
return array(
@@ -861,9 +864,9 @@ function elgg_set_plugin_user_setting($name, $value, $user_guid = null, $plugin_
/**
* Unsets a user-specific plugin setting
*
- * @param str $name Name of the setting
- * @param int $user_guid Defaults to logged in user
- * @param str $plugin_id Defaults to contextual plugin name
+ * @param string $name Name of the setting
+ * @param int $user_guid Defaults to logged in user
+ * @param string $plugin_id Defaults to contextual plugin name
*
* @return bool
* @since 1.8.0
@@ -1087,7 +1090,7 @@ function plugin_run_once() {
/**
* Runs unit tests for the entity objects.
*
- * @param sting $hook unit_test
+ * @param string $hook unit_test
* @param string $type system
* @param mixed $value Array of tests
* @param mixed $params Params
@@ -1102,6 +1105,49 @@ function plugins_test($hook, $type, $value, $params) {
}
/**
+ * Checks on deactivate plugin event if disabling it won't create unmet dependencies and blocks disable in such case.
+ *
+ * @param string $event deactivate
+ * @param string $type plugin
+ * @param array $params Parameters array containing entry with ELggPlugin instance under 'plugin_entity' key
+ * @return bool false to block plugin deactivation action
+ *
+ * @access private
+ */
+function _plugins_deactivate_dependency_check($event, $type, $params) {
+ $plugin_id = $params['plugin_entity']->getManifest()->getPluginID();
+ $plugin_name = $params['plugin_entity']->getManifest()->getName();
+
+ $active_plugins = elgg_get_plugins();
+
+ $dependents = array();
+ foreach ($active_plugins as $plugin) {
+ $manifest = $plugin->getManifest();
+ $requires = $manifest->getRequires();
+
+ foreach ($requires as $required) {
+ if ($required['type'] == 'plugin' && $required['name'] == $plugin_id) {
+ // there are active dependents
+ $dependents[$manifest->getPluginID()] = $plugin;
+ }
+ }
+ }
+
+ if ($dependents) {
+ $list = '<ul>';
+ // construct error message and prevent disabling
+ foreach ($dependents as $dependent) {
+ $list .= '<li>' . $dependent->getManifest()->getName() . '</li>';
+ }
+ $list .= '</ul>';
+
+ register_error(elgg_echo('ElggPlugin:Dependencies:ActiveDependent', array($plugin_name, $list)));
+
+ return false;
+ }
+}
+
+/**
* Initialize the plugin system
* Listens to system init and registers actions
*
@@ -1112,6 +1158,10 @@ function plugin_init() {
run_function_once("plugin_run_once");
elgg_register_plugin_hook_handler('unit_test', 'system', 'plugins_test');
+
+ // note - plugins are booted by the time this handler is registered
+ // deactivation due to error may have already occurred
+ elgg_register_event_handler('deactivate', 'plugin', '_plugins_deactivate_dependency_check');
elgg_register_action("plugins/settings/save", '', 'admin');
elgg_register_action("plugins/usersettings/save");
diff --git a/engine/lib/relationships.php b/engine/lib/relationships.php
index 01654b1ce..b0cd627fc 100644
--- a/engine/lib/relationships.php
+++ b/engine/lib/relationships.php
@@ -12,7 +12,7 @@
*
* @param stdClass $row Database row from the relationship table
*
- * @return stdClass or ElggMetadata
+ * @return ElggRelationship|stdClass
* @access private
*/
function row_to_elggrelationship($row) {
@@ -28,7 +28,7 @@ function row_to_elggrelationship($row) {
*
* @param int $id The ID of a relationship
*
- * @return mixed
+ * @return ElggRelationship|false
*/
function get_relationship($id) {
global $CONFIG;
@@ -109,7 +109,7 @@ function add_entity_relationship($guid_one, $relationship, $guid_two) {
* @param string $relationship The type of relationship
* @param int $guid_two The GUID of the entity the relationship is with
*
- * @return object|false Depending on success
+ * @return ElggRelationship|false Depending on success
*/
function check_entity_relationship($guid_one, $relationship, $guid_two) {
global $CONFIG;
@@ -123,7 +123,7 @@ function check_entity_relationship($guid_one, $relationship, $guid_two) {
AND relationship='$relationship'
AND guid_two=$guid_two limit 1";
- $row = get_data_row($query);
+ $row = row_to_elggrelationship(get_data_row($query));
if ($row) {
return $row;
}
@@ -220,7 +220,7 @@ function remove_entity_relationships($guid_one, $relationship = "", $inverse = f
* @param int $guid The GUID of the relationship owner
* @param bool $inverse_relationship Inverse relationship owners?
*
- * @return mixed
+ * @return ElggRelationship[]
*/
function get_entity_relationships($guid, $inverse_relationship = FALSE) {
global $CONFIG;
@@ -259,7 +259,7 @@ function get_entity_relationships($guid, $inverse_relationship = FALSE) {
*
* inverse_relationship => BOOL Inverse the relationship
*
- * @return mixed If count, int. If not count, array. false on errors.
+ * @return ElggEntity[]|mixed If count, int. If not count, array. false on errors.
* @since 1.7.0
*/
function elgg_get_entities_from_relationship($options) {
@@ -316,7 +316,7 @@ function elgg_get_entities_from_relationship($options) {
* Provide in table.column format.
* @param string $relationship Relationship string
* @param int $relationship_guid Entity guid to check
- * @param string $inverse_relationship Inverse relationship check?
+ * @param bool $inverse_relationship Inverse relationship check?
*
* @return mixed
* @since 1.7.0
@@ -363,7 +363,7 @@ $relationship_guid = NULL, $inverse_relationship = FALSE) {
/**
* Returns a viewable list of entities by relationship
*
- * @param array $options
+ * @param array $options Options array for retrieval of entities
*
* @see elgg_list_entities()
* @see elgg_get_entities_from_relationship()
@@ -381,7 +381,7 @@ function elgg_list_entities_from_relationship(array $options = array()) {
*
* @param array $options An options array compatible with
* elgg_get_entities_from_relationship()
- * @return mixed int If count, int. If not count, array. false on errors.
+ * @return ElggEntity[]|mixed int If count, int. If not count, array. false on errors.
* @since 1.8.0
*/
function elgg_get_entities_from_relationship_count(array $options = array()) {
@@ -398,7 +398,7 @@ function elgg_get_entities_from_relationship_count(array $options = array()) {
*
* @param array $options Options array
*
- * @return array
+ * @return string
* @since 1.8.0
*/
function elgg_list_entities_from_relationship_count($options) {
@@ -499,7 +499,7 @@ function already_attached($guid_one, $guid_two) {
* @param int $guid Entity GUID
* @param string $type The type of object to return e.g. 'file', 'friend_of' etc
*
- * @return an array of objects
+ * @return ElggEntity[]
* @access private
*/
function get_attachments($guid, $type = "") {
@@ -507,7 +507,7 @@ function get_attachments($guid, $type = "") {
'relationship' => 'attached',
'relationship_guid' => $guid,
'inverse_relationship' => false,
- 'types' => $type,
+ 'type' => $type,
'subtypes' => '',
'owner_guid' => 0,
'order_by' => 'time_created desc',
@@ -571,9 +571,8 @@ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par
if ($element instanceof ODDRelationship) {
$tmp = new ElggRelationship();
$tmp->import($element);
-
- return $tmp;
}
+ return $tmp;
}
/**
@@ -586,11 +585,10 @@ function import_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par
*
* @elgg_event_handler export all
* @return mixed
+ * @throws InvalidParameterException
* @access private
*/
function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $params) {
- global $CONFIG;
-
// Sanity check values
if ((!is_array($params)) && (!isset($params['guid']))) {
throw new InvalidParameterException(elgg_echo('InvalidParameterException:GUIDNotForExport'));
@@ -624,9 +622,9 @@ function export_relationship_plugin_hook($hook, $entity_type, $returnvalue, $par
* @access private
*/
function relationship_notification_hook($event, $type, $object) {
-
+ /* @var ElggRelationship $object */
$user_one = get_entity($object->guid_one);
- $user_two = get_entity($object->guid_two);
+ /* @var ElggUser $user_one */
return notify_user($object->guid_two,
$object->guid_one,
diff --git a/engine/lib/river.php b/engine/lib/river.php
index 33f34360e..e92040eb7 100644
--- a/engine/lib/river.php
+++ b/engine/lib/river.php
@@ -120,7 +120,7 @@ $posted = 0, $annotation_id = 0) {
* subtypes => STR|ARR Entity subtype string(s)
* type_subtype_pairs => ARR Array of type => subtype pairs where subtype
* can be an array of subtype strings
- *
+ *
* posted_time_lower => INT The lower bound on the time posted
* posted_time_upper => INT The upper bound on the time posted
*
@@ -380,10 +380,10 @@ function _elgg_prefetch_river_entities(array $river_items) {
// prefetch objects and subjects
$guids = array();
foreach ($river_items as $item) {
- if ($item->subject_guid && !retrieve_cached_entity($item->subject_guid)) {
+ if ($item->subject_guid && !_elgg_retrieve_cached_entity($item->subject_guid)) {
$guids[$item->subject_guid] = true;
}
- if ($item->object_guid && !retrieve_cached_entity($item->object_guid)) {
+ if ($item->object_guid && !_elgg_retrieve_cached_entity($item->object_guid)) {
$guids[$item->object_guid] = true;
}
}
@@ -402,7 +402,7 @@ function _elgg_prefetch_river_entities(array $river_items) {
$guids = array();
foreach ($river_items as $item) {
$object = $item->getObjectEntity();
- if ($object->container_guid && !retrieve_cached_entity($object->container_guid)) {
+ if ($object->container_guid && !_elgg_retrieve_cached_entity($object->container_guid)) {
$guids[$object->container_guid] = true;
}
}
@@ -434,8 +434,13 @@ function elgg_list_river(array $options = array()) {
'pagination' => TRUE,
'list_class' => 'elgg-list-river elgg-river', // @todo remove elgg-river in Elgg 1.9
);
-
+
$options = array_merge($defaults, $options);
+
+ if (!$options["limit"] && !$options["offset"]) {
+ // no need for pagination if listing is unlimited
+ $options["pagination"] = false;
+ }
$options['count'] = TRUE;
$count = elgg_get_river($options);
@@ -445,6 +450,7 @@ function elgg_list_river(array $options = array()) {
$options['count'] = $count;
$options['items'] = $items;
+
return elgg_view('page/components/list', $options);
}
@@ -500,6 +506,7 @@ function elgg_get_river_type_subtype_where_sql($table, $types, $subtypes, $pairs
return '';
}
+ $wheres = array();
$types_wheres = array();
$subtypes_wheres = array();
@@ -644,7 +651,7 @@ function update_river_access_by_object($object_guid, $access_id) {
}
/**
- * Page handler for activiy
+ * Page handler for activity
*
* @param array $page
* @return bool
@@ -663,10 +670,6 @@ function elgg_river_page_handler($page) {
}
set_input('page_type', $page_type);
- // content filter code here
- $entity_type = '';
- $entity_subtype = '';
-
require_once("{$CONFIG->path}pages/river.php");
return true;
}
diff --git a/engine/lib/sessions.php b/engine/lib/sessions.php
index 72ca0a1c2..e3d5ce9cd 100644
--- a/engine/lib/sessions.php
+++ b/engine/lib/sessions.php
@@ -87,6 +87,9 @@ function elgg_is_admin_logged_in() {
*/
function elgg_is_admin_user($user_guid) {
global $CONFIG;
+
+ $user_guid = (int)$user_guid;
+
// cannot use magic metadata here because of recursion
// must support the old way of getting admin from metadata
@@ -286,8 +289,6 @@ function check_rate_limit_exceeded($user_guid) {
* @throws LoginException
*/
function login(ElggUser $user, $persistent = false) {
- global $CONFIG;
-
// User is banned, return false.
if ($user->isBanned()) {
throw new LoginException(elgg_echo('LoginException:BannedUser'));
@@ -325,6 +326,12 @@ function login(ElggUser $user, $persistent = false) {
set_last_login($_SESSION['guid']);
reset_login_failure_count($user->guid); // Reset any previous failed login attempts
+ // if memcache is enabled, invalidate the user in memcache @see https://github.com/Elgg/Elgg/issues/3143
+ if (is_memcache_available()) {
+ // this needs to happen with a shutdown function because of the timing with set_last_login()
+ register_shutdown_function("_elgg_invalidate_memcache_for_entity", $_SESSION['guid']);
+ }
+
return true;
}
@@ -334,8 +341,6 @@ function login(ElggUser $user, $persistent = false) {
* @return bool
*/
function logout() {
- global $CONFIG;
-
if (isset($_SESSION['user'])) {
if (!elgg_trigger_event('logout', 'user', $_SESSION['user'])) {
return false;
@@ -616,10 +621,8 @@ function _elgg_session_destroy($id) {
global $sess_save_path;
$sess_file = "$sess_save_path/sess_$id";
- return(@unlink($sess_file));
+ return @unlink($sess_file);
}
-
- return false;
}
/**
diff --git a/engine/lib/sites.php b/engine/lib/sites.php
index d9eb2d25e..3de0eccc2 100644
--- a/engine/lib/sites.php
+++ b/engine/lib/sites.php
@@ -26,7 +26,7 @@ function elgg_get_site_entity($site_guid = 0) {
$site = get_entity($site_guid);
}
- if($site instanceof ElggSite){
+ if ($site instanceof ElggSite) {
$result = $site;
}
@@ -118,8 +118,6 @@ function create_site_entity($guid, $name, $description, $url) {
* @return bool
*/
function add_site_user($site_guid, $user_guid) {
- global $CONFIG;
-
$site_guid = (int)$site_guid;
$user_guid = (int)$user_guid;
@@ -150,8 +148,6 @@ function remove_site_user($site_guid, $user_guid) {
* @return mixed
*/
function add_site_object($site_guid, $object_guid) {
- global $CONFIG;
-
$site_guid = (int)$site_guid;
$object_guid = (int)$object_guid;
@@ -192,8 +188,8 @@ function get_site_objects($site_guid, $subtype = "", $limit = 10, $offset = 0) {
'relationship' => 'member_of_site',
'relationship_guid' => $site_guid,
'inverse_relationship' => TRUE,
- 'types' => 'object',
- 'subtypes' => $subtype,
+ 'type' => 'object',
+ 'subtype' => $subtype,
'limit' => $limit,
'offset' => $offset
));
@@ -242,7 +238,7 @@ function get_site_domain($guid) {
/**
* Unit tests for sites
*
- * @param sting $hook unit_test
+ * @param string $hook unit_test
* @param string $type system
* @param mixed $value Array of tests
* @param mixed $params Params
diff --git a/engine/lib/statistics.php b/engine/lib/statistics.php
index 5ee640549..4cb0bb0b8 100644
--- a/engine/lib/statistics.php
+++ b/engine/lib/statistics.php
@@ -95,15 +95,20 @@ function get_number_users($show_deactivated = false) {
* @return string
*/
function get_online_users() {
- $count = find_active_users(600, 10, 0, true);
- $objects = find_active_users(600, 10);
+ $limit = max(0, (int) get_input("limit", 10));
+ $offset = max(0, (int) get_input("offset", 0));
+
+ $count = find_active_users(600, $limit, $offset, true);
+ $objects = find_active_users(600, $limit, $offset);
if ($objects) {
return elgg_view_entity_list($objects, array(
'count' => $count,
- 'limit' => 10
+ 'limit' => $limit,
+ 'offset' => $offset
));
}
+ return '';
}
/**
diff --git a/engine/lib/system_log.php b/engine/lib/system_log.php
index 53fa24557..84302632e 100644
--- a/engine/lib/system_log.php
+++ b/engine/lib/system_log.php
@@ -10,6 +10,8 @@
/**
* Retrieve the system log based on a number of parameters.
*
+ * @todo too many args, and the first arg is too confusing
+ *
* @param int|array $by_user The guid(s) of the user(s) who initiated the event.
* Use 0 for unowned entries. Anything else falsey means anyone.
* @param string $event The event you are searching on.
@@ -22,12 +24,12 @@
* @param int $timebefore Lower time limit
* @param int $timeafter Upper time limit
* @param int $object_id GUID of an object
- * @param str $ip_address The IP address.
+ * @param string $ip_address The IP address.
* @return mixed
*/
-function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "",
-$limit = 10, $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0,
-$ip_address = false) {
+function get_system_log($by_user = "", $event = "", $class = "", $type = "", $subtype = "", $limit = 10,
+ $offset = 0, $count = false, $timebefore = 0, $timeafter = 0, $object_id = 0,
+ $ip_address = "") {
global $CONFIG;
@@ -166,6 +168,7 @@ function system_log($object, $event) {
if ($object instanceof Loggable) {
+ /* @var ElggEntity|ElggExtender $object */
if (datalist_get('version') < 2012012000) {
// this is a site that doesn't have the ip_address column yet
return;
@@ -184,7 +187,16 @@ function system_log($object, $event) {
$object_subtype = $object->getSubtype();
$event = sanitise_string($event);
$time = time();
- $ip_address = sanitise_string($_SERVER['REMOTE_ADDR']);
+
+ if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
+ $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']));
+ } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
+ $ip_address = array_pop(explode(',', $_SERVER['HTTP_X_REAL_IP']));
+ } else {
+ $ip_address = $_SERVER['REMOTE_ADDR'];
+ }
+ $ip_address = sanitise_string($ip_address);
+
$performed_by = elgg_get_logged_in_user_guid();
if (isset($object->access_id)) {
diff --git a/engine/lib/tags.php b/engine/lib/tags.php
index a0887d0f3..586a9b9e4 100644
--- a/engine/lib/tags.php
+++ b/engine/lib/tags.php
@@ -48,7 +48,7 @@ function calculate_tag_size($min, $max, $number_of_tags, $buckets = 6) {
* @param array $tags The array of tags.
* @param int $buckets The number of buckets
*
- * @return An associated array of tags with a weighting, this can then be mapped to a display class.
+ * @return array An associated array of tags with a weighting, this can then be mapped to a display class.
* @access private
*/
function generate_tag_cloud(array $tags, $buckets = 6) {
@@ -114,8 +114,8 @@ function generate_tag_cloud(array $tags, $buckets = 6) {
*
* joins => array() Additional joins
*
- * @return false/array - if no tags or error, false
- * otherwise, array of objects with ->tag and ->total values
+ * @return object[]|false If no tags or error, false
+ * otherwise, array of objects with ->tag and ->total values
* @since 1.7.1
*/
function elgg_get_tags(array $options = array()) {
@@ -172,6 +172,7 @@ function elgg_get_tags(array $options = array()) {
// catch for tags that were spaces
$wheres[] = "msv.string != ''";
+ $sanitised_tags = array();
foreach ($options['tag_names'] as $tag) {
$sanitised_tags[] = '"' . sanitise_string($tag) . '"';
}
diff --git a/engine/lib/upgrade.php b/engine/lib/upgrade.php
index f4f4b16f5..158ec9ec1 100644
--- a/engine/lib/upgrade.php
+++ b/engine/lib/upgrade.php
@@ -17,8 +17,9 @@
* @access private
*/
function upgrade_code($version, $quiet = FALSE) {
+ // do not remove - upgrade scripts depend on this
global $CONFIG;
-
+
$version = (int) $version;
$upgrade_path = elgg_get_config('path') . 'engine/lib/upgrades/';
$processed_upgrades = elgg_get_processed_upgrades();
@@ -244,7 +245,7 @@ function version_upgrade() {
// No version number? Oh snap...this is an upgrade from a clean installation < 1.7.
// Run all upgrades without error reporting and hope for the best.
- // See http://trac.elgg.org/elgg/ticket/1432 for more.
+ // See https://github.com/elgg/elgg/issues/1432 for more.
$quiet = !$dbversion;
// Note: Database upgrades are deprecated as of 1.8. Use code upgrades. See #1433
@@ -291,7 +292,6 @@ function elgg_upgrade_bootstrap_17_to_18() {
'2011010101.php',
);
- $upgrades_17 = array();
$upgrade_files = elgg_get_upgrade_files();
$processed_upgrades = array();
@@ -354,15 +354,12 @@ function _elgg_upgrade_unlock() {
* @access private
*/
function _elgg_upgrade_is_locked() {
- global $CONFIG, $DB_QUERY_CACHE;
-
+ global $CONFIG;
+
$is_locked = count(get_data("show tables like '{$CONFIG->dbprefix}upgrade_lock'"));
-
- // Invalidate query cache
- if ($DB_QUERY_CACHE) {
- $DB_QUERY_CACHE->clear();
- elgg_log("Query cache invalidated", 'NOTICE');
- }
-
+
+ // @todo why?
+ _elgg_invalidate_query_cache();
+
return $is_locked;
}
diff --git a/engine/lib/upgrades/2009102801.php b/engine/lib/upgrades/2009102801.php
index cab9a6835..3ad113fb2 100644
--- a/engine/lib/upgrades/2009102801.php
+++ b/engine/lib/upgrades/2009102801.php
@@ -203,14 +203,15 @@ function user_file_matrix($guid) {
return "$time_created/$user->guid/";
}
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE;
+global $ENTITY_CACHE, $CONFIG;
/**
* Upgrade file locations
*/
$users = mysql_query("SELECT guid, username
FROM {$CONFIG->dbprefix}users_entity WHERE username != ''");
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
$to = $CONFIG->dataroot . user_file_matrix($user->guid);
foreach (array('1_0', '1_1', '1_6') as $version) {
diff --git a/engine/lib/upgrades/2010033101.php b/engine/lib/upgrades/2010033101.php
index 0bffee001..4779295fd 100644
--- a/engine/lib/upgrades/2010033101.php
+++ b/engine/lib/upgrades/2010033101.php
@@ -1,7 +1,7 @@
<?php
/**
- * Conditional upgrade for UTF8 as described in http://trac.elgg.org/ticket/1928
+ * Conditional upgrade for UTF8 as described in https://github.com/elgg/elgg/issues/1928
*/
// get_version() returns the code version.
diff --git a/engine/lib/upgrades/2010061501.php b/engine/lib/upgrades/2010061501.php
index 9ff7d3102..744c28fd5 100644
--- a/engine/lib/upgrades/2010061501.php
+++ b/engine/lib/upgrades/2010061501.php
@@ -45,7 +45,7 @@ if ($dbversion < 2009100701) {
}
}
- global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE;
+ global $ENTITY_CACHE;
/**
Upgrade file locations
@@ -60,7 +60,9 @@ if ($dbversion < 2009100701) {
$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
WHERE username != ''", $link);
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
+
$to = $CONFIG->dataroot . user_file_matrix($user->guid);
foreach (array('1_0', '1_1', '1_6') as $version) {
diff --git a/engine/lib/upgrades/2010071001.php b/engine/lib/upgrades/2010071001.php
index 1b5d379d8..5594493a8 100644
--- a/engine/lib/upgrades/2010071001.php
+++ b/engine/lib/upgrades/2010071001.php
@@ -30,11 +30,12 @@ function user_file_matrix_2010071001($guid) {
$sizes = array('large', 'medium', 'small', 'tiny', 'master', 'topbar');
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG;
+global $ENTITY_CACHE, $CONFIG;
$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
WHERE username != ''");
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
$user_directory = user_file_matrix_2010071001($user->guid);
if (!$user_directory) {
diff --git a/engine/lib/upgrades/2010071002.php b/engine/lib/upgrades/2010071002.php
index 30bd6538c..52aa15ef5 100644
--- a/engine/lib/upgrades/2010071002.php
+++ b/engine/lib/upgrades/2010071002.php
@@ -4,12 +4,13 @@
*/
// loop through all users checking collections and notifications
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG;
+global $ENTITY_CACHE, $CONFIG;
global $NOTIFICATION_HANDLERS;
$users = mysql_query("SELECT guid, username FROM {$CONFIG->dbprefix}users_entity
WHERE username != ''");
while ($user = mysql_fetch_object($users)) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
$user = get_entity($user->guid);
foreach ($NOTIFICATION_HANDLERS as $method => $foo) {
diff --git a/engine/lib/upgrades/2011052801.php b/engine/lib/upgrades/2011052801.php
index 8084bc06c..b5a8e1018 100644
--- a/engine/lib/upgrades/2011052801.php
+++ b/engine/lib/upgrades/2011052801.php
@@ -2,7 +2,7 @@
/**
* Make sure all users have the relationship member_of_site
*/
-global $DB_QUERY_CACHE, $DB_PROFILE, $ENTITY_CACHE, $CONFIG;
+global $ENTITY_CACHE;
$db_prefix = get_config('dbprefix');
$limit = 100;
@@ -17,7 +17,8 @@ $q = "SELECT e.* FROM {$db_prefix}entities e
$users = get_data($q);
while ($users) {
- $DB_QUERY_CACHE = $DB_PROFILE = $ENTITY_CACHE = array();
+ $ENTITY_CACHE = array();
+ _elgg_invalidate_query_cache();
// do manually to not trigger any events because these aren't new users.
foreach ($users as $user) {
diff --git a/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php b/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php
index 07732f261..780038c32 100644
--- a/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php
+++ b/engine/lib/upgrades/2012041801-1.8.3-multiple_user_tokens-852225f7fd89f6c5.php
@@ -3,7 +3,7 @@
* Elgg 1.8.3 upgrade 2012041801
* multiple_user_tokens
*
- * Fixes http://trac.elgg.org/ticket/4291
+ * Fixes https://github.com/elgg/elgg/issues/4291
* Removes the unique index on users_apisessions for user_guid and site_guid
*/
diff --git a/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php b/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php
new file mode 100644
index 000000000..8eccf05e2
--- /dev/null
+++ b/engine/lib/upgrades/2013030600-1.8.13-update_user_location-8999eb8bf1bdd9a3.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * Elgg 1.8.14 upgrade 2013030600
+ * update_user_location
+ *
+ * Before Elgg 1.8, a location like "London, England" would be stored as an array.
+ * This script turns that back into a string.
+ */
+
+$ia = elgg_set_ignore_access(true);
+$options = array(
+ 'type' => 'user',
+ 'limit' => 0,
+);
+$batch = new ElggBatch('elgg_get_entities', $options);
+
+foreach ($batch as $entity) {
+ _elgg_invalidate_query_cache();
+
+ if (is_array($entity->location)) {
+ $entity->location = implode(', ', $entity->location);
+ }
+}
+elgg_set_ignore_access($ia);
diff --git a/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php b/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php
new file mode 100644
index 000000000..ee99bdbc8
--- /dev/null
+++ b/engine/lib/upgrades/2013051700-1.8.15-add_missing_group_index-52a63a3a3ffaced2.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013051700
+ * add_missing_group_index
+ *
+ * Some Elgg sites are missing the groups_entity full text index on name and
+ * description. This checks if it exists and adds it if it does not.
+ */
+
+$db_prefix = elgg_get_config('dbprefix');
+
+$full_text_index_exists = false;
+$results = get_data("SHOW INDEX FROM {$db_prefix}groups_entity");
+if ($results) {
+ foreach ($results as $result) {
+ if ($result->Index_type === 'FULLTEXT') {
+ $full_text_index_exists = true;
+ }
+ }
+}
+
+if ($full_text_index_exists == false) {
+ $query = "ALTER TABLE {$db_prefix}groups_entity
+ ADD FULLTEXT name_2 (name, description)";
+ if (!update_data($query)) {
+ elgg_log("Failed to add full text index to groups_entity table", 'ERROR');
+ }
+}
diff --git a/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php b/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php
new file mode 100644
index 000000000..d333a6cd2
--- /dev/null
+++ b/engine/lib/upgrades/2013052900-1.8.15-ipv6_in_syslog-f5c2cc0196e9e731.php
@@ -0,0 +1,12 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013052900
+ * ipv6_in_syslog
+ *
+ * Upgrade the ip column in system_log to be able to store ipv6 addresses
+ */
+
+$db_prefix = elgg_get_config('dbprefix');
+$q = "ALTER TABLE {$db_prefix}system_log MODIFY COLUMN ip_address varchar(46) NOT NULL";
+
+update_data($q); \ No newline at end of file
diff --git a/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php
new file mode 100644
index 000000000..538d74dd6
--- /dev/null
+++ b/engine/lib/upgrades/2013060900-1.8.15-site_secret-404fc165cf9e0ac9.php
@@ -0,0 +1,16 @@
+<?php
+/**
+ * Elgg 1.8.15 upgrade 2013060900
+ * site_secret
+ *
+ * Description
+ */
+
+$strength = _elgg_get_site_secret_strength();
+
+if ($strength !== 'strong') {
+ // a new key is needed immediately
+ register_translations(elgg_get_root_path() . 'languages/');
+
+ elgg_add_admin_notice('weak_site_key', elgg_echo("upgrade:site_secret_warning:$strength"));
+}
diff --git a/engine/lib/upgrades/create_upgrade.php b/engine/lib/upgrades/create_upgrade.php
index 3652e18a2..b34f31b7e 100644
--- a/engine/lib/upgrades/create_upgrade.php
+++ b/engine/lib/upgrades/create_upgrade.php
@@ -93,7 +93,7 @@ if (!$h) {
die("Could not open file $upgrade_file");
}
-if (!fputs($h, $upgrade_code)) {
+if (!fwrite($h, $upgrade_code)) {
die("Could not write to $upgrade_file");
} else {
elgg_set_version_dot_php_version($upgrade_version);
@@ -128,8 +128,9 @@ function elgg_set_version_dot_php_version($version) {
rewind($h);
- fputs($h, $out);
+ fwrite($h, $out);
fclose($h);
+ return true;
}
/**
diff --git a/engine/lib/user_settings.php b/engine/lib/user_settings.php
index e4069fb53..0e36dc46d 100644
--- a/engine/lib/user_settings.php
+++ b/engine/lib/user_settings.php
@@ -265,9 +265,9 @@ function elgg_set_user_default_access() {
* @access private
*/
function usersettings_pagesetup() {
- if (elgg_get_context() == "settings") {
- $user = elgg_get_page_owner_entity();
+ $user = elgg_get_page_owner_entity();
+ if ($user && elgg_get_context() == "settings") {
$params = array(
'name' => '1_account',
'text' => elgg_echo('usersettings:user:opt:linktext'),
@@ -308,7 +308,7 @@ function usersettings_page_handler($page) {
$user = get_user_by_username($page[1]);
elgg_set_page_owner_guid($user->guid);
} else {
- $user = elgg_get_logged_in_user_guid();
+ $user = elgg_get_logged_in_user_entity();
elgg_set_page_owner_guid($user->guid);
}
@@ -332,6 +332,7 @@ function usersettings_page_handler($page) {
require $path;
return true;
}
+ return false;
}
/**
diff --git a/engine/lib/users.php b/engine/lib/users.php
index 95ef9d176..a8fb9121c 100644
--- a/engine/lib/users.php
+++ b/engine/lib/users.php
@@ -237,7 +237,7 @@ function make_user_admin($user_guid) {
}
$r = update_data("UPDATE {$CONFIG->dbprefix}users_entity set admin='yes' where guid=$user_guid");
- invalidate_cache_for_entity($user_guid);
+ _elgg_invalidate_cache_for_entity($user_guid);
return $r;
}
@@ -273,7 +273,7 @@ function remove_user_admin($user_guid) {
}
$r = update_data("UPDATE {$CONFIG->dbprefix}users_entity set admin='no' where guid=$user_guid");
- invalidate_cache_for_entity($user_guid);
+ _elgg_invalidate_cache_for_entity($user_guid);
return $r;
}
@@ -290,7 +290,7 @@ function remove_user_admin($user_guid) {
* @param int $limit Number of results to return
* @param int $offset Any indexing offset
*
- * @return false|array On success, an array of ElggSites
+ * @return ElggSite[]|false On success, an array of ElggSites
*/
function get_user_sites($user_guid, $limit = 10, $offset = 0) {
$user_guid = (int)$user_guid;
@@ -302,7 +302,7 @@ function get_user_sites($user_guid, $limit = 10, $offset = 0) {
'relationship' => 'member_of_site',
'relationship_guid' => $user_guid,
'inverse_relationship' => FALSE,
- 'types' => 'site',
+ 'type' => 'site',
'limit' => $limit,
'offset' => $offset,
));
@@ -343,8 +343,6 @@ function user_add_friend($user_guid, $friend_guid) {
* @return bool Depending on success
*/
function user_remove_friend($user_guid, $friend_guid) {
- global $CONFIG;
-
$user_guid = (int) $user_guid;
$friend_guid = (int) $friend_guid;
@@ -379,7 +377,7 @@ function user_is_friend($user_guid, $friend_guid) {
* @param int $limit Number of results to return (default 10)
* @param int $offset Indexing offset, if any
*
- * @return false|array Either an array of ElggUsers or false, depending on success
+ * @return ElggUser[]|false Either an array of ElggUsers or false, depending on success
*/
function get_user_friends($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10,
$offset = 0) {
@@ -387,8 +385,8 @@ $offset = 0) {
return elgg_get_entities_from_relationship(array(
'relationship' => 'friend',
'relationship_guid' => $user_guid,
- 'types' => 'user',
- 'subtypes' => $subtype,
+ 'type' => 'user',
+ 'subtype' => $subtype,
'limit' => $limit,
'offset' => $offset
));
@@ -402,7 +400,7 @@ $offset = 0) {
* @param int $limit Number of results to return (default 10)
* @param int $offset Indexing offset, if any
*
- * @return false|array Either an array of ElggUsers or false, depending on success
+ * @return ElggUser[]|false Either an array of ElggUsers or false, depending on success
*/
function get_user_friends_of($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10,
$offset = 0) {
@@ -411,8 +409,8 @@ $offset = 0) {
'relationship' => 'friend',
'relationship_guid' => $user_guid,
'inverse_relationship' => TRUE,
- 'types' => 'user',
- 'subtypes' => $subtype,
+ 'type' => 'user',
+ 'subtype' => $subtype,
'limit' => $limit,
'offset' => $offset
));
@@ -428,7 +426,7 @@ $offset = 0) {
* @param int $timelower The earliest time the entity can have been created. Default: all
* @param int $timeupper The latest time the entity can have been created. Default: all
*
- * @return false|array An array of ElggObjects or false, depending on success
+ * @return ElggObject[]|false An array of ElggObjects or false, depending on success
*/
function get_user_friends_objects($user_guid, $subtype = ELGG_ENTITIES_ANY_VALUE, $limit = 10,
$offset = 0, $timelower = 0, $timeupper = 0) {
@@ -555,13 +553,18 @@ function get_user($guid) {
function get_user_by_username($username) {
global $CONFIG, $USERNAME_TO_GUID_MAP_CACHE;
+ // Fixes #6052. Username is frequently sniffed from the path info, which,
+ // unlike $_GET, is not URL decoded. If the username was not URL encoded,
+ // this is harmless.
+ $username = rawurldecode($username);
+
$username = sanitise_string($username);
$access = get_access_sql_suffix('e');
// Caching
if ((isset($USERNAME_TO_GUID_MAP_CACHE[$username]))
- && (retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) {
- return retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]);
+ && (_elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]))) {
+ return _elgg_retrieve_cached_entity($USERNAME_TO_GUID_MAP_CACHE[$username]);
}
$query = "SELECT e.* from {$CONFIG->dbprefix}users_entity u
@@ -594,9 +597,9 @@ function get_user_by_code($code) {
// Caching
if ((isset($CODE_TO_GUID_MAP_CACHE[$code]))
- && (retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]))) {
+ && (_elgg_retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]))) {
- return retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]);
+ return _elgg_retrieve_cached_entity($CODE_TO_GUID_MAP_CACHE[$code]);
}
$query = "SELECT e.* from {$CONFIG->dbprefix}users_entity u
@@ -675,25 +678,22 @@ function find_active_users($seconds = 600, $limit = 10, $offset = 0, $count = fa
* @return bool
*/
function send_new_password_request($user_guid) {
- global $CONFIG;
-
$user_guid = (int)$user_guid;
$user = get_entity($user_guid);
- if ($user) {
+ if ($user instanceof ElggUser) {
// generate code
$code = generate_random_cleartext_password();
$user->setPrivateSetting('passwd_conf_code', $code);
-
// generate link
- $link = $CONFIG->site->url . "resetpassword?u=$user_guid&c=$code";
+ $link = elgg_get_site_url() . "resetpassword?u=$user_guid&c=$code";
// generate email
$email = elgg_echo('email:resetreq:body', array($user->name, $_SERVER['REMOTE_ADDR'], $link));
- return notify_user($user->guid, $CONFIG->site->guid,
- elgg_echo('email:resetreq:subject'), $email, NULL, 'email');
+ return notify_user($user->guid, elgg_get_site_entity()->guid,
+ elgg_echo('email:resetreq:subject'), $email, array(), 'email');
}
return false;
@@ -710,19 +710,18 @@ function send_new_password_request($user_guid) {
* @return bool
*/
function force_user_password_reset($user_guid, $password) {
- global $CONFIG;
-
$user = get_entity($user_guid);
+ if ($user instanceof ElggUser) {
+ $ia = elgg_set_ignore_access();
- if ($user) {
- $salt = generate_random_cleartext_password(); // Reset the salt
- $user->salt = $salt;
+ $user->salt = generate_random_cleartext_password();
+ $hash = generate_user_password($user, $password);
+ $user->password = $hash;
+ $result = (bool)$user->save();
- $hash = generate_user_password($user, $password);
+ elgg_set_ignore_access($ia);
- $query = "UPDATE {$CONFIG->dbprefix}users_entity
- set password='$hash', salt='$salt' where guid=$user_guid";
- return update_data($query);
+ return $result;
}
return false;
@@ -742,7 +741,7 @@ function execute_new_password_request($user_guid, $conf_code) {
$user_guid = (int)$user_guid;
$user = get_entity($user_guid);
- if ($user) {
+ if ($user instanceof ElggUser) {
$saved_code = $user->getPrivateSetting('passwd_conf_code');
if ($saved_code && $saved_code == $conf_code) {
@@ -756,7 +755,7 @@ function execute_new_password_request($user_guid, $conf_code) {
$email = elgg_echo('email:resetpassword:body', array($user->name, $password));
return notify_user($user->guid, $CONFIG->site->guid,
- elgg_echo('email:resetpassword:subject'), $email, NULL, 'email');
+ elgg_echo('email:resetpassword:subject'), $email, array(), 'email');
}
}
}
@@ -841,7 +840,7 @@ function validate_username($username) {
for ($n = 0; $n < strlen($blacklist2); $n++) {
if (strpos($username, $blacklist2[$n]) !== false) {
$msg = elgg_echo('registration:invalidchars', array($blacklist2[$n], $blacklist2));
- $msg = htmlentities($msg, ENT_COMPAT, 'UTF-8');
+ $msg = htmlspecialchars($msg, ENT_QUOTES, 'UTF-8');
throw new RegistrationException($msg);
}
}
@@ -908,13 +907,11 @@ function validate_email_address($address) {
* @param string $invitecode An invite code from a friend
*
* @return int|false The new user's GUID; false on failure
+ * @throws RegistrationException
*/
function register_user($username, $password, $name, $email,
$allow_multiple_emails = false, $friend_guid = 0, $invitecode = '') {
- // Load the configuration
- global $CONFIG;
-
// no need to trim password.
$username = trim($username);
$name = trim(strip_tags($name));
@@ -1031,7 +1028,7 @@ function elgg_get_user_validation_status($user_guid) {
'metadata_name' => 'validated'
));
if ($md == false) {
- return;
+ return null;
}
if ($md[0]->value) {
@@ -1067,10 +1064,10 @@ function collections_submenu_items() {
* @return bool
* @access private
*/
-function friends_page_handler($page_elements, $handler) {
+function friends_page_handler($segments, $handler) {
elgg_set_context('friends');
- if (isset($page_elements[0]) && $user = get_user_by_username($page_elements[0])) {
+ if (isset($segments[0]) && $user = get_user_by_username($segments[0])) {
elgg_set_page_owner_guid($user->getGUID());
}
if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) {
@@ -1099,6 +1096,7 @@ function friends_page_handler($page_elements, $handler) {
* @access private
*/
function collections_page_handler($page_elements) {
+ gatekeeper();
elgg_set_context('friends');
$base = elgg_get_config('path');
if (isset($page_elements[0])) {
@@ -1197,13 +1195,11 @@ function set_last_login($user_guid) {
* @param string $object_type user
* @param ElggUser $object User object
*
- * @return bool
+ * @return void
* @access private
*/
function user_create_hook_add_site_relationship($event, $object_type, $object) {
- global $CONFIG;
-
- add_entity_relationship($object->getGUID(), 'member_of_site', $CONFIG->site->getGUID());
+ add_entity_relationship($object->getGUID(), 'member_of_site', elgg_get_site_entity()->guid);
}
/**
@@ -1233,6 +1229,7 @@ function user_avatar_hook($hook, $entity_type, $returnvalue, $params) {
*/
function elgg_user_hover_menu($hook, $type, $return, $params) {
$user = $params['entity'];
+ /* @var ElggUser $user */
if (elgg_is_logged_in()) {
if (elgg_get_logged_in_user_guid() != $user->guid) {
@@ -1309,7 +1306,12 @@ function elgg_user_hover_menu($hook, $type, $return, $params) {
/**
* Setup the menu shown with an entity
*
+ * @param string $hook
+ * @param string $type
+ * @param array $return
+ * @param array $params
* @return array
+ *
* @access private
*/
function elgg_users_setup_entity_menu($hook, $type, $return, $params) {
@@ -1321,6 +1323,7 @@ function elgg_users_setup_entity_menu($hook, $type, $return, $params) {
if (!elgg_instanceof($entity, 'user')) {
return $return;
}
+ /* @var ElggUser $entity */
if ($entity->isBanned()) {
$banned = elgg_echo('banned');
@@ -1334,9 +1337,10 @@ function elgg_users_setup_entity_menu($hook, $type, $return, $params) {
} else {
$return = array();
if (isset($entity->location)) {
+ $location = htmlspecialchars($entity->location, ENT_QUOTES, 'UTF-8', false);
$options = array(
'name' => 'location',
- 'text' => "<span>$entity->location</span>",
+ 'text' => "<span>$location</span>",
'href' => false,
'priority' => 150,
);
@@ -1587,7 +1591,7 @@ function users_init() {
/**
* Runs unit tests for ElggObject
*
- * @param sting $hook unit_test
+ * @param string $hook unit_test
* @param string $type system
* @param mixed $value Array of tests
* @param mixed $params Params
diff --git a/engine/lib/views.php b/engine/lib/views.php
index 01291b889..1142461fe 100644
--- a/engine/lib/views.php
+++ b/engine/lib/views.php
@@ -218,7 +218,7 @@ function elgg_register_ajax_view($view) {
/**
* Unregister a view for ajax calls
- *
+ *
* @param string $view The view name
* @return void
* @since 1.8.3
@@ -369,7 +369,7 @@ function elgg_view_exists($view, $viewtype = '', $recurse = true) {
* view, $view_name plugin hook.
*
* @warning Any variables in $_SESSION will override passed vars
- * upon name collision. See {@trac #2124}.
+ * upon name collision. See https://github.com/Elgg/Elgg/issues/2124
*
* @param string $view The name and location of the view to use
* @param array $vars Variables to pass to the view.
@@ -795,7 +795,7 @@ function elgg_view_menu($menu_name, array $vars = array()) {
* - bool 'full_view' Whether to show a full or condensed view.
*
* @tip This function can automatically appends annotations to entities if in full
- * view and a handler is registered for the entity:annotate. See {@trac 964} and
+ * view and a handler is registered for the entity:annotate. See https://github.com/Elgg/Elgg/issues/964 and
* {@link elgg_view_entity_annotations()}.
*
* @param ElggEntity $entity The entity to display
@@ -992,6 +992,11 @@ function elgg_view_annotation(ElggAnnotation $annotation, array $vars = array(),
function elgg_view_entity_list($entities, $vars = array(), $offset = 0, $limit = 10, $full_view = true,
$list_type_toggle = true, $pagination = true) {
+ if (!$vars["limit"] && !$vars["offset"]) {
+ // no need for pagination if listing is unlimited
+ $vars["pagination"] = false;
+ }
+
if (!is_int($offset)) {
$offset = (int)get_input('offset', 0);
}
@@ -1064,8 +1069,13 @@ function elgg_view_annotation_list($annotations, array $vars = array()) {
'full_view' => true,
'offset_key' => 'annoff',
);
-
+
$vars = array_merge($defaults, $vars);
+
+ if (!$vars["limit"] && !$vars["offset"]) {
+ // no need for pagination if listing is unlimited
+ $vars["pagination"] = false;
+ }
return elgg_view('page/components/list', $vars);
}
@@ -1107,7 +1117,7 @@ function elgg_view_entity_annotations(ElggEntity $entity, $full_view = true) {
* This is a shortcut for {@elgg_view page/elements/title}.
*
* @param string $title The page title
- * @param array $vars View variables (was submenu be displayed? (deprecated))
+ * @param array $vars View variables (was submenu be displayed? (deprecated))
*
* @return string The HTML (etc)
*/
@@ -1179,7 +1189,7 @@ function elgg_view_comments($entity, $add_comment = true, array $vars = array())
*
* @param string $image The icon and other information
* @param string $body Description content
- * @param array $vars Additional parameters for the view
+ * @param array $vars Additional parameters for the view
*
* @return string
* @since 1.8.0
@@ -1236,15 +1246,17 @@ function elgg_view_river_item($item, array $vars = array()) {
// subject is disabled or subject/object deleted
return '';
}
+
+ // @todo this needs to be cleaned up
// Don't hide objects in closed groups that a user can see.
- // see http://trac.elgg.org/ticket/4789
-// else {
-// // hide based on object's container
-// $visibility = ElggGroupItemVisibility::factory($object->container_guid);
-// if ($visibility->shouldHideItems) {
-// return '';
-// }
-// }
+ // see https://github.com/elgg/elgg/issues/4789
+ // else {
+ // // hide based on object's container
+ // $visibility = ElggGroupItemVisibility::factory($object->container_guid);
+ // if ($visibility->shouldHideItems) {
+ // return '';
+ // }
+ // }
$vars['item'] = $item;
@@ -1308,7 +1320,7 @@ function elgg_view_form($action, $form_vars = array(), $body_vars = array()) {
/**
* View an item in a list
*
- * @param object $item ElggEntity or ElggAnnotation
+ * @param ElggEntity|ElggAnnotation $item
* @param array $vars Additional parameters for the rendering
*
* @return string
@@ -1332,12 +1344,12 @@ function elgg_view_list_item($item, array $vars = array()) {
/**
* View one of the elgg sprite icons
- *
+ *
* Shorthand for <span class="elgg-icon elgg-icon-$name"></span>
- *
+ *
* @param string $name The specific icon to display
* @param string $class Additional class: float, float-alt, or custom class
- *
+ *
* @return string The html for displaying an icon
*/
function elgg_view_icon($name, $class = '') {
@@ -1451,17 +1463,13 @@ function elgg_get_views($dir, $base) {
*/
function elgg_view_tree($view_root, $viewtype = "") {
global $CONFIG;
- static $treecache;
+ static $treecache = array();
// Get viewtype
if (!$viewtype) {
$viewtype = elgg_get_viewtype();
}
- // Has the treecache been initialised?
- if (!isset($treecache)) {
- $treecache = array();
- }
// A little light internal caching
if (!empty($treecache[$view_root])) {
return $treecache[$view_root];
@@ -1640,7 +1648,7 @@ function elgg_views_boot() {
}
// set default icon sizes - can be overridden in settings.php or with plugin
- if (!$CONFIG->icon_sizes) {
+ if (!isset($CONFIG->icon_sizes)) {
$icon_sizes = array(
'topbar' => array('w' => 16, 'h' => 16, 'square' => TRUE, 'upscale' => TRUE),
'tiny' => array('w' => 25, 'h' => 25, 'square' => TRUE, 'upscale' => TRUE),
diff --git a/engine/lib/web_services.php b/engine/lib/web_services.php
index c8e4a13cc..51cad6f39 100644
--- a/engine/lib/web_services.php
+++ b/engine/lib/web_services.php
@@ -178,7 +178,7 @@ function authenticate_method($method) {
// check if user authentication is required
if ($API_METHODS[$method]["require_user_auth"] == true) {
if ($user_auth_result == false) {
- throw new APIException($user_pam->getFailureMessage());
+ throw new APIException($user_pam->getFailureMessage(), ErrorResult::$RESULT_FAIL_AUTHTOKEN);
}
}
@@ -1166,6 +1166,17 @@ function list_all_apis() {
* @access private
*/
function auth_gettoken($username, $password) {
+ // check if username is an email address
+ if (is_email_address($username)) {
+ $users = get_user_by_email($username);
+
+ // check if we have a unique user
+ if (is_array($users) && (count($users) == 1)) {
+ $username = $users[0]->username;
+ }
+ }
+
+ // validate username and password
if (true === elgg_authenticate($username, $password)) {
$token = create_user_token($username);
if ($token) {
@@ -1195,7 +1206,7 @@ $ERRORS = array();
*
* @return void
* @access private
- *
+ *
* @throws Exception
*/
function _php_api_error_handler($errno, $errmsg, $filename, $linenum, $vars) {
@@ -1267,14 +1278,14 @@ function service_handler($handler, $request) {
$request = explode('/', $request);
// after the handler, the first identifier is response format
- // ex) http://example.org/services/api/rest/xml/?method=test
- $reponse_format = array_shift($request);
+ // ex) http://example.org/services/api/rest/json/?method=test
+ $response_format = array_shift($request);
// Which view - xml, json, ...
- if ($reponse_format) {
- elgg_set_viewtype($reponse_format);
+ if ($response_format && elgg_is_valid_view_type($response_format)) {
+ elgg_set_viewtype($response_format);
} else {
- // default to xml
- elgg_set_viewtype("xml");
+ // default to json
+ elgg_set_viewtype("json");
}
if (!isset($CONFIG->servicehandler) || empty($handler)) {
diff --git a/engine/lib/widgets.php b/engine/lib/widgets.php
index d73dd6330..699462a1b 100644
--- a/engine/lib/widgets.php
+++ b/engine/lib/widgets.php
@@ -336,7 +336,7 @@ function elgg_default_widgets_init() {
*
* @param string $event The event
* @param string $type The type of object
- * @param object $entity The entity being created
+ * @param ElggEntity $entity The entity being created
* @return void
* @access private
*/
@@ -372,6 +372,7 @@ function elgg_create_default_widgets($event, $type, $entity) {
);
$widgets = elgg_get_entities_from_private_settings($options);
+ /* @var ElggWidget[] $widgets */
foreach ($widgets as $widget) {
// change the container and owner
diff --git a/engine/lib/xml.php b/engine/lib/xml.php
index ff82d7e8a..497459d83 100644
--- a/engine/lib/xml.php
+++ b/engine/lib/xml.php
@@ -104,7 +104,7 @@ function serialise_array_to_xml(array $data, $n = 0) {
*
* @param string $xml The XML
*
- * @return object
+ * @return ElggXMLElement
*/
function xml_to_object($xml) {
return new ElggXMLElement($xml);
diff --git a/engine/schema/mysql.sql b/engine/schema/mysql.sql
index 6c6e9db89..4714b71bb 100644
--- a/engine/schema/mysql.sql
+++ b/engine/schema/mysql.sql
@@ -361,7 +361,7 @@ CREATE TABLE `prefix_system_log` (
`access_id` int(11) NOT NULL,
`enabled` enum('yes','no') NOT NULL DEFAULT 'yes',
`time_created` int(11) NOT NULL,
- `ip_address` varchar(15) NOT NULL,
+ `ip_address` varchar(46) NOT NULL,
PRIMARY KEY (`id`),
KEY `object_id` (`object_id`),
KEY `object_class` (`object_class`),
diff --git a/engine/tests/api/access_collections.php b/engine/tests/api/access_collections.php
index ebcd7d318..4acfae596 100644
--- a/engine/tests/api/access_collections.php
+++ b/engine/tests/api/access_collections.php
@@ -54,7 +54,6 @@ class ElggCoreAccessCollectionsTest extends ElggCoreUnitTest {
}
public function testCreateGetDeleteACL() {
- global $DB_QUERY_CACHE;
$acl_name = 'test access collection';
$acl_id = create_access_collection($acl_name);
@@ -67,8 +66,6 @@ class ElggCoreAccessCollectionsTest extends ElggCoreUnitTest {
$this->assertEqual($acl->id, $acl_id);
if ($acl) {
- $DB_QUERY_CACHE = array();
-
$this->assertEqual($acl->name, $acl_name);
$result = delete_access_collection($acl_id);
diff --git a/engine/tests/api/annotations.php b/engine/tests/api/annotations.php
index 947292970..c0b0687cc 100644
--- a/engine/tests/api/annotations.php
+++ b/engine/tests/api/annotations.php
@@ -65,6 +65,86 @@ class ElggCoreAnnotationAPITest extends ElggCoreUnitTest {
$annotations = elgg_get_annotations($options);
$this->assertTrue(empty($annotations));
+ // nothing to delete so null returned
+ $this->assertNull(elgg_delete_annotations($options));
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggDisableAnnotations() {
+ $e = new ElggObject();
+ $e->save();
+
+ for ($i=0; $i<30; $i++) {
+ $e->annotate('test_annotation', rand(0,10000));
+ }
+
+ $options = array(
+ 'guid' => $e->getGUID(),
+ 'limit' => 0
+ );
+
+ $this->assertTrue(elgg_disable_annotations($options));
+
+ $annotations = elgg_get_annotations($options);
+ $this->assertTrue(empty($annotations));
+
+ access_show_hidden_entities(true);
+ $annotations = elgg_get_annotations($options);
+ $this->assertIdentical(30, count($annotations));
+ access_show_hidden_entities(false);
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggEnableAnnotations() {
+ $e = new ElggObject();
+ $e->save();
+
+ for ($i=0; $i<30; $i++) {
+ $e->annotate('test_annotation', rand(0,10000));
+ }
+
+ $options = array(
+ 'guid' => $e->getGUID(),
+ 'limit' => 0
+ );
+
+ $this->assertTrue(elgg_disable_annotations($options));
+
+ // cannot see any annotations so returns null
+ $this->assertNull(elgg_enable_annotations($options));
+
+ access_show_hidden_entities(true);
+ $this->assertTrue(elgg_enable_annotations($options));
+ access_show_hidden_entities(false);
+
+ $annotations = elgg_get_annotations($options);
+ $this->assertIdentical(30, count($annotations));
+
+ $this->assertTrue($e->delete());
+ }
+
+ public function testElggAnnotationExists() {
+ $e = new ElggObject();
+ $e->save();
+ $guid = $e->getGUID();
+
+ $this->assertFalse(elgg_annotation_exists($guid, 'test_annotation'));
+
+ $e->annotate('test_annotation', rand(0, 10000));
+ $this->assertTrue(elgg_annotation_exists($guid, 'test_annotation'));
+ // this metastring should always exist but an annotation of this name should not
+ $this->assertFalse(elgg_annotation_exists($guid, 'email'));
+
+ $options = array(
+ 'guid' => $guid,
+ 'limit' => 0
+ );
+ $this->assertTrue(elgg_disable_annotations($options));
+ $this->assertTrue(elgg_annotation_exists($guid, 'test_annotation'));
+
$this->assertTrue($e->delete());
+ $this->assertFalse(elgg_annotation_exists($guid, 'test_annotation'));
}
}
diff --git a/engine/tests/api/entity_getter_functions.php b/engine/tests/api/entity_getter_functions.php
index 9db248de9..fef9dc0c5 100644
--- a/engine/tests/api/entity_getter_functions.php
+++ b/engine/tests/api/entity_getter_functions.php
@@ -2648,7 +2648,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$name = 'test_annotation_' . rand(0, 9999);
$values = array();
$options = array(
- 'types' => 'object',
+ 'type' => 'object',
'subtypes' => $subtypes,
'limit' => 5
);
@@ -2687,7 +2687,7 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$order = array_keys($values);
$options = array(
- 'types' => 'object',
+ 'type' => 'object',
'subtypes' => $subtypes,
'limit' => 5,
'annotation_name' => $name,
@@ -2729,6 +2729,36 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
}
}
+ public function testElggGetEntitiesFromAnnotationCalculationCount() {
+ // add two annotations with a unique name to an entity
+ // then count the number of entities with that annotation name
+
+ $subtypes = $this->getRandomValidSubtypes(array('object'), 1);
+ $name = 'test_annotation_' . rand(0, 9999);
+ $values = array();
+ $options = array(
+ 'type' => 'object',
+ 'subtypes' => $subtypes,
+ 'limit' => 1
+ );
+ $es = elgg_get_entities($options);
+ $entity = $es[0];
+ $value = rand(0, 9999);
+ $entity->annotate($name, $value);
+ $value = rand(0, 9999);
+ $entity->annotate($name, $value);
+
+ $options = array(
+ 'type' => 'object',
+ 'subtypes' => $subtypes,
+ 'annotation_name' => $name,
+ 'calculation' => 'count',
+ 'count' => true,
+ );
+ $count = elgg_get_entities_from_annotation_calculation($options);
+ $this->assertEqual(1, $count);
+ }
+
public function testElggGetAnnotationsAnnotationNames() {
$options = array('annotation_names' => array());
$a_e_map = array();
@@ -2817,4 +2847,38 @@ class ElggCoreEntityGetterFunctionsTest extends ElggCoreUnitTest {
$entities = elgg_get_entities($options);
$this->assertFalse($entities);
}
+
+ public function testEGEEmptySubtypePlurality() {
+ $options = array(
+ 'type' => 'user',
+ 'subtypes' => ''
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertTrue(is_array($entities));
+
+ $options = array(
+ 'type' => 'user',
+ 'subtype' => ''
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertTrue(is_array($entities));
+
+ $options = array(
+ 'type' => 'user',
+ 'subtype' => array('')
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertTrue(is_array($entities));
+
+ $options = array(
+ 'type' => 'user',
+ 'subtypes' => array('')
+ );
+
+ $entities = elgg_get_entities($options);
+ $this->assertTrue(is_array($entities));
+ }
}
diff --git a/engine/tests/api/helpers.php b/engine/tests/api/helpers.php
index 62e4471e0..414fb4145 100644
--- a/engine/tests/api/helpers.php
+++ b/engine/tests/api/helpers.php
@@ -519,7 +519,7 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {
$this->assertIdentical($elements_sorted_string, $test_elements);
}
- // see http://trac.elgg.org/ticket/4288
+ // see https://github.com/elgg/elgg/issues/4288
public function testElggBatchIncOffset() {
// normal increment
$options = array(
@@ -578,6 +578,107 @@ class ElggCoreHelpersTest extends ElggCoreUnitTest {
$this->assertEqual(11, $j);
}
+ public function testElggBatchReadHandlesBrokenEntities() {
+ $num_test_entities = 8;
+ $guids = array();
+ for ($i = $num_test_entities; $i > 0; $i--) {
+ $entity = new ElggObject();
+ $entity->type = 'object';
+ $entity->subtype = 'test_5357_subtype';
+ $entity->access_id = ACCESS_PUBLIC;
+ $entity->save();
+ $guids[] = $entity->guid;
+ _elgg_invalidate_cache_for_entity($entity->guid);
+ }
+
+ // break entities such that the first fetch has one incomplete
+ // and the second and third fetches have only incompletes!
+ $db_prefix = elgg_get_config('dbprefix');
+ delete_data("
+ DELETE FROM {$db_prefix}objects_entity
+ WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}, {$guids[4]}, {$guids[5]})
+ ");
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'test_5357_subtype',
+ 'order_by' => 'e.guid',
+ );
+
+ $entities_visited = array();
+ $batch = new ElggBatch('elgg_get_entities', $options, null, 2);
+ /* @var ElggEntity[] $batch */
+ foreach ($batch as $entity) {
+ $entities_visited[] = $entity->guid;
+ }
+
+ // The broken entities should not have been visited
+ $this->assertEqual($entities_visited, array($guids[0], $guids[6], $guids[7]));
+
+ // cleanup (including leftovers from previous tests)
+ $entity_rows = elgg_get_entities(array_merge($options, array(
+ 'callback' => '',
+ 'limit' => false,
+ )));
+ $guids = array();
+ foreach ($entity_rows as $row) {
+ $guids[] = $row->guid;
+ }
+ delete_data("DELETE FROM {$db_prefix}entities WHERE guid IN (" . implode(',', $guids) . ")");
+ delete_data("DELETE FROM {$db_prefix}objects_entity WHERE guid IN (" . implode(',', $guids) . ")");
+ }
+
+ public function testElggBatchDeleteHandlesBrokenEntities() {
+ $num_test_entities = 8;
+ $guids = array();
+ for ($i = $num_test_entities; $i > 0; $i--) {
+ $entity = new ElggObject();
+ $entity->type = 'object';
+ $entity->subtype = 'test_5357_subtype';
+ $entity->access_id = ACCESS_PUBLIC;
+ $entity->save();
+ $guids[] = $entity->guid;
+ _elgg_invalidate_cache_for_entity($entity->guid);
+ }
+
+ // break entities such that the first fetch has one incomplete
+ // and the second and third fetches have only incompletes!
+ $db_prefix = elgg_get_config('dbprefix');
+ delete_data("
+ DELETE FROM {$db_prefix}objects_entity
+ WHERE guid IN ({$guids[1]}, {$guids[2]}, {$guids[3]}, {$guids[4]}, {$guids[5]})
+ ");
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'test_5357_subtype',
+ 'order_by' => 'e.guid',
+ );
+
+ $entities_visited = array();
+ $batch = new ElggBatch('elgg_get_entities', $options, null, 2, false);
+ /* @var ElggEntity[] $batch */
+ foreach ($batch as $entity) {
+ $entities_visited[] = $entity->guid;
+ $entity->delete();
+ }
+
+ // The broken entities should not have been visited
+ $this->assertEqual($entities_visited, array($guids[0], $guids[6], $guids[7]));
+
+ // cleanup (including leftovers from previous tests)
+ $entity_rows = elgg_get_entities(array_merge($options, array(
+ 'callback' => '',
+ 'limit' => false,
+ )));
+ $guids = array();
+ foreach ($entity_rows as $row) {
+ $guids[] = $row->guid;
+ }
+ delete_data("DELETE FROM {$db_prefix}entities WHERE guid IN (" . implode(',', $guids) . ")");
+ delete_data("DELETE FROM {$db_prefix}objects_entity WHERE guid IN (" . implode(',', $guids) . ")");
+ }
+
static function elgg_batch_callback_test($options, $reset = false) {
static $count = 1;
diff --git a/engine/tests/api/metadata.php b/engine/tests/api/metadata.php
index 825290d80..d23510c6a 100644
--- a/engine/tests/api/metadata.php
+++ b/engine/tests/api/metadata.php
@@ -123,9 +123,23 @@ class ElggCoreMetadataAPITest extends ElggCoreUnitTest {
$e->delete();
}
+ /**
+ * https://github.com/Elgg/Elgg/issues/4867
+ */
+ public function testElggGetEntityMetadataWhereSqlWithFalseValue() {
+ $pair = array('name' => 'test' , 'value' => false);
+ $result = elgg_get_entity_metadata_where_sql('e', 'metadata', null, null, $pair);
+ $where = preg_replace( '/\s+/', ' ', $result['wheres'][0]);
+ $this->assertTrue(strpos($where, "msn1.string = 'test' AND BINARY msv1.string = 0") > 0);
+
+ $result = elgg_get_entity_metadata_where_sql('e', 'metadata', array('test'), array(false));
+ $where = preg_replace( '/\s+/', ' ', $result['wheres'][0]);
+ $this->assertTrue(strpos($where, "msn.string IN ('test')) AND ( BINARY msv.string IN ('0')"));
+ }
+
// Make sure metadata with multiple values is correctly deleted when re-written
// by another user
- // http://trac.elgg.org/ticket/2776
+ // https://github.com/elgg/elgg/issues/2776
public function test_elgg_metadata_multiple_values() {
$u1 = new ElggUser();
$u1->username = rand();
diff --git a/engine/tests/api/metadata_cache.php b/engine/tests/api/metadata_cache.php
index 846116a7b..7fb328169 100644
--- a/engine/tests/api/metadata_cache.php
+++ b/engine/tests/api/metadata_cache.php
@@ -166,4 +166,11 @@ class ElggCoreMetadataCacheTest extends ElggCoreUnitTest {
$actual = $this->cache->filterMetadataHeavyEntities($guids, 6000);
$this->assertIdentical($actual, $expected);
}
+
+ public function testCreateMetadataInvalidates() {
+ $this->obj1->foo = 1;
+ create_metadata($this->guid1, 'foo', 2, '', elgg_get_logged_in_user_guid(), ACCESS_FRIENDS);
+
+ $this->assertEqual($this->obj1->foo, 2);
+ }
}
diff --git a/engine/tests/api/metastrings.php b/engine/tests/api/metastrings.php
index 0a8945084..5efdab972 100644
--- a/engine/tests/api/metastrings.php
+++ b/engine/tests/api/metastrings.php
@@ -55,8 +55,11 @@ class ElggCoreMetastringsTest extends ElggCoreUnitTest {
* Called after each test method.
*/
public function tearDown() {
- // do not allow SimpleTest to interpret Elgg notices as exceptions
- $this->swallowErrors();
+ access_show_hidden_entities(true);
+ elgg_delete_annotations(array(
+ 'guid' => $this->object->guid,
+ ));
+ access_show_hidden_entities(false);
}
/**
@@ -98,6 +101,31 @@ class ElggCoreMetastringsTest extends ElggCoreUnitTest {
}
}
+ public function testGetMetastringObjectFromIDWithDisabledAnnotation() {
+ $name = 'test_annotation_name' . rand();
+ $value = 'test_annotation_value' . rand();
+ $id = create_annotation($this->object->guid, $name, $value);
+ $annotation = elgg_get_annotation_from_id($id);
+ $this->assertTrue($annotation->disable());
+
+ $test = elgg_get_metastring_based_object_from_id($id, 'annotation');
+ $this->assertEqual(false, $test);
+ }
+
+ public function testGetMetastringBasedObjectWithDisabledAnnotation() {
+ $name = 'test_annotation_name' . rand();
+ $value = 'test_annotation_value' . rand();
+ $id = create_annotation($this->object->guid, $name, $value);
+ $annotation = elgg_get_annotation_from_id($id);
+ $this->assertTrue($annotation->disable());
+
+ $test = elgg_get_metastring_based_objects(array(
+ 'metastring_type' => 'annotations',
+ 'guid' => $this->object->guid,
+ ));
+ $this->assertEqual(array(), $test);
+ }
+
public function testEnableDisableByID() {
$db_prefix = elgg_get_config('dbprefix');
$annotations = $this->createAnnotations(1);
@@ -119,7 +147,6 @@ class ElggCoreMetastringsTest extends ElggCoreUnitTest {
// enable
$ashe = access_get_show_hidden_status();
access_show_hidden_entities(true);
- flush();
$this->assertTrue(elgg_set_metastring_based_object_enabled_by_id($id, 'yes', $type));
$test = get_data($q);
diff --git a/engine/tests/api/plugins.php b/engine/tests/api/plugins.php
index 114f3991b..d0f111c48 100644
--- a/engine/tests/api/plugins.php
+++ b/engine/tests/api/plugins.php
@@ -69,7 +69,7 @@ class ElggCorePluginsAPITest extends ElggCoreUnitTest {
'description' => 'A longer, more interesting description.',
'website' => 'http://www.elgg.org/',
'repository' => 'https://github.com/Elgg/Elgg',
- 'bugtracker' => 'http://trac.elgg.org',
+ 'bugtracker' => 'https://github.com/elgg/elgg/issues',
'donations' => 'http://elgg.org/supporter.php',
'copyright' => '(C) Elgg Foundation 2011',
'license' => 'GNU General Public License version 2',
@@ -174,7 +174,7 @@ class ElggCorePluginsAPITest extends ElggCoreUnitTest {
}
public function testElggPluginManifestGetBugtracker() {
- $this->assertEqual($this->manifest18->getBugTrackerURL(), 'http://trac.elgg.org');
+ $this->assertEqual($this->manifest18->getBugTrackerURL(), 'https://github.com/elgg/elgg/issues');
$this->assertEqual($this->manifest17->getBugTrackerURL(), '');
}
diff --git a/engine/tests/objects/entities.php b/engine/tests/objects/entities.php
index 248b85c9e..bac72079e 100644
--- a/engine/tests/objects/entities.php
+++ b/engine/tests/objects/entities.php
@@ -271,7 +271,7 @@ class ElggCoreEntityTest extends ElggCoreUnitTest {
$this->save_entity();
// test deleting incorrectly
- // @link http://trac.elgg.org/ticket/2273
+ // @link https://github.com/elgg/elgg/issues/2273
$this->assertNull($this->entity->deleteMetadata('impotent'));
$this->assertEqual($this->entity->important, 'indeed!');
diff --git a/engine/tests/objects/objects.php b/engine/tests/objects/objects.php
index 915594e0a..263ab2414 100644
--- a/engine/tests/objects/objects.php
+++ b/engine/tests/objects/objects.php
@@ -194,7 +194,7 @@ class ElggCoreObjectTest extends ElggCoreUnitTest {
$old = elgg_set_ignore_access(true);
}
- // see http://trac.elgg.org/ticket/1196
+ // see https://github.com/elgg/elgg/issues/1196
public function testElggEntityRecursiveDisableWhenLoggedOut() {
$e1 = new ElggObject();
$e1->access_id = ACCESS_PUBLIC;
diff --git a/engine/tests/objects/users.php b/engine/tests/objects/users.php
index a3573acb6..8a1033ac4 100644
--- a/engine/tests/objects/users.php
+++ b/engine/tests/objects/users.php
@@ -65,6 +65,9 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
$attributes['code'] = NULL;
$attributes['banned'] = 'no';
$attributes['admin'] = 'no';
+ $attributes['prev_last_action'] = NULL;
+ $attributes['last_login'] = NULL;
+ $attributes['prev_last_login'] = NULL;
ksort($attributes);
$entity_attributes = $this->user->expose_attributes();
@@ -142,7 +145,7 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
}
public function testElggUserNameCache() {
- // Trac #1305
+ // issue https://github.com/elgg/elgg/issues/1305
// very unlikely a user would have this username
$name = (string)time();
@@ -156,6 +159,22 @@ class ElggCoreUserTest extends ElggCoreUnitTest {
$this->assertFalse($user);
}
+ public function testGetUserByUsernameAcceptsUrlEncoded() {
+ $username = (string)time();
+ $this->user->username = $username;
+ $guid = $this->user->save();
+
+ // percent encode first letter
+ $first_letter = $username[0];
+ $first_letter = str_pad('%' . dechex(ord($first_letter)), 2, '0', STR_PAD_LEFT);
+ $username = $first_letter . substr($username, 1);
+
+ $user = get_user_by_username($username);
+ $this->assertTrue((bool) $user);
+ $this->assertEqual($guid, $user->guid);
+
+ $this->user->delete();
+ }
public function testElggUserMakeAdmin() {
global $CONFIG;
diff --git a/engine/tests/regression/trac_bugs.php b/engine/tests/regression/trac_bugs.php
index 691433a41..689275661 100644
--- a/engine/tests/regression/trac_bugs.php
+++ b/engine/tests/regression/trac_bugs.php
@@ -1,7 +1,7 @@
<?php
/**
- * Elgg Regression Tests -- Trac Bugfixes
- * Any bugfixes from Trac that require testing belong here.
+ * Elgg Regression Tests -- GitHub Bugfixes
+ * Any bugfixes from GitHub that require testing belong here.
*
* @package Elgg
* @subpackage Test
@@ -201,26 +201,28 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest {
}
/**
- * http://trac.elgg.org/ticket/3210 - Don't remove -s in friendly titles
- * http://trac.elgg.org/ticket/2276 - improve char encoding
+ * https://github.com/elgg/elgg/issues/3210 - Don't remove -s in friendly titles
+ * https://github.com/elgg/elgg/issues/2276 - improve char encoding
*/
public function test_friendly_title() {
$cases = array(
+ // acid test
+ "B&N > Amazon, OK? <bold> 'hey!' $34"
+ => "bn-amazon-ok-bold-hey-34",
+
// hyphen, underscore and ASCII whitespace replaced by separator,
// other non-alphanumeric ASCII removed
- "a-a_a a\na\ra\ta\va!a\"a#a\$a%a&a'a(a)a*a+a,a.a/a:a;a<a=a>a?a@a[a\\a]a^a`a{a|a}a~a"
- => "a-a-a-a-a-a-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
-
+ "a-a_a a\na\ra\ta\va!a\"a#a\$a%aa'a(a)a*a+a,a.a/a:a;a=a?a@a[a\\a]a^a`a{a|a}a~a"
+ => "a-a-a-a-a-a-aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+
// separators trimmed
- "-_ hello _-" => "hello",
+ "-_ hello _-"
+ => "hello",
// accents removed, lower case, other multibyte chars are URL encoded
"I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n, AND \xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E"
// Iñtërnâtiônàlizætiøn, AND 日本語
=> 'internationalizaetion-and-%E6%97%A5%E6%9C%AC%E8%AA%9E',
-
- // some HTML entity replacements
- "Me &amp; You" => 'me-and-you',
);
// where available, string is converted to NFC before transliteration
@@ -234,4 +236,170 @@ class ElggCoreRegressionBugsTest extends ElggCoreUnitTest {
$this->assertIdentical($expected, $friendly_title);
}
}
+
+ /**
+ * Test #5369 -- parse_urls()
+ * https://github.com/Elgg/Elgg/issues/5369
+ */
+ public function test_parse_urls() {
+
+ $cases = array(
+ 'no.link.here' =>
+ 'no.link.here',
+ 'simple link http://example.org test' =>
+ 'simple link <a href="http://example.org" rel="nofollow">http:/<wbr />/<wbr />example.org</a> test',
+ 'non-ascii http://ñew.org/ test' =>
+ 'non-ascii <a href="http://ñew.org/" rel="nofollow">http:/<wbr />/<wbr />ñew.org/<wbr /></a> test',
+
+ // section 2.1
+ 'percent encoded http://example.org/a%20b test' =>
+ 'percent encoded <a href="http://example.org/a%20b" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />a%20b</a> test',
+ // section 2.2: skipping single quote and parenthese
+ 'reserved characters http://example.org/:/?#[]@!$&*+,;= test' =>
+ 'reserved characters <a href="http://example.org/:/?#[]@!$&*+,;=" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />:/<wbr />?#[]@!$&*+,;=</a> test',
+ // section 2.3
+ 'unreserved characters http://example.org/a1-._~ test' =>
+ 'unreserved characters <a href="http://example.org/a1-._~" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />a1-._~</a> test',
+
+ 'parameters http://example.org/?val[]=1&val[]=2 test' =>
+ 'parameters <a href="http://example.org/?val[]=1&val[]=2" rel="nofollow">http:/<wbr />/<wbr />example.org/<wbr />?val[]=1&val[]=2</a> test',
+ 'port http://example.org:80/ test' =>
+ 'port <a href="http://example.org:80/" rel="nofollow">http:/<wbr />/<wbr />example.org:80/<wbr /></a> test',
+
+ 'parentheses (http://www.google.com) test' =>
+ 'parentheses (<a href="http://www.google.com" rel="nofollow">http:/<wbr />/<wbr />www.google.com</a>) test',
+ 'comma http://elgg.org, test' =>
+ 'comma <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>, test',
+ 'period http://elgg.org. test' =>
+ 'period <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>. test',
+ 'exclamation http://elgg.org! test' =>
+ 'exclamation <a href="http://elgg.org" rel="nofollow">http:/<wbr />/<wbr />elgg.org</a>! test',
+
+ 'already anchor <a href="http://twitter.com/">twitter</a> test' =>
+ 'already anchor <a href="http://twitter.com/">twitter</a> test',
+
+ 'ssl https://example.org/ test' =>
+ 'ssl <a href="https://example.org/" rel="nofollow">https:/<wbr />/<wbr />example.org/<wbr /></a> test',
+ 'ftp ftp://example.org/ test' =>
+ 'ftp <a href="ftp://example.org/" rel="nofollow">ftp:/<wbr />/<wbr />example.org/<wbr /></a> test',
+
+ 'web archive anchor <a href="http://web.archive.org/web/20000229040250/http://www.google.com/">google</a>' =>
+ 'web archive anchor <a href="http://web.archive.org/web/20000229040250/http://www.google.com/">google</a>',
+
+ 'single quotes already anchor <a href=\'http://www.yahoo.com\'>yahoo</a>' =>
+ 'single quotes already anchor <a href=\'http://www.yahoo.com\'>yahoo</a>',
+
+ 'unquoted already anchor <a href=http://www.yahoo.com>yahoo</a>' =>
+ 'unquoted already anchor <a href=http://www.yahoo.com>yahoo</a>',
+
+ 'parens in uri http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx' =>
+ 'parens in uri <a href="http://thedailywtf.com/Articles/A-(Long-Overdue)-BuildMaster-Introduction.aspx" rel="nofollow">http:/<wbr />/<wbr />thedailywtf.com/<wbr />Articles/<wbr />A-(Long-Overdue)-BuildMaster-Introduction.aspx</a>'
+ );
+ foreach ($cases as $input => $output) {
+ $this->assertEqual($output, parse_urls($input));
+ }
+ }
+
+ /**
+ * Ensure additional select columns do not end up in entity attributes.
+ *
+ * https://github.com/Elgg/Elgg/issues/5538
+ */
+ public function test_extra_columns_dont_appear_in_attributes() {
+ global $ENTITY_CACHE;
+
+ // may not have groups in DB - let's create one
+ $group = new ElggGroup();
+ $group->name = 'test_group';
+ $group->access_id = ACCESS_PUBLIC;
+ $this->assertTrue($group->save() !== false);
+
+ // entity cache interferes with our test
+ $ENTITY_CACHE = array();
+
+ foreach (array('site', 'user', 'group', 'object') as $type) {
+ $entities = elgg_get_entities(array(
+ 'type' => $type,
+ 'selects' => array('1 as _nonexistent_test_column'),
+ 'limit' => 1,
+ ));
+ if (!$this->assertTrue($entities, "Query for '$type' did not return an entity.")) {
+ continue;
+ }
+ $entity = $entities[0];
+ $this->assertNull($entity->_nonexistent_test_column, "Additional select columns are leaking to attributes for '$type'");
+ }
+
+ $group->delete();
+ }
+
+ /**
+ * Ensure that ElggBatch doesn't go into infinite loop when disabling annotations recursively when show hidden is enabled.
+ *
+ * https://github.com/Elgg/Elgg/issues/5952
+ */
+ public function test_disabling_annotations_infinite_loop() {
+
+ //let's have some entity
+ $group = new ElggGroup();
+ $group->name = 'test_group';
+ $group->access_id = ACCESS_PUBLIC;
+ $this->assertTrue($group->save() !== false);
+
+ $total = 51;
+ //add some annotations
+ for ($cnt = 0; $cnt < $total; $cnt++) {
+ $group->annotate('test_annotation', 'value_' . $total);
+ }
+
+ //disable them
+ $show_hidden = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $options = array(
+ 'guid' => $group->guid,
+ 'limit' => $total, //using strict limit to avoid real infinite loop and just see ElggBatch limiting on it before finishing the work
+ );
+ elgg_disable_annotations($options);
+ access_show_hidden_entities($show_hidden);
+
+ //confirm all being disabled
+ $annotations = $group->getAnnotations(array(
+ 'limit' => $total,
+ ));
+ foreach ($annotations as $annotation) {
+ $this->assertTrue($annotation->enabled == 'no');
+ }
+
+ //delete group and annotations
+ $group->delete();
+ }
+
+ public function test_ElggXMLElement_does_not_load_external_entities() {
+ $elLast = libxml_disable_entity_loader(false);
+
+ // build payload that should trigger loading of external entity
+ $payload = file_get_contents(dirname(dirname(__FILE__)) . '/test_files/xxe/request.xml');
+ $path = realpath(dirname(dirname(__FILE__)) . '/test_files/xxe/external_entity.txt');
+ $path = str_replace('\\', '/', $path);
+ if ($path[0] != '/') {
+ $path = '/' . $path;
+ }
+ $path = 'file://' . $path;
+ $payload = sprintf($payload, $path);
+
+ // make sure we can actually this in this environment
+ $element = new SimpleXMLElement($payload);
+ $can_load_entity = preg_match('/secret/', (string)$element->methodName);
+
+ $this->skipUnless($can_load_entity, "XXE vulnerability cannot be tested on this system");
+
+ if ($can_load_entity) {
+ $el = new ElggXMLElement($payload);
+ $chidren = $el->getChildren();
+ $content = $chidren[0]->getContent();
+ $this->assertNoPattern('/secret/', $content);
+ }
+
+ libxml_disable_entity_loader($elLast);
+ }
}
diff --git a/engine/tests/test_files/plugin_18/manifest.xml b/engine/tests/test_files/plugin_18/manifest.xml
index 5d788616a..c8b407511 100644
--- a/engine/tests/test_files/plugin_18/manifest.xml
+++ b/engine/tests/test_files/plugin_18/manifest.xml
@@ -7,7 +7,7 @@
<description>A longer, more interesting description.</description>
<website>http://www.elgg.org/</website>
<repository>https://github.com/Elgg/Elgg</repository>
- <bugtracker>http://trac.elgg.org</bugtracker>
+ <bugtracker>https://github.com/elgg/elgg/issues</bugtracker>
<donations>http://elgg.org/supporter.php</donations>
<copyright>(C) Elgg Foundation 2011</copyright>
<license>GNU General Public License version 2</license>
diff --git a/engine/tests/test_files/xxe/external_entity.txt b/engine/tests/test_files/xxe/external_entity.txt
new file mode 100644
index 000000000..536aca34d
--- /dev/null
+++ b/engine/tests/test_files/xxe/external_entity.txt
@@ -0,0 +1 @@
+secret \ No newline at end of file
diff --git a/engine/tests/test_files/xxe/request.xml b/engine/tests/test_files/xxe/request.xml
new file mode 100644
index 000000000..4390f9db2
--- /dev/null
+++ b/engine/tests/test_files/xxe/request.xml
@@ -0,0 +1,8 @@
+<?xml version="1.0"?>
+<!DOCTYPE foo [
+<!ELEMENT methodName ANY >
+<!ENTITY xxe SYSTEM "%s" >
+]>
+<methodCall>
+ <methodName>test&xxe;test</methodName>
+</methodCall>
diff --git a/htaccess_dist b/htaccess_dist
index 4c888e70a..44d129475 100644
--- a/htaccess_dist
+++ b/htaccess_dist
@@ -1,14 +1,11 @@
# Elgg htaccess directives
-# Copyright Curverider Ltd 2008-2009
-# License http://www.gnu.org/licenses/old-licenses/gpl-2.0.html GNU Public License version 2
-# Link http://elgg.org/
<Files "htaccess_dist">
order allow,deny
deny from all
</Files>
-# Don't listing directory
+# Don't allow listing directories
Options -Indexes
# Follow symbolic links
@@ -17,13 +14,28 @@ Options +FollowSymLinks
# Default handler
DirectoryIndex index.php
-# Turn on expiry
+
+############################
+# BROWSER CACHING
+
+# The expires module controls the Expires and Cache-Control headers. Elgg sets
+# these for dynamically generated files so this is just for static files.
<IfModule mod_expires.c>
ExpiresActive On
- ExpiresDefault "access plus 10 years"
+ ExpiresDefault "access plus 1 year"
</IfModule>
-# php 5, apache 1 and 2
+# Conditional requests are controlled through Last-Modified and ETag headers.
+# Elgg sets these on dynamically generated cacheable files so this is just for
+# static files. Note: Apache sends Last-Modified by default on static files so
+# I don't think we need to be sending ETag for these files.
+<FilesMatch "\.(jpg|jpeg|gif|png|mp3|flv|mov|avi|3pg|html|htm|swf|js|css|ico)$">
+ FileETag MTime Size
+</FilesMatch>
+
+
+############################
+# PHP SETTINGS
<IfModule mod_php5.c>
# limit the maximum memory consumed by the php script to 64 MB
php_value memory_limit 64M
@@ -37,6 +49,10 @@ DirectoryIndex index.php
php_value display_errors 0
</IfModule>
+
+############################
+# COMPRESSION
+
# Turn on mod_gzip if available
<IfModule mod_gzip.c>
mod_gzip_on yes
@@ -75,13 +91,9 @@ DirectoryIndex index.php
</IfModule>
-# Configure ETags
-<FilesMatch "\.(jpg|jpeg|gif|png|mp3|flv|mov|avi|3pg|html|htm|swf|js|ico)$">
- FileETag MTime Size
-</FilesMatch>
-# Add Proper MIME-Type for Favicon to allow expires to work
-AddType image/vnd.microsoft.icon .ico
+############################
+# REWRITE RULES
<IfModule mod_rewrite.c>
@@ -100,6 +112,14 @@ RewriteEngine on
#
#RewriteBase /
+
+# If your users receive the message "Sorry, logging in from a different domain is not permitted"
+# you must make sure your login form is served from the same hostname as your site pages.
+# See http://docs.elgg.org/wiki/Login_token_mismatch_error for more info.
+#
+# If you must add RewriteRules to change hostname, add them directly below (above all the others)
+
+
# In for backwards compatibility
RewriteRule ^pg\/([A-Za-z0-9\_\-]+)$ engine/handlers/page_handler.php?handler=$1&%{QUERY_STRING} [L]
RewriteRule ^pg\/([A-Za-z0-9\_\-]+)\/(.*)$ engine/handlers/page_handler.php?handler=$1&page=$2&%{QUERY_STRING} [L]
diff --git a/install/ElggInstaller.php b/install/ElggInstaller.php
index 775bbf5b6..78cdde90f 100644
--- a/install/ElggInstaller.php
+++ b/install/ElggInstaller.php
@@ -1148,11 +1148,21 @@ class ElggInstaller {
foreach ($formVars as $field => $info) {
if ($info['required'] == TRUE && !$submissionVars[$field]) {
$name = elgg_echo("install:database:label:$field");
- register_error("$name is required");
+ register_error(elgg_echo('install:error:requiredfield', array($name)));
return FALSE;
}
}
+ // according to postgres documentation: SQL identifiers and key words must
+ // begin with a letter (a-z, but also letters with diacritical marks and
+ // non-Latin letters) or an underscore (_). Subsequent characters in an
+ // identifier or key word can be letters, underscores, digits (0-9), or dollar signs ($).
+ // Refs #4994
+ if (!preg_match("/^[a-zA-Z_][\w]*$/", $submissionVars['dbprefix'])) {
+ register_error(elgg_echo('install:error:database_prefix'));
+ return FALSE;
+ }
+
return $this->checkDatabaseSettings(
$submissionVars['dbuser'],
$submissionVars['dbpassword'],
@@ -1404,7 +1414,7 @@ class ElggInstaller {
$submissionVars['wwwroot'] = sanitise_filepath($submissionVars['wwwroot']);
$site = new ElggSite();
- $site->name = $submissionVars['sitename'];
+ $site->name = strip_tags($submissionVars['sitename']);
$site->url = $submissionVars['wwwroot'];
$site->access_id = ACCESS_PUBLIC;
$site->email = $submissionVars['siteemail'];
diff --git a/install/cli/sample_installer.php b/install/cli/sample_installer.php
index 0bae0cd23..a51f9aae4 100644
--- a/install/cli/sample_installer.php
+++ b/install/cli/sample_installer.php
@@ -1,28 +1,12 @@
<?php
+
/**
* Sample cli installer script
*/
+// change to true to run this script. Change back to false when done.
$enabled = false;
-// Do not edit below this line. //////////////////////////////
-
-
-if (!$enabled) {
- echo "To enable this script, change \$enabled to true.\n";
- echo "You *must* disable this script after a successful installation.\n";
- exit;
-}
-
-if (PHP_SAPI !== 'cli') {
- echo "You must use the command line to run this script.";
- exit;
-}
-
-require_once(dirname(dirname(__FILE__)) . "/ElggInstaller.php");
-
-$installer = new ElggInstaller();
-
// none of the following may be empty
$params = array(
// database parameters
@@ -43,11 +27,29 @@ $params = array(
'password' => '',
);
+
+// Do not edit below this line. //////////////////////////////
+
+
+if (!$enabled) {
+ echo "To enable this script, change \$enabled to true.\n";
+ echo "You *must* disable this script after a successful installation.\n";
+ exit;
+}
+
+if (PHP_SAPI !== 'cli') {
+ echo "You must use the command line to run this script.";
+ exit;
+}
+
+require_once(dirname(dirname(__FILE__)) . "/ElggInstaller.php");
+
+$installer = new ElggInstaller();
+
// install and create the .htaccess file
$installer->batchInstall($params, TRUE);
// at this point installation has completed (otherwise an exception halted execution).
-
// try to rewrite the script to disable it.
if (is_writable(__FILE__)) {
$code = file_get_contents(__FILE__);
diff --git a/install/languages/en.php b/install/languages/en.php
index b2583fbc9..531379b1e 100644
--- a/install/languages/en.php
+++ b/install/languages/en.php
@@ -124,6 +124,7 @@ If you are ready to proceed, click the Next button.",
'install:error:htaccess' => 'Unable to create an .htaccess',
'install:error:settings' => 'Unable to create the settings file',
'install:error:databasesettings' => 'Unable to connect to the database with these settings.',
+ 'install:error:database_prefix' => 'Invalid characters in database prefix',
'install:error:oldmysql' => 'MySQL must be version 5.0 or above. Your server is using %s.',
'install:error:nodatabase' => 'Unable to use database %s. It may not exist.',
'install:error:cannotloadtables' => 'Cannot load the database tables',
@@ -131,7 +132,7 @@ If you are ready to proceed, click the Next button.",
'install:error:readsettingsphp' => 'Unable to read engine/settings.example.php',
'install:error:writesettingphp' => 'Unable to write engine/settings.php',
'install:error:requiredfield' => '%s is required',
- 'install:error:relative_path' => 'We don\'t think "%s" is an absoluate path for your data directory',
+ 'install:error:relative_path' => 'We don\'t think "%s" is an absolute path for your data directory',
'install:error:datadirectoryexists' => 'Your data directory %s does not exist.',
'install:error:writedatadirectory' => 'Your data directory %s is not writable by the web server.',
'install:error:locationdatadirectory' => 'Your data directory %s must be outside of your install path for security.',
diff --git a/js/lib/elgglib.js b/js/lib/elgglib.js
index af2c94000..a8e187f1d 100644
--- a/js/lib/elgglib.js
+++ b/js/lib/elgglib.js
@@ -474,8 +474,8 @@ elgg.parse_str = function(string) {
re = /([^&=]+)=?([^&]*)/g;
while (result = re.exec(string)) {
- key = decodeURIComponent(result[1])
- value = decodeURIComponent(result[2])
+ key = decodeURIComponent(result[1].replace(/\+/g, ' '));
+ value = decodeURIComponent(result[2].replace(/\+/g, ' '));
params[key] = value;
}
diff --git a/js/lib/languages.js b/js/lib/languages.js
index 99a1ba0ee..d218cbc4f 100644
--- a/js/lib/languages.js
+++ b/js/lib/languages.js
@@ -28,13 +28,11 @@ elgg.reload_all_translations = function(language) {
var lang = language || elgg.get_language();
var url, options;
- if (elgg.config.simplecache_enabled) {
- url = 'cache/js/default/languages/' + lang + '.' + elgg.config.lastcache + '.js';
- options = {};
- } else {
- url = 'ajax/view/js/languages';
- options = {data: {language: lang}};
- }
+ url = 'ajax/view/js/languages';
+ options = {data: {language: lang}};
+ if (elgg.config.simplecache_enabled) {
+ options.data.lc = elgg.config.lastcache;
+ }
options['success'] = function(json) {
elgg.add_translation(lang, json);
diff --git a/js/lib/security.js b/js/lib/security.js
index 61aa1cfcd..9c12f8586 100644
--- a/js/lib/security.js
+++ b/js/lib/security.js
@@ -7,6 +7,8 @@ elgg.security.token = {};
elgg.security.tokenRefreshFailed = false;
+elgg.security.tokenRefreshTimer = null;
+
/**
* Sets the currently active security token and updates all forms and links on the current page.
*
@@ -30,31 +32,17 @@ elgg.security.setToken = function(json) {
};
/**
- * Security tokens time out, so lets refresh those every so often.
+ * Security tokens time out so we refresh those every so often.
*
- * @todo handle error and bad return data
+ * @private
*/
elgg.security.refreshToken = function() {
elgg.action('security/refreshtoken', function(data) {
-
- // @todo might want to move this to setToken() once http://trac.elgg.org/ticket/3127
- // is implemented. It's here right now to avoid soggy code.
- if (!data || !(data.output.__elgg_ts && data.output.__elgg_token)) {
- elgg.register_error(elgg.echo('js:security:token_refresh_failed', [elgg.get_site_url()]));
- elgg.security.tokenRefreshFailed = true;
-
- // don't setToken because we refresh every 5 minutes and tokens are good for 1
- // hour by default
- return;
- }
-
- // if had problems last time, let them know it's working now
- if (elgg.security.tokenRefreshFailed) {
- elgg.system_message(elgg.echo('js:security:token_refreshed', [elgg.get_site_url()]));
- elgg.security.tokenRefreshFailed = false;
+ if (data && data.output.__elgg_ts && data.output.__elgg_token) {
+ elgg.security.setToken(data.output);
+ } else {
+ clearInterval(elgg.security.tokenRefreshTimer);
}
-
- elgg.security.setToken(data.output);
});
};
@@ -112,9 +100,8 @@ elgg.security.addToken = function(data) {
};
elgg.security.init = function() {
- //refresh security token every 5 minutes
- //this is set in the js/elgg PHP view.
- setInterval(elgg.security.refreshToken, elgg.security.interval);
+ // elgg.security.interval is set in the js/elgg PHP view.
+ elgg.security.tokenRefreshTimer = setInterval(elgg.security.refreshToken, elgg.security.interval);
};
elgg.register_hook_handler('boot', 'system', elgg.security.init); \ No newline at end of file
diff --git a/js/lib/session.js b/js/lib/session.js
index fa3d60aa9..a8d52733c 100644
--- a/js/lib/session.js
+++ b/js/lib/session.js
@@ -14,9 +14,9 @@ elgg.provide('elgg.session');
* {string} options[domain]
* {boolean} options[secure]
*
- * @return {string} The value of the cookie, if only name is specified
+ * @return {string|undefined} The value of the cookie, if only name is specified. Undefined if no value set
*/
-elgg.session.cookie = function (name, value, options) {
+elgg.session.cookie = function(name, value, options) {
var cookies = [], cookie = [], i = 0, date, valid = true;
//elgg.session.cookie()
@@ -47,21 +47,19 @@ elgg.session.cookie = function (name, value, options) {
}
cookies.push(name + '=' + value);
-
- if (elgg.isNumber(options.expires)) {
- if (elgg.isNumber(options.expires)) {
- date = new Date();
- date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
- } else if (options.expires.toUTCString) {
- date = options.expires;
- } else {
- valid = false;
- }
-
- if (valid) {
- cookies.push('expires=' + date.toUTCString());
- }
- }
+
+ if (options.expires) {
+ if (elgg.isNumber(options.expires)) {
+ date = new Date();
+ date.setTime(date.getTime() + (options.expires * 24 * 60 * 60 * 1000));
+ } else if (options.expires.toUTCString) {
+ date = options.expires;
+ }
+
+ if (date) {
+ cookies.push('expires=' + date.toUTCString());
+ }
+ }
// CAUTION: Needed to parenthesize options.path and options.domain
// in the following expressions, otherwise they evaluate to undefined
diff --git a/js/lib/ui.river.js b/js/lib/ui.river.js
index a56a664a4..c103fabb3 100644
--- a/js/lib/ui.river.js
+++ b/js/lib/ui.river.js
@@ -1,14 +1,14 @@
-elgg.provide('elgg.ui.river');
-
-elgg.ui.river.init = function() {
- $('#elgg-river-selector').change(function() {
- var url = window.location.href;
- if (window.location.search.length) {
- url = url.substring(0, url.indexOf('?'));
- }
- url += '?' + $(this).val();
- elgg.forward(url);
- });
-};
-
+elgg.provide('elgg.ui.river');
+
+elgg.ui.river.init = function() {
+ $('#elgg-river-selector').change(function() {
+ var url = window.location.href;
+ if (window.location.search.length) {
+ url = url.substring(0, url.indexOf('?'));
+ }
+ url += '?' + $(this).val();
+ elgg.forward(url);
+ });
+};
+
elgg.register_hook_handler('init', 'system', elgg.ui.river.init); \ No newline at end of file
diff --git a/js/lib/ui.userpicker.js b/js/lib/ui.userpicker.js
index 7298da114..669b84cdb 100644
--- a/js/lib/ui.userpicker.js
+++ b/js/lib/ui.userpicker.js
@@ -107,11 +107,11 @@ elgg.userpicker.viewUser = function(info) {
* @return Object
*/
elgg.userpicker.getSearchParams = function(obj) {
- if (obj.element.siblings('[name=match_on]').attr('checked')) {
+ if (obj.element.parent('.elgg-user-picker').find('input[name=match_on]').attr('checked')) {
return {'match_on[]': 'friends', 'term' : obj.term};
} else {
return {'match_on[]': 'users', 'term' : obj.term};
}
};
-elgg.register_hook_handler('init', 'system', elgg.userpicker.init); \ No newline at end of file
+elgg.register_hook_handler('init', 'system', elgg.userpicker.init);
diff --git a/js/tests/ElggLibTest.js b/js/tests/ElggLibTest.js
index 2a676e22a..bd39e7fb3 100644
--- a/js/tests/ElggLibTest.js
+++ b/js/tests/ElggLibTest.js
@@ -78,6 +78,7 @@ ElggLibTest.prototype.testNormalizeUrl = function() {
['https://example.com', 'https://example.com'],
['http://example-time.com', 'http://example-time.com'],
['//example.com', '//example.com'],
+ ['mod/my_plugin/graphics/image.jpg', elgg.config.wwwroot + 'mod/my_plugin/graphics/image.jpg'],
['ftp://example.com/file', 'ftp://example.com/file'],
['mailto:brett@elgg.org', 'mailto:brett@elgg.org'],
@@ -127,3 +128,13 @@ ElggLibTest.prototype.testParseUrl = function() {
});
};
+ElggLibTest.prototype.testParseStr = function() {
+
+ [
+ ["A+%2B+B=A+%2B+B", {"A + B": "A + B"}]
+
+ ].forEach(function(args) {
+ assertEquals(args[1], elgg.parse_str(args[0]));
+ });
+};
+
diff --git a/js/tests/README b/js/tests/README
index 4f86b27c6..f43c0c89d 100644
--- a/js/tests/README
+++ b/js/tests/README
@@ -12,9 +12,10 @@ based debuggers. Visit its wiki at the Google Code site for more information.
Sample Usage
============
1. Put jar file in the base directory of Elgg
- 2. Run the server: java -jar JsTestDriver-1.3.3d.jar --port 4224
+ 2. Run the server: java -jar JsTestDriver-1.3.5.jar --port 4224
3. Point a web browser at http://localhost:4224
- 4. Run the tests: java -jar JsTestDriver-1.3.3d.jar --config js/tests/jsTestDriver.conf --basePath . --tests all
+ 4. Click "Capture this browser"
+ 5. Run the tests: java -jar JsTestDriver-1.3.5.jar --config js/tests/jsTestDriver.conf --basePath . --tests all
Configuration Hints
diff --git a/languages/en.php b/languages/en.php
index 353896047..07407d1e1 100644
--- a/languages/en.php
+++ b/languages/en.php
@@ -105,6 +105,8 @@ $english = array(
'ElggPlugin:Dependencies:Priority:Before' => 'Before %s',
'ElggPlugin:Dependencies:Priority:Uninstalled' => '%s is not installed',
'ElggPlugin:Dependencies:Suggests:Unsatisfied' => 'Missing',
+
+ 'ElggPlugin:Dependencies:ActiveDependent' => 'There are other plugins that list %s as a dependency. You must disable the following plugins before disabling this one: %s',
'ElggPlugin:InvalidAndDeactivated' => '%s is an invalid plugin and has been deactivated.',
@@ -175,7 +177,7 @@ $english = array(
'ConfigurationException:NoSiteID' => "No site ID has been specified.",
'SecurityException:APIAccessDenied' => "Sorry, API access has been disabled by the administrator.",
'SecurityException:NoAuthMethods' => "No authentication methods were found that could authenticate this API request.",
- 'SecurityException:ForwardFailedToRedirect' => 'Redirect could not be issued due to headers already being sent. Halting execution for security. Search http://docs.elgg.org/ for more information.',
+ 'SecurityException:ForwardFailedToRedirect' => 'Redirect could not be issued due to headers already being sent. Halting execution for security. Output started in file %s at line %d. Search http://docs.elgg.org/ for more information.',
'InvalidParameterException:APIMethodOrFunctionNotSet' => "Method or function not set in call in expose_method()",
'InvalidParameterException:APIParametersArrayStructure' => "Parameters array structure is incorrect for call to expose method '%s'",
'InvalidParameterException:UnrecognisedHttpMethod' => "Unrecognised http method %s for api method '%s'",
@@ -359,6 +361,7 @@ $english = array(
'friendspicker:chararray' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
'avatar' => 'Avatar',
+ 'avatar:noaccess' => "You're not allowed to edit this user's avatar",
'avatar:create' => 'Create your avatar',
'avatar:edit' => 'Edit avatar',
'avatar:preview' => 'Preview',
@@ -594,10 +597,24 @@ $english = array(
'admin:settings' => 'Settings',
'admin:settings:basic' => 'Basic Settings',
'admin:settings:advanced' => 'Advanced Settings',
+ 'admin:settings:advanced/site_secret' => 'Site Secret',
'admin:site:description' => "This admin panel allows you to control global settings for your site. Choose an option below to get started.",
+ 'admin:settings:advanced:site_secret' => 'Site Secret',
'admin:site:opt:linktext' => "Configure site...",
'admin:site:access:warning' => "Changing the access setting only affects the permissions on content created in the future.",
+ 'admin:site:secret:intro' => 'Elgg uses a key to create security tokens for various purposes.',
+ 'admin:site:secret_regenerated' => "Your site secret has been regenerated.",
+ 'admin:site:secret:regenerate' => "Regenerate site secret",
+ 'admin:site:secret:regenerate:help' => "Note: This may inconvenience some users by invalidating tokens used in \"remember me\" cookies, e-mail validation requests, invitation codes, etc.",
+ 'site_secret:current_strength' => 'Key Strength',
+ 'site_secret:strength:weak' => "Weak",
+ 'site_secret:strength_msg:weak' => "We strongly recommend that you regenerate your site secret.",
+ 'site_secret:strength:moderate' => "Moderate",
+ 'site_secret:strength_msg:moderate' => "We recommend you regenerate your site secret for the best site security.",
+ 'site_secret:strength:strong' => "Strong",
+ 'site_secret:strength_msg:strong' => "&#x2713; Your site secret is sufficiently strong.",
+
'admin:dashboard' => 'Dashboard',
'admin:widget:online_users' => 'Online users',
'admin:widget:online_users:help' => 'Lists the users currently on the site',
@@ -902,6 +919,7 @@ $english = array(
'total' => 'Total',
'learnmore' => "Click here to learn more.",
+ 'unknown_error' => 'Unknown error',
'content' => "content",
'content:latest' => 'Latest activity',
@@ -1060,7 +1078,7 @@ Once you have logged in, we highly recommend that you change your password.
'upgrade:unlock' => 'Unlock upgrade',
'upgrade:unlock:confirm' => "The database is locked for another upgrade. Running concurrent upgrades is dangerous. You should only continue if you know there is not another upgrade running. Unlock?",
'upgrade:locked' => "Cannot upgrade. Another upgrade is running. To clear the upgrade lock, visit the Admin section.",
- 'upgrade:unlock:success' => "Upgrade unlocked suscessfully.",
+ 'upgrade:unlock:success' => "Upgrade unlocked successfully.",
'upgrade:unable_to_upgrade' => 'Unable to upgrade.',
'upgrade:unable_to_upgrade_info' =>
'This installation cannot be upgraded because legacy views
@@ -1075,6 +1093,8 @@ Once you have logged in, we highly recommend that you change your password.
'update:twitter_api:deactivated' => 'Twitter API (previously Twitter Service) was deactivated during the upgrade. Please activate it manually if required.',
'update:oauth_api:deactivated' => 'OAuth API (previously OAuth Lib) was deactivated during the upgrade. Please activate it manually if required.',
+ 'upgrade:site_secret_warning:moderate' => "You are encouraged to regenerate your site key to improve system security. See Configure &gt; Settings &gt; Site Secret",
+ 'upgrade:site_secret_warning:weak' => "You are strongly encouraged to regenerate your site key to improve system security. See Configure &gt; Settings &gt; Site Secret",
'deprecated:function' => '%s() was deprecated by %s()',
@@ -1189,10 +1209,11 @@ You cannot reply to this email.",
* Action gatekeeper
*/
'actiongatekeeper:missingfields' => 'Form is missing __token or __ts fields',
- 'actiongatekeeper:tokeninvalid' => "We encountered an error (token mismatch). This probably means that the page you were using expired.",
+ 'actiongatekeeper:tokeninvalid' => "The page you were using had expired. Please try again.",
'actiongatekeeper:timeerror' => 'The page you were using has expired. Please refresh and try again.',
'actiongatekeeper:pluginprevents' => 'A extension has prevented this form from being submitted.',
'actiongatekeeper:uploadexceeded' => 'The size of file(s) uploaded exceeded the limit set by your site administrator',
+ 'actiongatekeeper:crosssitelogin' => "Sorry, logging in from a different domain is not permitted. Please try again.",
/**
@@ -1211,7 +1232,7 @@ You cannot reply to this email.",
* Javascript
*/
- 'js:security:token_refresh_failed' => 'Cannot contact %s. You may experience problems saving content.',
+ 'js:security:token_refresh_failed' => 'Failed to contact %s. You may experience problems saving content. Please refresh this page.',
'js:security:token_refreshed' => 'Connection to %s restored!',
/**
diff --git a/mod/blog/actions/blog/save.php b/mod/blog/actions/blog/save.php
index 070c96398..82a9e6c51 100644
--- a/mod/blog/actions/blog/save.php
+++ b/mod/blog/actions/blog/save.php
@@ -2,6 +2,12 @@
/**
* Save blog entity
*
+ * Can be called by clicking save button or preview button. If preview button,
+ * we automatically save as draft. The preview button is only available for
+ * non-published drafts.
+ *
+ * Drafts are saved with the access set to private.
+ *
* @package Blog
*/
@@ -73,11 +79,7 @@ foreach ($values as $name => $default) {
switch ($name) {
case 'tags':
- if ($value) {
- $values[$name] = string_to_tag_array($value);
- } else {
- unset ($values[$name]);
- }
+ $values[$name] = string_to_tag_array($value);
break;
case 'excerpt':
@@ -99,11 +101,6 @@ foreach ($values as $name => $default) {
}
break;
- // don't try to set the guid
- case 'guid':
- unset($values['guid']);
- break;
-
default:
$values[$name] = $value;
break;
@@ -115,13 +112,16 @@ if ($save == false) {
$values['status'] = 'draft';
}
+// if draft, set access to private and cache the future access
+if ($values['status'] == 'draft') {
+ $values['future_access'] = $values['access_id'];
+ $values['access_id'] = ACCESS_PRIVATE;
+}
+
// assign values to the entity, stopping on error.
if (!$error) {
foreach ($values as $name => $value) {
- if (FALSE === ($blog->$name = $value)) {
- $error = elgg_echo('blog:error:cannot_save' . "$name=$value");
- break;
- }
+ $blog->$name = $value;
}
}
@@ -151,6 +151,11 @@ if (!$error) {
if (($new_post || $old_status == 'draft') && $status == 'published') {
add_to_river('river/object/blog/create', 'create', $blog->owner_guid, $blog->getGUID());
+ // we only want notifications sent when post published
+ register_notification_object('object', 'blog', elgg_echo('blog:newpost'));
+ elgg_trigger_event('publish', 'object', $blog);
+
+ // reset the creation time for posts that move from draft to published
if ($guid) {
$blog->time_created = time();
$blog->save();
diff --git a/mod/blog/lib/blog.php b/mod/blog/lib/blog.php
index 3c71dfbab..9753f27a8 100644
--- a/mod/blog/lib/blog.php
+++ b/mod/blog/lib/blog.php
@@ -39,8 +39,8 @@ function blog_get_page_content_read($guid = NULL) {
elgg_push_breadcrumb($blog->title);
$return['content'] = elgg_view_entity($blog, array('full_view' => true));
- //check to see if comment are on
- if ($blog->comments_on != 'Off') {
+ // check to see if we should allow comments
+ if ($blog->comments_on != 'Off' && $blog->status == 'published') {
$return['content'] .= elgg_view_comments($blog);
}
@@ -363,6 +363,10 @@ function blog_prepare_form_vars($post = NULL, $revision = NULL) {
$values[$field] = $post->$field;
}
}
+
+ if ($post->status == 'draft') {
+ $values['access_id'] = $post->future_access;
+ }
}
if (elgg_is_sticky_form('blog')) {
diff --git a/mod/blog/start.php b/mod/blog/start.php
index eb6eee05f..e724b91c2 100644
--- a/mod/blog/start.php
+++ b/mod/blog/start.php
@@ -41,8 +41,8 @@ function blog_init() {
// override the default url to view a blog object
elgg_register_entity_url_handler('object', 'blog', 'blog_url_handler');
- // notifications
- register_notification_object('object', 'blog', elgg_echo('blog:newpost'));
+ // notifications - need to register for unique event because of draft/published status
+ elgg_register_event_handler('publish', 'object', 'object_notifications');
elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'blog_notify_message');
// add blog link to
@@ -113,14 +113,23 @@ function blog_page_handler($page) {
switch ($page_type) {
case 'owner':
$user = get_user_by_username($page[1]);
+ if (!$user) {
+ forward('', '404');
+ }
$params = blog_get_page_content_list($user->guid);
break;
case 'friends':
$user = get_user_by_username($page[1]);
+ if (!$user) {
+ forward('', '404');
+ }
$params = blog_get_page_content_friends($user->guid);
break;
case 'archive':
$user = get_user_by_username($page[1]);
+ if (!$user) {
+ forward('', '404');
+ }
$params = blog_get_page_content_archive($user->guid, $page[2], $page[3]);
break;
case 'view':
@@ -139,7 +148,11 @@ function blog_page_handler($page) {
$params = blog_get_page_content_edit($page_type, $page[1], $page[2]);
break;
case 'group':
- if ($page[2] == 'all') {
+ $group = get_entity($page[1]);
+ if (!elgg_instanceof($group, 'group')) {
+ forward('', '404');
+ }
+ if (!isset($page[2]) || $page[2] == 'all') {
$params = blog_get_page_content_list($page[1]);
} else {
$params = blog_get_page_content_archive($page[1], $page[3], $page[4]);
@@ -214,7 +227,14 @@ function blog_entity_menu_setup($hook, $type, $return, $params) {
return $return;
}
- if ($entity->canEdit() && $entity->status != 'published') {
+ if ($entity->status != 'published') {
+ // draft status replaces access
+ foreach ($return as $index => $item) {
+ if ($item->getName() == 'access') {
+ unset($return[$index]);
+ }
+ }
+
$status_text = elgg_echo("blog:status:{$entity->status}");
$options = array(
'name' => 'published_status',
diff --git a/mod/blog/views/default/blog/sidebar/archives.php b/mod/blog/views/default/blog/sidebar/archives.php
index 3d8f28ca4..5098e6e3e 100644
--- a/mod/blog/views/default/blog/sidebar/archives.php
+++ b/mod/blog/views/default/blog/sidebar/archives.php
@@ -14,7 +14,7 @@ if (elgg_instanceof($page_owner, 'user')) {
// This is a limitation of the URL schema.
if ($page_owner && $vars['page'] != 'friends') {
- $dates = get_entity_dates('object', 'blog', $page_owner->getGUID());
+ $dates = array_reverse(get_entity_dates('object', 'blog', $page_owner->getGUID()));
if ($dates) {
$title = elgg_echo('blog:archives');
diff --git a/mod/blog/views/default/forms/blog/save.php b/mod/blog/views/default/forms/blog/save.php
index 36fa2e0e8..f825acca1 100644
--- a/mod/blog/views/default/forms/blog/save.php
+++ b/mod/blog/views/default/forms/blog/save.php
@@ -10,7 +10,7 @@ $vars['entity'] = $blog;
$draft_warning = $vars['draft_warning'];
if ($draft_warning) {
- $draft_warning = '<span class="message warning">' . $draft_warning . '</span>';
+ $draft_warning = '<span class="mbm elgg-text-help">' . $draft_warning . '</span>';
}
$action_buttons = '';
diff --git a/mod/blog/views/default/river/object/blog/create.php b/mod/blog/views/default/river/object/blog/create.php
index a054c1061..b808f1bdc 100644
--- a/mod/blog/views/default/river/object/blog/create.php
+++ b/mod/blog/views/default/river/object/blog/create.php
@@ -4,10 +4,12 @@
*/
$object = $vars['item']->getObjectEntity();
-$excerpt = strip_tags($object->excerpt);
+
+$excerpt = $object->excerpt ? $object->excerpt : $object->description;
+$excerpt = strip_tags($excerpt);
$excerpt = elgg_get_excerpt($excerpt);
echo elgg_view('river/elements/layout', array(
'item' => $vars['item'],
'message' => $excerpt,
-)); \ No newline at end of file
+));
diff --git a/mod/bookmarks/languages/en.php b/mod/bookmarks/languages/en.php
index d4980280d..970b39415 100644
--- a/mod/bookmarks/languages/en.php
+++ b/mod/bookmarks/languages/en.php
@@ -9,7 +9,7 @@ $english = array(
* Menu items and titles
*/
'bookmarks' => "Bookmarks",
- 'bookmarks:add' => "Add bookmark",
+ 'bookmarks:add' => "Add a bookmark",
'bookmarks:edit' => "Edit bookmark",
'bookmarks:owner' => "%s's bookmarks",
'bookmarks:friends' => "Friends' bookmarks",
diff --git a/mod/bookmarks/pages/bookmarks/all.php b/mod/bookmarks/pages/bookmarks/all.php
index bdb8fc793..5c6011ad9 100644
--- a/mod/bookmarks/pages/bookmarks/all.php
+++ b/mod/bookmarks/pages/bookmarks/all.php
@@ -13,9 +13,8 @@ elgg_register_title_button();
$content = elgg_list_entities(array(
'type' => 'object',
'subtype' => 'bookmarks',
- 'limit' => 10,
'full_view' => false,
- 'view_toggle_type' => false
+ 'view_toggle_type' => false,
));
if (!$content) {
diff --git a/mod/bookmarks/pages/bookmarks/friends.php b/mod/bookmarks/pages/bookmarks/friends.php
index 15b1da098..173996346 100644
--- a/mod/bookmarks/pages/bookmarks/friends.php
+++ b/mod/bookmarks/pages/bookmarks/friends.php
@@ -7,7 +7,7 @@
$page_owner = elgg_get_page_owner_entity();
if (!$page_owner) {
- forward('bookmarks/all');
+ forward('', '404');
}
elgg_push_breadcrumb($page_owner->name, "bookmarks/owner/$page_owner->username");
diff --git a/mod/bookmarks/pages/bookmarks/owner.php b/mod/bookmarks/pages/bookmarks/owner.php
index a024ff352..b7b907916 100644
--- a/mod/bookmarks/pages/bookmarks/owner.php
+++ b/mod/bookmarks/pages/bookmarks/owner.php
@@ -7,7 +7,7 @@
$page_owner = elgg_get_page_owner_entity();
if (!$page_owner) {
- forward('bookmarks/all');
+ forward('', '404');
}
elgg_push_breadcrumb($page_owner->name);
@@ -18,7 +18,6 @@ $content .= elgg_list_entities(array(
'type' => 'object',
'subtype' => 'bookmarks',
'container_guid' => $page_owner->guid,
- 'limit' => 10,
'full_view' => false,
'view_toggle_type' => false
));
diff --git a/mod/bookmarks/start.php b/mod/bookmarks/start.php
index 3846f5165..caea43587 100644
--- a/mod/bookmarks/start.php
+++ b/mod/bookmarks/start.php
@@ -56,6 +56,9 @@ function bookmarks_init() {
// Listen to notification events and supply a more useful message
elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'bookmarks_notify_message');
+ // Register bookmarks view for ecml parsing
+ elgg_register_plugin_hook_handler('get_views', 'ecml', 'bookmarks_ecml_views_hook');
+
// Register a URL handler for bookmarks
elgg_register_entity_url_handler('object', 'bookmarks', 'bookmark_url');
@@ -282,8 +285,11 @@ function bookmarks_page_menu($hook, $type, $return, $params) {
if (!$page_owner) {
$page_owner = elgg_get_logged_in_user_entity();
}
-
+
if ($page_owner instanceof ElggGroup) {
+ if (!$page_owner->isMember()) {
+ return $return;
+ }
$title = elgg_echo('bookmarks:bookmarklet:group');
} else {
$title = elgg_echo('bookmarks:bookmarklet');
@@ -295,3 +301,16 @@ function bookmarks_page_menu($hook, $type, $return, $params) {
return $return;
}
+
+/**
+ * Return bookmarks views to parse for ecml
+ *
+ * @param string $hook
+ * @param string $type
+ * @param array $return
+ * @param array $params
+ */
+function bookmarks_ecml_views_hook($hook, $type, $return, $params) {
+ $return['object/bookmarks'] = elgg_echo('item:object:bookmarks');
+ return $return;
+}
diff --git a/mod/categories/pages/categories/listing.php b/mod/categories/pages/categories/listing.php
index 8924506e9..d51e6c19e 100644
--- a/mod/categories/pages/categories/listing.php
+++ b/mod/categories/pages/categories/listing.php
@@ -15,8 +15,8 @@ $type = get_input("type", 'object');
$params = array(
'metadata_name' => 'universal_categories',
'metadata_value' => $category,
- 'types' => $type,
- 'subtypes' => $subtype,
+ 'type' => $type,
+ 'subtype' => $subtype,
'owner_guid' => $owner_guid,
'limit' => $limit,
'full_view' => FALSE,
diff --git a/mod/custom_index/index.php b/mod/custom_index/index.php
index 53990a006..05771f576 100644
--- a/mod/custom_index/index.php
+++ b/mod/custom_index/index.php
@@ -31,7 +31,7 @@ $files = elgg_list_entities($list_params);
//get the newest members who have an avatar
$newest_members = elgg_list_entities_from_metadata(array(
'metadata_names' => 'icontime',
- 'types' => 'user',
+ 'type' => 'user',
'limit' => 10,
'full_view' => false,
'pagination' => false,
diff --git a/mod/developers/languages/en.php b/mod/developers/languages/en.php
index 856efe008..266b5406e 100644
--- a/mod/developers/languages/en.php
+++ b/mod/developers/languages/en.php
@@ -28,7 +28,8 @@ $english = array(
'developers:label:show_strings' => "Show raw translation strings",
'developers:help:show_strings' => "This displays the translation strings used by elgg_echo().",
'developers:label:wrap_views' => "Wrap views",
- 'developers:help:wrap_views' => "This wraps almost every view with HTML comments. Useful for finding the view creating particular HTML.",
+ 'developers:help:wrap_views' => "This wraps almost every view with HTML comments. Useful for finding the view creating particular HTML.
+ This can break non-HTML views in the default viewtype. See developers_wrap_views() for details.",
'developers:label:log_events' => "Log events and plugin hooks",
'developers:help:log_events' => "Write events and plugin hooks to the log. Warning: there are many of these per page.",
diff --git a/mod/developers/manifest.xml b/mod/developers/manifest.xml
index e31998872..23e726e2b 100644
--- a/mod/developers/manifest.xml
+++ b/mod/developers/manifest.xml
@@ -8,7 +8,7 @@
<blurb>Developer tools for Elgg</blurb>
<description>A set of tools for writing plugins and themes. It is recommended that you have this plugin at the top of the plugin list.</description>
<website>http://www.elgg.org/</website>
- <bugtracker>http://trac.elgg.org</bugtracker>
+ <bugtracker>https://github.com/Elgg/Elgg/issues</bugtracker>
<copyright>See COPYRIGHT.txt</copyright>
<license>GNU General Public License version 2</license>
diff --git a/mod/developers/start.php b/mod/developers/start.php
index 413a8ed9b..94d0f652c 100644
--- a/mod/developers/start.php
+++ b/mod/developers/start.php
@@ -89,6 +89,15 @@ function developers_clear_strings() {
/**
* Post-process a view to add wrapper comments to it
+ *
+ * 1. Only process views served with the 'default' viewtype.
+ * 2. Does not wrap views that begin with js/ or css/ as they are not HTML.
+ * 3. Does not wrap views that are images (start with icon/). Is this still true?
+ * 4. Does not wrap input and output views (why?).
+ * 5. Does not wrap html head or the primary page shells
+ *
+ * @warning this will break views in the default viewtype that return non-HTML data
+ * that do not match the above restrictions.
*/
function developers_wrap_views($hook, $type, $result, $params) {
if (elgg_get_viewtype() != "default") {
diff --git a/mod/developers/views/default/theme_preview/components/image_block.php b/mod/developers/views/default/theme_preview/components/image_block.php
index 0bb16428b..ecd35ac65 100644
--- a/mod/developers/views/default/theme_preview/components/image_block.php
+++ b/mod/developers/views/default/theme_preview/components/image_block.php
@@ -1,6 +1,6 @@
-<?php
-$ipsum = elgg_view('developers/ipsum');
-
-$user = new ElggUser();
-$image = elgg_view_entity_icon($user, 'small');
-echo elgg_view_image_block($image, "$ipsum $ipsum $ipsum $ipsum $ipsum $ipsum $ipsum");
+<?php
+$ipsum = elgg_view('developers/ipsum');
+
+$user = new ElggUser();
+$image = elgg_view_entity_icon($user, 'small');
+echo elgg_view_image_block($image, "$ipsum $ipsum $ipsum $ipsum $ipsum $ipsum $ipsum");
diff --git a/mod/developers/views/default/theme_preview/components/list.php b/mod/developers/views/default/theme_preview/components/list.php
index 8096bda04..fcb6f768a 100644
--- a/mod/developers/views/default/theme_preview/components/list.php
+++ b/mod/developers/views/default/theme_preview/components/list.php
@@ -1,19 +1,19 @@
-<?php
-
-$obj1 = new ElggObject();
-$obj1->title = "Object 1";
-$obj1->description = $ipsum;
-
-$obj2 = new ElggObject();
-$obj2->title = "Object 2";
-$obj2->description = $ipsum;
-
-$obj3 = new ElggObject();
-$obj3->title = "Object 3";
-$obj3->description = $ipsum;
-
-$obj4 = new ElggObject();
-$obj4->title = "Object 4";
-$obj4->description = $ipsum;
-
-echo elgg_view('page/components/list', array('items' => array($obj1, $obj2, $obj3, $obj4)));
+<?php
+
+$obj1 = new ElggObject();
+$obj1->title = "Object 1";
+$obj1->description = $ipsum;
+
+$obj2 = new ElggObject();
+$obj2->title = "Object 2";
+$obj2->description = $ipsum;
+
+$obj3 = new ElggObject();
+$obj3->title = "Object 3";
+$obj3->description = $ipsum;
+
+$obj4 = new ElggObject();
+$obj4->title = "Object 4";
+$obj4->description = $ipsum;
+
+echo elgg_view('page/components/list', array('items' => array($obj1, $obj2, $obj3, $obj4)));
diff --git a/mod/developers/views/default/theme_preview/components/messages.php b/mod/developers/views/default/theme_preview/components/messages.php
index ac4d2bfd7..a53255291 100644
--- a/mod/developers/views/default/theme_preview/components/messages.php
+++ b/mod/developers/views/default/theme_preview/components/messages.php
@@ -1,5 +1,5 @@
-<ul>
- <li class="elgg-message elgg-state-success mas">Success message (.elgg-state-success)</li>
- <li class="elgg-message elgg-state-error mas">Error message (.elgg-state-error)</li>
- <li class="elgg-message elgg-state-notice mas">Notice message (.elgg-state-notice)</li>
-</ul>
+<ul>
+ <li class="elgg-message elgg-state-success mas">Success message (.elgg-state-success)</li>
+ <li class="elgg-message elgg-state-error mas">Error message (.elgg-state-error)</li>
+ <li class="elgg-message elgg-state-notice mas">Notice message (.elgg-state-notice)</li>
+</ul>
diff --git a/mod/developers/views/default/theme_preview/components/table.php b/mod/developers/views/default/theme_preview/components/table.php
index 8b8b13e76..7d619dcea 100644
--- a/mod/developers/views/default/theme_preview/components/table.php
+++ b/mod/developers/views/default/theme_preview/components/table.php
@@ -1,12 +1,12 @@
-<table class="<?php echo $vars['class']; ?>">
-<?php
- echo "<thead><tr><th>column 1</th><th>column 2</th></tr></thead>";
- for ($i = 1; $i < 5; $i++) {
- echo '<tr>';
- for ($j = 1; $j < 3; $j++) {
- echo "<td>value $j</td>";
- }
- echo '</tr>';
- }
-?>
+<table class="<?php echo $vars['class']; ?>">
+<?php
+ echo "<thead><tr><th>column 1</th><th>column 2</th></tr></thead>";
+ for ($i = 1; $i < 5; $i++) {
+ echo '<tr>';
+ for ($j = 1; $j < 3; $j++) {
+ echo "<td>value $j</td>";
+ }
+ echo '</tr>';
+ }
+?>
</table> \ No newline at end of file
diff --git a/mod/developers/views/default/theme_preview/icons/avatars.php b/mod/developers/views/default/theme_preview/icons/avatars.php
index f50a6b70d..3aa1eda26 100644
--- a/mod/developers/views/default/theme_preview/icons/avatars.php
+++ b/mod/developers/views/default/theme_preview/icons/avatars.php
@@ -1,36 +1,36 @@
-<?php
- $user = new ElggUser();
- $group = new ElggGroup();
-
- $sizes = array('large', 'medium', 'small', 'tiny');
-?>
-<table class="elgg-table">
- <tr>
- <th></th>
- <?php
- foreach ($sizes as $size) {
- echo "<th>$size</th>";
- }
- ?>
- </tr>
- <tr>
- <th>User</th>
- <?php
- foreach ($sizes as $size) {
- echo '<td>';
- echo elgg_view_entity_icon($user, $size, array('use_hover' => false));
- echo '</td>';
- }
- ?>
- </tr>
- <tr>
- <th>Group</th>
- <?php
- foreach ($sizes as $size) {
- echo '<td>';
- echo elgg_view_entity_icon($group, $size, array('use_hover' => false));
- echo '</td>';
- }
- ?>
- </tr>
-</table>
+<?php
+ $user = new ElggUser();
+ $group = new ElggGroup();
+
+ $sizes = array('large', 'medium', 'small', 'tiny');
+?>
+<table class="elgg-table">
+ <tr>
+ <th></th>
+ <?php
+ foreach ($sizes as $size) {
+ echo "<th>$size</th>";
+ }
+ ?>
+ </tr>
+ <tr>
+ <th>User</th>
+ <?php
+ foreach ($sizes as $size) {
+ echo '<td>';
+ echo elgg_view_entity_icon($user, $size, array('use_hover' => false));
+ echo '</td>';
+ }
+ ?>
+ </tr>
+ <tr>
+ <th>Group</th>
+ <?php
+ foreach ($sizes as $size) {
+ echo '<td>';
+ echo elgg_view_entity_icon($group, $size, array('use_hover' => false));
+ echo '</td>';
+ }
+ ?>
+ </tr>
+</table>
diff --git a/mod/developers/views/default/theme_preview/icons/sprites.php b/mod/developers/views/default/theme_preview/icons/sprites.php
index 134dd9aca..3edb0bd7c 100644
--- a/mod/developers/views/default/theme_preview/icons/sprites.php
+++ b/mod/developers/views/default/theme_preview/icons/sprites.php
@@ -1,61 +1,61 @@
-<?php
-$icons = array(
- 'arrow-left',
- 'arrow-right',
- 'arrow-two-head',
- 'calendar',
- 'checkmark',
- 'clip',
- 'cursor-drag-arrow',
- 'delete-alt',
- 'delete',
- 'download',
- 'facebook',
- 'home',
- 'hover-menu',
- 'link',
- 'mail-alt',
- 'mail',
- 'print-alt',
- 'print',
- 'push-pin-alt',
- 'push-pin',
- 'redo',
- 'refresh',
- 'round-arrow-left',
- 'round-arrow-right',
- 'round-checkmark',
- 'round-minus',
- 'round-plus',
- 'rss',
- 'search-focus',
- 'search',
- 'settings-alt',
- 'settings',
- 'share',
- 'shop-cart',
- 'speech-bubble-alt',
- 'speech-bubble',
- 'star-alt',
- 'star-empty',
- 'star',
- 'tag',
- 'thumbs-down-alt',
- 'thumbs-down',
- 'thumbs-up-alt',
- 'thumbs-up',
- 'trash',
- 'twitter',
- 'undo',
- 'user',
- 'users',
-);
-?>
-
-<ul class="elgg-gallery">
-<?php
- foreach ($icons as $icon) {
- echo "<li title=\".elgg-icon-$icon\" style=\"margin:10px\">" . elgg_view_icon($icon) . "</li>";
- }
-?>
+<?php
+$icons = array(
+ 'arrow-left',
+ 'arrow-right',
+ 'arrow-two-head',
+ 'calendar',
+ 'checkmark',
+ 'clip',
+ 'cursor-drag-arrow',
+ 'delete-alt',
+ 'delete',
+ 'download',
+ 'facebook',
+ 'home',
+ 'hover-menu',
+ 'link',
+ 'mail-alt',
+ 'mail',
+ 'print-alt',
+ 'print',
+ 'push-pin-alt',
+ 'push-pin',
+ 'redo',
+ 'refresh',
+ 'round-arrow-left',
+ 'round-arrow-right',
+ 'round-checkmark',
+ 'round-minus',
+ 'round-plus',
+ 'rss',
+ 'search-focus',
+ 'search',
+ 'settings-alt',
+ 'settings',
+ 'share',
+ 'shop-cart',
+ 'speech-bubble-alt',
+ 'speech-bubble',
+ 'star-alt',
+ 'star-empty',
+ 'star',
+ 'tag',
+ 'thumbs-down-alt',
+ 'thumbs-down',
+ 'thumbs-up-alt',
+ 'thumbs-up',
+ 'trash',
+ 'twitter',
+ 'undo',
+ 'user',
+ 'users',
+);
+?>
+
+<ul class="elgg-gallery">
+<?php
+ foreach ($icons as $icon) {
+ echo "<li title=\".elgg-icon-$icon\" style=\"margin:10px\">" . elgg_view_icon($icon) . "</li>";
+ }
+?>
</ul> \ No newline at end of file
diff --git a/mod/developers/views/default/theme_preview/modules/modules.php b/mod/developers/views/default/theme_preview/modules/modules.php
index e0d39c0da..04f5917b4 100644
--- a/mod/developers/views/default/theme_preview/modules/modules.php
+++ b/mod/developers/views/default/theme_preview/modules/modules.php
@@ -1,23 +1,23 @@
-<?php
-
-$ipsum = elgg_view('developers/ipsum');
-
-?>
-<div class="elgg-grid">
- <div class="elgg-col elgg-col-1of2">
- <div class="pam">
- <?php
- echo elgg_view_module('aside', 'Aside (.elgg-module-aside)', $ipsum);
- echo elgg_view_module('popup', 'Popup (.elgg-module-popup)', $ipsum);
- ?>
- </div>
- </div>
- <div class="elgg-col elgg-col-1of2">
- <div class="pam">
- <?php
- echo elgg_view_module('info', 'Info (.elgg-module-info)', $ipsum);
- echo elgg_view_module('featured', 'Featured (.elgg-module-featured)', $ipsum);
- ?>
- </div>
- </div>
+<?php
+
+$ipsum = elgg_view('developers/ipsum');
+
+?>
+<div class="elgg-grid">
+ <div class="elgg-col elgg-col-1of2">
+ <div class="pam">
+ <?php
+ echo elgg_view_module('aside', 'Aside (.elgg-module-aside)', $ipsum);
+ echo elgg_view_module('popup', 'Popup (.elgg-module-popup)', $ipsum);
+ ?>
+ </div>
+ </div>
+ <div class="elgg-col elgg-col-1of2">
+ <div class="pam">
+ <?php
+ echo elgg_view_module('info', 'Info (.elgg-module-info)', $ipsum);
+ echo elgg_view_module('featured', 'Featured (.elgg-module-featured)', $ipsum);
+ ?>
+ </div>
+ </div>
</div> \ No newline at end of file
diff --git a/mod/developers/views/default/theme_preview/navigation/breadcrumbs.php b/mod/developers/views/default/theme_preview/navigation/breadcrumbs.php
index c910b2aa4..0439bd577 100644
--- a/mod/developers/views/default/theme_preview/navigation/breadcrumbs.php
+++ b/mod/developers/views/default/theme_preview/navigation/breadcrumbs.php
@@ -1,10 +1,10 @@
-<?php
-elgg_push_breadcrumb('First', "#");
-elgg_push_breadcrumb('Second', "#");
-elgg_push_breadcrumb('Third');
-
-echo elgg_view('navigation/breadcrumbs', array('class' => mts));
-
-elgg_pop_breadcrumb();
-elgg_pop_breadcrumb();
-elgg_pop_breadcrumb();
+<?php
+elgg_push_breadcrumb('First', "#");
+elgg_push_breadcrumb('Second', "#");
+elgg_push_breadcrumb('Third');
+
+echo elgg_view('navigation/breadcrumbs', array('class' => mts));
+
+elgg_pop_breadcrumb();
+elgg_pop_breadcrumb();
+elgg_pop_breadcrumb();
diff --git a/mod/developers/views/default/theme_preview/navigation/default.php b/mod/developers/views/default/theme_preview/navigation/default.php
index bfd26162f..6efcd8890 100644
--- a/mod/developers/views/default/theme_preview/navigation/default.php
+++ b/mod/developers/views/default/theme_preview/navigation/default.php
@@ -1,11 +1,11 @@
-<?php
-
-$params = array();
-$params['menu'] = array();
-$params['menu']['default'] = array();
-for ($i=1; $i<=5; $i++) {
- $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
-}
-$params['menu']['default'][2]->setSelected(true);
-
-echo elgg_view('navigation/menu/default', $params);
+<?php
+
+$params = array();
+$params['menu'] = array();
+$params['menu']['default'] = array();
+for ($i=1; $i<=5; $i++) {
+ $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
+}
+$params['menu']['default'][2]->setSelected(true);
+
+echo elgg_view('navigation/menu/default', $params);
diff --git a/mod/developers/views/default/theme_preview/navigation/extras.php b/mod/developers/views/default/theme_preview/navigation/extras.php
index 43b19f8e3..01bc6d434 100644
--- a/mod/developers/views/default/theme_preview/navigation/extras.php
+++ b/mod/developers/views/default/theme_preview/navigation/extras.php
@@ -1,18 +1,18 @@
-<?php
-
-$params = array();
-$params['menu'] = array();
-$params['menu']['default'] = array();
-$params['menu']['default'][] = new ElggMenuItem(1, elgg_view_icon('push-pin-alt'), "#");
-$params['menu']['default'][] = new ElggMenuItem(2, elgg_view_icon('rss'), "#");
-$params['menu']['default'][] = new ElggMenuItem(3, elgg_view_icon('star-alt'), "#");
-$params['name'] = 'extras';
-$params['class'] = 'elgg-menu-hz';
-
-?>
-
-<div class="elgg-sidebar">
-<?php
- echo elgg_view('navigation/menu/default', $params);
-?>
+<?php
+
+$params = array();
+$params['menu'] = array();
+$params['menu']['default'] = array();
+$params['menu']['default'][] = new ElggMenuItem(1, elgg_view_icon('push-pin-alt'), "#");
+$params['menu']['default'][] = new ElggMenuItem(2, elgg_view_icon('rss'), "#");
+$params['menu']['default'][] = new ElggMenuItem(3, elgg_view_icon('star-alt'), "#");
+$params['name'] = 'extras';
+$params['class'] = 'elgg-menu-hz';
+
+?>
+
+<div class="elgg-sidebar">
+<?php
+ echo elgg_view('navigation/menu/default', $params);
+?>
</div> \ No newline at end of file
diff --git a/mod/developers/views/default/theme_preview/navigation/filter.php b/mod/developers/views/default/theme_preview/navigation/filter.php
index ea1c8b033..70cd31d2a 100644
--- a/mod/developers/views/default/theme_preview/navigation/filter.php
+++ b/mod/developers/views/default/theme_preview/navigation/filter.php
@@ -1,13 +1,13 @@
-<?php
-
-$params = array();
-$params['menu'] = array();
-$params['menu']['default'] = array();
-for ($i=1; $i<=5; $i++) {
- $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
-}
-$params['menu']['default'][2]->setSelected(true);
-
-$params['name'] = 'filter';
-
-echo elgg_view('navigation/menu/default', $params);
+<?php
+
+$params = array();
+$params['menu'] = array();
+$params['menu']['default'] = array();
+for ($i=1; $i<=5; $i++) {
+ $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
+}
+$params['menu']['default'][2]->setSelected(true);
+
+$params['name'] = 'filter';
+
+echo elgg_view('navigation/menu/default', $params);
diff --git a/mod/developers/views/default/theme_preview/navigation/horizontal.php b/mod/developers/views/default/theme_preview/navigation/horizontal.php
index f404f42c0..44e04cd7c 100644
--- a/mod/developers/views/default/theme_preview/navigation/horizontal.php
+++ b/mod/developers/views/default/theme_preview/navigation/horizontal.php
@@ -1,12 +1,12 @@
-<?php
-
-$params = array();
-$params['menu'] = array();
-$params['menu']['default'] = array();
-for ($i=1; $i<=5; $i++) {
- $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
-}
-$params['menu']['default'][2]->setSelected(true);
-$params['class'] = 'elgg-menu-hz';
-
-echo elgg_view('navigation/menu/default', $params);
+<?php
+
+$params = array();
+$params['menu'] = array();
+$params['menu']['default'] = array();
+for ($i=1; $i<=5; $i++) {
+ $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
+}
+$params['menu']['default'][2]->setSelected(true);
+$params['class'] = 'elgg-menu-hz';
+
+echo elgg_view('navigation/menu/default', $params);
diff --git a/mod/developers/views/default/theme_preview/navigation/owner_block.php b/mod/developers/views/default/theme_preview/navigation/owner_block.php
index 20b93d166..f5f203947 100644
--- a/mod/developers/views/default/theme_preview/navigation/owner_block.php
+++ b/mod/developers/views/default/theme_preview/navigation/owner_block.php
@@ -1,13 +1,13 @@
-<?php
-$params = array();
-$params['menu'] = array();
-$params['menu']['default'] = array();
-for ($i=1; $i<=5; $i++) {
- $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
-}
-$params['menu']['default'][2]->setSelected(true);
-$params['name'] = 'owner-block';
-
-echo '<div class="elgg-sidebar">';
-echo elgg_view('navigation/menu/default', $params);
-echo '</div>';
+<?php
+$params = array();
+$params['menu'] = array();
+$params['menu']['default'] = array();
+for ($i=1; $i<=5; $i++) {
+ $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
+}
+$params['menu']['default'][2]->setSelected(true);
+$params['name'] = 'owner-block';
+
+echo '<div class="elgg-sidebar">';
+echo elgg_view('navigation/menu/default', $params);
+echo '</div>';
diff --git a/mod/developers/views/default/theme_preview/navigation/page.php b/mod/developers/views/default/theme_preview/navigation/page.php
index a57edc2e2..1da6a1fd9 100644
--- a/mod/developers/views/default/theme_preview/navigation/page.php
+++ b/mod/developers/views/default/theme_preview/navigation/page.php
@@ -1,20 +1,20 @@
-<?php
-
-$params = array();
-$params['menu'] = array();
-$params['menu']['default'] = array();
-for ($i=1; $i<=5; $i++) {
- $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
-}
-$params['menu']['default'][2]->setSelected(true);
-
-$m = new ElggMenuItem(10, "Child", "#");
-$m->setParent($params['menu']['default'][1]);
-$params['menu']['default'][1]->addChild($m);
-?>
-
-<div class="elgg-sidebar">
-<?php
- echo elgg_view('navigation/menu/page', $params);
-?>
+<?php
+
+$params = array();
+$params['menu'] = array();
+$params['menu']['default'] = array();
+for ($i=1; $i<=5; $i++) {
+ $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
+}
+$params['menu']['default'][2]->setSelected(true);
+
+$m = new ElggMenuItem(10, "Child", "#");
+$m->setParent($params['menu']['default'][1]);
+$params['menu']['default'][1]->addChild($m);
+?>
+
+<div class="elgg-sidebar">
+<?php
+ echo elgg_view('navigation/menu/page', $params);
+?>
</div> \ No newline at end of file
diff --git a/mod/developers/views/default/theme_preview/navigation/pagination.php b/mod/developers/views/default/theme_preview/navigation/pagination.php
index 90ae48edf..f5e1b632d 100644
--- a/mod/developers/views/default/theme_preview/navigation/pagination.php
+++ b/mod/developers/views/default/theme_preview/navigation/pagination.php
@@ -1,8 +1,8 @@
-<?php
-$params = array(
- 'count' => 1000,
- 'limit' => 10,
- 'offset' => 230,
-);
-
+<?php
+$params = array(
+ 'count' => 1000,
+ 'limit' => 10,
+ 'offset' => 230,
+);
+
echo elgg_view('navigation/pagination', $params); \ No newline at end of file
diff --git a/mod/developers/views/default/theme_preview/navigation/site.php b/mod/developers/views/default/theme_preview/navigation/site.php
index 329036b80..90bb8ff46 100644
--- a/mod/developers/views/default/theme_preview/navigation/site.php
+++ b/mod/developers/views/default/theme_preview/navigation/site.php
@@ -1,18 +1,18 @@
-<?php
-
-$params = array();
-$params['menu'] = array();
-$params['menu']['default'] = array();
-for ($i=1; $i<=5; $i++) {
- $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
-}
-$params['menu']['default'][2]->setSelected(true);
-?>
-
-<div class="elgg-page-header">
- <div class="elgg-inner">
- <?php
- echo elgg_view('navigation/menu/site', $params);
- ?>
- </div>
-</div>
+<?php
+
+$params = array();
+$params['menu'] = array();
+$params['menu']['default'] = array();
+for ($i=1; $i<=5; $i++) {
+ $params['menu']['default'][] = new ElggMenuItem($i, "Page $i", "#");
+}
+$params['menu']['default'][2]->setSelected(true);
+?>
+
+<div class="elgg-page-header">
+ <div class="elgg-inner">
+ <?php
+ echo elgg_view('navigation/menu/site', $params);
+ ?>
+ </div>
+</div>
diff --git a/mod/developers/views/default/theme_preview/navigation/tabs.php b/mod/developers/views/default/theme_preview/navigation/tabs.php
index 81fe4e669..dd282dc83 100644
--- a/mod/developers/views/default/theme_preview/navigation/tabs.php
+++ b/mod/developers/views/default/theme_preview/navigation/tabs.php
@@ -1,10 +1,10 @@
-<?php
-$params = array(
- 'tabs' => array(
- array('title' => 'First', 'url' => "#"),
- array('title' => 'Second', 'url' => "#", 'selected' => true),
- array('title' => 'Third', 'url' => "#"),
- )
-);
-
+<?php
+$params = array(
+ 'tabs' => array(
+ array('title' => 'First', 'url' => "#"),
+ array('title' => 'Second', 'url' => "#", 'selected' => true),
+ array('title' => 'Third', 'url' => "#"),
+ )
+);
+
echo elgg_view('navigation/tabs', $params); \ No newline at end of file
diff --git a/mod/developers/views/default/theme_preview/typography/headings.php b/mod/developers/views/default/theme_preview/typography/headings.php
index 1eb96c75c..d843853e3 100644
--- a/mod/developers/views/default/theme_preview/typography/headings.php
+++ b/mod/developers/views/default/theme_preview/typography/headings.php
@@ -1,6 +1,6 @@
-<h1>Level 1 heading</h1>
-<h2>Level 2 heading</h2>
-<h3>Level 3 heading</h3>
-<h4>Level 4 heading</h4>
-<h5>Level 5 heading</h5>
+<h1>Level 1 heading</h1>
+<h2>Level 2 heading</h2>
+<h3>Level 3 heading</h3>
+<h4>Level 4 heading</h4>
+<h5>Level 5 heading</h5>
<h6>Level 6 heading</h6> \ No newline at end of file
diff --git a/mod/developers/views/default/theme_preview/typography/misc.php b/mod/developers/views/default/theme_preview/typography/misc.php
index 93a279c36..0b9fd9db7 100644
--- a/mod/developers/views/default/theme_preview/typography/misc.php
+++ b/mod/developers/views/default/theme_preview/typography/misc.php
@@ -1,16 +1,16 @@
-<ul>
- <li>I am <a href="?abc123">the a tag</a> example</li>
- <li>I am <abbr title="test">the abbr tag</abbr> example</li>
- <li>I am <acronym>the acronym tag</acronym> example</li>
- <li>I am <b>the b tag</b> example</li>
- <li>I am <code>the code tag</code> example</li>
- <li>I am <del>the del tag</del> example</li>
- <li>I am <em>the em tag</em> example</li>
- <li>I am <i>the i tag</i> example</li>
- <li>I am <strong>the strong tag</strong> example</li>
-</ul>
-<blockquote><p>Paragraph inside Blockquote: <?php echo $ipsum; ?></p></blockquote>
-<pre>
- <strong>Preformated:</strong>Testing one row
- and another
-</pre>
+<ul>
+ <li>I am <a href="?abc123">the a tag</a> example</li>
+ <li>I am <abbr title="test">the abbr tag</abbr> example</li>
+ <li>I am <acronym>the acronym tag</acronym> example</li>
+ <li>I am <b>the b tag</b> example</li>
+ <li>I am <code>the code tag</code> example</li>
+ <li>I am <del>the del tag</del> example</li>
+ <li>I am <em>the em tag</em> example</li>
+ <li>I am <i>the i tag</i> example</li>
+ <li>I am <strong>the strong tag</strong> example</li>
+</ul>
+<blockquote><p>Paragraph inside Blockquote: <?php echo $ipsum; ?></p></blockquote>
+<pre>
+ <strong>Preformated:</strong>Testing one row
+ and another
+</pre>
diff --git a/mod/developers/views/default/theme_preview/typography/paragraph.php b/mod/developers/views/default/theme_preview/typography/paragraph.php
index 54d548f46..a3a7b2cfa 100644
--- a/mod/developers/views/default/theme_preview/typography/paragraph.php
+++ b/mod/developers/views/default/theme_preview/typography/paragraph.php
@@ -1,19 +1,19 @@
-<p>Lorem ipsum dolor sit amet, <a href="#" title="test link">test link</a>
-adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. Donec
-faucibus. Nunc iaculis suscipit dui. Nam sit amet sem. Aliquam libero
-nisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. Praesent
-mattis, massa quis luctus <strong>strong</strong>, turpis mi volutpat justo, eu
-volutpat enim diam eget metus. Maecenas ornare tortor. Donec sed tellus
-eget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem,
-consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue
-quis tellus.</p>
-
-<p>Lorem ipsum dolor sit amet, <em>emphasis</em> consectetuer
-adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. Donec
-faucibus. Nunc iaculis suscipit dui. Nam sit amet sem. Aliquam libero
-nisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. Praesent
-mattis, massa quis luctus fermentum, turpis mi volutpat justo, eu
-volutpat enim diam eget metus. Maecenas ornare tortor. Donec sed tellus
-eget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem,
-consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue
+<p>Lorem ipsum dolor sit amet, <a href="#" title="test link">test link</a>
+adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. Donec
+faucibus. Nunc iaculis suscipit dui. Nam sit amet sem. Aliquam libero
+nisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. Praesent
+mattis, massa quis luctus <strong>strong</strong>, turpis mi volutpat justo, eu
+volutpat enim diam eget metus. Maecenas ornare tortor. Donec sed tellus
+eget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem,
+consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue
+quis tellus.</p>
+
+<p>Lorem ipsum dolor sit amet, <em>emphasis</em> consectetuer
+adipiscing elit. Nullam dignissim convallis est. Quisque aliquam. Donec
+faucibus. Nunc iaculis suscipit dui. Nam sit amet sem. Aliquam libero
+nisi, imperdiet at, tincidunt nec, gravida vehicula, nisl. Praesent
+mattis, massa quis luctus fermentum, turpis mi volutpat justo, eu
+volutpat enim diam eget metus. Maecenas ornare tortor. Donec sed tellus
+eget sapien fringilla nonummy. Mauris a ante. Suspendisse quam sem,
+consequat at, commodo vitae, feugiat in, nunc. Morbi imperdiet augue
quis tellus.</p> \ No newline at end of file
diff --git a/mod/embed/start.php b/mod/embed/start.php
index e8a3f8c14..1da35aa46 100644
--- a/mod/embed/start.php
+++ b/mod/embed/start.php
@@ -13,14 +13,19 @@ elgg_register_event_handler('init', 'system', 'embed_init');
*/
function embed_init() {
elgg_extend_view('css/elgg', 'embed/css');
-
- elgg_register_plugin_hook_handler('register', 'menu:longtext', 'embed_longtext_menu');
+ elgg_extend_view('css/admin', 'embed/css');
+
+ if (elgg_is_logged_in()) {
+ elgg_register_plugin_hook_handler('register', 'menu:longtext', 'embed_longtext_menu');
+ }
elgg_register_plugin_hook_handler('register', 'menu:embed', 'embed_select_tab', 1000);
// Page handler for the modal media embed
elgg_register_page_handler('embed', 'embed_page_handler');
- elgg_register_js('elgg.embed', 'js/embed/embed.js', 'footer');
+ $embed_js = elgg_get_simplecache_url('js', 'embed/embed');
+ elgg_register_simplecache_view('js/embed/embed');
+ elgg_register_js('elgg.embed', $embed_js, 'footer');
}
/**
@@ -39,10 +44,12 @@ function embed_longtext_menu($hook, $type, $items, $vars) {
}
$url = 'embed';
- if (elgg_get_page_owner_guid()) {
- $url = 'embed?container_guid=' . elgg_get_page_owner_guid();
+
+ $page_owner = elgg_get_page_owner_entity();
+ if (elgg_instanceof($page_owner, 'group') && $page_owner->isMember()) {
+ $url = 'embed?container_guid=' . $page_owner->getGUID();
}
-
+
$items[] = ElggMenuItem::factory(array(
'name' => 'embed',
'href' => $url,
@@ -95,7 +102,12 @@ function embed_page_handler($page) {
$container_guid = (int)get_input('container_guid');
if ($container_guid) {
- elgg_set_page_owner_guid($container_guid);
+ $container = get_entity($container_guid);
+
+ if (elgg_instanceof($container, 'group') && $container->isMember()) {
+ // embedding inside a group so save file to group files
+ elgg_set_page_owner_guid($container_guid);
+ }
}
echo elgg_view('embed/layout');
diff --git a/mod/embed/views/default/js/embed/embed.php b/mod/embed/views/default/js/embed/embed.php
index eb6153abf..e84427f24 100644
--- a/mod/embed/views/default/js/embed/embed.php
+++ b/mod/embed/views/default/js/embed/embed.php
@@ -1,3 +1,4 @@
+//<script>
elgg.provide('elgg.embed');
elgg.embed.init = function() {
@@ -90,6 +91,18 @@ elgg.embed.submit = function(event) {
$('.embed-wrapper .elgg-form-file-upload').show();
}
}
+
+ // ie 7 and 8 have a null response because of the use of an iFrame
+ // so just show the list after upload.
+ // http://jquery.malsup.com/form/#file-upload claims you can wrap JSON
+ // in a textarea, but a quick test didn't work, and that is fairly
+ // intrusive to the rest of the ajax system.
+ else if (response === undefined && $.browser.msie) {
+ var forward = $('input[name=embed_forward]').val();
+ var url = elgg.normalize_url('embed/tab/' + forward);
+ url = elgg.embed.addContainerGUID(url);
+ $('.embed-wrapper').parent().load(url);
+ }
},
error : function(xhr, status) {
// @todo nothing for now
diff --git a/mod/externalpages/start.php b/mod/externalpages/start.php
index 74da7f828..f0ffa6b9d 100644
--- a/mod/externalpages/start.php
+++ b/mod/externalpages/start.php
@@ -12,7 +12,7 @@ function expages_init() {
elgg_register_page_handler('terms', 'expages_page_handler');
elgg_register_page_handler('privacy', 'expages_page_handler');
elgg_register_page_handler('expages', 'expages_page_handler');
-
+
// Register public external pages
elgg_register_plugin_hook_handler('public_pages', 'walled_garden', 'expages_public');
@@ -65,7 +65,7 @@ function expages_page_handler($page, $handler) {
$type = strtolower($handler);
$title = elgg_echo("expages:$type");
- $content = elgg_view_title($title);
+ $header = elgg_view_title($title);
$object = elgg_get_entities(array(
'type' => 'object',
@@ -80,11 +80,11 @@ function expages_page_handler($page, $handler) {
$content = elgg_view('expages/wrapper', array('content' => $content));
if (elgg_is_logged_in() || !elgg_get_config('walled_garden')) {
- $body = elgg_view_layout('one_sidebar', array('content' => $content));
+ $body = elgg_view_layout('one_sidebar', array('title' => $title, 'content' => $content));
echo elgg_view_page($title, $body);
} else {
elgg_load_css('elgg.walled_garden');
- $body = elgg_view_layout('walled_garden', array('content' => $content));
+ $body = elgg_view_layout('walled_garden', array('content' => $header . $content));
echo elgg_view_page($title, $body, 'walled_garden');
}
return true;
diff --git a/mod/file/actions/file/upload.php b/mod/file/actions/file/upload.php
index d6dce2528..e20c4079f 100644
--- a/mod/file/actions/file/upload.php
+++ b/mod/file/actions/file/upload.php
@@ -71,9 +71,7 @@ $file->title = $title;
$file->description = $desc;
$file->access_id = $access_id;
$file->container_guid = $container_guid;
-
-$tags = explode(",", $tags);
-$file->tags = $tags;
+$file->tags = string_to_tag_array($tags);
// we have a file upload, so process it
if (isset($_FILES['upload']['name']) && !empty($_FILES['upload']['name'])) {
@@ -167,6 +165,23 @@ if (isset($_FILES['upload']['name']) && !empty($_FILES['upload']['name'])) {
$file->largethumb = $prefix."largethumb".$filestorename;
unset($thumblarge);
}
+ } elseif ($file->icontime) {
+ // if it is not an image, we do not need thumbnails
+ unset($file->icontime);
+
+ $thumb = new ElggFile();
+
+ $thumb->setFilename($prefix . "thumb" . $filestorename);
+ $thumb->delete();
+ unset($file->thumbnail);
+
+ $thumb->setFilename($prefix . "smallthumb" . $filestorename);
+ $thumb->delete();
+ unset($file->smallthumb);
+
+ $thumb->setFilename($prefix . "largethumb" . $filestorename);
+ $thumb->delete();
+ unset($file->largethumb);
}
} else {
// not saving a file but still need to save the entity to push attributes to database
@@ -204,4 +219,4 @@ if ($new_file) {
}
forward($file->getURL());
-}
+}
diff --git a/mod/file/pages/file/friends.php b/mod/file/pages/file/friends.php
index f504bdc1f..d55c1e62b 100644
--- a/mod/file/pages/file/friends.php
+++ b/mod/file/pages/file/friends.php
@@ -7,7 +7,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('file/all');
+ forward('', '404');
}
elgg_push_breadcrumb(elgg_echo('file'), "file/all");
diff --git a/mod/file/pages/file/owner.php b/mod/file/pages/file/owner.php
index fb87af1b2..99cf62714 100644
--- a/mod/file/pages/file/owner.php
+++ b/mod/file/pages/file/owner.php
@@ -10,7 +10,7 @@ group_gatekeeper();
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('file/all');
+ forward('', '404');
}
elgg_push_breadcrumb(elgg_echo('file'), "file/all");
@@ -36,10 +36,9 @@ $title = elgg_echo("file:user", array($owner->name));
// List files
$content = elgg_list_entities(array(
- 'types' => 'object',
- 'subtypes' => 'file',
+ 'type' => 'object',
+ 'subtype' => 'file',
'container_guid' => $owner->guid,
- 'limit' => 10,
'full_view' => FALSE,
));
if (!$content) {
diff --git a/mod/file/pages/file/search.php b/mod/file/pages/file/search.php
index 402a28933..d60dfb755 100644
--- a/mod/file/pages/file/search.php
+++ b/mod/file/pages/file/search.php
@@ -74,8 +74,8 @@ if ($listtype == "gallery") {
}
$params = array(
- 'types' => 'object',
- 'subtypes' => 'file',
+ 'type' => 'object',
+ 'subtype' => 'file',
'container_guid' => $page_owner_guid,
'limit' => $limit,
'full_view' => false,
diff --git a/mod/file/pages/file/world.php b/mod/file/pages/file/world.php
index 770dfd6e8..96c8de785 100644
--- a/mod/file/pages/file/world.php
+++ b/mod/file/pages/file/world.php
@@ -9,14 +9,11 @@ elgg_push_breadcrumb(elgg_echo('file'));
elgg_register_title_button();
-$limit = get_input("limit", 10);
-
$title = elgg_echo('file:all');
$content = elgg_list_entities(array(
- 'types' => 'object',
- 'subtypes' => 'file',
- 'limit' => $limit,
+ 'type' => 'object',
+ 'subtype' => 'file',
'full_view' => FALSE
));
if (!$content) {
diff --git a/mod/groups/actions/groups/edit.php b/mod/groups/actions/groups/edit.php
index d0689be2e..f19b90566 100644
--- a/mod/groups/actions/groups/edit.php
+++ b/mod/groups/actions/groups/edit.php
@@ -54,6 +54,20 @@ if ($group_guid && !$group->canEdit()) {
// Assume we can edit or this is a new group
if (sizeof($input) > 0) {
foreach($input as $shortname => $value) {
+ // update access collection name if group name changes
+ if (!$is_new_group && $shortname == 'name' && $value != $group->name) {
+ $group_name = html_entity_decode($value, ENT_QUOTES, 'UTF-8');
+ $ac_name = sanitize_string(elgg_echo('groups:group') . ": " . $group_name);
+ $acl = get_access_collection($group->group_acl);
+ if ($acl) {
+ // @todo Elgg api does not support updating access collection name
+ $db_prefix = elgg_get_config('dbprefix');
+ $query = "UPDATE {$db_prefix}access_collections SET name = '$ac_name'
+ WHERE id = $group->group_acl";
+ update_data($query);
+ }
+ }
+
$group->$shortname = $value;
}
}
@@ -92,7 +106,21 @@ if (!$is_new_group && $new_owner_guid && $new_owner_guid != $old_owner_guid) {
// verify new owner is member and old owner/admin is logged in
if (is_group_member($group_guid, $new_owner_guid) && ($old_owner_guid == $user->guid || $user->isAdmin())) {
$group->owner_guid = $new_owner_guid;
-
+ $group->container_guid = $new_owner_guid;
+
+ $metadata = elgg_get_metadata(array(
+ 'guid' => $group_guid,
+ 'limit' => false,
+ ));
+ if ($metadata) {
+ foreach ($metadata as $md) {
+ if ($md->owner_guid == $old_owner_guid) {
+ $md->owner_guid = $new_owner_guid;
+ $md->save();
+ }
+ }
+ }
+
// @todo Remove this when #4683 fixed
$owner_has_changed = true;
$old_icontime = $group->icontime;
diff --git a/mod/groups/actions/groups/membership/invite.php b/mod/groups/actions/groups/membership/invite.php
index db90ecf3a..a96165b0e 100644
--- a/mod/groups/actions/groups/membership/invite.php
+++ b/mod/groups/actions/groups/membership/invite.php
@@ -7,43 +7,48 @@
$logged_in_user = elgg_get_logged_in_user_entity();
-$user_guid = get_input('user_guid');
-if (!is_array($user_guid)) {
- $user_guid = array($user_guid);
+$user_guids = get_input('user_guid');
+if (!is_array($user_guids)) {
+ $user_guids = array($user_guids);
}
$group_guid = get_input('group_guid');
+$group = get_entity($group_guid);
-if (sizeof($user_guid)) {
- foreach ($user_guid as $u_id) {
- $user = get_entity($u_id);
- $group = get_entity($group_guid);
-
- if ($user && $group && ($group instanceof ElggGroup) && $group->canEdit()) {
-
- if (!check_entity_relationship($group->guid, 'invited', $user->guid)) {
-
- // Create relationship
- add_entity_relationship($group->guid, 'invited', $user->guid);
-
- // Send email
- $url = elgg_normalize_url("groups/invitations/$user->username");
- $result = notify_user($user->getGUID(), $group->owner_guid,
- elgg_echo('groups:invite:subject', array($user->name, $group->name)),
- elgg_echo('groups:invite:body', array(
- $user->name,
- $logged_in_user->name,
- $group->name,
- $url,
- )),
- NULL);
- if ($result) {
- system_message(elgg_echo("groups:userinvited"));
- } else {
- register_error(elgg_echo("groups:usernotinvited"));
- }
- } else {
- register_error(elgg_echo("groups:useralreadyinvited"));
- }
+if (count($user_guids) > 0 && elgg_instanceof($group, 'group') && $group->canEdit()) {
+ foreach ($user_guids as $guid) {
+ $user = get_user($guid);
+ if (!$user) {
+ continue;
+ }
+
+ if (check_entity_relationship($group->guid, 'invited', $user->guid)) {
+ register_error(elgg_echo("groups:useralreadyinvited"));
+ continue;
+ }
+
+ if (check_entity_relationship($user->guid, 'member', $group->guid)) {
+ // @todo add error message
+ continue;
+ }
+
+ // Create relationship
+ add_entity_relationship($group->guid, 'invited', $user->guid);
+
+ // Send notification
+ $url = elgg_normalize_url("groups/invitations/$user->username");
+ $result = notify_user($user->getGUID(), $group->owner_guid,
+ elgg_echo('groups:invite:subject', array($user->name, $group->name)),
+ elgg_echo('groups:invite:body', array(
+ $user->name,
+ $logged_in_user->name,
+ $group->name,
+ $url,
+ )),
+ NULL);
+ if ($result) {
+ system_message(elgg_echo("groups:userinvited"));
+ } else {
+ register_error(elgg_echo("groups:usernotinvited"));
}
}
}
diff --git a/mod/groups/lib/discussion.php b/mod/groups/lib/discussion.php
index ab2fe4849..874e21b2d 100644
--- a/mod/groups/lib/discussion.php
+++ b/mod/groups/lib/discussion.php
@@ -39,9 +39,8 @@ function discussion_handle_list_page($guid) {
elgg_set_page_owner_guid($guid);
$group = get_entity($guid);
- if (!$group) {
- register_error(elgg_echo('group:notfound'));
- forward();
+ if (!elgg_instanceof($group, 'group')) {
+ forward('', '404');
}
elgg_push_breadcrumb($group->name);
diff --git a/mod/groups/lib/groups.php b/mod/groups/lib/groups.php
index d8d0f568d..d5bec1862 100644
--- a/mod/groups/lib/groups.php
+++ b/mod/groups/lib/groups.php
@@ -55,7 +55,7 @@ function groups_handle_all_page() {
}
$filter = elgg_view('groups/group_sort_menu', array('selected' => $selected_tab));
-
+
$sidebar = elgg_view('groups/sidebar/find');
$sidebar .= elgg_view('groups/sidebar/featured');
@@ -73,13 +73,14 @@ function groups_search_page() {
elgg_push_breadcrumb(elgg_echo('search'));
$tag = get_input("tag");
- $title = elgg_echo('groups:search:title', array($tag));
+ $display_query = _elgg_get_display_query($tag);
+ $title = elgg_echo('groups:search:title', array($display_query));
// groups plugin saves tags as "interests" - see groups_fields_setup() in start.php
$params = array(
'metadata_name' => 'interests',
'metadata_value' => $tag,
- 'types' => 'group',
+ 'type' => 'group',
'full_view' => FALSE,
);
$content = elgg_list_entities_from_metadata($params);
@@ -115,7 +116,9 @@ function groups_handle_owned_page() {
}
elgg_push_breadcrumb($title);
- elgg_register_title_button();
+ if (elgg_get_plugin_setting('limited_groups', 'groups') != 'yes' || elgg_is_admin_logged_in()) {
+ elgg_register_title_button();
+ }
$content = elgg_list_entities(array(
'type' => 'group',
@@ -150,7 +153,9 @@ function groups_handle_mine_page() {
}
elgg_push_breadcrumb($title);
- elgg_register_title_button();
+ if (elgg_get_plugin_setting('limited_groups', 'groups') != 'yes' || elgg_is_admin_logged_in()) {
+ elgg_register_title_button();
+ }
$content = elgg_list_entities_from_relationship(array(
'type' => 'group',
@@ -181,7 +186,7 @@ function groups_handle_mine_page() {
*/
function groups_handle_edit_page($page, $guid = 0) {
gatekeeper();
-
+
if ($page == 'add') {
elgg_set_page_owner_guid(elgg_get_logged_in_user_guid());
$title = elgg_echo('groups:add');
@@ -204,7 +209,7 @@ function groups_handle_edit_page($page, $guid = 0) {
$content = elgg_echo('groups:noaccess');
}
}
-
+
$params = array(
'content' => $content,
'title' => $title,
@@ -255,8 +260,8 @@ function groups_handle_profile_page($guid) {
elgg_push_context('group_profile');
$group = get_entity($guid);
- if (!$group) {
- forward('groups/all');
+ if (!elgg_instanceof($group, 'group')) {
+ forward('', '404');
}
elgg_push_breadcrumb($group->name);
@@ -266,7 +271,7 @@ function groups_handle_profile_page($guid) {
$content = elgg_view('groups/profile/layout', array('entity' => $group));
$sidebar = '';
- if (group_gatekeeper(false)) {
+ if (group_gatekeeper(false)) {
if (elgg_is_active_plugin('search')) {
$sidebar .= elgg_view('groups/sidebar/search', array('entity' => $group));
}
@@ -275,18 +280,18 @@ function groups_handle_profile_page($guid) {
$subscribed = false;
if (elgg_is_active_plugin('notifications')) {
global $NOTIFICATION_HANDLERS;
-
+
foreach ($NOTIFICATION_HANDLERS as $method => $foo) {
$relationship = check_entity_relationship(elgg_get_logged_in_user_guid(),
'notify' . $method, $guid);
-
+
if ($relationship) {
$subscribed = true;
break;
}
}
}
-
+
$sidebar .= elgg_view('groups/sidebar/my_status', array(
'entity' => $group,
'subscribed' => $subscribed
@@ -334,7 +339,7 @@ function groups_handle_activity_page($guid) {
if (!$content) {
$content = '<p>' . elgg_echo('groups:activity:none') . '</p>';
}
-
+
$params = array(
'content' => $content,
'title' => $title,
@@ -366,12 +371,15 @@ function groups_handle_members_page($guid) {
elgg_push_breadcrumb($group->name, $group->getURL());
elgg_push_breadcrumb(elgg_echo('groups:members'));
+ $db_prefix = elgg_get_config('dbprefix');
$content = elgg_list_entities_from_relationship(array(
'relationship' => 'member',
'relationship_guid' => $group->guid,
'inverse_relationship' => true,
- 'types' => 'user',
+ 'type' => 'user',
'limit' => 20,
+ 'joins' => array("JOIN {$db_prefix}users_entity u ON e.guid=u.guid"),
+ 'order_by' => 'u.name ASC',
));
$params = array(
@@ -424,7 +432,7 @@ function groups_handle_invite_page($guid) {
/**
* Manage requests to join a group
- *
+ *
* @param int $guid Group entity GUID
*/
function groups_handle_requests_page($guid) {
@@ -440,7 +448,7 @@ function groups_handle_requests_page($guid) {
if ($group && $group->canEdit()) {
elgg_push_breadcrumb($group->name, $group->getURL());
elgg_push_breadcrumb($title);
-
+
$requests = elgg_get_entities_from_relationship(array(
'type' => 'user',
'relationship' => 'membership_request',
@@ -561,6 +569,8 @@ function groups_prepare_form_vars($group = null) {
if ($group->access_id != ACCESS_PUBLIC && $group->access_id != ACCESS_LOGGED_IN) {
// group only access - this is done to handle access not created when group is created
$values['vis'] = ACCESS_PRIVATE;
+ } else {
+ $values['vis'] = $group->access_id;
}
$values['entity'] = $group;
diff --git a/mod/groups/start.php b/mod/groups/start.php
index 4e49d9e55..6002a535c 100644
--- a/mod/groups/start.php
+++ b/mod/groups/start.php
@@ -142,13 +142,17 @@ function groups_setup_sidebar_menus() {
$page_owner = elgg_get_page_owner_entity();
if (elgg_in_context('group_profile')) {
+ if (!elgg_instanceof($page_owner, 'group')) {
+ forward('', '404');
+ }
+
if (elgg_is_logged_in() && $page_owner->canEdit() && !$page_owner->isPublicMembership()) {
$url = elgg_get_site_url() . "groups/requests/{$page_owner->getGUID()}";
$count = elgg_get_entities_from_relationship(array(
'type' => 'user',
'relationship' => 'membership_request',
- 'relationship_guid' => $guid,
+ 'relationship_guid' => $page_owner->getGUID(),
'inverse_relationship' => true,
'count' => true,
));
@@ -572,7 +576,7 @@ function groups_write_acl_plugin_hook($hook, $entity_type, $returnvalue, $params
'relationship' => 'member',
'relationship_guid' => $user_guid,
'inverse_relationship' => FALSE,
- 'limit' => 999
+ 'limit' => false
));
if ($groups) {
@@ -1013,7 +1017,7 @@ function discussion_reply_notifications($event, $type, $annotation) {
'relationship' => 'notify' . $method,
'relationship_guid' => $topic->getContainerGUID(),
'inverse_relationship' => true,
- 'types' => 'user',
+ 'type' => 'user',
'limit' => 0,
));
diff --git a/mod/groups/views/default/forms/groups/edit.php b/mod/groups/views/default/forms/groups/edit.php
index 41d97e6c3..e2dc5455a 100644
--- a/mod/groups/views/default/forms/groups/edit.php
+++ b/mod/groups/views/default/forms/groups/edit.php
@@ -91,9 +91,18 @@ if (isset($vars['entity'])) {
}
if ($entity && ($owner_guid == elgg_get_logged_in_user_guid() || elgg_is_admin_logged_in())) {
- $owner_guid = $vars['entity']->owner_guid;
$members = array();
- foreach ($vars['entity']->getMembers(0) as $member) {
+
+ $options = array(
+ 'relationship' => 'member',
+ 'relationship_guid' => $vars['entity']->getGUID(),
+ 'inverse_relationship' => true,
+ 'type' => 'user',
+ 'limit' => 0,
+ );
+
+ $batch = new ElggBatch('elgg_get_entities_from_relationship', $options);
+ foreach ($batch as $member) {
$members[$member->guid] = "$member->name (@$member->username)";
}
?>
diff --git a/mod/groups/views/default/groups/css.php b/mod/groups/views/default/groups/css.php
index 6f710ddab..32dd2b74d 100644
--- a/mod/groups/views/default/groups/css.php
+++ b/mod/groups/views/default/groups/css.php
@@ -9,10 +9,6 @@
.groups-profile > .elgg-image {
margin-right: 10px;
}
-.groups-profile img {
- width: 100%;
- height: auto;
-}
.groups-stats {
background: #eeeeee;
padding: 5px;
diff --git a/mod/groups/views/default/groups/profile/summary.php b/mod/groups/views/default/groups/profile/summary.php
index f1221f19a..3f7496871 100644
--- a/mod/groups/views/default/groups/profile/summary.php
+++ b/mod/groups/views/default/groups/profile/summary.php
@@ -25,7 +25,14 @@ if (!$owner) {
<div class="groups-profile clearfix elgg-image-block">
<div class="elgg-image">
<div class="groups-profile-icon">
- <?php echo elgg_view_entity_icon($group, 'large', array('href' => '')); ?>
+ <?php
+ // we don't force icons to be square so don't set width/height
+ echo elgg_view_entity_icon($group, 'large', array(
+ 'href' => '',
+ 'width' => '',
+ 'height' => '',
+ ));
+ ?>
</div>
<div class="groups-stats">
<p>
diff --git a/mod/groups/views/default/groups/sidebar/featured.php b/mod/groups/views/default/groups/sidebar/featured.php
index 8bd51ab5c..f3f8f8d2d 100644
--- a/mod/groups/views/default/groups/sidebar/featured.php
+++ b/mod/groups/views/default/groups/sidebar/featured.php
@@ -8,7 +8,7 @@
$featured_groups = elgg_get_entities_from_metadata(array(
'metadata_name' => 'featured_group',
'metadata_value' => 'yes',
- 'types' => 'group',
+ 'type' => 'group',
'limit' => 10,
));
diff --git a/mod/groups/views/default/groups/sidebar/members.php b/mod/groups/views/default/groups/sidebar/members.php
index 11273d0e6..1199a0c34 100644
--- a/mod/groups/views/default/groups/sidebar/members.php
+++ b/mod/groups/views/default/groups/sidebar/members.php
@@ -20,7 +20,7 @@ $body = elgg_list_entities_from_relationship(array(
'relationship' => 'member',
'relationship_guid' => $vars['entity']->guid,
'inverse_relationship' => true,
- 'types' => 'user',
+ 'type' => 'user',
'limit' => $limit,
'list_type' => 'gallery',
'gallery_class' => 'elgg-gallery-users',
diff --git a/mod/groups/views/default/groups/sidebar/my_status.php b/mod/groups/views/default/groups/sidebar/my_status.php
index 4c36c0213..1e4e84b80 100644
--- a/mod/groups/views/default/groups/sidebar/my_status.php
+++ b/mod/groups/views/default/groups/sidebar/my_status.php
@@ -14,7 +14,7 @@ $subscribed = elgg_extract('subscribed', $vars);
if (!elgg_is_logged_in()) {
return true;
}
-$t = new ElggMenuItem();
+
// membership status
$is_member = $group->isMember($user);
$is_owner = $group->getOwnerEntity() == $user;
@@ -41,7 +41,7 @@ if ($is_owner) {
}
// notification info
-if (elgg_is_active_plugin('notifications')) {
+if (elgg_is_active_plugin('notifications') && $is_member) {
if ($subscribed) {
elgg_register_menu_item('groups:my_status', array(
'name' => 'subscription_status',
diff --git a/mod/groups/views/default/object/groupforumtopic.php b/mod/groups/views/default/object/groupforumtopic.php
index 34e0ee3cc..e6988d16e 100644
--- a/mod/groups/views/default/object/groupforumtopic.php
+++ b/mod/groups/views/default/object/groupforumtopic.php
@@ -73,7 +73,10 @@ if ($full) {
$info = elgg_view_image_block($poster_icon, $list_body);
- $body = elgg_view('output/longtext', array('value' => $topic->description));
+ $body = elgg_view('output/longtext', array(
+ 'value' => $topic->description,
+ 'class' => 'clearfix',
+ ));
echo <<<HTML
$info
diff --git a/mod/htmlawed/start.php b/mod/htmlawed/start.php
index 12b6470a3..25a70a4aa 100644
--- a/mod/htmlawed/start.php
+++ b/mod/htmlawed/start.php
@@ -156,10 +156,8 @@ function htmlawed_tag_post_processor($element, $attributes = false) {
* Runs unit tests for htmlawed
*
* @return array
- * */
+ */
function htmlawed_test($hook, $type, $value, $params) {
- global $CONFIG;
-
$value[] = dirname(__FILE__) . '/tests/tags.php';
return $value;
}
diff --git a/mod/htmlawed/tests/tags.php b/mod/htmlawed/tests/tags.php
index b3914a9d6..05fe829f4 100644
--- a/mod/htmlawed/tests/tags.php
+++ b/mod/htmlawed/tests/tags.php
@@ -1,45 +1,47 @@
<?php
+
/**
* Dupplicated tags in htmlawed
*/
class HtmLawedDuplicateTagsTest extends ElggCoreUnitTest {
- /**
- * Called before each test object.
- */
- public function __construct() {
- parent::__construct();
- }
-
- /**
- * Called before each test method.
- */
- public function setUp() {
- }
-
- /**
- * Called after each test method.
- */
- public function tearDown() {
- // do not allow SimpleTest to interpret Elgg notices as exceptions
- $this->swallowErrors();
- }
-
- /**
- * Called after each test object.
- */
- public function __destruct() {
- elgg_set_ignore_access($this->ia);
- // all __destruct() code should go above here
- parent::__destruct();
- }
-
- public function testNotDuplicateTags() {
- $filter_html = '<ul><li>item</li></ul>';
- set_input('test', $filter_html);
-
- $expected = $filter_html;
- $result = get_input('test');
- $this->assertEqual($result, $expected);
- }
+ /**
+ * Called before each test object.
+ */
+ public function __construct() {
+ parent::__construct();
+ }
+
+ /**
+ * Called before each test method.
+ */
+ public function setUp() {
+
+ }
+
+ /**
+ * Called after each test method.
+ */
+ public function tearDown() {
+ // do not allow SimpleTest to interpret Elgg notices as exceptions
+ $this->swallowErrors();
+ }
+
+ /**
+ * Called after each test object.
+ */
+ public function __destruct() {
+ // all __destruct() code should go above here
+ parent::__destruct();
+ }
+
+ public function testNotDuplicateTags() {
+ $filter_html = '<ul><li>item</li></ul>';
+ set_input('test', $filter_html);
+
+ $expected = $filter_html;
+ $result = get_input('test');
+ $this->assertEqual($result, $expected);
+ }
+
} \ No newline at end of file
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed.php b/mod/htmlawed/vendors/htmLawed/htmLawed.php
index 0d9624961..63f8c4162 100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawed.php
+++ b/mod/htmlawed/vendors/htmLawed/htmLawed.php
@@ -1,9 +1,9 @@
<?php
/*
-htmLawed 1.1.11, 5 June 2012
+htmLawed 1.1.16, 29 August 2013
Copyright Santosh Patnaik
-Dual licensed with LGPL 3 and GPL 2 or later
+Dual licensed with LGPL 3 and GPL 2+
A PHP Labware internal utility; www.bioinformatics.org/phplabware/internal_utilities/htmLawed
See htmLawed_README.txt/htm
@@ -194,7 +194,10 @@ for($i=-1, $ci=count($t); ++$i<$ci;){
echo '&lt;', $s, $e, $a, '&gt;';
}
if(isset($x[0])){
- if($do < 3 or isset($ok['#pcdata'])){echo $x;}
+ if(strlen(trim($x)) && (($ql && isset($cB[$p])) or (isset($cB[$in]) && !$ql))){
+ echo '<div>', $x, '</div>';
+ }
+ elseif($do < 3 or isset($ok['#pcdata'])){echo $x;}
elseif(strpos($x, "\x02\x04")){
foreach(preg_split('`(\x01\x02[^\x01\x02]+\x02\x01)`', $x, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY) as $v){
echo (substr($v, 0, 2) == "\x01\x02" ? $v : ($do > 4 ? preg_replace('`\S`', '', $v) : ''));
@@ -202,7 +205,7 @@ for($i=-1, $ci=count($t); ++$i<$ci;){
}elseif($do > 4){echo preg_replace('`\S`', '', $x);}
}
// get markup
- if(!preg_match('`^(/?)([a-zA-Z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)){$x = $t[$i]; continue;}
+ if(!preg_match('`^(/?)([a-z1-6]+)([^>]*)>(.*)`sm', $t[$i], $r)){$x = $t[$i]; continue;}
$s = null; $e = null; $a = null; $x = null; list($all, $s, $e, $a, $x) = $r;
// close tag
if($s){
@@ -333,7 +336,7 @@ $c = isset($C['schemes'][$c]) ? $C['schemes'][$c] : $C['schemes']['*'];
static $d = 'denied:';
if(isset($c['!']) && substr($p, 0, 7) != $d){$p = "$d$p";}
if(isset($c['*']) or !strcspn($p, '#?;') or (substr($p, 0, 7) == $d)){return "{$b}{$p}{$a}";} // All ok, frag, query, param
-if(preg_match('`^([a-z\d\-+.&#; ]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])){ // Denied prot
+if(preg_match('`^([^:?[@!$()*,=/\'\]]+?)(:|&#(58|x3a);|%3a|\\\\0{0,4}3a).`i', $p, $m) && !isset($c[strtolower($m[1])])){ // Denied prot
return "{$b}{$d}{$p}{$a}";
}
if($C['abs_url']){
@@ -488,7 +491,7 @@ global $S;
$rl = isset($S[$e]) ? $S[$e] : array();
$a = array(); $nfr = 0;
foreach($aA as $k=>$v){
- if(((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) or isset($rl[$k])) && ((!isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])) && (isset($aN[$k][$e]) or (isset($aNU[$k]) && !isset($aNU[$k][$e])))){
+ if(((isset($C['deny_attribute']['*']) ? isset($C['deny_attribute'][$k]) : !isset($C['deny_attribute'][$k])) && (isset($aN[$k][$e]) or (isset($aNU[$k]) && !isset($aNU[$k][$e]))) && !isset($rl['n'][$k]) && !isset($rl['n']['*'])) or isset($rl[$k])){
if(isset($aNE[$k])){$v = $k;}
elseif(!empty($lcase) && (($e != 'button' or $e != 'input') or $k == 'type')){ // Rather loose but ?not cause issues
$v = (isset($aNL[($v2 = strtolower($v))])) ? $v2 : $v;
@@ -622,7 +625,7 @@ if($e == 'u'){$e = 'span'; return 'text-decoration: underline;';}
static $fs = array('0'=>'xx-small', '1'=>'xx-small', '2'=>'small', '3'=>'medium', '4'=>'large', '5'=>'x-large', '6'=>'xx-large', '7'=>'300%', '-1'=>'smaller', '-2'=>'60%', '+1'=>'larger', '+2'=>'150%', '+3'=>'200%', '+4'=>'300%');
if($e == 'font'){
$a2 = '';
- if(preg_match('`face\s*=\s*(\'|")([^=]+?)\\1`i', $a, $m) or preg_match('`face\s*=\s*([^"])(\S+)`i', $a, $m)){
+ if(preg_match('`face\s*=\s*(\'|")([^=]+?)\\1`i', $a, $m) or preg_match('`face\s*=(\s*)(\S+)`i', $a, $m)){
$a2 .= ' font-family: '. str_replace('"', '\'', trim($m[2])). ';';
}
if(preg_match('`color\s*=\s*(\'|")?(.+?)(\\1|\s|$)`i', $a, $m)){
@@ -641,41 +644,50 @@ return '';
function hl_tidy($t, $w, $p){
// Tidy/compact HTM
if(strpos(' pre,script,textarea', "$p,")){return $t;}
-$t = str_replace(' </', '</', preg_replace(array('`(<\w[^>]*(?<!/)>)\s+`', '`\s+`', '`(<\w[^>]*(?<!/)>) `'), array(' $1', ' ', '$1'), preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)(</\2>)`sm'), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t)));
+$t = preg_replace('`\s+`', ' ', preg_replace_callback(array('`(<(!\[CDATA\[))(.+?)(\]\]>)`sm', '`(<(!--))(.+?)(-->)`sm', '`(<(pre|script|textarea)[^>]*?>)(.+?)(</\2>)`sm'), create_function('$m', 'return $m[1]. str_replace(array("<", ">", "\n", "\r", "\t", " "), array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), $m[3]). $m[4];'), $t));
if(($w = strtolower($w)) == -1){
return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array('<', '>', "\n", "\r", "\t", ' '), $t);
}
$s = strpos(" $w", 't') ? "\t" : ' ';
$s = preg_match('`\d`', $w, $m) ? str_repeat($s, $m[0]) : str_repeat($s, ($s == "\t" ? 1 : 2));
-$n = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0;
+$N = preg_match('`[ts]([1-9])`', $w, $m) ? $m[1] : 0;
$a = array('br'=>1);
-$b = array('button'=>1, 'input'=>1, 'option'=>1);
+$b = array('button'=>1, 'input'=>1, 'option'=>1, 'param'=>1);
$c = array('caption'=>1, 'dd'=>1, 'dt'=>1, 'h1'=>1, 'h2'=>1, 'h3'=>1, 'h4'=>1, 'h5'=>1, 'h6'=>1, 'isindex'=>1, 'label'=>1, 'legend'=>1, 'li'=>1, 'object'=>1, 'p'=>1, 'pre'=>1, 'td'=>1, 'textarea'=>1, 'th'=>1);
-$d = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'colgroup'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'fieldset'=>1, 'form'=>1, 'hr'=>1, 'iframe'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1);
-ob_start();
-if(isset($d[$p])){echo str_repeat($s, ++$n);}
-$t = explode('<', $t);
-echo ltrim(array_shift($t));
-for($i=-1, $j=count($t); ++$i<$j;){
- $r = ''; list($e, $r) = explode('>', $t[$i]);
- $x = $e[0] == '/' ? 0 : (substr($e, -1) == '/' ? 1 : ($e[0] != '!' ? 2 : -1));
- $y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0);
- $e = "<$e>";
- if(isset($d[$y])){
- if(!$x){echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);}
- else{echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));}
- echo ltrim($r); continue;
+$d = array('address'=>1, 'blockquote'=>1, 'center'=>1, 'colgroup'=>1, 'dir'=>1, 'div'=>1, 'dl'=>1, 'fieldset'=>1, 'form'=>1, 'hr'=>1, 'iframe'=>1, 'map'=>1, 'menu'=>1, 'noscript'=>1, 'ol'=>1, 'optgroup'=>1, 'rbc'=>1, 'rtc'=>1, 'ruby'=>1, 'script'=>1, 'select'=>1, 'table'=>1, 'tbody'=>1, 'tfoot'=>1, 'thead'=>1, 'tr'=>1, 'ul'=>1);
+$T = explode('<', $t);
+$X = 1;
+while($X){
+ $n = $N;
+ $t = $T;
+ ob_start();
+ if(isset($d[$p])){echo str_repeat($s, ++$n);}
+ echo ltrim(array_shift($t));
+ for($i=-1, $j=count($t); ++$i<$j;){
+ $r = ''; list($e, $r) = explode('>', $t[$i]);
+ $x = $e[0] == '/' ? 0 : (substr($e, -1) == '/' ? 1 : ($e[0] != '!' ? 2 : -1));
+ $y = !$x ? ltrim($e, '/') : ($x > 0 ? substr($e, 0, strcspn($e, ' ')) : 0);
+ $e = "<$e>";
+ if(isset($d[$y])){
+ if(!$x){
+ if($n){echo "\n", str_repeat($s, --$n), "$e\n", str_repeat($s, $n);}
+ else{++$N; ob_end_clean(); continue 2;}
+ }
+ else{echo "\n", str_repeat($s, $n), "$e\n", str_repeat($s, ($x != 1 ? ++$n : $n));}
+ echo $r; continue;
+ }
+ $f = "\n". str_repeat($s, $n);
+ if(isset($c[$y])){
+ if(!$x){echo $e, $f, $r;}
+ else{echo $f, $e, $r;}
+ }elseif(isset($b[$y])){echo $f, $e, $r;
+ }elseif(isset($a[$y])){echo $e, $f, $r;
+ }elseif(!$y){echo $f, $e, $f, $r;
+ }else{echo $e, $r;}
}
- $f = "\n". str_repeat($s, $n);
- if(isset($c[$y])){
- if(!$x){echo $e, $f, ltrim($r);}
- else{echo $f, $e, $r;}
- }elseif(isset($b[$y])){echo $f, $e, $r;
- }elseif(isset($a[$y])){echo $e, $f, ltrim($r);
- }elseif(!$y){echo $f, $e, $f, ltrim($r);
- }else{echo $e, $r;}
-}
-$t = preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents());
+ $X = 0;
+}
+$t = str_replace(array("\n ", " \n"), "\n", preg_replace('`[\n]\s*?[\n]+`', "\n", ob_get_contents()));
ob_end_clean();
if(($l = strpos(" $w", 'r') ? (strpos(" $w", 'n') ? "\r\n" : "\r") : 0)){
$t = str_replace("\n", $l, $t);
@@ -686,7 +698,7 @@ return str_replace(array("\x01", "\x02", "\x03", "\x04", "\x05", "\x07"), array(
function hl_version(){
// rel
-return '1.1.11';
+return '1.1.16';
// eof
}
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawedTest.php b/mod/htmlawed/vendors/htmLawed/htmLawedTest.php
index 806aa4641..3a5b92155 100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawedTest.php
+++ b/mod/htmlawed/vendors/htmLawed/htmLawedTest.php
@@ -1,10 +1,10 @@
<?php
/*
-htmLawedTest.php, 22 October 2011
-htmLawed 1.1.11, 5 June 2012
+htmLawedTest.php, 28 May 2013
+htmLawed 1.1.16, 29 August 2013
Copyright Santosh Patnaik
-Dual licensed with LGPL 3 and GPL 2 or later
+Dual licensed with LGPL 3 and GPL 2+
A PHP Labware internal utility - http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed
Test htmLawed; user provides text input; input and processed input are shown as highlighted code and rendered HTML; also shown are execution time and peak memory usage
@@ -22,7 +22,7 @@ $_sid = 'sid'; // session name; alphanum.
$_slife = 30; // session life in min.
// errors
-error_reporting(E_ALL | (defined('E_STRICT') ? E_STRICT : 1));
+error_reporting(E_ALL | (defined('E_STRICT') ? E_STRICT : 0));
ini_set('display_errors', $_errs);
// session
@@ -144,15 +144,16 @@ body{background-color:#efefef;}
body, button, div, html, input, p{font-size:13px; font-family:'Lucida grande', Verdana, Arial, Helvetica, sans-serif;}
button, input{font-size: 85%;}
div.help{border-top: 1px dotted gray; margin-top: 15px; padding-top: 15px; color:#999999;}
-#inputC, #inputD, #inputF, #inputR, #outputD, #outputF, #outputH, #outputR, #settingF{display:block;}
+#inputC, #inputD, #inputF, #inputR, #outputD, #outputF, #outputH, #outputR, #settingF, #diff{display:block;}
#inputC, #settingF{background-color:white; border:1px gray solid; padding:3px;}
#inputC li{margin: 0; padding: 0;}
#inputC ul{margin: 0; padding: 0; margin-left: 14px;}
#inputC input{margin: 0; margin-left: 2px; margin-right: 2px; padding: 1px; vertical-align: middle;}
#inputD{overflow:auto; background-color:#ffff99; border:1px #cc9966 solid; padding:3px;}
#inputR{overflow:auto; background-color:#ffffcc; border:1px #ffcc99 solid; padding:3px;}
-#inputC, #settingF, #inputD, #inputR, #outputD, #outputR, textarea{font-size:100%; font-family:'Bitstream vera sans mono', 'courier new', 'courier', monospace;}
-#outputD{overflow:auto; background-color: #99ffcc; border:1px #66cc99 solid; padding:3px;}
+#inputC, #settingF, #inputD, #inputR, #outputD, #outputR, #diff, textarea{font-size:100%; font-family:'Bitstream vera sans mono', 'courier new', 'courier', monospace;}
+#outputD{overflow:auto; background-color: #99ffcc; border:1px #66cc99 solid; padding:3px;}
+#diff{overflow:auto; background-color: white; border:1px #dcdcdc solid; padding:3px;}
#outputH{overflow:auto; background-color:white; padding:3px; border:1px #dcdcdc solid;}
#outputR{overflow:auto; background-color: #ccffcc; border:1px #99cc99 solid; padding:3px;}
span.cmtcdata{color: orange;}
@@ -261,9 +262,6 @@ function sndUnproc(){
var i = document.getElementById('text');
if(!i){return;}
i = i.value;
- i = i.replace(/>/g, '&gt;');
- i = i.replace(/</g, '&lt;');
- i = i.replace(/"/g, '&quot;');
var w = window.open('htmLawedTest.php?pre=1', 'hlprehtm');
var f = document.createElement('form');
f.enctype = 'application/x-www-form-urlencoded';
@@ -271,10 +269,14 @@ function sndUnproc(){
f.acceptCharset = '<?php echo htmlspecialchars($_POST['enc']); ?>';
if(f.style){f.style.display = 'none';}
else{f.visibility = 'hidden';}
- f.innerHTML = '<p style="display:none;"><input style="display:none;" type="hidden" name="token" id="token" value="<?php echo $token; ?>" /><input style="display:none;" type="hidden" name="<?php echo htmlspecialchars($_sid); ?>" id="<?php echo htmlspecialchars($_sid); ?>" value="' + readCookie('<?php echo htmlspecialchars($_sid); ?>') + '" /><input style="display:none;" type="hidden" name="inputH" id="inputH" value="'+ i+ '" /></p>';
+ f.innerHTML = '<p style="display:none;"><input style="display:none;" type="hidden" name="token" id="token" value="<?php echo $token; ?>" /><input style="display:none;" type="hidden" name="<?php echo htmlspecialchars($_sid); ?>" id="<?php echo htmlspecialchars($_sid); ?>" value="' + readCookie('<?php echo htmlspecialchars($_sid); ?>') + '" /></p>';
f.action = 'htmLawedTest.php?pre=1';
f.target = 'hlprehtm';
f.method = 'post';
+ var t = document.createElement('textarea');
+ t.name = 'inputH';
+ t.value = i;
+ f.appendChild(t);
var b = document.getElementsByTagName('body')[0];
b.appendChild(f);
f.submit();
@@ -284,9 +286,6 @@ function sndValidn(id, type){
var i = document.getElementById(id);
if(!i){return;}
i = i.value;
- i = i.replace(/>/g, '&gt;');
- i = i.replace(/</g, '&lt;');
- i = i.replace(/"/g, '&quot;');
var w = window.open('http://validator.w3.org/check', 'validate'+id+type);
var f = document.createElement('form');
f.enctype = 'application/x-www-form-urlencoded';
@@ -294,9 +293,13 @@ function sndValidn(id, type){
f.acceptCharset = '<?php echo htmlspecialchars($_POST['enc']); ?>';
if(f.style){f.style.display = 'none';}
else{f.visibility = 'hidden';}
- f.innerHTML = '<p style="display:none;"><input style="display:none;" type="hidden" name="fragment" id="fragment" value="'+ i+ '" /><input style="display:none;" type="hidden" name="prefill" id="prefill" value="1" /><input style="display:none;" type="hidden" name="prefill_doctype" id="prefill_doctype" value="'+ type+ '" /><input style="display:none;" type="hidden" name="group" id="group" value="1" /><input type="hidden" name="ss" id="ss" value="1" /></p>';
+ f.innerHTML = '<p style="display:none;"><input style="display:none;" type="hidden" name="prefill" id="prefill" value="1" /><input style="display:none;" type="hidden" name="prefill_doctype" id="prefill_doctype" value="'+ type+ '" /><input style="display:none;" type="hidden" name="group" id="group" value="1" /><input type="hidden" name="ss" id="ss" value="1" /></p>';
f.action = 'http://validator.w3.org/check';
f.target = 'validate'+id+type;
+ var t = document.createElement('textarea');
+ t.name = 'fragment';
+ t.value = i;
+ f.appendChild(t);
var b = document.getElementsByTagName('body')[0];
b.appendChild(f);
f.submit();
@@ -376,6 +379,58 @@ tRs = {
}
};
tRs.adEv(window, 'load', tRs.adBtn);
+// Diff Match and Patch javascript code by Neil Fraser; Apache license 2.0; http://code.google.com/p/google-diff-match-patch/
+(function(){function diff_match_patch(){this.Diff_Timeout=1;this.Diff_EditCost=4;this.Match_Threshold=0.5;this.Match_Distance=1E3;this.Patch_DeleteThreshold=0.5;this.Patch_Margin=4;this.Match_MaxBits=32}
+diff_match_patch.prototype.diff_main=function(a,b,c,d){"undefined"==typeof d&&(d=0>=this.Diff_Timeout?Number.MAX_VALUE:(new Date).getTime()+1E3*this.Diff_Timeout);if(null==a||null==b)throw Error("Null input. (diff_main)");if(a==b)return a?[[0,a]]:[];"undefined"==typeof c&&(c=!0);var e=c,f=this.diff_commonPrefix(a,b),c=a.substring(0,f),a=a.substring(f),b=b.substring(f),f=this.diff_commonSuffix(a,b),g=a.substring(a.length-f),a=a.substring(0,a.length-f),b=b.substring(0,b.length-f),a=this.diff_compute_(a,
+b,e,d);c&&a.unshift([0,c]);g&&a.push([0,g]);this.diff_cleanupMerge(a);return a};
+diff_match_patch.prototype.diff_compute_=function(a,b,c,d){if(!a)return[[1,b]];if(!b)return[[-1,a]];var e=a.length>b.length?a:b,f=a.length>b.length?b:a,g=e.indexOf(f);if(-1!=g)return c=[[1,e.substring(0,g)],[0,f],[1,e.substring(g+f.length)]],a.length>b.length&&(c[0][0]=c[2][0]=-1),c;if(1==f.length)return[[-1,a],[1,b]];return(e=this.diff_halfMatch_(a,b))?(f=e[0],a=e[1],g=e[2],b=e[3],e=e[4],f=this.diff_main(f,g,c,d),c=this.diff_main(a,b,c,d),f.concat([[0,e]],c)):c&&100<a.length&&100<b.length?this.diff_lineMode_(a,
+b,d):this.diff_bisect_(a,b,d)};
+diff_match_patch.prototype.diff_lineMode_=function(a,b,c){var d=this.diff_linesToChars_(a,b),a=d.chars1,b=d.chars2,d=d.lineArray,a=this.diff_main(a,b,!1,c);this.diff_charsToLines_(a,d);this.diff_cleanupSemantic(a);a.push([0,""]);for(var e=d=b=0,f="",g="";b<a.length;){switch(a[b][0]){case 1:e++;g+=a[b][1];break;case -1:d++;f+=a[b][1];break;case 0:if(1<=d&&1<=e){a.splice(b-d-e,d+e);b=b-d-e;d=this.diff_main(f,g,!1,c);for(e=d.length-1;0<=e;e--)a.splice(b,0,d[e]);b+=d.length}d=e=0;g=f=""}b++}a.pop();return a};
+diff_match_patch.prototype.diff_bisect_=function(a,b,c){for(var d=a.length,e=b.length,f=Math.ceil((d+e)/2),g=f,h=2*f,j=Array(h),i=Array(h),k=0;k<h;k++)j[k]=-1,i[k]=-1;j[g+1]=0;i[g+1]=0;for(var k=d-e,p=0!=k%2,q=0,s=0,o=0,v=0,u=0;u<f&&!((new Date).getTime()>c);u++){for(var n=-u+q;n<=u-s;n+=2){var l=g+n,m;m=n==-u||n!=u&&j[l-1]<j[l+1]?j[l+1]:j[l-1]+1;for(var r=m-n;m<d&&r<e&&a.charAt(m)==b.charAt(r);)m++,r++;j[l]=m;if(m>d)s+=2;else if(r>e)q+=2;else if(p&&(l=g+k-n,0<=l&&l<h&&-1!=i[l])){var t=d-i[l];if(m>=
+t)return this.diff_bisectSplit_(a,b,m,r,c)}}for(n=-u+o;n<=u-v;n+=2){l=g+n;t=n==-u||n!=u&&i[l-1]<i[l+1]?i[l+1]:i[l-1]+1;for(m=t-n;t<d&&m<e&&a.charAt(d-t-1)==b.charAt(e-m-1);)t++,m++;i[l]=t;if(t>d)v+=2;else if(m>e)o+=2;else if(!p&&(l=g+k-n,0<=l&&l<h&&-1!=j[l]&&(m=j[l],r=g+m-l,t=d-t,m>=t)))return this.diff_bisectSplit_(a,b,m,r,c)}}return[[-1,a],[1,b]]};
+diff_match_patch.prototype.diff_bisectSplit_=function(a,b,c,d,e){var f=a.substring(0,c),g=b.substring(0,d),a=a.substring(c),b=b.substring(d),f=this.diff_main(f,g,!1,e),e=this.diff_main(a,b,!1,e);return f.concat(e)};
+diff_match_patch.prototype.diff_linesToChars_=function(a,b){function c(a){for(var b="",c=0,f=-1,g=d.length;f<a.length-1;){f=a.indexOf("\n",c);-1==f&&(f=a.length-1);var q=a.substring(c,f+1),c=f+1;(e.hasOwnProperty?e.hasOwnProperty(q):void 0!==e[q])?b+=String.fromCharCode(e[q]):(b+=String.fromCharCode(g),e[q]=g,d[g++]=q)}return b}var d=[],e={};d[0]="";var f=c(a),g=c(b);return{chars1:f,chars2:g,lineArray:d}};
+diff_match_patch.prototype.diff_charsToLines_=function(a,b){for(var c=0;c<a.length;c++){for(var d=a[c][1],e=[],f=0;f<d.length;f++)e[f]=b[d.charCodeAt(f)];a[c][1]=e.join("")}};diff_match_patch.prototype.diff_commonPrefix=function(a,b){if(!a||!b||a.charAt(0)!=b.charAt(0))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(f,e)==b.substring(f,e)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
+diff_match_patch.prototype.diff_commonSuffix=function(a,b){if(!a||!b||a.charAt(a.length-1)!=b.charAt(b.length-1))return 0;for(var c=0,d=Math.min(a.length,b.length),e=d,f=0;c<e;)a.substring(a.length-e,a.length-f)==b.substring(b.length-e,b.length-f)?f=c=e:d=e,e=Math.floor((d-c)/2+c);return e};
+diff_match_patch.prototype.diff_commonOverlap_=function(a,b){var c=a.length,d=b.length;if(0==c||0==d)return 0;c>d?a=a.substring(c-d):c<d&&(b=b.substring(0,c));c=Math.min(c,d);if(a==b)return c;for(var d=0,e=1;;){var f=a.substring(c-e),f=b.indexOf(f);if(-1==f)return d;e+=f;if(0==f||a.substring(c-e)==b.substring(0,e))d=e,e++}};
+diff_match_patch.prototype.diff_halfMatch_=function(a,b){function c(a,b,c){for(var d=a.substring(c,c+Math.floor(a.length/4)),e=-1,g="",h,j,n,l;-1!=(e=b.indexOf(d,e+1));){var m=f.diff_commonPrefix(a.substring(c),b.substring(e)),r=f.diff_commonSuffix(a.substring(0,c),b.substring(0,e));g.length<r+m&&(g=b.substring(e-r,e)+b.substring(e,e+m),h=a.substring(0,c-r),j=a.substring(c+m),n=b.substring(0,e-r),l=b.substring(e+m))}return 2*g.length>=a.length?[h,j,n,l,g]:null}if(0>=this.Diff_Timeout)return null;
+var d=a.length>b.length?a:b,e=a.length>b.length?b:a;if(4>d.length||2*e.length<d.length)return null;var f=this,g=c(d,e,Math.ceil(d.length/4)),d=c(d,e,Math.ceil(d.length/2)),h;if(!g&&!d)return null;h=d?g?g[4].length>d[4].length?g:d:d:g;var j;a.length>b.length?(g=h[0],d=h[1],e=h[2],j=h[3]):(e=h[0],j=h[1],g=h[2],d=h[3]);h=h[4];return[g,d,e,j,h]};
+diff_match_patch.prototype.diff_cleanupSemantic=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=0,h=0,j=0,i=0;f<a.length;)0==a[f][0]?(c[d++]=f,g=j,h=i,i=j=0,e=a[f][1]):(1==a[f][0]?j+=a[f][1].length:i+=a[f][1].length,e&&e.length<=Math.max(g,h)&&e.length<=Math.max(j,i)&&(a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,d--,f=0<d?c[d-1]:-1,i=j=h=g=0,e=null,b=!0)),f++;b&&this.diff_cleanupMerge(a);this.diff_cleanupSemanticLossless(a);for(f=1;f<a.length;){if(-1==a[f-1][0]&&1==a[f][0]){b=a[f-1][1];c=a[f][1];
+d=this.diff_commonOverlap_(b,c);e=this.diff_commonOverlap_(c,b);if(d>=e){if(d>=b.length/2||d>=c.length/2)a.splice(f,0,[0,c.substring(0,d)]),a[f-1][1]=b.substring(0,b.length-d),a[f+1][1]=c.substring(d),f++}else if(e>=b.length/2||e>=c.length/2)a.splice(f,0,[0,b.substring(0,e)]),a[f-1][0]=1,a[f-1][1]=c.substring(0,c.length-e),a[f+1][0]=-1,a[f+1][1]=b.substring(e),f++;f++}f++}};
+diff_match_patch.prototype.diff_cleanupSemanticLossless=function(a){function b(a,b){if(!a||!b)return 6;var c=a.charAt(a.length-1),d=b.charAt(0),e=c.match(diff_match_patch.nonAlphaNumericRegex_),f=d.match(diff_match_patch.nonAlphaNumericRegex_),g=e&&c.match(diff_match_patch.whitespaceRegex_),h=f&&d.match(diff_match_patch.whitespaceRegex_),c=g&&c.match(diff_match_patch.linebreakRegex_),d=h&&d.match(diff_match_patch.linebreakRegex_),i=c&&a.match(diff_match_patch.blanklineEndRegex_),j=d&&b.match(diff_match_patch.blanklineStartRegex_);
+return i||j?5:c||d?4:e&&!g&&h?3:g||h?2:e||f?1:0}for(var c=1;c<a.length-1;){if(0==a[c-1][0]&&0==a[c+1][0]){var d=a[c-1][1],e=a[c][1],f=a[c+1][1],g=this.diff_commonSuffix(d,e);if(g)var h=e.substring(e.length-g),d=d.substring(0,d.length-g),e=h+e.substring(0,e.length-g),f=h+f;for(var g=d,h=e,j=f,i=b(d,e)+b(e,f);e.charAt(0)===f.charAt(0);){var d=d+e.charAt(0),e=e.substring(1)+f.charAt(0),f=f.substring(1),k=b(d,e)+b(e,f);k>=i&&(i=k,g=d,h=e,j=f)}a[c-1][1]!=g&&(g?a[c-1][1]=g:(a.splice(c-1,1),c--),a[c][1]=
+h,j?a[c+1][1]=j:(a.splice(c+1,1),c--))}c++}};diff_match_patch.nonAlphaNumericRegex_=/[^a-zA-Z0-9]/;diff_match_patch.whitespaceRegex_=/\s/;diff_match_patch.linebreakRegex_=/[\r\n]/;diff_match_patch.blanklineEndRegex_=/\n\r?\n$/;diff_match_patch.blanklineStartRegex_=/^\r?\n\r?\n/;
+diff_match_patch.prototype.diff_cleanupEfficiency=function(a){for(var b=!1,c=[],d=0,e=null,f=0,g=!1,h=!1,j=!1,i=!1;f<a.length;){if(0==a[f][0])a[f][1].length<this.Diff_EditCost&&(j||i)?(c[d++]=f,g=j,h=i,e=a[f][1]):(d=0,e=null),j=i=!1;else if(-1==a[f][0]?i=!0:j=!0,e&&(g&&h&&j&&i||e.length<this.Diff_EditCost/2&&3==g+h+j+i))a.splice(c[d-1],0,[-1,e]),a[c[d-1]+1][0]=1,d--,e=null,g&&h?(j=i=!0,d=0):(d--,f=0<d?c[d-1]:-1,j=i=!1),b=!0;f++}b&&this.diff_cleanupMerge(a)};
+diff_match_patch.prototype.diff_cleanupMerge=function(a){a.push([0,""]);for(var b=0,c=0,d=0,e="",f="",g;b<a.length;)switch(a[b][0]){case 1:d++;f+=a[b][1];b++;break;case -1:c++;e+=a[b][1];b++;break;case 0:1<c+d?(0!==c&&0!==d&&(g=this.diff_commonPrefix(f,e),0!==g&&(0<b-c-d&&0==a[b-c-d-1][0]?a[b-c-d-1][1]+=f.substring(0,g):(a.splice(0,0,[0,f.substring(0,g)]),b++),f=f.substring(g),e=e.substring(g)),g=this.diff_commonSuffix(f,e),0!==g&&(a[b][1]=f.substring(f.length-g)+a[b][1],f=f.substring(0,f.length-
+g),e=e.substring(0,e.length-g))),0===c?a.splice(b-d,c+d,[1,f]):0===d?a.splice(b-c,c+d,[-1,e]):a.splice(b-c-d,c+d,[-1,e],[1,f]),b=b-c-d+(c?1:0)+(d?1:0)+1):0!==b&&0==a[b-1][0]?(a[b-1][1]+=a[b][1],a.splice(b,1)):b++,c=d=0,f=e=""}""===a[a.length-1][1]&&a.pop();c=!1;for(b=1;b<a.length-1;)0==a[b-1][0]&&0==a[b+1][0]&&(a[b][1].substring(a[b][1].length-a[b-1][1].length)==a[b-1][1]?(a[b][1]=a[b-1][1]+a[b][1].substring(0,a[b][1].length-a[b-1][1].length),a[b+1][1]=a[b-1][1]+a[b+1][1],a.splice(b-1,1),c=!0):a[b][1].substring(0,
+a[b+1][1].length)==a[b+1][1]&&(a[b-1][1]+=a[b+1][1],a[b][1]=a[b][1].substring(a[b+1][1].length)+a[b+1][1],a.splice(b+1,1),c=!0)),b++;c&&this.diff_cleanupMerge(a)};diff_match_patch.prototype.diff_xIndex=function(a,b){var c=0,d=0,e=0,f=0,g;for(g=0;g<a.length;g++){1!==a[g][0]&&(c+=a[g][1].length);-1!==a[g][0]&&(d+=a[g][1].length);if(c>b)break;e=c;f=d}return a.length!=g&&-1===a[g][0]?f:f+(b-e)};
+diff_match_patch.prototype.diff_prettyHtml=function(a){for(var b=[],c=/&/g,d=/</g,e=/>/g,f=/\n/g,g=0;g<a.length;g++){var h=a[g][0],j=a[g][1],j=j.replace(c,"&amp;").replace(d,"&lt;").replace(e,"&gt;").replace(f,"<span style=\"color: #dcdcdc;\">&not;</span><br>");switch(h){case 1:b[g]='<ins style="background:#ccffcc; text-decoration: none;">'+j+"</ins>";break;case -1:b[g]='<del style="background:#ffffcc; text-decoration: line-through; color: orange;">'+j+"</del>";break;case 0:b[g]="<span>"+j+"</span>"}}return b.join("")};
+diff_match_patch.prototype.diff_text1=function(a){for(var b=[],c=0;c<a.length;c++)1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_text2=function(a){for(var b=[],c=0;c<a.length;c++)-1!==a[c][0]&&(b[c]=a[c][1]);return b.join("")};diff_match_patch.prototype.diff_levenshtein=function(a){for(var b=0,c=0,d=0,e=0;e<a.length;e++){var f=a[e][0],g=a[e][1];switch(f){case 1:c+=g.length;break;case -1:d+=g.length;break;case 0:b+=Math.max(c,d),d=c=0}}return b+=Math.max(c,d)};
+diff_match_patch.prototype.diff_toDelta=function(a){for(var b=[],c=0;c<a.length;c++)switch(a[c][0]){case 1:b[c]="+"+encodeURI(a[c][1]);break;case -1:b[c]="-"+a[c][1].length;break;case 0:b[c]="="+a[c][1].length}return b.join("\t").replace(/%20/g," ")};
+diff_match_patch.prototype.diff_fromDelta=function(a,b){for(var c=[],d=0,e=0,f=b.split(/\t/g),g=0;g<f.length;g++){var h=f[g].substring(1);switch(f[g].charAt(0)){case "+":try{c[d++]=[1,decodeURI(h)]}catch(j){throw Error("Illegal escape in diff_fromDelta: "+h);}break;case "-":case "=":var i=parseInt(h,10);if(isNaN(i)||0>i)throw Error("Invalid number in diff_fromDelta: "+h);h=a.substring(e,e+=i);"="==f[g].charAt(0)?c[d++]=[0,h]:c[d++]=[-1,h];break;default:if(f[g])throw Error("Invalid diff operation in diff_fromDelta: "+
+f[g]);}}if(e!=a.length)throw Error("Delta length ("+e+") does not equal source text length ("+a.length+").");return c};diff_match_patch.prototype.match_main=function(a,b,c){if(null==a||null==b||null==c)throw Error("Null input. (match_main)");c=Math.max(0,Math.min(c,a.length));return a==b?0:a.length?a.substring(c,c+b.length)==b?c:this.match_bitap_(a,b,c):-1};
+diff_match_patch.prototype.match_bitap_=function(a,b,c){function d(a,d){var e=a/b.length,g=Math.abs(c-d);return!f.Match_Distance?g?1:e:e+g/f.Match_Distance}if(b.length>this.Match_MaxBits)throw Error("Pattern too long for this browser.");var e=this.match_alphabet_(b),f=this,g=this.Match_Threshold,h=a.indexOf(b,c);-1!=h&&(g=Math.min(d(0,h),g),h=a.lastIndexOf(b,c+b.length),-1!=h&&(g=Math.min(d(0,h),g)));for(var j=1<<b.length-1,h=-1,i,k,p=b.length+a.length,q,s=0;s<b.length;s++){i=0;for(k=p;i<k;)d(s,c+
+k)<=g?i=k:p=k,k=Math.floor((p-i)/2+i);p=k;i=Math.max(1,c-k+1);var o=Math.min(c+k,a.length)+b.length;k=Array(o+2);for(k[o+1]=(1<<s)-1;o>=i;o--){var v=e[a.charAt(o-1)];k[o]=0===s?(k[o+1]<<1|1)&v:(k[o+1]<<1|1)&v|(q[o+1]|q[o])<<1|1|q[o+1];if(k[o]&j&&(v=d(s,o-1),v<=g))if(g=v,h=o-1,h>c)i=Math.max(1,2*c-h);else break}if(d(s+1,c)>g)break;q=k}return h};
+diff_match_patch.prototype.match_alphabet_=function(a){for(var b={},c=0;c<a.length;c++)b[a.charAt(c)]=0;for(c=0;c<a.length;c++)b[a.charAt(c)]|=1<<a.length-c-1;return b};
+diff_match_patch.prototype.patch_addContext_=function(a,b){if(0!=b.length){for(var c=b.substring(a.start2,a.start2+a.length1),d=0;b.indexOf(c)!=b.lastIndexOf(c)&&c.length<this.Match_MaxBits-this.Patch_Margin-this.Patch_Margin;)d+=this.Patch_Margin,c=b.substring(a.start2-d,a.start2+a.length1+d);d+=this.Patch_Margin;(c=b.substring(a.start2-d,a.start2))&&a.diffs.unshift([0,c]);(d=b.substring(a.start2+a.length1,a.start2+a.length1+d))&&a.diffs.push([0,d]);a.start1-=c.length;a.start2-=c.length;a.length1+=
+c.length+d.length;a.length2+=c.length+d.length}};
+diff_match_patch.prototype.patch_make=function(a,b,c){var d;if("string"==typeof a&&"string"==typeof b&&"undefined"==typeof c)d=a,b=this.diff_main(d,b,!0),2<b.length&&(this.diff_cleanupSemantic(b),this.diff_cleanupEfficiency(b));else if(a&&"object"==typeof a&&"undefined"==typeof b&&"undefined"==typeof c)b=a,d=this.diff_text1(b);else if("string"==typeof a&&b&&"object"==typeof b&&"undefined"==typeof c)d=a;else if("string"==typeof a&&"string"==typeof b&&c&&"object"==typeof c)d=a,b=c;else throw Error("Unknown call format to patch_make.");
+if(0===b.length)return[];for(var c=[],a=new diff_match_patch.patch_obj,e=0,f=0,g=0,h=d,j=0;j<b.length;j++){var i=b[j][0],k=b[j][1];if(!e&&0!==i)a.start1=f,a.start2=g;switch(i){case 1:a.diffs[e++]=b[j];a.length2+=k.length;d=d.substring(0,g)+k+d.substring(g);break;case -1:a.length1+=k.length;a.diffs[e++]=b[j];d=d.substring(0,g)+d.substring(g+k.length);break;case 0:k.length<=2*this.Patch_Margin&&e&&b.length!=j+1?(a.diffs[e++]=b[j],a.length1+=k.length,a.length2+=k.length):k.length>=2*this.Patch_Margin&&
+e&&(this.patch_addContext_(a,h),c.push(a),a=new diff_match_patch.patch_obj,e=0,h=d,f=g)}1!==i&&(f+=k.length);-1!==i&&(g+=k.length)}e&&(this.patch_addContext_(a,h),c.push(a));return c};diff_match_patch.prototype.patch_deepCopy=function(a){for(var b=[],c=0;c<a.length;c++){var d=a[c],e=new diff_match_patch.patch_obj;e.diffs=[];for(var f=0;f<d.diffs.length;f++)e.diffs[f]=d.diffs[f].slice();e.start1=d.start1;e.start2=d.start2;e.length1=d.length1;e.length2=d.length2;b[c]=e}return b};
+diff_match_patch.prototype.patch_apply=function(a,b){if(0==a.length)return[b,[]];var a=this.patch_deepCopy(a),c=this.patch_addPadding(a),b=c+b+c;this.patch_splitMax(a);for(var d=0,e=[],f=0;f<a.length;f++){var g=a[f].start2+d,h=this.diff_text1(a[f].diffs),j,i=-1;if(h.length>this.Match_MaxBits){if(j=this.match_main(b,h.substring(0,this.Match_MaxBits),g),-1!=j&&(i=this.match_main(b,h.substring(h.length-this.Match_MaxBits),g+h.length-this.Match_MaxBits),-1==i||j>=i))j=-1}else j=this.match_main(b,h,g);
+if(-1==j)e[f]=!1,d-=a[f].length2-a[f].length1;else if(e[f]=!0,d=j-g,g=-1==i?b.substring(j,j+h.length):b.substring(j,i+this.Match_MaxBits),h==g)b=b.substring(0,j)+this.diff_text2(a[f].diffs)+b.substring(j+h.length);else if(g=this.diff_main(h,g,!1),h.length>this.Match_MaxBits&&this.diff_levenshtein(g)/h.length>this.Patch_DeleteThreshold)e[f]=!1;else{this.diff_cleanupSemanticLossless(g);for(var h=0,k,i=0;i<a[f].diffs.length;i++){var p=a[f].diffs[i];0!==p[0]&&(k=this.diff_xIndex(g,h));1===p[0]?b=b.substring(0,
+j+k)+p[1]+b.substring(j+k):-1===p[0]&&(b=b.substring(0,j+k)+b.substring(j+this.diff_xIndex(g,h+p[1].length)));-1!==p[0]&&(h+=p[1].length)}}}b=b.substring(c.length,b.length-c.length);return[b,e]};
+diff_match_patch.prototype.patch_addPadding=function(a){for(var b=this.Patch_Margin,c="",d=1;d<=b;d++)c+=String.fromCharCode(d);for(d=0;d<a.length;d++)a[d].start1+=b,a[d].start2+=b;var d=a[0],e=d.diffs;if(0==e.length||0!=e[0][0])e.unshift([0,c]),d.start1-=b,d.start2-=b,d.length1+=b,d.length2+=b;else if(b>e[0][1].length){var f=b-e[0][1].length;e[0][1]=c.substring(e[0][1].length)+e[0][1];d.start1-=f;d.start2-=f;d.length1+=f;d.length2+=f}d=a[a.length-1];e=d.diffs;0==e.length||0!=e[e.length-1][0]?(e.push([0,
+c]),d.length1+=b,d.length2+=b):b>e[e.length-1][1].length&&(f=b-e[e.length-1][1].length,e[e.length-1][1]+=c.substring(0,f),d.length1+=f,d.length2+=f);return c};
+diff_match_patch.prototype.patch_splitMax=function(a){for(var b=this.Match_MaxBits,c=0;c<a.length;c++)if(!(a[c].length1<=b)){var d=a[c];a.splice(c--,1);for(var e=d.start1,f=d.start2,g="";0!==d.diffs.length;){var h=new diff_match_patch.patch_obj,j=!0;h.start1=e-g.length;h.start2=f-g.length;if(""!==g)h.length1=h.length2=g.length,h.diffs.push([0,g]);for(;0!==d.diffs.length&&h.length1<b-this.Patch_Margin;){var g=d.diffs[0][0],i=d.diffs[0][1];1===g?(h.length2+=i.length,f+=i.length,h.diffs.push(d.diffs.shift()),
+j=!1):-1===g&&1==h.diffs.length&&0==h.diffs[0][0]&&i.length>2*b?(h.length1+=i.length,e+=i.length,j=!1,h.diffs.push([g,i]),d.diffs.shift()):(i=i.substring(0,b-h.length1-this.Patch_Margin),h.length1+=i.length,e+=i.length,0===g?(h.length2+=i.length,f+=i.length):j=!1,h.diffs.push([g,i]),i==d.diffs[0][1]?d.diffs.shift():d.diffs[0][1]=d.diffs[0][1].substring(i.length))}g=this.diff_text2(h.diffs);g=g.substring(g.length-this.Patch_Margin);i=this.diff_text1(d.diffs).substring(0,this.Patch_Margin);""!==i&&
+(h.length1+=i.length,h.length2+=i.length,0!==h.diffs.length&&0===h.diffs[h.diffs.length-1][0]?h.diffs[h.diffs.length-1][1]+=i:h.diffs.push([0,i]));j||a.splice(++c,0,h)}}};diff_match_patch.prototype.patch_toText=function(a){for(var b=[],c=0;c<a.length;c++)b[c]=a[c];return b.join("")};
+diff_match_patch.prototype.patch_fromText=function(a){var b=[];if(!a)return b;for(var a=a.split("\n"),c=0,d=/^@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@$/;c<a.length;){var e=a[c].match(d);if(!e)throw Error("Invalid patch string: "+a[c]);var f=new diff_match_patch.patch_obj;b.push(f);f.start1=parseInt(e[1],10);""===e[2]?(f.start1--,f.length1=1):"0"==e[2]?f.length1=0:(f.start1--,f.length1=parseInt(e[2],10));f.start2=parseInt(e[3],10);""===e[4]?(f.start2--,f.length2=1):"0"==e[4]?f.length2=0:(f.start2--,f.length2=
+parseInt(e[4],10));for(c++;c<a.length;){e=a[c].charAt(0);try{var g=decodeURI(a[c].substring(1))}catch(h){throw Error("Illegal escape in patch_fromText: "+g);}if("-"==e)f.diffs.push([-1,g]);else if("+"==e)f.diffs.push([1,g]);else if(" "==e)f.diffs.push([0,g]);else if("@"==e)break;else if(""!==e)throw Error('Invalid patch mode "'+e+'" in: '+g);c++}}return b};diff_match_patch.patch_obj=function(){this.diffs=[];this.start2=this.start1=null;this.length2=this.length1=0};
+diff_match_patch.patch_obj.prototype.toString=function(){var a,b;a=0===this.length1?this.start1+",0":1==this.length1?this.start1+1:this.start1+1+","+this.length1;b=0===this.length2?this.start2+",0":1==this.length2?this.start2+1:this.start2+1+","+this.length2;a=["@@ -"+a+" +"+b+" @@\n"];var c;for(b=0;b<this.diffs.length;b++){switch(this.diffs[b][0]){case 1:c="+";break;case -1:c="-";break;case 0:c=" "}a[b+1]=c+encodeURI(this.diffs[b][1])+"\n"}return a.join("").replace(/%20/g," ")};
+this.diff_match_patch=diff_match_patch;this.DIFF_DELETE=-1;this.DIFF_INSERT=1;this.DIFF_EQUAL=0;})()
+var dmp = new diff_match_patch(); function diffLaunch(){var text1 = document.getElementById('text').value; var text2 = document.getElementById('text2').value; dmp.Diff_Timeout = 0; dmp.Diff_EditCost = 4; var d = dmp.diff_main(text1, text2); var ds = dmp.diff_prettyHtml(d); document.getElementById('diff').innerHTML = ds;
+}
//--><!]]></script>
<title>htmLawed (<?php echo hl_version();?>) test</title>
</head>
@@ -545,7 +600,7 @@ if($do){
$st = microtime();
$out = htmLawed($_POST['text'], $cfg, $_POST['spec']);
$et = microtime();
- echo '<br /><a href="htmLawedTest.php" title="[toggle visibility] syntax-highlighted" onclick="javascript:toggle(\'inputR\'); return false;"><span class="notice">Input code &raquo;</span></a> <span class="help" title="tags estimated as half of total &gt; and &lt; chars; values may be inaccurate for non-ASCII text"><small><big>', strlen($_POST['text']), '</big> chars, ~<big>', ($tag = round((substr_count($_POST['text'], '>') + substr_count($_POST['text'], '<'))/2)), '</big> tag', ($tag > 1 ? 's' : ''), '</small>&nbsp;</span><div id="inputR" style="display: none;">', format($_POST['text']), '</div><script type="text/javascript">hl(\'inputR\');</script>', (!isset($_POST['text'][$_hlimit]) ? ' <a href="htmLawedTest.php" title="[toggle visibility] hexdump; non-viewable characters like line-returns are shown as dots" onclick="javascript:toggle(\'inputD\'); return false;"><span class="notice">Input binary &raquo;&nbsp;</span></a><div id="inputD" style="display: none;">'. hexdump($_POST['text']). '</div>' : ''), ' <a href="htmLawedTest.php" title="[toggle visibility] finalized internal settings as interpreted by htmLawed; for developers" onclick="javascript:toggle(\'settingF\'); return false;"><span class="notice">Finalized internal settings &raquo;&nbsp;</span></a> <div id="settingF" style="display: none;">', str_replace(array(' ', "\t", ' '), array(' ', '&nbsp; ', '&nbsp; '), nl2br(htmlspecialchars(print_r($GLOBALS['hlcfg']['config'], true)))), '</div><script type="text/javascript">hl(\'settingF\');</script>', '<br /><a href="htmLawedTest.php" title="[toggle visibility] suitable for copy-paste" onclick="javascript:toggle(\'outputF\'); return false;"><span class="notice">Output &raquo;</span></a> <span class="help" title="approx., server-specific value excluding the \'include()\' call"><small>htmLawed processing time <big>', number_format(((substr($et,0,9)) + (substr($et,-10)) - (substr($st,0,9)) - (substr($st,-10))),4), '</big> s</small></span>', (($mem = memory_get_peak_usage()) !== false ? '<span class="help"><small>, peak memory usage <big>'. round(($mem-$pre_mem)/1048576, 2). '</big> <small>MB</small>' : ''), '</small></span><div id="outputF" style="display: block;"><div><textarea id="text2" class="textarea" name="text2" rows="5" cols="100" style="width: 100%;">', htmlspecialchars($out), '</textarea></div><button type="button" onclick="javascript:document.getElementById(\'text2\').focus();document.getElementById(\'text2\').select()" title="select all to copy" style="float:right;">Select all</button>';
+ echo '<br /><a href="htmLawedTest.php" title="[toggle visibility] syntax-highlighted" onclick="javascript:toggle(\'inputR\'); return false;"><span class="notice">Input code &raquo;</span></a> <span class="help" title="tags estimated as half of total &gt; and &lt; chars; values may be inaccurate for non-ASCII text"><small><big>', strlen($_POST['text']), '</big> chars, ~<big>', ($tag = round((substr_count($_POST['text'], '>') + substr_count($_POST['text'], '<'))/2)), '</big> tag', ($tag > 1 ? 's' : ''), '</small>&nbsp;</span><div id="inputR" style="display: none;">', format($_POST['text']), '</div><script type="text/javascript">hl(\'inputR\');</script>', (!isset($_POST['text'][$_hlimit]) ? ' <a href="htmLawedTest.php" title="[toggle visibility] hexdump; non-viewable characters like line-returns are shown as dots" onclick="javascript:toggle(\'inputD\'); return false;"><span class="notice">Input binary &raquo;&nbsp;</span></a><div id="inputD" style="display: none;">'. hexdump($_POST['text']). '</div>' : ''), ' <a href="htmLawedTest.php" title="[toggle visibility] finalized internal settings as interpreted by htmLawed; for developers" onclick="javascript:toggle(\'settingF\'); return false;"><span class="notice">Finalized internal settings &raquo;&nbsp;</span></a> <div id="settingF" style="display: none;">$config: ', str_replace(array(' ', "\t", ' '), array(' ', '&nbsp; ', '&nbsp; '), nl2br(htmlspecialchars(print_r($GLOBALS['hlcfg']['config'], true)))), '<br />$spec: ', str_replace(array(' ', "\t", ' '), array(' ', '&nbsp; ', '&nbsp; '), nl2br(htmlspecialchars(print_r($GLOBALS['hlcfg']['spec'], true)))), '</div><script type="text/javascript">hl(\'settingF\');</script>', '<br /><a href="htmLawedTest.php" title="[toggle visibility] suitable for copy-paste" onclick="javascript:toggle(\'outputF\'); return false;"><span class="notice">Output &raquo;</span></a> <span class="help" title="approx., server-specific value excluding the \'include()\' call"><small>htmLawed processing time <big>', number_format(((substr($et,0,9)) + (substr($et,-10)) - (substr($st,0,9)) - (substr($st,-10))),4), '</big> s</small></span>', (($mem = memory_get_peak_usage()) !== false ? '<span class="help"><small>, peak memory usage <big>'. round(($mem-$pre_mem)/1048576, 2). '</big> <small>MB</small>' : ''), '</small></span><div id="outputF" style="display: block;"><div><textarea id="text2" class="textarea" name="text2" rows="5" cols="100" style="width: 100%;">', htmlspecialchars($out), '</textarea></div><button type="button" onclick="javascript:document.getElementById(\'text2\').focus();document.getElementById(\'text2\').select()" title="select all to copy" style="float:right;">Select all</button>';
if($_w3c_validate && $validation)
{
?>
@@ -555,7 +610,7 @@ if($do){
<?php
}
- echo '</div><br /><a href="htmLawedTest.php" title="[toggle visibility] syntax-highlighted" onclick="javascript:toggle(\'outputR\'); return false;"><span class="notice">Output code &raquo;</span></a><div id="outputR" style="display: block;">', format($out), '</div><script type="text/javascript">hl(\'outputR\');</script>', (!isset($_POST['text'][$_hlimit]) ? '<br /><a href="htmLawedTest.php" title="[toggle visibility] hexdump; non-viewable characters like line-returns are shown as dots" onclick="javascript:toggle(\'outputD\'); return false;"><span class="notice">Output binary &raquo;</span></a><div id="outputD" style="display: none;">'. hexdump($out). '</div>' : ''), '<br /><a href="htmLawedTest.php" title="[toggle visibility] XHTML 1 Transitional doctype" onclick="javascript:toggle(\'outputH\'); return false;"><span class="notice">Output rendered &raquo;</span></a><div id="outputH" style="display: block;">', $out, '</div>';
+ echo '</div><br /><a href="htmLawedTest.php" title="[toggle visibility] syntax-highlighted" onclick="javascript:toggle(\'outputR\'); return false;"><span class="notice">Output code &raquo;</span></a><div id="outputR" style="display: block;">', format($out), '</div><script type="text/javascript">hl(\'outputR\');</script>', (!isset($_POST['text'][$_hlimit]) ? ' <a href="htmLawedTest.php" title="[toggle visibility] hexdump; non-viewable characters like line-returns are shown as dots" onclick="javascript:toggle(\'outputD\'); return false;"><span class="notice">Output binary &raquo;</span></a><div id="outputD" style="display: none;">'. hexdump($out). '</div>' : ''), ' <a href="htmLawedTest.php" title="[toggle visibility] inline output-input diff; might not be perfectly accurate, semantically or otherwise " onclick="javascript:toggle(\'diff\'); diffLaunch(); return false;"><span class="notice">Diff &raquo;</span></a> <div id="diff" style="display: none;"></div><br /><a href="htmLawedTest.php" title="[toggle visibility] XHTML 1 Transitional doctype" onclick="javascript:toggle(\'outputH\'); return false;"><span class="notice">Output rendered &raquo;</span></a><div id="outputH" style="display: block;">', $out, '</div>';
}
else{
?>
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm b/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm
index 6dd78fb2e..819b39e06 100644..100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm
+++ b/mod/htmlawed/vendors/htmLawed/htmLawed_README.htm
@@ -1,2160 +1,2178 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
-<meta http-equiv="Content-Language" content="en" />
-<meta name="description" content="htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter - htmLawed_README.txt - presented with rTxt2htm, a PHP Labware utility" />
-<meta name="keywords" content="htmLawed, HTM, HTML, HTML Tidy, converter, filter, formatter, purifier, sanitizer, XSS, input, PHP, software, code, script, security, cross-site scripting, hack, sanitize, remove, standards, tags, attributes, elements, htmLawed_README.txt, rTxt2htm, PHP Labware" />
-<style type="text/css" media="all">
-<!--/*--><![CDATA[/*><!--*/
-a {text-decoration:none; color: blue;}
-
-a:hover {color: red;}
-
-a:visited {color: blue;}
-
-body {margin: 0; padding: 0;}
-
-body, div, html, p {font-family: Georgia, 'Times new roman', Times;}
-
-code.code {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
-
-div.comment {padding: 5px; color: #999999; font-size: 80%;}
-
-div.comment a {color: #6699cc;}
-
-div#body {width: 70%; margin: 5px; padding: 5px;} /* holds non-toc content */
-
-div#toc {position: fixed; top: 5px; left: 73%; z-index: 2; margin-top: 5px; margin-left: 5px; border: 1px solid gray; padding: 5px; background-color: #ededed; width: 23%; overflow: auto; max-height:94%; font-size: 90%;} /* holds content table (toc) */
-
-div#top {font-size: 14px; margin: 5px; padding: 5px;} /* holds all content */
-
-div.monospace {overflow: auto; font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
-
-div.sub-section {padding-left: 15px;}
-
-div.sub-sub-section {padding-left: 30px;}
-
-h1 {font-size: 22px; margin-top: 5px; margin-bottom: 5px;}
-
-h2 {font-size: 20px; float: left; margin-top: 15px; margin-bottom: 5px;}
-
-h3 {font-size: 18px; float: left; margin-top: 15px; margin-bottom: 5px;}
-
-h4 {font-size: 16px; float: left; margin-top: 15px; margin-bottom: 5px;}
-
-hr {margin-top: 15px; margin-bottom: 5px;}
-
-input, textarea {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
-
-p.subtle {color: gray; padding: 0; padding-top: 10px; margin: 0;}
-
-p.subtle a, p.subtle a:visited {color: #6699cc;}
-
-span.item-no {color: black;}
-
-span.subtle {color: gray; margin: 0; padding:0;}
-
-span.subtle a, span.subtle a:visited {color: #6699cc;}
-
-span.term {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
-
-span.toc-item {color: black;}
-
-span.totop {float: right; margin-top: 15px; margin-bottom: 5px;}
-
-span.totop a, span.totop a:visited {color: #6699cc;}
-
-@media screen { /* fixes for old IE */
-
- * html, * html body {overflow-y: auto!important; height: 100%; margin: 0; padding: 0;}
-
- * html div#body {height: 100%; overflow-y: auto; position: relative;}
-
- * html div#toc {position: absolute;}
-
-}
-
-/*]]>*/-->
-</style>
-<title>htmLawed documentation | htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter</title>
-</head>
-<body>
-<div id="top">
-<h1><a id="peak" name="peak"></a>htmLawed documentation</h1>
-
-<div id="toc"><span class="toc-item"><a href="#s1"><span class="item-no">1</span>&#160; About htmLawed</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.1"><span class="item-no">1.1</span>&#160; Example uses</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.2"><span class="item-no">1.2</span>&#160; Features</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.3"><span class="item-no">1.3</span>&#160; History</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.4"><span class="item-no">1.4</span>&#160; License &amp; copyright</a></span><br />
-&#160; <span class="toc-item"><a href="#s1.5"><span class="item-no">1.5</span>&#160; Terms used here</a></span><br />
-<span class="toc-item"><a href="#s2"><span class="item-no">2</span>&#160; Usage</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.1"><span class="item-no">2.1</span>&#160; Simple</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.2"><span class="item-no">2.2</span>&#160; Configuring htmLawed using the <span class="term">$config</span>&#160;parameter</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.3"><span class="item-no">2.3</span>&#160; Extra HTML specifications using the <span class="term">$spec</span>&#160;parameter</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.4"><span class="item-no">2.4</span>&#160; Performance time &amp; memory usage</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.5"><span class="item-no">2.5</span>&#160; Some security risks to keep in mind</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.6"><span class="item-no">2.6</span>&#160; Use without modifying old <span class="term">kses()</span>&#160;code</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.7"><span class="item-no">2.7</span>&#160; Tolerance for ill-written HTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.8"><span class="item-no">2.8</span>&#160; Limitations &amp; work-arounds</a></span><br />
-&#160; <span class="toc-item"><a href="#s2.9"><span class="item-no">2.9</span>&#160; Examples of usage</a></span><br />
-<span class="toc-item"><a href="#s3"><span class="item-no">3</span>&#160; Details</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.1"><span class="item-no">3.1</span>&#160; Invalid/dangerous characters</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.2"><span class="item-no">3.2</span>&#160; Character references/entities</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.3"><span class="item-no">3.3</span>&#160; HTML elements</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.1"><span class="item-no">3.3.1</span>&#160; HTML comments and <span class="term">CDATA</span>&#160;sections</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.2"><span class="item-no">3.3.2</span>&#160; Tag-transformation for better XHTML-Strict</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.3"><span class="item-no">3.3.3</span>&#160; Tag balancing and proper nesting</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.4"><span class="item-no">3.3.4</span>&#160; Elements requiring child elements</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.3.5"><span class="item-no">3.3.5</span>&#160; Beautify or compact HTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.4"><span class="item-no">3.4</span>&#160; Attributes</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.1"><span class="item-no">3.4.1</span>&#160; Auto-addition of XHTML-required attributes</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.2"><span class="item-no">3.4.2</span>&#160; Duplicate/invalid <span class="term">id</span>&#160;values</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.3"><span class="item-no">3.4.3</span>&#160; URL schemes (protocols) and scripts in attribute values</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.4"><span class="item-no">3.4.4</span>&#160; Absolute &amp; relative URLs</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.5"><span class="item-no">3.4.5</span>&#160; Lower-cased, standard attribute values</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.6"><span class="item-no">3.4.6</span>&#160; Transformation of deprecated attributes</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.7"><span class="item-no">3.4.7</span>&#160; Anti-spam &amp; <span class="term">href</span></a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.8"><span class="item-no">3.4.8</span>&#160; Inline style properties</a></span><br />
-&#160; &#160; <span class="toc-item"><a href="#s3.4.9"><span class="item-no">3.4.9</span>&#160; Hook function for tag content</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.5"><span class="item-no">3.5</span>&#160; Simple configuration directive for most valid XHTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.6"><span class="item-no">3.6</span>&#160; Simple configuration directive for most <em>safe</em>&#160;HTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.7"><span class="item-no">3.7</span>&#160; Using a hook function</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.8"><span class="item-no">3.8</span>&#160; Obtaining <em>finalized</em>&#160;parameter values</a></span><br />
-&#160; <span class="toc-item"><a href="#s3.9"><span class="item-no">3.9</span>&#160; Retaining non-HTML tags in input with mixed markup</a></span><br />
-<span class="toc-item"><a href="#s4"><span class="item-no">4</span>&#160; Other</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.1"><span class="item-no">4.1</span>&#160; Support</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.2"><span class="item-no">4.2</span>&#160; Known issues</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.3"><span class="item-no">4.3</span>&#160; Change-log</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.4"><span class="item-no">4.4</span>&#160; Testing</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.5"><span class="item-no">4.5</span>&#160; Upgrade, &amp; old versions</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.6"><span class="item-no">4.6</span>&#160; Comparison with <span class="term">HTMLPurifier</span></a></span><br />
-&#160; <span class="toc-item"><a href="#s4.7"><span class="item-no">4.7</span>&#160; Use through application plug-ins/modules</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.8"><span class="item-no">4.8</span>&#160; Use in non-PHP applications</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.9"><span class="item-no">4.9</span>&#160; Donate</a></span><br />
-&#160; <span class="toc-item"><a href="#s4.10"><span class="item-no">4.10</span>&#160; Acknowledgements</a></span><br />
-<span class="toc-item"><a href="#s5"><span class="item-no">5</span>&#160; Appendices</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.1"><span class="item-no">5.1</span>&#160; Characters discouraged in HTML</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.2"><span class="item-no">5.2</span>&#160; Valid attribute-element combinations</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.3"><span class="item-no">5.3</span>&#160; CSS 2.1 properties accepting URLs</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.4"><span class="item-no">5.4</span>&#160; Microsoft Windows 1252 character replacements</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.5"><span class="item-no">5.5</span>&#160; URL format</a></span><br />
-&#160; <span class="toc-item"><a href="#s5.6"><span class="item-no">5.6</span>&#160; Brief on htmLawed code</a></span></div><!-- ended div toc -->
-
-<div id="body">
-<br />
-<div class="comment">htmLawed_README.txt, 8 June 2012<br />
-htmLawed 1.1.11, 5 June 2012<br />
-Copyright Santosh Patnaik<br />
-Dual licensed with LGPL 3 and GPL 2 or later<br />
-A PHP Labware internal utility &#45; <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a>&#160;</div>
-<br />
-
-<div class="section"><h2>
-<a name="s1" id="s1"></a><span class="item-no">1</span>&#160; About htmLawed
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed is a highly customizable single-file PHP script to make text secure, and standard- and admin policy-compliant for use in the body of HTML 4, XHTML 1 or 1.1, or generic XML documents. It is thus a configurable input (X)HTML filter, processor, purifier, sanitizer, beautifier, etc., and an alternative to the <a href="http://tidy.sourceforge.net">HTMLTidy</a>&#160;application.<br />
-<br />
-&#160; The <em>lawing in</em>&#160;of input text is needed to ensure that HTML code in the text is standard-compliant, does not introduce security vulnerabilities, and does not break the aesthetics, design or layout of web-pages. htmLawed tries to do this by, for example, making HTML well-formed with balanced and properly nested tags, neutralizing code that may be used for cross-site scripting (<span class="term">XSS</span>) attacks, and allowing only specified HTML elements/tags and attributes.<br />
-
-<div class="sub-section"><h3>
-<a name="s1.1" id="s1.1"></a><span class="item-no">1.1</span>&#160; Example uses
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; * &#160;Filtering of text submitted as comments on blogs to allow only certain HTML elements<br />
-<br />
-&#160; * &#160;Making RSS/Atom newsfeed item-content standard-compliant: often one uses an excerpt from an HTML document for the content, and with unbalanced tags, non-numerical entities, etc., such excerpts may not be XML-compliant<br />
-<br />
-&#160; * &#160;Text processing for stricter XML standard-compliance: e.g., to have lowercased <span class="term">x</span>&#160;in hexadecimal numeric entities becomes necessary if an XHTML document with MathML content needs to be served as <span class="term">application/xml</span><br />
-<br />
-&#160; * &#160;Scraping text or data from web-pages<br />
-<br />
-&#160; * &#160;Pretty-printing HTML code<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s1.2" id="s1.2"></a><span class="item-no">1.2</span>&#160; Features
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Key: <span class="term">&#42;</span>&#160;security feature, <span class="term">^</span>&#160;standard compliance, <span class="term">~</span>&#160;requires setting right options, <span class="term">&#96;</span>&#160;different from <span class="term">Kses</span><br />
-<br />
-&#160; * &#160;make input more <strong>secure</strong>&#160;and <strong>standard-compliant</strong><br />
-&#160; * &#160;use for HTML 4, XHTML 1.0 or 1.1, or even generic <strong>XML</strong>&#160;documents &#160;^~`<br />
-<br />
-&#160; * &#160;<strong>beautify</strong>&#160;or <strong>compact</strong>&#160;HTML &#160;^~`<br />
-<br />
-&#160; * &#160;<strong>restrict elements</strong>&#160; ^~`<br />
-&#160; * &#160;proper closure of empty elements like <span class="term">img</span>&#160; ^`<br />
-&#160; * &#160;<strong>transform deprecated elements</strong>&#160;like <span class="term">u</span>&#160; ^~`<br />
-&#160; * &#160;HTML <strong>comments</strong>&#160;and <span class="term">CDATA</span>&#160;sections can be permitted &#160;^~`<br />
-&#160; * &#160;elements like <span class="term">script</span>, <span class="term">object</span>&#160;and <span class="term">form</span>&#160;can be permitted &#160;~<br />
-<br />
-&#160; * &#160;<strong>restrict attributes</strong>, including <strong>element-specifically</strong>&#160; ^~`<br />
-&#160; * &#160;remove <strong>invalid attributes</strong>&#160; ^`<br />
-&#160; * &#160;element and attribute names are <strong>lower-cased</strong>&#160; ^<br />
-&#160; * &#160;provide <strong>required attributes</strong>, like <span class="term">alt</span>&#160;for <span class="term">image</span>&#160; ^`<br />
-&#160; * &#160;<strong>transform deprecated attributes</strong>&#160; ^~`<br />
-&#160; * &#160;attributes <strong>declared only once</strong>&#160; ^`<br />
-<br />
-&#160; * &#160;<strong>restrict attribute values</strong>, including <strong>element-specifically</strong>&#160; ^~`<br />
-&#160; * &#160;a value is declared for <em>empty</em>&#160;(<em>minimized</em>) attributes like <span class="term">checked</span>&#160; ^<br />
-&#160; * &#160;check for potentially dangerous attribute values &#160;*~<br />
-&#160; * &#160;ensure <strong>unique</strong>&#160;<span class="term">id</span>&#160;attribute values &#160;^~`<br />
-&#160; * &#160;<strong>double-quote</strong>&#160;attribute values &#160;^<br />
-&#160; * &#160;lower-case <strong>standard attribute values</strong>&#160;like <span class="term">password</span>&#160; ^`<br />
-<br />
-&#160; * &#160;<strong>attribute-specific URL protocol/scheme restriction</strong>&#160; *~`<br />
-&#160; * &#160;disable <strong>dynamic expressions</strong>&#160;in <span class="term">style</span>&#160;values &#160;*~`<br />
-<br />
-&#160; * &#160;neutralize invalid named character entities &#160;^`<br />
-&#160; * &#160;<strong>convert</strong>&#160;hexadecimal numeric entities to decimal ones, or vice versa &#160;^~`<br />
-&#160; * &#160;convert named entities to numeric ones for generic XML use &#160;^~`<br />
-<br />
-&#160; * &#160;remove <strong>null</strong>&#160;characters &#160;*<br />
-&#160; * &#160;neutralize potentially dangerous proprietary Netscape <strong>Javascript entities</strong>&#160; *<br />
-&#160; * &#160;replace potentially dangerous <strong>soft-hyphen</strong>&#160;character in URL-accepting attribute values with spaces &#160;*<br />
-<br />
-&#160; * &#160;remove common <strong>invalid characters</strong>&#160;not allowed in HTML or XML &#160;^`<br />
-&#160; * &#160;replace <strong>characters from Microsoft applications</strong>&#160;like <span class="term">Word</span>&#160;that are discouraged in HTML or XML &#160;^~`<br />
-&#160; * &#160;neutralize entities for characters invalid or discouraged in HTML or XML &#160;^`<br />
-&#160; * &#160;appropriately neutralize <span class="term">&lt;</span>, <span class="term">&amp;</span>, <span class="term">"</span>, and <span class="term">&gt;</span>&#160;characters &#160;^*`<br />
-<br />
-&#160; * &#160;understands improperly spaced tag content (like, spread over more than a line) and properly spaces them &#160;`<br />
-&#160; * &#160;attempts to <strong>balance tags</strong>&#160;for well-formedness &#160;^~`<br />
-&#160; * &#160;understands when <strong>omitable closing tags</strong>&#160;like <span class="term">&lt;/p&gt;</span>&#160;(allowed in HTML 4, transitional, e.g.) are missing &#160;^~`<br />
-&#160; * &#160;attempts to permit only <strong>validly nested tags</strong>&#160; ^~`<br />
-&#160; * &#160;option to <strong>remove or neutralize bad content</strong>&#160;^~`<br />
-&#160; * &#160;attempts to <strong>rectify common errors of plain-text misplacement</strong>&#160;(e.g., directly inside <span class="term">blockquote</span>) ^~`<br />
-<br />
-&#160; * &#160;fast, <strong>non-OOP</strong>&#160;code of ~45 kb incurring peak basal memory usage of ~0.5 MB<br />
-&#160; * &#160;<strong>compatible</strong>&#160;with pre-existing code using <span class="term">Kses</span>&#160;(the filter used by <span class="term">WordPress</span>)<br />
-<br />
-&#160; * &#160;optional <strong>anti-spam</strong>&#160;measures such as addition of <span class="term">rel="nofollow"</span>&#160;and link-disabling &#160;~`<br />
-&#160; * &#160;optionally makes <strong>relative URLs absolute</strong>, and vice versa &#160;~`<br />
-<br />
-&#160; * &#160;optionally mark <span class="term">&amp;</span>&#160;to identify the entities for <span class="term">&amp;</span>, <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;introduced by htmLawed &#160;~`<br />
-<br />
-&#160; * &#160;allows deployment of powerful <strong>hook functions</strong>&#160;to <strong>inject</strong>&#160;HTML, <strong>consolidate</strong>&#160;<span class="term">style</span>&#160;attributes to <span class="term">class</span>, finely check attribute values, etc. &#160;~`<br />
-<br />
-&#160; * &#160;<strong>independent of character encoding</strong>&#160;of input and does not affect it<br />
-<br />
-&#160; * &#160;<strong>tolerance for ill-written HTML</strong>&#160;to a certain degree<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s1.3" id="s1.3"></a><span class="item-no">1.3</span>&#160; History
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed was developed for use with <span class="term">LabWiki</span>, a wiki software developed at PHP Labware, as a suitable software could not be found. Existing PHP software like <span class="term">Kses</span>&#160;and <span class="term">HTMLPurifier</span>&#160;were deemed inadequate, slow, resource-intensive, or dependent on external applications like <span class="term">HTML Tidy</span>.<br />
-<br />
-&#160; htmLawed started as a modification of Ulf Harnhammar's <span class="term">Kses</span>&#160;(version 0.2.2) software, and is compatible with code that uses <span class="term">Kses</span>; see <a href="#s2.6">section 2.6</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s1.4" id="s1.4"></a><span class="item-no">1.4</span>&#160; License &amp; copyright
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed is free and open-source software dual licensed under LGPL license version <a href="http://www.gnu.org/licenses/lgpl-3.0.txt">3</a>&#160;and GPL license version <a href="http://www.gnu.org/licenses/gpl-2.0.txt">2</a>&#160;or later, and copyrighted by Santosh Patnaik, MD, PhD.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s1.5" id="s1.5"></a><span class="item-no">1.5</span>&#160; Terms used here
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; * &#160;<em>administrator</em>&#160;- or admin; person setting up the code to pass input through htmLawed; also, <em>user</em><br />
-&#160; * &#160;<em>attributes</em>&#160;- name-value pairs like <span class="term">href="http&#58;//x.com"</span>&#160;in opening tags<br />
-&#160; * &#160;<em>author</em>&#160;- <em>writer</em><br />
-&#160; * &#160;<em>character</em>&#160;- atomic unit of text; internally represented by a numeric <em>code-point</em>&#160;as specified by the <em>encoding</em>&#160;or <em>charset</em>&#160;in use<br />
-&#160; * &#160;<em>entity</em>&#160;- markup like <span class="term">&amp;gt;</span>&#160;and <span class="term">&amp;#160;</span>&#160;used to refer to a character<br />
-&#160; * &#160;<em>element</em>&#160;- HTML element like <span class="term">a</span>&#160;and <span class="term">img</span><br />
-&#160; * &#160;<em>element content</em>&#160;- &#160;content between the opening and closing tags of an element, like <span class="term">click</span>&#160;of <span class="term">&lt;a href="x"&gt;click&lt;/a&gt;</span><br />
-&#160; * &#160;<em>HTML</em>&#160;- implies XHTML unless specified otherwise<br />
-&#160; * &#160;<em>input</em>&#160;- text string given to htmLawed to process<br />
-&#160; * &#160;<em>processing</em>&#160;- involves filtering, correction, etc., of input<br />
-&#160; * &#160;<em>safe</em>&#160;- absence or reduction of certain characters and HTML elements and attributes in the input that can otherwise potentially and circumstantially expose web-site users to security vulnerabilities like cross-site scripting attacks (XSS)<br />
-&#160; * &#160;<em>scheme</em>&#160;- URL protocol like <span class="term">http</span>&#160;and <span class="term">ftp</span><br />
-&#160; * &#160;<em>specs</em>&#160;- standard specifications<br />
-&#160; * &#160;<em>style property</em>&#160;- terms like <span class="term">border</span>&#160;and <span class="term">height</span>&#160;for which declarations are made in values for the <span class="term">style</span>&#160;attribute of elements<br />
-&#160; * &#160;<em>tag</em>&#160;- markers like <span class="term">&lt;a href="x"&gt;</span>&#160;and <span class="term">&lt;/a&gt;</span>&#160;delineating element content; the opening tag can contain attributes<br />
-&#160; * &#160;<em>tag content</em>&#160;- consists of tag markers <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, element names like <span class="term">div</span>, and possibly attributes<br />
-&#160; * &#160;<em>user</em>&#160;- administrator<br />
-&#160; * &#160;<em>writer</em>&#160;- end-user like a blog commenter providing the input that is to be processed; also, <em>author</em><br />
-
-</div>
-</div>
-<div class="section"><h2>
-<a name="s2" id="s2"></a><span class="item-no">2</span>&#160; Usage
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed should work with PHP 4.4 and higher. Either <span class="term">include()</span>&#160;the <span class="term">htmLawed.php</span>&#160;file or copy-paste the entire code.<br />
-<br />
-&#160; To easily <strong>test</strong>&#160;htmLawed using a form-based interface, use the provided <a href="htmLawedTest.php">demo</a>&#160;(<span class="term">htmLawed.php</span>&#160;and <span class="term">htmLawedTest.php</span>&#160;should be in the same directory on the web-server).<br />
-<br />
-&#160; <strong>Note</strong>: For code for usage of the htmLawed class (for htmLawed in OOP), please refer to the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website; the filtering itself can be configured, etc., as described here.<br />
-
-<div class="sub-section"><h3>
-<a name="s2.1" id="s2.1"></a><span class="item-no">2.1</span>&#160; Simple
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The input text to be processed, <span class="term">$text</span>, is passed as an argument of type string; <span class="term">htmLawed()</span>&#160;returns the processed string:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text);</code>
-<br />
-<br />
-&#160; <strong>Note</strong>: If input is from a <span class="term">$_GET</span>&#160;or <span class="term">$_POST</span>&#160;value, and <span class="term">magic quotes</span>&#160;are enabled on the PHP setup, run <span class="term">stripslashes()</span>&#160;on the input before passing to htmLawed.<br />
-<br />
-&#160; By default, htmLawed will process the text allowing all valid HTML elements/tags, secure URL scheme/CSS style properties, etc. It will allow <span class="term">CDATA</span>&#160;sections and HTML comments, balance tags, and ensure proper nesting of elements. Such actions can be configured using two other optional arguments -- <span class="term">$config</span>&#160;and <span class="term">$spec</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, $config, $spec);</code>
-<br />
-<br />
-&#160; These extra parameters are detailed below. Some examples are shown in <a href="#s2.9">section 2.9</a>.<br />
-<br />
-&#160; <strong>Note</strong>: For maximum protection against <span class="term">XSS</span>&#160;and other scripting attacks (e.g., by disallowing Javascript code), consider using the <span class="term">safe</span>&#160;parameter; see <a href="#s3.6">section 3.6</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.2" id="s2.2"></a><span class="item-no">2.2</span>&#160; Configuring htmLawed using the <span class="term">$config</span>&#160;parameter
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; <span class="term">$config</span>&#160;instructs htmLawed on how to tackle certain tasks. When <span class="term">$config</span>&#160;is not specified, or not set as an array (e.g., <span class="term">$config = 1</span>), htmLawed will take default actions. One or many of the task-action or value-specification pairs can be specified in <span class="term">$config</span>&#160;as array key-value pairs. If a parameter is not specified, htmLawed will use the default value/action indicated further below.<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;comment&#39;=&gt;0, &#39;cdata&#39;=&gt;1);</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, $config);</code>
-<br />
-<br />
-&#160; Or,<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, array(&#39;comment&#39;=&gt;0, &#39;cdata&#39;=&gt;1));</code>
-<br />
-<br />
-&#160; Below are the possible value-specification combinations. In PHP code, values that are integers should not be quoted and should be used as numeric types (unless meant as string/text).<br />
-<br />
-&#160; Key: <span class="term">&#42;</span>&#160;default, <span class="term">^</span>&#160;different default when htmLawed is used in the Kses-compatible mode (see <a href="#s2.6">section 2.6</a>), <span class="term">~</span>&#160;different default when <span class="term">valid_xhtml</span>&#160;is set to <span class="term">1</span>&#160;(see <a href="#s3.5">section 3.5</a>), <span class="term">"</span>&#160;different default when <span class="term">safe</span>&#160;is set to <span class="term">1</span>&#160;(see <a href="#s3.6">section 3.6</a>)<br />
-<br />
-&#160; <strong>abs_url</strong><br />
-&#160; Make URLs absolute or relative; <span class="term">$config["base_url"]</span>&#160;needs to be set; see <a href="#s3.4.4">section 3.4.4</a><br />
-<br />
-&#160; <span class="term">-1</span>&#160;- make relative<br />
-&#160; <span class="term">0</span>&#160;- no action &#160;*<br />
-&#160; <span class="term">1</span>&#160;- make absolute<br />
-<br />
-&#160; <strong>and_mark</strong><br />
-&#160; Mark <span class="term">&amp;</span>&#160;characters in the original input; see <a href="#s3.2">section 3.2</a><br />
-<br />
-&#160; <strong>anti_link_spam</strong><br />
-&#160; Anti-link-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no measure taken &#160;*<br />
-&#160; <span class="term">array("regex1", "regex2")</span>&#160;- will ensure a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;in its value in case the <span class="term">href</span>&#160;attribute value matches the regular expression pattern <span class="term">regex1</span>, and/or will remove <span class="term">href</span>&#160;if its value matches the regular expression pattern <span class="term">regex2</span>. E.g., <span class="term">array("/./", "/&#58;//\W&#42;(?!(abc\.com|xyz\.org))/")</span>; see <a href="#s3.4.7">section 3.4.7</a>&#160;for more.<br />
-<br />
-&#160; <strong>anti_mail_spam</strong><br />
-&#160; Anti-mail-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no measure taken &#160;*<br />
-&#160; <span class="term">word</span>&#160;- <span class="term">@</span>&#160;in mail address in <span class="term">href</span>&#160;attribute value is replaced with specified <span class="term">word</span><br />
-<br />
-&#160; <strong>balance</strong><br />
-&#160; Balance tags for well-formedness and proper nesting; see <a href="#s3.3.3">section 3.3.3</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no<br />
-&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
-<br />
-&#160; <strong>base_url</strong><br />
-&#160; Base URL value that needs to be set if <span class="term">$config["abs_url"]</span>&#160;is not <span class="term">0</span>; see <a href="#s3.4.4">section 3.4.4</a><br />
-<br />
-&#160; <strong>cdata</strong><br />
-&#160; Handling of <span class="term">CDATA</span>&#160;sections; see <a href="#s3.3.1">section 3.3.1</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- don't consider <span class="term">CDATA</span>&#160;sections as markup and proceed as if plain text &#160;^"<br />
-&#160; <span class="term">1</span>&#160;- remove<br />
-&#160; <span class="term">2</span>&#160;- allow, but neutralize any <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;inside by converting them to named entities<br />
-&#160; <span class="term">3</span>&#160;- allow &#160;*<br />
-<br />
-&#160; <strong>clean_ms_char</strong><br />
-&#160; Replace discouraged characters introduced by Microsoft Word, etc.; see <a href="#s3.1">section 3.1</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- yes<br />
-&#160; <span class="term">2</span>&#160;- yes, but replace special single &amp; double quotes with ordinary ones<br />
-<br />
-&#160; <strong>comment</strong><br />
-&#160; Handling of HTML comments; see <a href="#s3.3.1">section 3.3.1</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- don't consider comments as markup and proceed as if plain text &#160;^"<br />
-&#160; <span class="term">1</span>&#160;- remove<br />
-&#160; <span class="term">2</span>&#160;- allow, but neutralize any <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;inside by converting to named entities<br />
-&#160; <span class="term">3</span>&#160;- allow &#160;*<br />
-<br />
-&#160; <strong>css_expression</strong><br />
-&#160; Allow dynamic CSS expression by not removing the expression from CSS property values in <span class="term">style</span>&#160;attributes; see <a href="#s3.4.8">section 3.4.8</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- remove &#160;*<br />
-&#160; <span class="term">1</span>&#160;- allow<br />
-<br />
-&#160; <strong>deny_attribute</strong><br />
-&#160; Denied HTML attributes; see <a href="#s3.4">section 3.4</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- none &#160;*<br />
-&#160; <span class="term">string</span>&#160;- dictated by values in <span class="term">string</span><br />
-&#160; <span class="term">on&#42;</span>&#160;(like <span class="term">onfocus</span>) attributes not allowed - "<br />
-<br />
-&#160; <strong>direct_nest_list</strong><br />
-&#160; Allow direct nesting of a list within another without requiring it to be a list item; see <a href="#s3.3.4">section 3.3.4</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- yes<br />
-<br />
-&#160; <strong>elements</strong><br />
-&#160; Allowed HTML elements; see <a href="#s3.3">section 3.3</a><br />
-<br />
-&#160; <span class="term">&#42; -center -dir -font -isindex -menu -s -strike -u</span>&#160;- &#160;~<br />
-&#160; <span class="term">applet, embed, iframe, object, script</span>&#160;not allowed - "<br />
-<br />
-&#160; <strong>hexdec_entity</strong><br />
-&#160; Allow hexadecimal numeric entities and do not convert to the more widely accepted decimal ones, or convert decimal to hexadecimal ones; see <a href="#s3.2">section 3.2</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no<br />
-&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
-&#160; <span class="term">2</span>&#160;- convert decimal to hexadecimal ones<br />
-<br />
-&#160; <strong>hook</strong><br />
-&#160; Name of an optional hook function to alter the input string, <span class="term">$config</span>&#160;or <span class="term">$spec</span>&#160;before htmLawed starts its main work; see <a href="#s3.7">section 3.7</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no hook function &#160;*<br />
-&#160; <span class="term">name</span>&#160;- <span class="term">name</span>&#160;is name of the hook function (<span class="term">kses_hook</span>&#160; ^)<br />
-<br />
-&#160; <strong>hook_tag</strong><br />
-&#160; Name of an optional hook function to alter tag content finalized by htmLawed; see <a href="#s3.4.9">section 3.4.9</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no hook function &#160;*<br />
-&#160; <span class="term">name</span>&#160;- <span class="term">name</span>&#160;is name of the hook function<br />
-<br />
-&#160; <strong>keep_bad</strong><br />
-&#160; Neutralize bad tags by converting <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;to entities, or remove them; see <a href="#s3.3.3">section 3.3.3</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- remove &#160;^<br />
-&#160; <span class="term">1</span>&#160;- neutralize both tags and element content<br />
-&#160; <span class="term">2</span>&#160;- remove tags but neutralize element content<br />
-&#160; <span class="term">3</span>&#160;and <span class="term">4</span>&#160;- like <span class="term">1</span>&#160;and <span class="term">2</span>&#160;but remove if text (<span class="term">pcdata</span>) is invalid in parent element<br />
-&#160; <span class="term">5</span>&#160;and <span class="term">6</span>&#160;* - &#160;like <span class="term">3</span>&#160;and <span class="term">4</span>&#160;but line-breaks, tabs and spaces are left<br />
-<br />
-&#160; <strong>lc_std_val</strong><br />
-&#160; For XHTML compliance, predefined, standard attribute values, like <span class="term">get</span>&#160;for the <span class="term">method</span>&#160;attribute of <span class="term">form</span>, must be lowercased; see <a href="#s3.4.5">section 3.4.5</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no<br />
-&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
-<br />
-&#160; <strong>make_tag_strict</strong><br />
-&#160; Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: <span class="term">applet</span>&#160;<span class="term">center</span>&#160;<span class="term">dir</span>&#160;<span class="term">embed</span>&#160;<span class="term">font</span>&#160;<span class="term">isindex</span>&#160;<span class="term">menu</span>&#160;<span class="term">s</span>&#160;<span class="term">strike</span>&#160;<span class="term">u</span>; see <a href="#s3.3.2">section 3.3.2</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;^<br />
-&#160; <span class="term">1</span>&#160;- yes, but leave <span class="term">applet</span>, <span class="term">embed</span>&#160;and <span class="term">isindex</span>&#160;elements that currently can't be transformed &#160;*<br />
-&#160; <span class="term">2</span>&#160;- yes, removing <span class="term">applet</span>, <span class="term">embed</span>&#160;and <span class="term">isindex</span>&#160;elements and their contents (nested elements remain) &#160;~<br />
-<br />
-&#160; <strong>named_entity</strong><br />
-&#160; Allow non-universal named HTML entities, or convert to numeric ones; see <a href="#s3.2">section 3.2</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- convert<br />
-&#160; <span class="term">1</span>&#160;- allow &#160;*<br />
-<br />
-&#160; <strong>no_deprecated_attr</strong><br />
-&#160; Allow deprecated attributes or transform them; see <a href="#s3.4.6">section 3.4.6</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- allow &#160;^<br />
-&#160; <span class="term">1</span>&#160;- transform, but <span class="term">name</span>&#160;attributes for <span class="term">a</span>&#160;and <span class="term">map</span>&#160;are retained &#160;*<br />
-&#160; <span class="term">2</span>&#160;- transform<br />
-<br />
-&#160; <strong>parent</strong><br />
-&#160; Name of the parent element, possibly imagined, that will hold the input; see <a href="#s3.3">section 3.3</a><br />
-<br />
-&#160; <strong>safe</strong><br />
-&#160; Magic parameter to make input the most secure against XSS without needing to specify other relevant <span class="term">$config</span>&#160;parameters; see <a href="#s3.6">section 3.6</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- will auto-adjust other relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">"</span>&#160;in this list)<br />
-<br />
-&#160; <strong>schemes</strong><br />
-&#160; Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or <span class="term">!</span>&#160;to <em>deny</em>&#160;any URL); <span class="term">&#42;</span>&#160;covers all unspecified attributes; see <a href="#s3.4.3">section 3.4.3</a><br />
-<br />
-&#160; <span class="term">href&#58; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; &#42;&#58;file, http, https</span>&#160; *<br />
-&#160; <span class="term">&#42;&#58; ftp, gopher, http, https, mailto, news, nntp, telnet</span>&#160; ^<br />
-&#160; <span class="term">href&#58; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style&#58; !; &#42;&#58;file, http, https</span>&#160; "<br />
-<br />
-&#160; <strong>show_setting</strong><br />
-&#160; Name of a PHP variable to assign the <em>finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values; see <a href="#s3.8">section 3.8</a><br />
-<br />
-&#160; <strong>style_pass</strong><br />
-&#160; Do not look at <span class="term">style</span>&#160;attribute values, letting them through without any alteration<br />
-<br />
-&#160; <span class="term">0</span>&#160;- no *<br />
-&#160; <span class="term">1</span>&#160;- htmLawed will let through any <span class="term">style</span>&#160;value; see <a href="#s3.4.8">section 3.4.8</a><br />
-<br />
-&#160; <strong>tidy</strong><br />
-&#160; Beautify or compact HTML code; see <a href="#s3.3.5">section 3.3.5</a><br />
-<br />
-&#160; <span class="term">-1</span>&#160;- compact<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;or <span class="term">string</span>&#160;- beautify (custom format specified by <span class="term">string</span>)<br />
-<br />
-&#160; <strong>unique_ids</strong><br />
-&#160; <span class="term">id</span>&#160;attribute value checks; see <a href="#s3.4.2">section 3.4.2</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;^<br />
-&#160; <span class="term">1</span>&#160;- remove duplicate and/or invalid ones &#160;*<br />
-&#160; <span class="term">word</span>&#160;- remove invalid ones and replace duplicate ones with new and unique ones based on the <span class="term">word</span>; the admin-specified <span class="term">word</span>, like <span class="term">my_</span>, should begin with a letter (a-z) and can contain letters, digits, <span class="term">.</span>, <span class="term">_</span>, <span class="term">-</span>, and <span class="term">&#58;</span>.<br />
-<br />
-&#160; <strong>valid_xhtml</strong><br />
-&#160; Magic parameter to make input the most valid XHTML without needing to specify other relevant <span class="term">$config</span>&#160;parameters; see <a href="#s3.5">section 3.5</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- will auto-adjust other relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">~</span>&#160;in this list)<br />
-<br />
-&#160; <strong>xml:lang</strong><br />
-&#160; Auto-adding <span class="term">xml&#58;lang</span>&#160;attribute; see <a href="#s3.4.1">section 3.4.1</a><br />
-<br />
-&#160; <span class="term">0</span>&#160;- no &#160;*<br />
-&#160; <span class="term">1</span>&#160;- add if <span class="term">lang</span>&#160;attribute is present<br />
-&#160; <span class="term">2</span>&#160;- add if <span class="term">lang</span>&#160;attribute is present, and remove <span class="term">lang</span>&#160; ~<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.3" id="s2.3"></a><span class="item-no">2.3</span>&#160; Extra HTML specifications using the $spec parameter
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The <span class="term">$spec</span>&#160;argument can be used to disallow an otherwise legal attribute for an element, or to restrict the attribute's values. This can also be helpful as a security measure (e.g., in certain versions of browsers, certain values can cause buffer overflows and denial of service attacks), or in enforcing admin policy compliance. <span class="term">$spec</span>&#160;is specified as a string of text containing one or more <em>rules</em>, with multiple rules separated from each other by a semi-colon (<span class="term">;</span>). E.g.,<br />
-<br />
-
-<code class="code">&#160; &#160; $spec = &#39;i=-&#42;; td, tr=style, id, -&#42;; a=id(match="/[a-z][a-z\d.&#58;\-&#96;"]&#42;/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, $config, $spec);</code>
-<br />
-<br />
-&#160; Or,<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, $config, &#39;i=-&#42;; td, tr=style, id, -&#42;; a=id(match="/[a-z][a-z\d.&#58;\-&#96;"]&#42;/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt&#39;);</code>
-<br />
-<br />
-&#160; A rule begins with an HTML <strong>element</strong>&#160;name(s) (<em>rule-element</em>), for which the rule applies, followed by an equal (<span class="term">=</span>) sign. A rule-element may represent multiple elements if comma (,)-separated element names are used. E.g., <span class="term">th,td,tr=</span>.<br />
-<br />
-&#160; Rest of the rule consists of comma-separated HTML <strong>attribute names</strong>. A minus (<span class="term">-</span>) character before an attribute means that the attribute is not permitted inside the rule-element. E.g., <span class="term">-width</span>. To deny all attributes, <span class="term">-&#42;</span>&#160;can be used.<br />
-<br />
-&#160; Following shows examples of rule excerpts with rule-element <span class="term">a</span>&#160;and the attributes that are being permitted:<br />
-<br />
-&#160; * &#160;<span class="term">a=</span>&#160;- all<br />
-&#160; * &#160;<span class="term">a=id</span>&#160;- all<br />
-&#160; * &#160;<span class="term">a=href, title, -id, -onclick</span>&#160;- all except <span class="term">id</span>&#160;and <span class="term">onclick</span><br />
-&#160; * &#160;<span class="term">a=&#42;, id, -id</span>&#160;- all except <span class="term">id</span><br />
-&#160; * &#160;<span class="term">a=-&#42;</span>&#160;- none<br />
-&#160; * &#160;<span class="term">a=-&#42;, href, title</span>&#160;- none except <span class="term">href</span>&#160;and <span class="term">title</span><br />
-&#160; * &#160;<span class="term">a=-&#42;, -id, href, title</span>&#160;- none except <span class="term">href</span>&#160;and <span class="term">title</span><br />
-<br />
-&#160; Rules regarding <strong>attribute values</strong>&#160;are optionally specified inside round brackets after attribute names in slash ('/')-separated <em>parameter = value</em>&#160;pairs. E.g., <span class="term">title(maxlen=30/minlen=5)</span>. None, or one or more of the following parameters may be specified:<br />
-<br />
-&#160; * &#160;<span class="term">oneof</span>&#160;- one or more choices separated by <span class="term">|</span>&#160;that the value should match; if only one choice is provided, then the value must match that choice<br />
-<br />
-&#160; * &#160;<span class="term">noneof</span>&#160;- one or more choices separated by <span class="term">|</span>&#160;that the value should not match<br />
-<br />
-&#160; * &#160;<span class="term">maxlen</span>&#160;and <span class="term">minlen</span>&#160;- upper and lower limits for the number of characters in the attribute value; specified in numbers<br />
-<br />
-&#160; * &#160;<span class="term">maxval</span>&#160;and <span class="term">minval</span>&#160;- upper and lower limits for the numerical value specified in the attribute value; specified in numbers<br />
-<br />
-&#160; * &#160;<span class="term">match</span>&#160;and <span class="term">nomatch</span>&#160;- pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers<br />
-<br />
-&#160; * &#160;<span class="term">default</span>&#160;- a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters<br />
-<br />
-&#160; If <span class="term">default</span>&#160;is not set and the attribute value does not satisfy any of the specified parameters, then the attribute is removed. The <span class="term">default</span>&#160;value can also be used to force all attribute declarations to take the same value (by getting the values declared illegal by setting, e.g., <span class="term">maxlen</span>&#160;to <span class="term">-1</span>).<br />
-<br />
-&#160; Examples with <em>input</em>&#160;<span class="term">&lt;input title="WIDTH" value="10em" /&gt;&lt;input title="length" value="5" /&gt;</span>&#160;are shown below.<br />
-<br />
-&#160; <em>Rule</em>: <span class="term">input=title(maxlen=60/minlen=6), value</span><br />
-&#160; <em>Output</em>: <span class="term">&lt;input value="10em" /&gt;&lt;input title="length" value="5" /&gt;</span><br />
-<br />
-&#160; <em>Rule</em>: <span class="term">input=title(), value(maxval=8/default=6)</span><br />
-&#160; <em>Output</em>: <span class="term">&lt;input title="WIDTH" value="6" /&gt;&lt;input title="length" value="5" /&gt;</span><br />
-<br />
-&#160; <em>Rule</em>: <span class="term">input=title(nomatch=%w.d%i), value(match=%em%/default=6em)</span><br />
-&#160; <em>Output</em>: <span class="term">&lt;input value="10em" /&gt;&lt;input title="length" value="6em" /&gt;</span><br />
-<br />
-&#160; <em>Rule</em>: <span class="term">input=title(oneof=height|depth/default=depth), value(noneof=5|6)</span><br />
-&#160; <em>Output</em>: <span class="term">&lt;input title="depth" value="10em" /&gt;&lt;input title="depth" /&gt;</span><br />
-<br />
-&#160; <strong>Special characters</strong>: The characters <span class="term">;</span>, <span class="term">,</span>, <span class="term">/</span>, <span class="term">(</span>, <span class="term">)</span>, <span class="term">|</span>, <span class="term">~</span>&#160;and space have special meanings in the rules. Words in the rules that use such characters, or the characters themselves, should be <em>escaped</em>&#160;by enclosing in pairs of double-quotes (<span class="term">"</span>). A back-tick (<span class="term">&#96;</span>) can be used to escape a literal <span class="term">"</span>. An example rule illustrating this is <span class="term">input=value(maxlen=30/match="/^\w/"/default="your &#96;"ID&#96;"")</span>.<br />
-<br />
-&#160; <strong>Note</strong>: To deny an attribute for all elements for which it is legal, <span class="term">$config["deny_attribute"]</span>&#160;(see <a href="#s3.4">section 3.4</a>) can be used instead of <span class="term">$spec</span>. Also, attributes can be allowed element-specifically through <span class="term">$spec</span>&#160;while being denied globally through <span class="term">$config["deny_attribute"]</span>. The <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) can also be used to implement the <span class="term">$spec</span>&#160;functionality.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.4" id="s2.4"></a><span class="item-no">2.4</span>&#160; Performance time &amp; memory usage
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The time and memory used by htmLawed depends on its configuration and the size of the input, and the amount, nestedness and well-formedness of the HTML markup within it. In particular, tag balancing and beautification each can increase the processing time by about a quarter.<br />
-<br />
-&#160; The htmLawed <a href="htmLawedTest.php">demo</a>&#160;can be used to evaluate the performance and effects of different types of input and <span class="term">$config</span>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.5" id="s2.5"></a><span class="item-no">2.5</span>&#160; Some security risks to keep in mind
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; When setting the parameters/arguments (like those to allow certain HTML elements) for use with htmLawed, one should bear in mind that the setting may let through potentially <em>dangerous</em>&#160;HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc.<br />
-<br />
-&#160; Unless end-users, either people or software, supplying the content are completely trusted, security issues arising from the degree of HTML usage permission has to be kept in mind. For example, following increase security risks:<br />
-<br />
-&#160; * &#160;Allowing <span class="term">script</span>, <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>&#160;or <span class="term">object</span>&#160;elements, or certain of their attributes like <span class="term">allowscriptaccess</span><br />
-<br />
-&#160; * &#160;Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., <span class="term">&lt;!--[if gte IE 4]&gt;&lt;script&gt;alert("xss");&lt;/script&gt;&lt;![endif]--&gt;</span><br />
-<br />
-&#160; * &#160;Allowing dynamic CSS expressions (a feature of the IE browser)<br />
-<br />
-&#160; * &#160;Allowing the <span class="term">style</span>&#160;attribute<br />
-<br />
-&#160; To remove <em>unsecure</em>&#160;HTML, code-developers using htmLawed must set <span class="term">$config</span>&#160;appropriately. E.g., <span class="term">$config["elements"] = "&#42; -script"</span>&#160;to deny the <span class="term">script</span>&#160;element (<a href="#s3.3">section 3.3</a>), <span class="term">$config["safe"] = 1</span>&#160;to auto-configure ceratin htmLawed parameters for maximizing security (<a href="#s3.6">section 3.6</a>), etc.<br />
-<br />
-&#160; Permitting the <span class="term">&#42;style&#42;</span>&#160;attribute brings in risks of <em>click-jacking</em>, <em>phishing</em>, web-page overlays, etc., <em>even</em>&#160;when the <span class="term">safe</span>&#160;parameter is enabled (see <a href="#s3.6">section 3.6</a>). Except for URLs and a few other things like CSS dynamic expressions, htmLawed currently does not check every CSS style property. It does provide ways for the code-developer implementing htmLawed to do such checks through htmLawed's <span class="term">$spec</span>&#160;argument, and through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.8">section 3.4.8</a>&#160;for more). Disallowing <span class="term">style</span>&#160;completely and relying on CSS classes and stylesheet files is recommended.<br />
-<br />
-&#160; htmLawed does not check or correct the character <strong>encoding</strong>&#160;of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML <span class="term">meta</span>&#160;tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.6" id="s2.6"></a><span class="item-no">2.6</span>&#160; Use without modifying old <span class="term">kses()</span>&#160;code
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The <span class="term">Kses</span>&#160;PHP script is used by many applications (like <span class="term">WordPress</span>). It is possible to have such applications use htmLawed instead, since it is compatible with code that calls the <span class="term">kses()</span>&#160;function declared in the <span class="term">Kses</span>&#160;file (usually named <span class="term">kses.php</span>). E.g., application code like this will continue to work after replacing <span class="term">Kses</span>&#160;with htmLawed:<br />
-<br />
-
-<code class="code">&#160; &#160; $comment_filtered = kses($comment_input, array(&#39;a&#39;=&gt;array(), &#39;b&#39;=&gt;array(), &#39;i&#39;=&gt;array()));</code>
-<br />
-<br />
-&#160; For some of the <span class="term">$config</span>&#160;parameters, htmLawed will use values other than the default ones. These are indicated by <span class="term">^</span>&#160;in <a href="#s2.2">section 2.2</a>. To force htmLawed to use other values, function <span class="term">kses()</span>&#160;in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.<br />
-<br />
-&#160; If the application uses a <span class="term">Kses</span>&#160;file that has the <span class="term">kses()</span>&#160;function declared, then, to have the application use htmLawed instead of <span class="term">Kses</span>, simply rename <span class="term">htmLawed.php</span>&#160;(to <span class="term">kses.php</span>, e.g.) and replace the <span class="term">Kses</span>&#160;file (or just replace the code in the <span class="term">Kses</span>&#160;file with the htmLawed code). If the <span class="term">kses()</span>&#160;function in the <span class="term">Kses</span>&#160;file had been renamed by the application developer (e.g., in <span class="term">WordPress</span>, it is named <span class="term">wp_kses()</span>), then appropriately rename the <span class="term">kses()</span>&#160;function in the htmLawed code.<br />
-<br />
-&#160; If the <span class="term">Kses</span>&#160;file used by the application has been highly altered by the application developers, then one may need a different approach. E.g., with <span class="term">WordPress</span>, it is best to copy the htmLawed code to <span class="term">wp_includes/kses.php</span>, rename the newly added function <span class="term">kses()</span>&#160;to <span class="term">wp_kses()</span>, and delete the code for the original <span class="term">wp_kses()</span>&#160;function.<br />
-<br />
-&#160; If the <span class="term">Kses</span>&#160;code has a non-empty hook function (e.g., <span class="term">wp_kses_hook()</span>&#160;in case of <span class="term">WordPress</span>), then the code for htmLawed's <span class="term">kses_hook()</span>&#160;function should be appropriately edited. However, the requirement of the hook function should be re-evaluated considering that htmLawed has extra capabilities. With <span class="term">WordPress</span>, the hook function is an essential one. The following code is suggested for the htmLawed <span class="term">kses_hook()</span>&#160;in case of <span class="term">WordPress</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; function kses_hook($string, &amp;$cf, &amp;$spec){</code>
-<br />
-
-<code class="code">&#160; &#160; // kses compatibility</code>
-<br />
-
-<code class="code">&#160; &#160; $allowed_html = $spec;</code>
-<br />
-
-<code class="code">&#160; &#160; $allowed_protocols = array();</code>
-<br />
-
-<code class="code">&#160; &#160; foreach($cf[&#39;schemes&#39;] as $v){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;foreach($v as $k2=&gt;$v2){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; if(!in_array($k2, $allowed_protocols)){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;$allowed_protocols[] = $k2;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;}</code>
-<br />
-
-<code class="code">&#160; &#160; }</code>
-<br />
-
-<code class="code">&#160; &#160; return wp_kses_hook($string, $allowed_html, $allowed_protocols);</code>
-<br />
-
-<code class="code">&#160; &#160; // eof</code>
-<br />
-
-<code class="code">&#160; &#160; }</code>
-<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.7" id="s2.7"></a><span class="item-no">2.7</span>&#160; Tolerance for ill-written HTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be <em>read</em>&#160;as HTML, and be considered mere plain text instead. Following statements indicate the degree of <em>looseness</em>&#160;that htmLawed can work with, and can be provided in instructions to writers:<br />
-<br />
-&#160; * &#160;Tags must be flanked by <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;with no <span class="term">&gt;</span>&#160;inside -- any needed <span class="term">&gt;</span>&#160;should be put in as <span class="term">&amp;gt;</span>. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and <span class="term">&gt;</span>, like <span class="term">&lt;div &gt;</span>&#160;and <span class="term">&lt;img / &gt;</span>, but not after the <span class="term">&lt;</span>.<br />
-<br />
-&#160; * &#160;Element and attribute names need not be lower-cased.<br />
-<br />
-&#160; * &#160;Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.<br />
-<br />
-&#160; * &#160;Attribute values may not be double-quoted, or may be single-quoted.<br />
-<br />
-&#160; * &#160;Left-padding of numeric entities (like, <span class="term">&amp;#0160;</span>, <span class="term">&amp;x07ff;</span>) with <span class="term">0</span>&#160;is okay as long as the number of characters between between the <span class="term">&amp;</span>&#160;and the <span class="term">;</span>&#160;does not exceed 8. All entities must end with <span class="term">;</span>&#160;though.<br />
-<br />
-&#160; * &#160;Named character entities must be properly cased. E.g., <span class="term">&amp;Lt;</span>&#160;or <span class="term">&amp;TILDE;</span>&#160;will not be let through without modification.<br />
-<br />
-&#160; * &#160;HTML comments should not be inside element tags (okay between tags), and should begin with <span class="term">&lt;!--</span>&#160;and end with <span class="term">--&gt;</span>. Characters like <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">--&gt;</span>&#160;inside should be put in as <span class="term">--&amp;gt;</span>. Any <span class="term">--</span>&#160;inside will be automatically converted to <span class="term">-</span>, and a space will be added before the comment delimiter <span class="term">--&gt;</span>.<br />
-<br />
-&#160; * &#160;<span class="term">CDATA</span>&#160;sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with <span class="term">&lt;[CDATA[</span>&#160;and end with <span class="term">]]&gt;</span>. Characters like <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">]]&gt;</span>&#160;inside should be put in as <span class="term">]]&amp;gt;</span>.<br />
-<br />
-&#160; * &#160;For attribute values, character entities <span class="term">&amp;lt;</span>, <span class="term">&amp;gt;</span>&#160;and <span class="term">&amp;amp;</span>&#160;should be used instead of characters <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;(when <span class="term">&amp;</span>&#160;is not part of a character entity). This applies even for Javascript code in values of attributes like <span class="term">onclick</span>.<br />
-<br />
-&#160; * &#160;Characters <span class="term">&lt;</span>, <span class="term">&gt;</span>, <span class="term">&amp;</span>&#160;and <span class="term">"</span>&#160;that are part of actual Javascript, etc., code in <span class="term">script</span>&#160;elements should be used as such and not be put in as entities like <span class="term">&amp;gt;</span>. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside <span class="term">CDATA</span>&#160;sections.<br />
-<br />
-&#160; * &#160;Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on <span class="term">$config["keep_bad"]</span>, some code/text may be lost.<br />
-<br />
-&#160; * &#160;Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.<br />
-<br />
-&#160; * &#160;With <span class="term">$config["unique_ids"]</span>&#160;not <span class="term">0</span>&#160;and the <span class="term">id</span>&#160;attribute being permitted, writers should carefully avoid using duplicate or invalid <span class="term">id</span>&#160;values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when <span class="term">&lt;a id="home"&gt;&lt;/a&gt;&lt;input id="home" /&gt;&lt;label for="home"&gt;&lt;/label&gt;</span>&#160;is processed into<br />
-<span class="term">&lt;a id="home"&gt;&lt;/a&gt;&lt;input id="prefix_home" /&gt;&lt;label for="home"&gt;&lt;/label&gt;</span>.<br />
-<br />
-&#160; * &#160;Note that even if intended HTML is lost in a highly ill-written input, the processed output will be more secure and standard-compliant.<br />
-<br />
-&#160; * &#160;For URLs, unless <span class="term">$config["scheme"]</span>&#160;is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., <span class="term">htt&amp;#112;</span>&#160;(which many browsers will read as the harmless <span class="term">http</span>) may be considered bad by htmLawed.<br />
-<br />
-&#160; * &#160;htmLawed will attempt to put plain text present directly inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span>&#160;elements (illegal as per the specs) inside auto-generated <span class="term">div</span>&#160;elements.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.8" id="s2.8"></a><span class="item-no">2.8</span>&#160; Limitations &amp; work-arounds
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed's main objective is to make the input text <em>more</em>&#160;standard-compliant, secure for web-page readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.<br />
-<br />
-&#160; It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specs (like asking for normalization of white-spacing within <span class="term">textarea</span>&#160;elements) are clearly wrong. Regarding security, note that <em>unsafe</em>&#160;HTML code is not necessarily legally invalid.<br />
-<br />
-&#160; * &#160;htmLawed is meant for input that goes into the <span class="term">body</span>&#160;of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements <span class="term">frameset</span>, <span class="term">frame</span>&#160;and <span class="term">noframes</span>.<br />
-<br />
-&#160; * &#160;It cannot transform the non-standard <span class="term">embed</span>&#160;elements to the standard-compliant <span class="term">object</span>&#160;elements. Yet, it can allow <span class="term">embed</span>&#160;elements if permitted (<span class="term">embed</span>&#160;is widely used and supported). Admins can certainly use the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) to deploy a custom embed-to-object converter function.<br />
-<br />
-&#160; * &#160;The only non-standard element that may be permitted is <span class="term">embed</span>; others like <span class="term">noembed</span>&#160;and <span class="term">nobr</span>&#160;cannot be permitted without modifying the htmLawed code.<br />
-<br />
-&#160; * &#160;It cannot handle input that has non-HTML code like <span class="term">SVG</span>&#160;and <span class="term">MathML</span>. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in <a href="#s3.9">section 3.9</a>. A third way may be to some how take advantage of the <span class="term">$config["and_mark"]</span>&#160;parameter (see <a href="#s3.2">section 3.2</a>).<br />
-<br />
-&#160; * &#160;By default, htmLawed won't check many attribute values for standard compliance. E.g., <span class="term">width="20m"</span>&#160;with the dimension in non-standard <span class="term">m</span>&#160;is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span>&#160;to enforce finer checks.<br />
-<br />
-&#160; * &#160;The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specs. Only a few of the proprietary attributes are supported.<br />
-<br />
-&#160; * &#160;Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span>&#160;for finer checks. Perhaps the best option is to disallow <span class="term">style</span>&#160;but allow <span class="term">class</span>&#160;attributes with the right <span class="term">oneof</span>&#160;or <span class="term">match</span>&#160;values for <span class="term">class</span>, and have the various class style properties in <span class="term">.css</span>&#160;CSS stylesheet files.<br />
-<br />
-&#160; * &#160;htmLawed does not parse emoticons, decode <em>BBcode</em>, or <em>wikify</em>, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to <span class="term">br</span>&#160;elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.<br />
-<br />
-&#160; * &#160;htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate <span class="term">target</span>&#160;and <span class="term">onclick</span>&#160;attributes to <span class="term">a</span>). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;Nesting-based checks are not possible. E.g., one cannot disallow <span class="term">p</span>&#160;elements specifically inside <span class="term">td</span>&#160;while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert <span class="term">http</span>&#160;to <span class="term">https</span>. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;Pairs of opening and closing tags that do not enclose any content (like <span class="term">&lt;em&gt;&lt;/em&gt;</span>) are not removed. This may be against the standard specs for certain elements (e.g., <span class="term">table</span>). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.<br />
-<br />
-&#160; * &#160;htmLawed does not check for certain element orderings described in the standard specs (e.g., in a <span class="term">table</span>, <span class="term">tbody</span>&#160;is allowed before <span class="term">tfoot</span>). Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;htmLawed does not check the number of nested elements. E.g., it will allow two <span class="term">caption</span>&#160;elements in a <span class="term">table</span>&#160;element, illegal as per the specs. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; * &#160;htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers (<span class="term">/&#42;</span>) in <span class="term">style</span>&#160;attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like <span class="term">&amp;#101;xpression...</span>. If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) to more specifically identify CSS expressions in the <span class="term">style</span>&#160;attribute values. Also, using <span class="term">$config["style_pass"]</span>, it is possible to have htmLawed pass <span class="term">style</span>&#160;attribute values without even looking at them (<a href="#s3.4.8">section 3.4.8</a>).<br />
-<br />
-&#160; * &#160;htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., <span class="term">&lt;a href="http&#58;//x%22+style=%22background-image&#58;xss"&gt;x&lt;/a&gt;</span>). These arise when browsers mis-identify markup in <em>escaped</em>&#160;text, defeating the very purpose of escaping text (a bad browser will read the given example as <span class="term">&lt;a href="http&#58;//x" style="background-image&#58;xss"&gt;x&lt;/a&gt;</span>).<br />
-<br />
-&#160; * &#160;Because of poor Unicode support in PHP, htmLawed does not remove the <em>high value</em>&#160;HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see <a href="#s3.1">section 3.1</a>).<br />
-<br />
-&#160; * &#160;htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML <span class="term">meta</span>&#160;tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).<br />
-<br />
-&#160; * &#160;Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s2.9" id="s2.9"></a><span class="item-no">2.9</span>&#160; Examples of usage
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Safest, allowing only <em>safe</em>&#160;HTML markup --<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;safe&#39;=&gt;1);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in);</code>
-<br />
-<br />
-&#160; Simplest, allowing all valid HTML markup except <span class="term">javascript&#58;</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in);</code>
-<br />
-<br />
-&#160; Allowing all valid HTML markup including <span class="term">javascript&#58;</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;schemes&#39;=&gt;&#39;&#42;&#58;&#42;&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Allowing only <span class="term">safe</span>&#160;HTML and the elements <span class="term">a</span>, <span class="term">em</span>, and <span class="term">strong</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;safe&#39;=&gt;1, &#39;elements&#39;=&gt;&#39;a, em, strong&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Not allowing elements <span class="term">script</span>&#160;and <span class="term">object</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;elements&#39;=&gt;&#39;&#42; -script -object&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Not allowing attributes <span class="term">id</span>&#160;and <span class="term">style</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;id, style&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Permitting only attributes <span class="term">title</span>&#160;and <span class="term">href</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;&#42; -title -href&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Remove bad/disallowed tags altogether instead of converting them to entities --<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;keep_bad&#39;=&gt;0);</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
-<br />
-<br />
-&#160; Allowing attribute <span class="term">title</span>&#160;only in <span class="term">a</span>&#160;and not allowing attributes <span class="term">id</span>, <span class="term">style</span>, or scriptable <em>on*</em>&#160;attributes like <span class="term">onclick</span>&#160;--<br />
-<br />
-
-<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;title, id, style, on&#42;&#39;);</code>
-<br />
-
-<code class="code">&#160; &#160; $spec = &#39;a=title&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; $out = htmLawed($in, $config, $spec);</code>
-<br />
-<br />
-&#160; Some case-studies are presented below.<br />
-<br />
-&#160; <strong>1.</strong>&#160;A blog administrator wants to allow only <span class="term">a</span>, <span class="term">em</span>, <span class="term">strike</span>, <span class="term">strong</span>&#160;and <span class="term">u</span>&#160;in comments, but needs <span class="term">strike</span>&#160;and <span class="term">u</span>&#160;transformed to <span class="term">span</span>&#160;for better XHTML 1-strict compliance, and, he wants the <span class="term">a</span>&#160;links to be to <span class="term">http</span>&#160;or <span class="term">https</span>&#160;resources:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;elements&#39;=&gt;&#39;a, em, strike, strong, u&#39;, &#39;make_tag_strict&#39;=&gt;1, &#39;safe&#39;=&gt;1, &#39;schemes&#39;=&gt;&#39;&#42;&#58;http, https&#39;), &#39;a=href&#39;);</code>
-<br />
-<br />
-&#160; <strong>2.</strong>&#160;An author uses a custom-made web application to load content on his web-site. He is the only one using that application and the content he generates has all types of HTML, including scripts. The web application uses htmLawed primarily as a tool to correct errors that creep in while writing HTML and to take care of the occasional <em>bad</em>&#160;characters in copy-paste text introduced by Microsoft Office. The web application provides a preview before submitted input is added to the content. For the previewing process, htmLawed is set up as follows:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;css_expression&#39;=&gt;1, &#39;keep_bad&#39;=&gt;1, &#39;make_tag_strict&#39;=&gt;1, &#39;schemes&#39;=&gt;&#39;&#42;&#58;&#42;&#39;, &#39;valid_xhtml&#39;=&gt;1));</code>
-<br />
-<br />
-&#160; For the final submission process, <span class="term">keep_bad</span>&#160;is set to <span class="term">6</span>. A value of <span class="term">1</span>&#160;for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.<br />
-<br />
-&#160; <strong>3.</strong>&#160;A data-miner is scraping information in a specific table of similar web-pages and is collating the data rows, and uses htmLawed to reduce unnecessary markup and white-spaces:<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;elements&#39;=&gt;&#39;tr, td&#39;, &#39;tidy&#39;=&gt;-1), &#39;tr, td =&#39;);</code>
-<br />
-
-</div>
-</div>
-<div class="section"><h2>
-<a name="s3" id="s3"></a><span class="item-no">3</span>&#160; Details
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<div class="sub-section"><h3>
-<a name="s3.1" id="s3.1"></a><span class="item-no">3.1</span>&#160; Invalid/dangerous characters
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Valid characters (more correctly, their code-points) in HTML or XML are, hexadecimally, <span class="term">9</span>, <span class="term">a</span>, <span class="term">d</span>, <span class="term">20</span>&#160;to <span class="term">d7ff</span>, and <span class="term">e000</span>&#160;to <span class="term">10ffff</span>, except <span class="term">fffe</span>&#160;and <span class="term">ffff</span>&#160;(decimally, <span class="term">9</span>, <span class="term">10</span>, <span class="term">13</span>, <span class="term">32</span>&#160;to <span class="term">55295</span>, and <span class="term">57344</span>&#160;to <span class="term">1114111</span>, except <span class="term">65534</span>&#160;and <span class="term">65535</span>). htmLawed removes the invalid characters <span class="term">0</span>&#160;to <span class="term">8</span>, <span class="term">b</span>, <span class="term">c</span>, and <span class="term">e</span>&#160;to <span class="term">1f</span>.<br />
-<br />
-&#160; Because of PHP's poor native support for multi-byte characters, htmLawed cannot check for the remaining invalid code-points. However, for various reasons, it is very unlikely for any of those characters to be in the input.<br />
-<br />
-&#160; Characters that are discouraged (see <a href="#s5.1">section 5.1</a>) but not invalid are not removed by htmLawed.<br />
-<br />
-&#160; It (function <span class="term">hl_tag()</span>) also replaces the potentially dangerous (in some Mozilla [Firefox] and Opera browsers) soft-hyphen character (code-point, hexadecimally, <span class="term">ad</span>, or decimally, <span class="term">173</span>) in attribute values with spaces. Where required, the characters <span class="term">&lt;</span>, <span class="term">&gt;</span>, <span class="term">&amp;</span>, and <span class="term">"</span>&#160;are converted to entities.<br />
-<br />
-&#160; With <span class="term">$config["clean_ms_char"]</span>&#160;set as <span class="term">1</span>&#160;or <span class="term">2</span>, many of the discouraged characters (decimal code-points <span class="term">127</span>&#160;to <span class="term">159</span>&#160;except <span class="term">133</span>) that many Microsoft applications incorrectly use (as per the <span class="term">Windows 1252</span>&#160;[<span class="term">Cp-1252</span>] or a similar encoding system), and the character for decimal code-point <span class="term">133</span>, are converted to appropriate decimal numerical entities (or removed for a few cases)-- see appendix in <a href="#s5.4">section 5.4</a>. This can help avoid some display issues arising from copying-pasting of content.<br />
-<br />
-&#160; With <span class="term">$config["clean_ms_char"]</span>&#160;set as <span class="term">2</span>, characters for the hexadecimal code-points <span class="term">82</span>, <span class="term">91</span>, and <span class="term">92</span>&#160;(for special single-quotes), and <span class="term">84</span>, <span class="term">93</span>, and <span class="term">94</span>&#160;(for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.<br />
-<br />
-&#160; The character values are replaced with entities/characters and not character values referred to by the entities/characters to keep this task independent of the character-encoding of input text.<br />
-<br />
-&#160; The <span class="term">$config["clean_ms_char"]</span>&#160;parameter should not be used if authors do not copy-paste Microsoft-created text, or if the input text is not believed to use the <span class="term">Windows 1252</span>&#160;(<span class="term">Cp-1252</span>) or a similar encoding like <span class="term">Cp-1251</span>. Further, the input form and the web-pages displaying it or its content should have the character encoding appropriately marked-up.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.2" id="s3.2"></a><span class="item-no">3.2</span>&#160; Character references/entities
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Valid character entities take the form <span class="term">&amp;&#42;;</span>&#160;where <span class="term">&#42;</span>&#160;is <span class="term">#x</span>&#160;followed by a hexadecimal number (hexadecimal numeric entity; like <span class="term">&amp;#xA0;</span>&#160;for non-breaking space), or alphanumeric like <span class="term">gt</span>&#160;(external or named entity; like <span class="term">&amp;nbsp;</span>&#160;for non-breaking space), or <span class="term">#</span>&#160;followed by a number (decimal numeric entity; like <span class="term">&amp;#160;</span>&#160;for non-breaking space). Character entities referring to the soft-hyphen character (the <span class="term">&amp;shy;</span>&#160;or <span class="term">\xad</span>&#160;character; hexadecimal code-point <span class="term">ad</span>&#160;[decimal <span class="term">173</span>]) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.<br />
-<br />
-&#160; htmLawed (function <span class="term">hl_ent()</span>):<br />
-<br />
-&#160; * &#160;Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)<br />
-<br />
-&#160; * &#160;Lowercases the <span class="term">X</span>&#160;(for XML-compliance) and <span class="term">A-F</span>&#160;of hexadecimal numeric entities<br />
-<br />
-&#160; * &#160;Neutralizes entities referring to characters that are HTML-invalid (see <a href="#s3.1">section 3.1</a>)<br />
-<br />
-&#160; * &#160;Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, <span class="term">7f</span>&#160;to <span class="term">84</span>, <span class="term">86</span>&#160;to <span class="term">9f</span>, and <span class="term">fdd0</span>&#160;to <span class="term">fddf</span>, or decimally, <span class="term">127</span>&#160;to <span class="term">132</span>, <span class="term">134</span>&#160;to <span class="term">159</span>, and <span class="term">64991</span>&#160;to <span class="term">64976</span>). Entities referring to the remaining discouraged characters (see <a href="#s5.1">section 5.1</a>&#160;for a full list) are let through.<br />
-<br />
-&#160; * &#160;Neutralizes named entities that are not in the specs.<br />
-<br />
-&#160; * &#160;Optionally converts valid HTML-specific named entities except <span class="term">&amp;gt;</span>, <span class="term">&amp;lt;</span>, <span class="term">&amp;quot;</span>, and <span class="term">&amp;amp;</span>&#160;to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is <span class="term">2</span>) for generic XML-compliance. For this, <span class="term">$config["named_entity"]</span>&#160;should be <span class="term">1</span>.<br />
-<br />
-&#160; * &#160;Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, <span class="term">$config["hexdec_entity"]</span>&#160;should be <span class="term">0</span>.<br />
-<br />
-&#160; * &#160;Optionally converts decimal numeric entities to the hexadecimal ones. For this, <span class="term">$config["hexdec_entity"]</span>&#160;should be <span class="term">2</span>.<br />
-<br />
-&#160; <em>Neutralization</em>&#160;refers to the <em>entitification</em>&#160;of <span class="term">&amp;</span>&#160;to <span class="term">&amp;amp;</span>.<br />
-<br />
-&#160; <strong>Note</strong>: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's <span class="term">html_entity_decode</span>&#160;<a href="http://www.php.net/html_entity_decode">function</a>&#160;for that.<br />
-<br />
-&#160; <strong>Note</strong>: If <span class="term">$config["and_mark"]</span>&#160;is set, and set to a value other than <span class="term">0</span>, then the <span class="term">&amp;</span>&#160;characters in the original input are replaced with the control character for the hexadecimal code-point <span class="term">6</span>&#160;(<span class="term">\x06</span>; <span class="term">&amp;</span>&#160;characters introduced by htmLawed, e.g., after converting <span class="term">&lt;</span>&#160;to <span class="term">&amp;lt;</span>, are not affected). This allows one to distinguish, say, an <span class="term">&amp;gt;</span>&#160;introduced by htmLawed and an <span class="term">&amp;gt;</span>&#160;put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence <span class="term">o(&gt;&lt;)o</span>&#160;to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the <span class="term">\x06</span>&#160;can break documents. Before use in such documents, and preferably before any storage, any remaining <span class="term">\x06</span>&#160;should be changed back to <span class="term">&amp;</span>, e.g., with:<br />
-<br />
-
-<code class="code">&#160; &#160; $final = str_replace("\x06", &#39;&amp;&#39;, $prelim);</code>
-<br />
-<br />
-&#160; Also, see <a href="#s3.9">section 3.9</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.3" id="s3.3"></a><span class="item-no">3.3</span>&#160; HTML elements
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on <span class="term">$config["keep_bad"]</span>, are either <em>neutralized</em>&#160;(converted to plain text by entitification of <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>) or removed.<br />
-<br />
-&#160; E.g., with only <span class="term">em</span>&#160;permitted:<br />
-<br />
-&#160; Input:<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is &lt;a href="http&#58;//a.com&gt;a.com&lt;/a&gt;.</code>
-<br />
-<br />
-&#160; Output, with <span class="term">$config["keep_bad"] = 0</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is a.com.</code>
-<br />
-<br />
-&#160; Output, with <span class="term">$config["keep_bad"]</span>&#160;not <span class="term">0</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is &amp;lt;a href=""&amp;gt;a.com&amp;lt;/a&amp;gt;.</code>
-<br />
-<br />
-&#160; See <a href="#s3.3.3">section 3.3.3</a>&#160;for differences between the various non-zero <span class="term">$config["keep_bad"]</span>&#160;values.<br />
-<br />
-&#160; htmLawed by default permits these 86 elements:<br />
-<br />
-
-<code class="code">&#160; &#160; a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var</code>
-<br />
-<br />
-&#160; Except for <span class="term">embed</span>&#160;(included because of its wide-spread use) and the Ruby elements (<span class="term">rb</span>, <span class="term">rbc</span>, <span class="term">rp</span>, <span class="term">rt</span>, <span class="term">rtc</span>, <span class="term">ruby</span>; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude <span class="term">center</span>, <span class="term">dir</span>, <span class="term">font</span>, <span class="term">isindex</span>, <span class="term">menu</span>, <span class="term">s</span>, <span class="term">strike</span>, and <span class="term">u</span>.<br />
-<br />
-&#160; With <span class="term">$config["safe"] = 1</span>, the default set will exclude <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>, <span class="term">object</span>&#160;and <span class="term">script</span>; see <a href="#s3.6">section 3.6</a>.<br />
-<br />
-&#160; When <span class="term">$config["elements"]</span>, which specifies allowed elements, is <em>properly</em>&#160;defined, and neither empty nor set to <span class="term">0</span>&#160;or <span class="term">&#42;</span>, the default set is not used. To have elements added to or removed from the default set, a <span class="term">+/-</span>&#160;notation is used. E.g., <span class="term">&#42;-script-object</span>&#160;implies that only <span class="term">script</span>&#160;and <span class="term">object</span>&#160;are disallowed, whereas <span class="term">&#42;+embed</span>&#160;means that <span class="term">noembed</span>&#160;is also allowed. Elements can also be specified as comma separated names. E.g., <span class="term">a, b, i</span>&#160;means only <span class="term">a</span>, <span class="term">b</span>&#160;and <span class="term">i</span>&#160;are permitted. In this notation, <span class="term">&#42;</span>, <span class="term">+</span>&#160;and <span class="term">-</span>&#160;have no significance and can actually cause a mis-reading.<br />
-<br />
-&#160; Some more examples of <span class="term">$config["elements"]</span>&#160;values indicating permitted elements (note that empty spaces are liberally allowed for clarity):<br />
-<br />
-&#160; * &#160;<span class="term">a, blockquote, code, em, strong</span>&#160;-- only <span class="term">a</span>, <span class="term">blockquote</span>, <span class="term">code</span>, <span class="term">em</span>, and <span class="term">strong</span><br />
-&#160; * &#160;<span class="term">&#42;-script</span>&#160;-- all excluding <span class="term">script</span><br />
-&#160; * &#160;<span class="term">&#42; -center -dir -font -isindex -menu -s -strike -u</span>&#160;-- only XHTML-Strict elements<br />
-&#160; * &#160;<span class="term">&#42;+noembed-script</span>&#160;-- all including <span class="term">noembed</span>&#160;excluding <span class="term">script</span><br />
-<br />
-&#160; Some mis-usages (and the resulting permitted elements) that can be avoided:<br />
-<br />
-&#160; * &#160;<span class="term">-&#42;</span>&#160;-- none; instead of htmLawed, one might just use, e.g., the <span class="term">htmlspecialchars()</span>&#160;PHP function<br />
-&#160; * &#160;<span class="term">&#42;, -script</span>&#160;-- all except <span class="term">script</span>; admin probably meant <span class="term">&#42;-script</span><br />
-&#160; * &#160;<span class="term">-&#42;, a, em, strong</span>&#160;-- all; admin probably meant <span class="term">a, em, strong</span><br />
-&#160; * &#160;<span class="term">&#42;</span>&#160;-- all; admin need not have set <span class="term">elements</span><br />
-&#160; * &#160;<span class="term">&#42;-form+form</span>&#160;-- all; a <span class="term">+</span>&#160;will always over-ride any <span class="term">-</span><br />
-&#160; * &#160;<span class="term">&#42;, noembed</span>&#160;-- only <span class="term">noembed</span>; admin probably meant <span class="term">&#42;+noembed</span><br />
-&#160; * &#160;<span class="term">a, +b, i</span>&#160;-- only <span class="term">a</span>&#160;and <span class="term">i</span>; admin probably meant <span class="term">a, b, i</span><br />
-<br />
-&#160; Basically, when using the <span class="term">+/-</span>&#160;notation, commas (<span class="term">,</span>) should not be used, and vice versa, and <span class="term">&#42;</span>&#160;should be used with the former but not the latter.<br />
-<br />
-&#160; <strong>Note</strong>: Even if an element that is not in the default set is allowed through <span class="term">$config["elements"]</span>, like <span class="term">noembed</span>&#160;in the last example, it will eventually be removed during tag balancing unless such balancing is turned off (<span class="term">$config["balance"]</span>&#160;set to <span class="term">0</span>). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function <span class="term">hl_bal()</span>&#160;to accommodate the element and its nesting properties.<br />
-<br />
-&#160; <strong>A possibly second way to specify allowed elements</strong>&#160;is to set <span class="term">$config["parent"]</span>&#160;to an element name that supposedly will hold the input, and to set <span class="term">$config["balance"]</span>&#160;to <span class="term">1</span>. During tag balancing (see <a href="#s3.3.3">section 3.3.3</a>), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to <span class="term">div</span>&#160;if <span class="term">$config["parent"]</span>&#160;is empty, <span class="term">body</span>, or an element not in htmLawed's default set of 86 elements.<br />
-<br />
-&#160; <em>Tag transformation</em>&#160;is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see <a href="#s3.3.2">section 3.3.2</a>.<br />
-
-<div class="sub-sub-section"><h4>
-<a name="s3.3.1" id="s3.3.1"></a><span class="item-no">3.3.1</span>&#160; Handling of comments and CDATA sections
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; <span class="term">CDATA</span>&#160;sections have the format <span class="term">&lt;![CDATA[...anything but not "]]&gt;"...]]&gt;</span>, and HTML comments, <span class="term">&lt;!--...anything but not "--&gt;"... --&gt;</span>. Neither HTML comments nor <span class="term">CDATA</span>&#160;sections can reside inside tags. HTML comments can exist anywhere else, but <span class="term">CDATA</span>&#160;sections can exist only where plain text is allowed (e.g., immediately inside <span class="term">td</span>&#160;element content but not immediately inside <span class="term">tr</span>&#160;element content).<br />
-<br />
-&#160; htmLawed (function <span class="term">hl_cmtcd()</span>) handles HTML comments or <span class="term">CDATA</span>&#160;sections depending on the values of <span class="term">$config["comment"]</span>&#160;or <span class="term">$config["cdata"]</span>. If <span class="term">0</span>, such markup is not looked for and the text is processed like plain text. If <span class="term">1</span>, it is removed completely. If <span class="term">2</span>, it is preserved but any <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;inside are changed to entities. If <span class="term">3</span>, they are left as such.<br />
-<br />
-&#160; Note that for the last two cases, HTML comments and <span class="term">CDATA</span>&#160;sections will always be removed from tag content (function <span class="term">hl_tag()</span>).<br />
-<br />
-&#160; Examples:<br />
-<br />
-&#160; Input:<br />
-
-<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 0, $config["cdata"] = 2</span>):<br />
-
-<code class="code">&#160; &#160; &amp;lt;-- home link --&amp;gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 1, $config["cdata"] = 2</span>):<br />
-
-<code class="code">&#160; &#160; &lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 2</span>):<br />
-
-<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 1</span>):<br />
-
-<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;Home&lt;/a&gt;</code>
-<br />
-&#160; Output (<span class="term">$config["comment"] = 3, $config["cdata"] = 3</span>):<br />
-
-<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;y]]&gt;Home&lt;/a&gt;</code>
-<br />
-<br />
-&#160; For standard-compliance, comments are given the form <span class="term">&lt;!--comment --&gt;</span>, and any <span class="term">--</span>&#160;in the content is made <span class="term">-</span>.<br />
-<br />
-&#160; When <span class="term">$config["safe"] = 1</span>, CDATA sections and comments are considered plain text unless <span class="term">$config["comment"]</span>&#160;or <span class="term">$config["cdata"]</span>&#160;is explicitly specified; see <a href="#s3.6">section 3.6</a>.<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.3.2" id="s3.3.2"></a><span class="item-no">3.3.2</span>&#160; Tag-transformation for better XHTML-Strict
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["make_tag_strict"]</span>&#160;is set and not <span class="term">0</span>, following non-XHTML-Strict elements (and attributes), even if admin-permitted, are mutated as indicated (element content remains intact; function <span class="term">hl_tag2()</span>):<br />
-<br />
-&#160; * &#160;applet - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
-&#160; * &#160;center - <span class="term">div style="text-align&#58; center;"</span><br />
-&#160; * &#160;dir - <span class="term">ul</span><br />
-&#160; * &#160;embed - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
-&#160; * &#160;font (face, size, color) - &#160; &#160;<span class="term">span style="font-family&#58; ; font-size&#58; ; color&#58; ;"</span>&#160;(size transformation <a href="http://style.cleverchimp.com/font_size_intervals/altintervals.html">reference</a>)<br />
-&#160; * &#160;isindex - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
-&#160; * &#160;menu - <span class="term">ul</span><br />
-&#160; * &#160;s - <span class="term">span style="text-decoration&#58; line-through;"</span><br />
-&#160; * &#160;strike - <span class="term">span style="text-decoration&#58; line-through;"</span><br />
-&#160; * &#160;u - <span class="term">span style="text-decoration&#58; underline;"</span><br />
-<br />
-&#160; For an element with a pre-existing <span class="term">style</span>&#160;attribute value, the extra style properties are appended.<br />
-<br />
-&#160; Example input:<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;center&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;The PHP &lt;s&gt;software&lt;/s&gt; script used for this &lt;strike&gt;web-page&lt;/strike&gt; web-page is &lt;font style="font-weight&#58; bold " face=arial size=&#39;+3&#39; color &#160; = &#160;"red &#160;"&gt;htmLawedTest.php&lt;/font&gt;, from &lt;u style= &#39;color&#58;green&#39;&gt;PHP Labware&lt;/u&gt;.</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/center&gt;</code>
-<br />
-<br />
-&#160; The output:<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;div style="text-align&#58; center;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;The PHP &lt;span style="text-decoration&#58; line-through;"&gt;software&lt;/span&gt; script used for this &lt;span style="text-decoration&#58; line-through;"&gt;web-page&lt;/span&gt; web-page is &lt;span style="font-weight&#58; bold; font-family&#58; arial; color&#58; red; font-size&#58; 200%;"&gt;htmLawedTest.php&lt;/span&gt;, from &lt;span style="color&#58;green; text-decoration&#58; underline;"&gt;PHP Labware&lt;/span&gt;.</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/div&gt;</code>
-<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.3.3" id="s3.3.3"></a><span class="item-no">3.3.3</span>&#160; Tag balancing and proper nesting
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["balance"]</span>&#160;is set to <span class="term">1</span>, htmLawed (function <span class="term">hl_bal()</span>) checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).<br />
-<br />
-&#160; Depending on the value of <span class="term">$config["keep_bad"]</span>&#160;(see <a href="#s2.2">section 2.2</a>&#160;and <a href="#s3.3">section 3.3</a>), illegal content may be removed or neutralized to plain text by converting &lt; and &gt; to entities:<br />
-<br />
-&#160; <span class="term">0</span>&#160;- remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see <a href="#s2.6">section 2.6</a>)<br />
-&#160; <span class="term">1</span>&#160;- neutralize tags and keep element content<br />
-&#160; <span class="term">2</span>&#160;- remove tags but keep element content<br />
-&#160; <span class="term">3</span>&#160;and <span class="term">4</span>&#160;- like <span class="term">1</span>&#160;and <span class="term">2</span>, but keep element content only if text (<span class="term">pcdata</span>) is valid in parent element as per specs<br />
-&#160; <span class="term">5</span>&#160;and <span class="term">6</span>&#160;- &#160;like <span class="term">3</span>&#160;and <span class="term">4</span>, but line-breaks, tabs and spaces are left<br />
-<br />
-&#160; Example input (disallowing the <span class="term">p</span>&#160;element):<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;&#42;&gt; Pseudo-tags &lt;&#42;&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;p&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; Disallowed tag p</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/p&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;ul&gt;Bad&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
-<br />
-<br />
-&#160; The output with <span class="term">$config["keep_bad"] = 1</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;xml&amp;gt;Non-HTML tag xml&amp;lt;/xml&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;p&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; Disallowed tag p</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;/p&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;ul&gt;Bad&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
-<br />
-<br />
-&#160; The output with <span class="term">$config["keep_bad"] = 3</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;xml&amp;gt;Non-HTML tag xml&amp;lt;/xml&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;p&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; Disallowed tag p</code>
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;/p&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;ul&gt;&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
-<br />
-<br />
-&#160; The output with <span class="term">$config["keep_bad"] = 6</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
-<br />
-
-<code class="code">&#160; &#160; Non-HTML tag xml</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; Disallowed tag p</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;ul&gt;&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
-<br />
-<br />
-&#160; An option like <span class="term">1</span>&#160;is useful, e.g., when a writer previews his submission, whereas one like <span class="term">3</span>&#160;is useful before content is finalized and made available to all.<br />
-<br />
-&#160; <strong>Note:</strong>&#160;In the example above, unlike <span class="term">&lt;&#42;&gt;</span>, <span class="term">&lt;xml&gt;</span>&#160;gets considered as a tag (even though there is no HTML element named <span class="term">xml</span>). In general, text matching the regular expression pattern <span class="term">&lt;(/?)([a-zA-Z][a-zA-Z1-6]&#42;)([^&gt;]&#42;?)\s?&gt;</span>&#160;is considered a tag (phrase enclosed by the angled brackets <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...).<br />
-<br />
-&#160; Nesting/content rules for each of the 86 elements in htmLawed's default set (see <a href="#s3.3">section 3.3</a>) are defined in function <span class="term">hl_bal()</span>. This means that if a non-standard element besides <span class="term">embed</span>&#160;is being permitted through <span class="term">$config["elements"]</span>, the element's tag content will end up getting removed if <span class="term">$config["balance"]</span>&#160;is set to <span class="term">1</span>.<br />
-<br />
-&#160; Plain text and/or certain elements nested inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span>&#160;need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as <span class="term">form</span>, the input <span class="term">B&#58;&lt;input type="text" value="b" /&gt;C&#58;&lt;input type="text" value="c" /&gt;</span>&#160;is converted to <span class="term">&lt;div&gt;B&#58;&lt;input type="text" value="b" /&gt;C&#58;&lt;input type="text" value="c" /&gt;&lt;/div&gt;</span>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.3.4" id="s3.3.4"></a><span class="item-no">3.3.4</span>&#160; Elements requiring child elements
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; As per specs, the following elements require legal child elements nested inside them:<br />
-<br />
-
-<code class="code">&#160; &#160; blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul</code>
-<br />
-<br />
-&#160; In some cases, the specs stipulate the number and/or the ordering of the child elements. A <span class="term">table</span>&#160;can have 0 or 1 <span class="term">caption</span>, <span class="term">tbody</span>, <span class="term">tfoot</span>, and <span class="term">thead</span>, but they must be in this order: <span class="term">caption</span>, <span class="term">thead</span>, <span class="term">tfoot</span>, <span class="term">tbody</span>.<br />
-<br />
-&#160; htmLawed currently does not check for conformance to these rules. Note that any non-compliance in this regard will not introduce security vulnerabilities, crash browser applications, or affect the rendering of web-pages.<br />
-<br />
-&#160; With <span class="term">$config["direct_list_nest"]</span>&#160;set to <span class="term">1</span>, htmLawed will allow direct nesting of an <span class="term">ol</span>&#160;or <span class="term">ul</span>&#160;list within another <span class="term">ol</span>&#160;or <span class="term">ul</span>&#160;without requiring the child list to be within an <span class="term">li</span>&#160;of the parent list. While this is not standard-compliant, directly nested lists are rendered properly by almost all browsers. The parameter <span class="term">$config["direct_list_nest"]</span>&#160;has no effect if tag-balancing (<a href="#s3.3.3">section 3.3.3</a>) is turned off.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.3.5" id="s3.3.5"></a><span class="item-no">3.3.5</span>&#160; Beautify or compact HTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; By default, htmLawed will neither <em>beautify</em>&#160;HTML code by formatting it with indentations, etc., nor will it make it compact by removing un-needed white-space.(It does always properly white-space tag content.)<br />
-<br />
-&#160; As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside <span class="term">pre</span>&#160;elements) are all considered equivalent, and referred to as <em>white-spaces</em>. Browser applications are supposed to consider contiguous white-spaces as just a single space, and to disregard white-spaces trailing opening tags or preceding closing tags. This white-space <em>normalization</em>&#160;allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such <em>pretty</em>&#160;HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.<br />
-<br />
-&#160; With the <span class="term">$config</span>&#160;parameter <span class="term">tidy</span>, htmLawed can be used to beautify or compact the input text. Input with just plain text and no HTML markup is also subject to this. Besides <span class="term">pre</span>, the <span class="term">script</span>&#160;and <span class="term">textarea</span>&#160;elements, CDATA sections, and HTML comments are not subjected to the tidying process.<br />
-<br />
-&#160; To <em>compact</em>, use <span class="term">$config["tidy"] = -1</span>; single instances or runs of white-spaces are replaced with a single space, and white-spaces trailing and leading open and closing tags, respectively, are removed.<br />
-<br />
-&#160; To <em>beautify</em>, <span class="term">$config["tidy"]</span>&#160;is set as <span class="term">1</span>, or for customized tidying, as a string like <span class="term">2s2n</span>. The <span class="term">s</span>&#160;or <span class="term">t</span>&#160;character specifies the use of spaces or tabs for indentation. The first and third characters, any of the digits 0-9, specify the number of spaces or tabs per indentation, and any parental lead spacing (extra indenting of the whole block of input text). The <span class="term">r</span>&#160;and <span class="term">n</span>&#160;characters are used to specify line-break characters: <span class="term">n</span>&#160;for <span class="term">\n</span>&#160;(Unix/Mac OS X line-breaks), <span class="term">rn</span>&#160;or <span class="term">nr</span>&#160;for <span class="term">\r\n</span>&#160;(Windows/DOS line-breaks), or <span class="term">r</span>&#160;for <span class="term">\r</span>.<br />
-<br />
-&#160; The <span class="term">$config["tidy"]</span>&#160;value of <span class="term">1</span>&#160;is equivalent to <span class="term">2s0n</span>. Other <span class="term">$config["tidy"]</span>&#160;values are read loosely: a value of <span class="term">4</span>&#160;is equivalent to <span class="term">4s0n</span>; <span class="term">t2</span>, to <span class="term">1t2n</span>; <span class="term">s</span>, to <span class="term">2s0n</span>; <span class="term">2TR</span>, to <span class="term">2t0r</span>; <span class="term">T1</span>, to <span class="term">1t1n</span>; <span class="term">nr3</span>, to <span class="term">3s0nr</span>, and so on. Except in the indentations and line-spacings, runs of white-spaces are replaced with a single space during beautification.<br />
-<br />
-&#160; Input formatting using <span class="term">$config["tidy"]</span>&#160;is not recommended when input text has mixed markup (like HTML + PHP).<br />
-
-</div>
-</div>
-<div class="sub-section"><h3>
-<a name="s3.4" id="s3.4"></a><span class="item-no">3.4</span>&#160; Attributes
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed will only permit attributes described in the HTML specs (including deprecated ones). It also permits some attributes for use with the <span class="term">embed</span>&#160;element (the non-standard <span class="term">embed</span>&#160;element is supported in htmLawed because of its widespread use), and the the <span class="term">xml&#58;space</span>&#160;attribute (valid only in XHTML 1.1). A list of such 111 attributes and the elements they are allowed in is in <a href="#s5.2">section 5.2</a>.<br />
-<br />
-&#160; When <span class="term">$config["deny_attribute"]</span>&#160;is not set, or set to <span class="term">0</span>, or empty (<span class="term">""</span>), all the 111 attributes are permitted. Otherwise, <span class="term">$config["deny_attribute"]</span>&#160;can be set as a list of comma-separated names of the denied attributes. <span class="term">on&#42;</span>&#160;can be used to refer to the group of potentially dangerous, script-accepting attributes: <span class="term">onblur</span>, <span class="term">onchange</span>, <span class="term">onclick</span>, <span class="term">ondblclick</span>, <span class="term">onfocus</span>, <span class="term">onkeydown</span>, <span class="term">onkeypress</span>, <span class="term">onkeyup</span>, <span class="term">onmousedown</span>, <span class="term">onmousemove</span>, <span class="term">onmouseout</span>, <span class="term">onmouseover</span>, <span class="term">onmouseup</span>, <span class="term">onreset</span>, <span class="term">onselect</span>&#160;and <span class="term">onsubmit</span>.<br />
-<br />
-&#160; Note that attributes specified in <span class="term">$config["deny_attribute"]</span>&#160;are denied globally, for all elements. To deny attributes for only specific elements, <span class="term">$spec</span>&#160;(see <a href="#s2.3">section 2.3</a>) can be used. <span class="term">$spec</span>&#160;can also be used to element-specifically permit an attribute otherwise denied through <span class="term">$config["deny_attribute"]</span>.<br />
-<br />
-&#160; With <span class="term">$config["safe"] = 1</span>&#160;(<a href="#s3.6">section 3.6</a>), the <span class="term">on&#42;</span>&#160;attributes are automatically disallowed.<br />
-<br />
-&#160; <strong>Note</strong>: To deny all but a few attributes globally, a simpler way to specify <span class="term">$config["deny_attribute"]</span>&#160;would be to use the notation <span class="term">&#42; -attribute1 -attribute2 ...</span>. Thus, a value of <span class="term">&#42; -title -href</span>&#160;implies that except <span class="term">href</span>&#160;and <span class="term">title</span>&#160;(where allowed as per standards) all other attributes are to be removed. With this notation, the value for the parameter <span class="term">safe</span>&#160;(<a href="#s3.6">section 3.6</a>) will have no effect on <span class="term">deny_attribute</span>.<br />
-<br />
-&#160; htmLawed (function <span class="term">hl_tag()</span>) also:<br />
-<br />
-&#160; * &#160;Lower-cases attribute names<br />
-&#160; * &#160;Removes duplicate attributes (last one stays)<br />
-&#160; * &#160;Gives attributes the form <span class="term">name="value"</span>&#160;and single-spaces them, removing unnecessary white-spacing<br />
-&#160; * &#160;Provides <em>required</em>&#160;attributes (see <a href="#s3.4.1">section 3.4.1</a>)<br />
-&#160; * &#160;Double-quotes values and escapes any <span class="term">"</span>&#160;inside them<br />
-&#160; * &#160;Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point <span class="term">ad</span>) in the values with spaces<br />
-&#160; * &#160;Allows custom function to additionally filter/modify attribute values (see <a href="#s3.4.9">section 3.4.9</a>)<br />
-
-<div class="sub-sub-section"><h4>
-<a name="s3.4.1" id="s3.4.1"></a><span class="item-no">3.4.1</span>&#160; Auto-addition of XHTML-required attributes
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If indicated attributes for the following elements are found missing, htmLawed (function <span class="term">hl_tag()</span>) will add them (with values same as attribute names unless indicated otherwise below):<br />
-<br />
-&#160; * &#160;area - alt (<span class="term">area</span>)<br />
-&#160; * &#160;area, img - src, alt (<span class="term">image</span>)<br />
-&#160; * &#160;bdo - dir (<span class="term">ltr</span>)<br />
-&#160; * &#160;form - action<br />
-&#160; * &#160;map - name<br />
-&#160; * &#160;optgroup - label<br />
-&#160; * &#160;param - name<br />
-&#160; * &#160;script - type (<span class="term">text/javascript</span>)<br />
-&#160; * &#160;textarea - rows (<span class="term">10</span>), cols (<span class="term">50</span>)<br />
-<br />
-&#160; Additionally, with <span class="term">$config["xml&#58;lang"]</span>&#160;set to <span class="term">1</span>&#160;or <span class="term">2</span>, if the <span class="term">lang</span>&#160;but not the <span class="term">xml&#58;lang</span>&#160;attribute is declared, then the latter is added too, with a value copied from that of <span class="term">lang</span>. This is for better standard-compliance. With <span class="term">$config["xml&#58;lang"]</span>&#160;set to <span class="term">2</span>, the <span class="term">lang</span>&#160;attribute is removed (XHTML 1.1 specs).<br />
-<br />
-&#160; Note that the <span class="term">name</span>&#160;attribute for <span class="term">map</span>, invalid in XHTML 1.1, is also transformed if required -- see <a href="#s3.4.6">section 3.4.6</a>.<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.2" id="s3.4.2"></a><span class="item-no">3.4.2</span>&#160; Duplicate/invalid <span class="term">id</span>&#160;values
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["unique_ids"]</span>&#160;is <span class="term">1</span>, htmLawed (function <span class="term">hl_tag()</span>) removes <span class="term">id</span>&#160;attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, <span class="term">&#58;</span>, <span class="term">.</span>, <span class="term">-</span>&#160;and <span class="term">_</span>) or duplicate. If <span class="term">$config["unique_ids"]</span>&#160;is a word, any duplicate but otherwise valid value will be appropriately prefixed with the word to ensure its uniqueness. The word should begin with a letter and should contain only letters, numbers, <span class="term">&#58;</span>, <span class="term">.</span>, <span class="term">_</span>&#160;and <span class="term">-</span>.<br />
-<br />
-&#160; Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of <span class="term">id</span>&#160;values as it uses a global variable (<span class="term">$GLOBALS["hl_Ids"]</span>&#160;array). Further, an admin can restrict the use of certain <span class="term">id</span>&#160;values by presetting this variable before htmLawed is called into use. E.g.:<br />
-<br />
-
-<code class="code">&#160; &#160; $GLOBALS[&#39;hl_Ids&#39;] = array(&#39;top&#39;=&gt;1, &#39;bottom&#39;=&gt;1, &#39;myform&#39;=&gt;1); // id values not allowed in input</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text); // filter input</code>
-<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.3" id="s3.4.3"></a><span class="item-no">3.4.3</span>&#160; URL schemes (protocols) and scripts in attribute values
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed edits attributes that take URLs as values if they are found to contain un-permitted schemes. E.g., if the <span class="term">afp</span>&#160;scheme is not permitted, then <span class="term">&lt;a href="afp&#58;//domain.org"&gt;</span>&#160;becomes <span class="term">&lt;a href="denied&#58;afp&#58;//domain.org"&gt;</span>, and if Javascript is not permitted <span class="term">&lt;a onclick="javascript&#58;xss();"&gt;</span>&#160;becomes <span class="term">&lt;a onclick="denied&#58;javascript&#58;xss();"&gt;</span>.<br />
-<br />
-&#160; By default htmLawed permits these schemes in URLs for the <span class="term">href</span>&#160;attribute:<br />
-<br />
-
-<code class="code">&#160; &#160; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet</code>
-<br />
-<br />
-&#160; Also, only <span class="term">file</span>, <span class="term">http</span>&#160;and <span class="term">https</span>&#160;are permitted in attributes whose names start with <span class="term">o</span>&#160;(like <span class="term">onmouseover</span>), and in these attributes that accept URLs:<br />
-<br />
-
-<code class="code">&#160; &#160; action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap</code>
-<br />
-<br />
-&#160; These default sets are used when <span class="term">$config["schemes"]</span>&#160;is not set (see <a href="#s2.2">section 2.2</a>). To over-ride the defaults, <span class="term">$config["schemes"]</span>&#160;is defined as a string of semi-colon-separated sub-strings of type <span class="term">attribute&#58; comma-separated schemes</span>. E.g., <span class="term">href&#58; mailto, http, https; onclick&#58; javascript; src&#58; http, https</span>. For unspecified attributes, <span class="term">file</span>, <span class="term">http</span>&#160;and <span class="term">https</span>&#160;are permitted. This can be changed by passing schemes for <span class="term">&#42;</span>&#160;in <span class="term">$config["schemes"]</span>. E.g., <span class="term">href&#58; mailto, http, https; &#42;&#58; https, https</span>.<br />
-<br />
-&#160; <span class="term">&#42;</span>&#160;can be put in the list of schemes to permit all protocols. E.g., <span class="term">style&#58; &#42;; img&#58; http, https</span>&#160;results in protocols not being checked in <span class="term">style</span>&#160;attribute values. However, in such cases, any relative-to-absolute URL conversion, or vice versa, (<a href="#s3.4.4">section 3.4.4</a>) is not done.<br />
-<br />
-&#160; Thus, <em>to allow Javascript</em>, one can set <span class="term">$config["schemes"]</span>&#160;as <span class="term">href&#58; mailto, http, https; &#42;&#58; http, https, javascript</span>, or <span class="term">href&#58; mailto, http, https, javascript; &#42;&#58; http, https, javascript</span>, or <span class="term">&#42;&#58; &#42;</span>, and so on.<br />
-<br />
-&#160; As a side-note, one may find <span class="term">style&#58; &#42;</span>&#160;useful as URLs in <span class="term">style</span>&#160;attributes can be specified in a variety of ways, and the patterns that htmLawed uses to identify URLs may mistakenly identify non-URL text.<br />
-<br />
-&#160; <span class="term">!</span>&#160;can be put in the list of schemes to disallow all protocols as well as <em>local</em>&#160;URLs. Thus, with <span class="term">href&#58; http, style&#58; !</span>, '&lt;a href="http://cnn.com" style="background-image: url('local.jpg');"&gt;CNN&lt;/a&gt;' will become '&lt;a href="http://cnn.com" style="background-image: url('denied:local.jpg');"&gt;CNN&lt;/a&gt;'.<br />
-<br />
-&#160; <strong>Note</strong>: If URL-accepting attributes other than those listed above are being allowed, then the scheme will not be checked unless the attribute name contains the string <span class="term">src</span>&#160;(e.g., <span class="term">dynsrc</span>) or starts with <span class="term">o</span>&#160;(e.g., <span class="term">onbeforecopy</span>).<br />
-<br />
-&#160; With <span class="term">$config["safe"] = 1</span>, all URLs are disallowed in the <span class="term">style</span>&#160;attribute values.<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.4" id="s3.4.4"></a><span class="item-no">3.4.4</span>&#160; Absolute &amp; relative URLs in attribute values
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can make absolute URLs in attributes like <span class="term">href</span>&#160;relative (<span class="term">$config["abs_url"]</span>&#160;is <span class="term">-1</span>), and vice versa (<span class="term">$config["abs_url"]</span>&#160;is <span class="term">1</span>). URLs in scripts are not considered for this, and so are URLs like <span class="term">#section_6</span>&#160;(fragment), <span class="term">?name=Tim#show</span>&#160;(starting with query string), and <span class="term">;var=1?name=Tim#show</span>&#160;(starting with parameters). Further, this requires that <span class="term">$config["base_url"]</span>&#160;be set properly, with the <span class="term">&#58;//</span>&#160;and a trailing slash (<span class="term">/</span>), with no query string, etc. E.g., <span class="term">file&#58;///D&#58;/page/</span>, <span class="term">https&#58;//abc.com/x/y/</span>, or <span class="term">http&#58;//localhost/demo/</span>&#160;are okay, but <span class="term">file&#58;///D&#58;/page/?help=1</span>, <span class="term">abc.com/x/y/</span>&#160;and <span class="term">http&#58;//localhost/demo/index.htm</span>&#160;are not.<br />
-<br />
-&#160; For making absolute URLs relative, only those URLs that have the <span class="term">$config["base_url"]</span>&#160;string at the beginning are converted. E.g., with <span class="term">$config["base_url"] = "https&#58;//abc.com/x/y/"</span>, <span class="term">https&#58;//abc.com/x/y/a.gif</span>&#160;and <span class="term">https&#58;//abc.com/x/y/z/b.gif</span>&#160;become <span class="term">a.gif</span>&#160;and <span class="term">z/b.gif</span>&#160;respectively, while <span class="term">https&#58;//abc.com/x/c.gif</span>&#160;is not changed.<br />
-<br />
-&#160; When making relative URLs absolute, only values for scheme, network location (host-name) and path values in the base URL are inherited. See <a href="#s5.5">section 5.5</a>&#160;for more about the URL specification as per RFC <a href="http://www.ietf.org/rfc/rfc1808.txt">1808</a>.<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.5" id="s3.4.5"></a><span class="item-no">3.4.5</span>&#160; Lower-cased, standard attribute values
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Optionally, for standard-compliance, htmLawed (function <span class="term">hl_tag()</span>) lower-cases standard attribute values to give, e.g., <span class="term">input type="password"</span>&#160;instead of <span class="term">input type="Password"</span>, if <span class="term">$config["lc_std_val"]</span>&#160;is <span class="term">1</span>. Attribute values matching those listed below for any of the elements (plus those for the <span class="term">type</span>&#160;attribute of <span class="term">button</span>&#160;or <span class="term">input</span>) are lower-cased:<br />
-<br />
-
-<code class="code">&#160; &#160; all, baseline, bottom, button, center, char, checkbox, circle, col, colgroup, cols, data, default, file, get, groups, hidden, image, justify, left, ltr, middle, none, object, password, poly, post, preserve, radio, rect, ref, reset, right, row, rowgroup, rows, rtl, submit, text, top</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml&#58;space</code>
-<br />
-<br />
-&#160; The following <em>empty</em>&#160;(<em>minimized</em>) attributes are always assigned lower-cased values (same as the names):<br />
-<br />
-
-<code class="code">&#160; &#160; checked, compact, declare, defer, disabled, ismap, multiple, nohref, noresize, noshade, nowrap, readonly, selected</code>
-<br />
-
-</div>
-<div class="sub-sub-section"><h4>
-<a name="s3.4.6" id="s3.4.6"></a><span class="item-no">3.4.6</span>&#160; Transformation of deprecated attributes
-</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["no_deprecated_attr"]</span>&#160;is <span class="term">0</span>, then deprecated attributes (see appendix in <a href="#s5.2">section 5.2</a>) are removed and, in most cases, their values are transformed to CSS style properties and added to the <span class="term">style</span>&#160;attributes (function <span class="term">hl_tag()</span>). Except for <span class="term">bordercolor</span>&#160;for <span class="term">table</span>, <span class="term">tr</span>&#160;and <span class="term">td</span>, the scores of proprietary attributes that were never part of any cross-browser standard are not supported.<br />
-<br />
-&#160; <strong>Note</strong>: The attribute <span class="term">target</span>&#160;for <span class="term">a</span>&#160;is allowed even though it is not in XHTML 1.0 specs. This is because of the attribute's wide-spread use and browser-support, and because the attribute is valid in XHTML 1.1 onwards.<br />
-<br />
-&#160; * &#160;align - for <span class="term">img</span>&#160;with value of <span class="term">left</span>&#160;or <span class="term">right</span>, becomes, e.g., <span class="term">float&#58; left</span>; for <span class="term">div</span>&#160;and <span class="term">table</span>&#160;with value <span class="term">center</span>, becomes <span class="term">margin&#58; auto</span>; all others become, e.g., <span class="term">text-align&#58; right</span><br />
-<br />
-&#160; * &#160;bgcolor - E.g., <span class="term">bgcolor="#ffffff"</span>&#160;becomes <span class="term">background-color&#58; #ffffff</span><br />
-&#160; * &#160;border - E.g., <span class="term">height= "10"</span>&#160;becomes <span class="term">height&#58; 10px</span><br />
-&#160; * &#160;bordercolor - E.g., <span class="term">bordercolor=#999999</span>&#160;becomes <span class="term">border-color&#58; #999999;</span><br />
-&#160; * &#160;compact - <span class="term">font-size&#58; 85%</span><br />
-&#160; * &#160;clear - E.g., 'clear="all" becomes <span class="term">clear&#58; both</span><br />
-<br />
-&#160; * &#160;height - E.g., <span class="term">height= "10"</span>&#160;becomes <span class="term">height&#58; 10px</span>&#160;and <span class="term">height="&#42;"</span>&#160;becomes <span class="term">height&#58; auto</span><br />
-<br />
-&#160; * &#160;hspace - E.g., <span class="term">hspace="10"</span>&#160;becomes <span class="term">margin-left&#58; 10px; margin-right&#58; 10px</span><br />
-&#160; * &#160;language - <span class="term">language="VBScript"</span>&#160;becomes <span class="term">type="text/vbscript"</span><br />
-&#160; * &#160;name - E.g., <span class="term">name="xx"</span>&#160;becomes <span class="term">id="xx"</span><br />
-&#160; * &#160;noshade - <span class="term">border-style&#58; none; border&#58; 0; background-color&#58; gray; color&#58; gray</span><br />
-&#160; * &#160;nowrap - <span class="term">white-space&#58; nowrap</span><br />
-&#160; * &#160;size - E.g., <span class="term">size="10"</span>&#160;becomes <span class="term">height&#58; 10px</span><br />
-&#160; * &#160;start - removed<br />
-&#160; * &#160;type - E.g., <span class="term">type="i"</span>&#160;becomes <span class="term">list-style-type&#58; lower-roman</span><br />
-&#160; * &#160;value - removed<br />
-&#160; * &#160;vspace - E.g., <span class="term">vspace="10"</span>&#160;becomes <span class="term">margin-top&#58; 10px; margin-bottom&#58; 10px</span><br />
-&#160; * &#160;width - like <span class="term">height</span><br />
-<br />
-&#160; Example input:<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;img src="j.gif" alt="image" name="dad&#39;s" /&gt;&lt;img src="k.gif" alt="image" id="dad_off" name="dad" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;br clear="left" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;hr noshade size="1" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding&#58;5px;" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;table width="50em" align="center" bgcolor="red"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;&lt;tr&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;td width="20%"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;div align="center"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;h3 align="right"&gt;Section&lt;/h3&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;p align="right"&gt;Para&lt;/p&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;ol type="a" start="e"&gt;&lt;li value="x"&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;/div&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;td width="&#42;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;ol type="1"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;&lt;/tr&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/table&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;br clear="all" /&gt;</code>
-<br />
-<br />
-&#160; And the output with <span class="term">$config["no_deprecated_attr"] = 1</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; &lt;img src="j.gif" alt="image" /&gt;&lt;img src="k.gif" alt="image" id="dad_off" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;br style="clear&#58; left;" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;hr style="border-style&#58; none; border&#58; 0; background-color&#58; gray; color&#58; gray; size&#58; 1px;" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;img src="i.gif" alt="image" width="10em" height="20" style="padding&#58;5px; float&#58; left; margin-left&#58; 10px; margin-right&#58; 10px; margin-top&#58; 10px; margin-bottom&#58; 10px; border&#58; 1px;" id="img" /&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;table width="50em" style="margin&#58; auto; background-color&#58; red;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;&lt;tr&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;td style="width&#58; 20%;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;div style="margin&#58; auto;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;h3 style="text-align&#58; right;"&gt;Section&lt;/h3&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;p style="text-align&#58; right;"&gt;Para&lt;/p&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; &lt;ol style="list-style-type&#58; lower-latin;"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;/div&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;td style="width&#58; auto;"&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160;&lt;ol style="list-style-type&#58; decimal;"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160;&lt;/tr&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;/table&gt;</code>
-<br />
-
-<code class="code">&#160; &#160; &lt;br style="clear&#58; both;" /&gt;</code>
-<br />
-<br />
-&#160; For <span class="term">lang</span>, deprecated in XHTML 1.1, transformation is taken care of through <span class="term">$config["xml&#58;lang"]</span>; see <a href="#s3.4.1">section 3.4.1</a>.<br />
-<br />
-&#160; The attribute <span class="term">name</span>&#160;is deprecated in <span class="term">form</span>, <span class="term">iframe</span>, and <span class="term">img</span>, and is replaced with <span class="term">id</span>&#160;if an <span class="term">id</span>&#160;attribute doesn't exist and if the <span class="term">name</span>&#160;value is appropriate for <span class="term">id</span>. For such replacements for <span class="term">a</span>&#160;and <span class="term">map</span>, for which the <span class="term">name</span>&#160;attribute is deprecated in XHTML 1.1, <span class="term">$config["no_deprecated_attr"]</span>&#160;should be set to <span class="term">2</span>&#160;(when set to <span class="term">1</span>, for these two elements, the <span class="term">name</span>&#160;attribute is retained).<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.4.7" id="s3.4.7"></a><span class="item-no">3.4.7</span>&#160; Anti-spam &amp; <span class="term">href</span>
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed (function <span class="term">hl_tag()</span>) can check the <span class="term">href</span>&#160;attribute values (link addresses) as an anti-spam (email or link spam) measure.<br />
-<br />
-&#160; If <span class="term">$config["anti_mail_spam"]</span>&#160;is not <span class="term">0</span>, the <span class="term">@</span>&#160;of email addresses in <span class="term">href</span>&#160;values like <span class="term">mailto&#58;a@b.com</span>&#160;is replaced with text specified by <span class="term">$config["anti_mail_spam"]</span>. The text should be of a form that makes it clear to others that the address needs to be edited before a mail is sent; e.g., <span class="term">&lt;remove_this_antispam&gt;@</span>&#160;(makes the example address <span class="term">a&lt;remove_this_antispam&gt;@b.com</span>).<br />
-<br />
-&#160; For regular links, one can choose to have a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;in its value (which tells some search engines to not follow a link). This can discourage link spammers. Additionally, or as an alternative, one can choose to empty the <span class="term">href</span>&#160;value altogether (disable the link).<br />
-<br />
-&#160; For use of these options, <span class="term">$config["anti_link_spam"]</span>&#160;should be set as an array with values <span class="term">regex1</span>&#160;and <span class="term">regex2</span>, both or one of which can be empty (like <span class="term">array("", "regex2")</span>) to indicate that that option is not to be used. Otherwise, <span class="term">regex1</span>&#160;or <span class="term">regex2</span>&#160;should be PHP- and PCRE-compatible regular expression patterns: <span class="term">href</span>&#160;values will be matched against them and those matching the pattern will accordingly be treated.<br />
-<br />
-&#160; Note that the regular expressions should have <em>delimiters</em>, and be well-formed and preferably fast. Absolute efficiency/accuracy is often not needed.<br />
-<br />
-&#160; An example, to have a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;for all links, and to disable links that do not point to domains <span class="term">abc.com</span>&#160;and <span class="term">xyz.org</span>:<br />
-<br />
-
-<code class="code">&#160; &#160; $config["anti_link_spam"] = array(&#39;&#96;.&#96;&#39;, &#39;&#96;&#58;//\W&#42;(?!(abc\.com|xyz\.org))&#96;&#39;);</code>
-<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.4.8" id="s3.4.8"></a><span class="item-no">3.4.8</span>&#160; Inline style properties
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can check URL schemes and dynamic expressions (to guard against Javascript, etc., script-based insecurities) in inline CSS style property values in the <span class="term">style</span>&#160;attributes. (CSS properties like <span class="term">background-image</span>&#160;that accept URLs in their values are noted in <a href="#s5.3">section 5.3</a>.) Dynamic CSS expressions that allow scripting in the IE browser, and can be a vulnerability, can be removed from property values by setting <span class="term">$config["css_expression"]</span>&#160;to <span class="term">1</span>&#160;(default setting). Note that when <span class="term">$config["css_expression"]</span>&#160;is set to <span class="term">1</span>, htmLawed will remove <span class="term">/&#42;</span>&#160;from the <span class="term">style</span>&#160;values.<br />
-<br />
-&#160; <strong>Note</strong>: Because of the various ways of representing characters in attribute values (URL-escapement, entitification, etc.), htmLawed might alter the values of the <span class="term">style</span>&#160;attribute values, and may even falsely identify dynamic CSS expressions and URL schemes in them. If this is an important issue, checking of URLs and dynamic expressions can be turned off (<span class="term">$config["schemes"] = "...style&#58;&#42;..."</span>, see <a href="#s3.4.3">section 3.4.3</a>, and <span class="term">$config["css_expression"] = 0</span>). Alternately, admins can use their own custom function for finer handling of <span class="term">style</span>&#160;values through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; It is also possible to have htmLawed let through any <span class="term">style</span>&#160;value by setting <span class="term">$config["style_pass"]</span>&#160;to <span class="term">1</span>.<br />
-<br />
-&#160; As such, it is better to set up a CSS file with class declarations, disallow the <span class="term">style</span>&#160;attribute, set a <span class="term">$spec</span>&#160;rule (see <a href="#s2.3">section 2.3</a>) for <span class="term">class</span>&#160;for the <span class="term">oneof</span>&#160;or <span class="term">match</span>&#160;parameter, and ask writers to make use of the <span class="term">class</span>&#160;attribute.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.4.9" id="s3.4.9"></a><span class="item-no">3.4.9</span>&#160; Hook function for tag content
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; It is possible to utilize a custom hook function to alter the tag content htmLawed has finalized (i.e., after it has checked/corrected for required attributes, transformed attributes, lower-cased attribute names, etc.).<br />
-<br />
-&#160; When <span class="term">$config</span>&#160;parameter <span class="term">hook_tag</span>&#160;is set to the name of a function, htmLawed (function <span class="term">hl_tag()</span>) will pass on the element name, and, in the case of an opening tag, the <em>finalized</em>&#160;attribute name-value pairs as array elements to the function. The function, after completing a task such as filtering or tag transformation, will typically return an empty string, the full opening tag string like <span class="term">&lt;element_name attribute_1_name="attribute_1_value"...&gt;</span>&#160;(for empty elements like <span class="term">img</span>&#160;and <span class="term">input</span>, the element-closing slash <span class="term">/</span>&#160;should also be included), etc.<br />
-<br />
-&#160; Any <span class="term">hook_tag</span>&#160;function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as <span class="term">a</span>&#160;in the closing <span class="term">&lt;/a&gt;</span>&#160;tag of the element <span class="term">&lt;a href="http&#58;//cnn.com"&gt;CNN&lt;/a&gt;</span>. Unlike for opening tags, no other value (i.e., the attribute name-value array) is passed to the function since a closing tag contains only element names. Typically, the function will return an empty string or a full closing tag (like <span class="term">&lt;/a&gt;</span>).<br />
-<br />
-&#160; This is a <strong>powerful functionality</strong>&#160;that can be exploited for various objectives: consolidate-and-convert inline <span class="term">style</span>&#160;attributes to <span class="term">class</span>, convert <span class="term">embed</span>&#160;elements to <span class="term">object</span>, permit only one <span class="term">caption</span>&#160;element in a <span class="term">table</span>&#160;element, disallow embedding of certain types of media, <strong>inject HTML</strong>, use <a href="http://csstidy.sourceforge.net">CSSTidy</a>&#160;to sanitize <span class="term">style</span>&#160;attribute values, etc.<br />
-<br />
-&#160; As an example, the custom hook code below can be used to force a series of specifically ordered <span class="term">id</span>&#160;attributes on all elements, and a specific <span class="term">param</span>&#160;element inside all <span class="term">object</span>&#160;elements:<br />
-<br />
-
-<code class="code">&#160; &#160; function my_tag_function($element, $attribute_array=0){</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; // If second argument is not received, it means a closing tag is being handled</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; if(is_numeric($attribute_array)){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; return "&lt;/$element&gt;";</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; static $id = 0;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; // Remove any duplicate element</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; if($element == &#39;param&#39; &amp;&amp; isset($attribute_array[&#39;allowscriptaccess&#39;])){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; return &#39;&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; $new_element = &#39;&#39;;</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; // Force a serialized ID number</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; $attribute_array[&#39;id&#39;] = &#39;my_&#39;. $id;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; ++$id;</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; // Inject param for allowscriptaccess</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; if($element == &#39;object&#39;){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; $new_element = &#39;&lt;param id=&#39;my_&#39;. $id; allowscriptaccess="never" /&gt;&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; ++$id;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; $string = &#39;&#39;;</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; foreach($attribute_array as $k=&gt;$v){</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; &#160; $string .= " {$k}=\"{$v}\"";</code>
-<br />
-
-<code class="code">&#160; &#160; &#160; }</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; static $empty_elements = array(&#39;area&#39;=&gt;1, &#39;br&#39;=&gt;1, &#39;col&#39;=&gt;1, &#39;embed&#39;=&gt;1, &#39;hr&#39;=&gt;1, &#39;img&#39;=&gt;1, &#39;input&#39;=&gt;1, &#39;isindex&#39;=&gt;1, &#39;param&#39;=&gt;1);</code>
-<br />
-<br />
-
-<code class="code">&#160; &#160; &#160; return "&lt;{$element}{$string}". (isset($in_array($element, $empty_elements) ? &#39; /&#39; &#58; &#39;&#39;). &#39;&gt;&#39;. $new_element;</code>
-<br />
-
-<code class="code">&#160; &#160; }</code>
-<br />
-<br />
-&#160; The <span class="term">hook_tag</span>&#160;parameter is different from the <span class="term">hook</span>&#160;parameter (<a href="#s3.7">section 3.7</a>).<br />
-<br />
-&#160; Snippets of hook function code developed by others may be available on the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website.<br />
-
-</div>
-</div>
-<div class="sub-section"><h3>
-<a name="s3.5" id="s3.5"></a><span class="item-no">3.5</span>&#160; Simple configuration directive for most valid XHTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["valid_xhtml"]</span>&#160;is set to <span class="term">1</span>, some relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">~</span>&#160;in <a href="#s2.2">section 2.2</a>) are auto-adjusted. This allows one to pass the <span class="term">$config</span>&#160;argument with a simpler value. If a value for a parameter auto-set through <span class="term">valid_xhtml</span>&#160;is still manually provided, then that value will over-ride the auto-set value.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.6" id="s3.6"></a><span class="item-no">3.6</span>&#160; Simple configuration directive for most <em>safe</em>&#160;HTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; <em>Safe</em>&#160;HTML refers to HTML that is restricted to reduce the vulnerability for scripting attacks (such as XSS) based on HTML code which otherwise may still be legal and compliant with the HTML standard specs. When elements such as <span class="term">script</span>&#160;and <span class="term">object</span>, and attributes such as <span class="term">onmouseover</span>&#160;and <span class="term">style</span>&#160;are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered <span class="term">safe</span>&#160;depends on the nature of the web application and the trust-level accorded to its users.<br />
-<br />
-&#160; htmLawed allows an admin to use <span class="term">$config["safe"]</span>&#160;to auto-adjust multiple <span class="term">$config</span>&#160;parameters (such as <span class="term">elements</span>&#160;which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by <span class="term">"</span>&#160;in <a href="#s2.2">section 2.2</a>). Thus, one can pass the <span class="term">$config</span>&#160;argument with a simpler value.<br />
-<br />
-&#160; With the value of <span class="term">1</span>, htmLawed considers <span class="term">CDATA</span>&#160;sections and HTML comments as plain text, and prohibits the <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>, <span class="term">object</span>&#160;and <span class="term">script</span>&#160;elements, and the <span class="term">on&#42;</span>&#160;attributes like <span class="term">onclick</span>. ( There are <span class="term">$config</span>&#160;parameters like <span class="term">css_expression</span>&#160;that are not affected by the value set for <span class="term">safe</span>&#160;but whose default values still contribute towards a more <em>safe</em>&#160;output.) Further, URLs with schemes (see <a href="#s3.4.3">section 3.4.3</a>) are neutralized so that, e.g., <span class="term">style="moz-binding&#58;url(http&#58;//danger)"</span>&#160;becomes <span class="term">style="moz-binding&#58;url(denied&#58;http&#58;//danger)"</span>.<br />
-<br />
-&#160; Admins, however, may still want to completely deny the <span class="term">style</span>&#160;attribute, e.g., with code like<br />
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text, array(&#39;safe&#39;=&gt;1, &#39;deny_attribute&#39;=&gt;&#39;style&#39;));</code>
-<br />
-<br />
-&#160; Permitting the <span class="term">style</span>&#160;attribute brings in risks of <em>click-jacking</em>, etc. CSS property values can render a page non-functional or be used to deface it. Except for URLs, dynamic expressions, and some other things, htmLawed does not completely check <span class="term">style</span>&#160;values. It does provide ways for the code-developer implementing htmLawed to do such checks through the <span class="term">$spec</span>&#160;argument, and through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.8">section 3.4.8</a>&#160;for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.<br />
-<br />
-&#160; If a value for a parameter auto-set through <span class="term">safe</span>&#160;is still manually provided, then that value can over-ride the auto-set value. E.g., with <span class="term">$config["safe"] = 1</span>&#160;and <span class="term">$config["elements"] = "&#42;+script"</span>, <span class="term">script</span>, but not <span class="term">applet</span>, is allowed.<br />
-<br />
-&#160; A page illustrating the efficacy of htmLawed's anti-XSS abilities with <span class="term">safe</span>&#160;set to <span class="term">1</span>&#160;against XSS vectors listed by <a href="http://ha.ckers.org/xss.html">RSnake</a>&#160;may be available <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/rsnake/RSnakeXSSTest.htm">here</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.7" id="s3.7"></a><span class="item-no">3.7</span>&#160; Using a hook function
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; If <span class="term">$config["hook"]</span>&#160;is not set to <span class="term">0</span>, then htmLawed will allow preliminarily processed input to be altered by a hook function named by <span class="term">$config["hook"]</span>&#160;before starting the main work (but after handling of characters, entities, HTML comments and <span class="term">CDATA</span>&#160;sections -- see code for function <span class="term">htmLawed()</span>).<br />
-<br />
-&#160; The hook function also allows one to alter the <em>finalized</em>&#160;values of <span class="term">$config</span>&#160;and <span class="term">$spec</span>.<br />
-<br />
-&#160; Note that the <span class="term">hook</span>&#160;parameter is different from the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; Snippets of hook function code developed by others may be available on the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.8" id="s3.8"></a><span class="item-no">3.8</span>&#160; Obtaining <em>finalized</em>&#160;parameter values
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed can assign the <em>finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values to a variable named by <span class="term">$config["show_setting"]</span>. The variable, made global by htmLawed, is set as an array with three keys: <span class="term">config</span>, with the <span class="term">$config</span>&#160;value, <span class="term">spec</span>, with the <span class="term">$spec</span>&#160;value, and <span class="term">time</span>, with a value that is the Unix time (the output of PHP's <span class="term">microtime()</span>&#160;function) when the value was assigned. Admins should use a PHP-compliant variable name (e.g., one that does not begin with a numerical digit) that does not conflict with variable names in their non-htmLawed code.<br />
-<br />
-&#160; The values, which are also post-hook function (if any), can be used to auto-generate information (on, e.g., the elements that are permitted) for input writers.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s3.9" id="s3.9"></a><span class="item-no">3.9</span>&#160; Retaining non-HTML tags in input with mixed markup
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; htmLawed does not remove certain characters that though invalid are nevertheless discouraged in HTML documents as per the specs (see <a href="#s5.1">section 5.1</a>). This can be utilized to deal with input that contains mixed markup. Input that may have HTML markup as well as some other markup that is based on the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters is considered to have mixed markup. The non-HTML markup can be rather proprietary (like markup for emoticons/smileys), or standard (like MathML or SVG). Or it can be programming code meant for execution/evaluation (such as embedded PHP code).<br />
-<br />
-&#160; To deal with such mixed markup, the input text can be pre-processed to hide the non-HTML markup by specifically replacing the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters with some of the HTML-discouraged characters (see <a href="#s3.1.2">section 3.1.2</a>). Post-htmLawed processing, the replacements are reverted.<br />
-<br />
-&#160; An example (mixed HTML and PHP code in input text):<br />
-<br />
-
-<code class="code">&#160; &#160; $text = preg_replace(&#39;&#96;&lt;\?php(.+?)\?&gt;&#96;sm&#39;, "\x83?php\\1?\x84", $text);</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = htmLawed($text);</code>
-<br />
-
-<code class="code">&#160; &#160; $processed = preg_replace(&#39;&#96;\x83\?php(.+?)\?\x84&#96;sm&#39;, &#39;&lt;?php$1?&gt;&#39;, $processed);</code>
-<br />
-<br />
-&#160; This code will not work if <span class="term">$config["clean_ms_char"]</span>&#160;is set to <span class="term">1</span>&#160;(<a href="#s3.1">section 3.1</a>), in which case one should instead deploy a hook function (<a href="#s3.7">section 3.7</a>). (htmLawed internally uses certain control characters, code-points <span class="term">1</span>&#160;to <span class="term">7</span>, and use of these characters as markers in the logic of hook functions may cause issues.)<br />
-<br />
-&#160; Admins may also be able to use <span class="term">$config["and_mark"]</span>&#160;to deal with such mixed markup; see <a href="#s3.2">section 3.2</a>.<br />
-
-</div>
-</div>
-<div class="section"><h2>
-<a name="s4" id="s4"></a><span class="item-no">4</span>&#160; Other
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<div class="sub-section"><h3>
-<a name="s4.1" id="s4.1"></a><span class="item-no">4.1</span>&#160; Support
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; A careful re-reading of this documentation will very likely answer your questions.<br />
-<br />
-&#160; Software updates and forum-based community-support may be found at <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a>. For general PHP issues (not htmLawed-specific), support may be found through internet searches and at <a href="http://php.net">http://php.net</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.2" id="s4.2"></a><span class="item-no">4.2</span>&#160; Known issues
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; See <a href="#s2.8">section 2.8</a>.<br />
-<br />
-&#160; Readers are advised to cross-check information given in this document.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.3" id="s4.3"></a><span class="item-no">4.3</span>&#160; Change-log
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the <span class="term">htmLawed.php</span>&#160;file may be updated independently if the secondary files are revised.)<br />
-<br />
-&#160; <em>Version number - Release date. Notes</em><br />
-<br />
-&#160; 1.1.11 - 5 June 2012. Fix for possible problem with handling of multi-byte characters in attribute values in an mbstring.func_overload enviroment. <span class="term">$config["hook_tag"]</span>, if specified, now receives names of elements in closing tags.<br />
-<br />
-&#160; 1.1.10 - 22 October 2011. Fix for a bug in the <span class="term">tidy</span>&#160;functionality that caused the entire input to be replaced with a single space; new parameter, <span class="term">$config["direct_list_nest"]</span>&#160;to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)<br />
-<br />
-&#160; 1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of <span class="term">li</span>&#160;within <span class="term">dir</span><br />
-<br />
-&#160; 1.1.9.4 - 3 July 2010. Parameter <span class="term">schemes</span>&#160;now accepts <span class="term">!</span>&#160;so any URL, even a local one, can be <em>denied</em>. An issue in which a second URL value in <span class="term">style</span>&#160;properties was not checked was fixed.<br />
-<br />
-&#160; 1.1.9.3 - 17 May 2010. Checks for correct nesting of <span class="term">param</span><br />
-<br />
-&#160; 1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes<br />
-<br />
-&#160; 1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for <span class="term">flashvars</span>&#160;attribute for <span class="term">embed</span><br />
-<br />
-&#160; 1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values<br />
-<br />
-&#160; 1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice<br />
-<br />
-&#160; 1.1.8 - 23 April 2009. Parameter <span class="term">deny_attribute</span>&#160;now accepts the wild-card <span class="term">&#42;</span>, making it simpler to specify its value when all but a few attributes are being denied; fixed a bug in interpreting <span class="term">$spec</span><br />
-<br />
-&#160; 1.1.7 - 11-12 March 2009. Attributes globally denied through <span class="term">deny_attribute</span>&#160;can be allowed element-specifically through <span class="term">$spec</span>; <span class="term">$config["style_pass"]</span>&#160;allowing letting through any <span class="term">style</span>&#160;value introduced; altered logic to catch certain types of dynamic crafted CSS expressions<br />
-<br />
-&#160; 1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions<br />
-<br />
-&#160; 1.1.2 - 22 January 2009. Fixed bug in parsing of <span class="term">font</span>&#160;attributes during tag transformation<br />
-<br />
-&#160; 1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent<br />
-<br />
-&#160; 1.1 - 29 June 2008. <span class="term">$config["hook_tag"]</span>&#160;and <span class="term">$config["format"]</span>&#160;introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug<br />
-<br />
-&#160; 1.0.9 - 11 June 2008. Fixed bug in invalid HTML code-point entity check<br />
-<br />
-&#160; 1.0.8 - 15 May 2008. <span class="term">bordercolor</span>&#160;attribute for <span class="term">table</span>, <span class="term">td</span>&#160;and <span class="term">tr</span><br />
-<br />
-&#160; 1.0.7 - 1 May 2008. Support for <span class="term">wmode</span>&#160;attribute for <span class="term">embed</span>; <span class="term">$config["show_setting"]</span>&#160;introduced; improved <span class="term">$config["elements"]</span>&#160;evaluation<br />
-<br />
-&#160; 1.0.6 - 20 April 2008. <span class="term">$config["and_mark"]</span>&#160;introduced<br />
-<br />
-&#160; 1.0.5 - 12 March 2008. <span class="term">style</span>&#160;URL schemes essentially disallowed when $config <span class="term">safe</span>&#160;is on; improved regex for CSS expression search<br />
-<br />
-&#160; 1.0.4 - 10 March 2008. Improved corrections for <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span><br />
-<br />
-&#160; 1.0.3 - 3 March 2008. Character entities for soft-hyphens are now replaced with spaces (instead of being removed); a bug allowing <span class="term">td</span>&#160;directly inside <span class="term">table</span>&#160;fixed; <span class="term">safe</span>&#160;<span class="term">$config</span>&#160;parameter added<br />
-<br />
-&#160; 1.0.2 - 13 February 2008. Improved implementation of <span class="term">$config["keep_bad"]</span><br />
-<br />
-&#160; 1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions (<span class="term">hl_tag()</span>&#160;and <span class="term">hl_prot()</span>); no error display with <span class="term">hl_regex()</span><br />
-<br />
-&#160; 1.0 - 2 November 2007. First release<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.4" id="s4.4"></a><span class="item-no">4.4</span>&#160; Testing
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; To test htmLawed using a form interface, a <a href="htmLawedTest.php">demo</a>&#160;web-page is provided with the htmLawed distribution (<span class="term">htmLawed.php</span>&#160;and <span class="term">htmLawedTest.php</span>&#160;should be in the same directory on the web-server). A file with <a href="htmLawed_TESTCASE.txt">test-cases</a>&#160;is also provided.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.5" id="s4.5"></a><span class="item-no">4.5</span>&#160; Upgrade, &amp; old versions
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Upgrading is as simple as replacing the previous version of <span class="term">htmLawed.php</span>&#160;(assuming it was not modified for customized features). As htmLawed output is almost always used in static documents, upgrading should not affect old, finalized content.<br />
-<br />
-&#160; <strong>Important</strong>&#160; The following upgrades may affect the functionality of a specific htmLawed as indicated by their corresponding notes:<br />
-<br />
-&#160; (1) From version 1.1-1.1.10 to 1.1.11, if a <span class="term">hook_tag</span>&#160;function is in use: In version 1.1.11, elements in closing tags (and not just the opening tags) are also passed to the function. There are no attribute names/values to pass, so a <span class="term">hook_tag</span>&#160;function receives only the element name. The <span class="term">hook_tag</span>&#160;function therefore may have to be edited. See <a href="#s3.4.9">section 3.4.9</a>.<br />
-<br />
-&#160; Old versions of htmLawed may be available online. E.g., for version 1.0, check <a href="http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip">http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip</a>, for 1.1.1, htmLawed111.zip, and for 1.1.10, htmLawed1110.zip.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.6" id="s4.6"></a><span class="item-no">4.6</span>&#160; Comparison with <span class="term">HTMLPurifier</span>
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; The HTMLPurifier PHP library by Edward Yang is a very good HTML filtering script that uses object oriented PHP code. Compared to htmLawed, it (as of mid-2009):<br />
-<br />
-&#160; * &#160;does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)<br />
-<br />
-&#160; * &#160;is 15-20 times bigger (scores of files totalling more than 750 kb)<br />
-<br />
-&#160; * &#160;consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)<br />
-<br />
-&#160; * &#160;is expectedly slower<br />
-<br />
-&#160; * &#160;does not allow admins to fully allow all valid HTML (because of incomplete HTML support, it always considers elements like <span class="term">script</span>&#160;illegal)<br />
-<br />
-&#160; * &#160;lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)<br />
-<br />
-&#160; * &#160;has poor documentation<br />
-<br />
-&#160; However, HTMLPurifier has finer checks for character encodings and attribute values, and can log warnings and errors. Visit the HTMLPurifier <a href="http://htmlpurifier.org">website</a>&#160;for updated information.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.7" id="s4.7"></a><span class="item-no">4.7</span>&#160; Use through application plug-ins/modules
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Plug-ins/modules to implement htmLawed in applications such as Drupal and DokuWiki may have been developed. Please check the application websites and the forum on the htmLawed <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">site</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.8" id="s4.8"></a><span class="item-no">4.8</span>&#160; Use in non-PHP applications
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Non-PHP applications written in Python, Ruby, etc., may be able to use htmLawed through system calls to the PHP engine. Such code may have been documented on the internet. Also check the forum on the htmLawed <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">site</a>.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.9" id="s4.9"></a><span class="item-no">4.9</span>&#160; Donate
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; A donation in any currency and amount to appreciate or support this software can be sent by <a href="http://paypal.com">PayPal</a>&#160;to this email address: drpatnaik at yahoo dot com.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s4.10" id="s4.10"></a><span class="item-no">4.10</span>&#160; Acknowledgements
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Nicholas Alipaz, Bryan Blakey, Pádraic Brady, Ulf Harnhammer, Gareth Heyes, Klaus Leithoff, Lukasz Pilorz, Shelley Powers, Edward Yang, and many anonymous users.<br />
-<br />
-&#160; Thank you!<br />
-
-</div>
-</div>
-<div class="section"><h2>
-<a name="s5" id="s5"></a><span class="item-no">5</span>&#160; Appendices
-</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<div class="sub-section"><h3>
-<a name="s5.1" id="s5.1"></a><span class="item-no">5.1</span>&#160; Characters discouraged in XHTML
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Characters represented by the following hexadecimal code-points are <em>not</em>&#160;invalid, even though some validators may issue messages stating otherwise.<br />
-<br />
-&#160; <span class="term">7f</span>&#160;to <span class="term">84</span>, <span class="term">86</span>&#160;to <span class="term">9f</span>, <span class="term">fdd0</span>&#160;to <span class="term">fddf</span>, <span class="term">1fffe</span>, <span class="term">1ffff</span>, <span class="term">2fffe</span>, <span class="term">2ffff</span>, <span class="term">3fffe</span>, <span class="term">3ffff</span>, <span class="term">4fffe</span>, <span class="term">4ffff</span>, <span class="term">5fffe</span>, <span class="term">5ffff</span>, <span class="term">6fffe</span>, <span class="term">6ffff</span>, <span class="term">7fffe</span>, <span class="term">7ffff</span>, <span class="term">8fffe</span>, <span class="term">8ffff</span>, <span class="term">9fffe</span>, <span class="term">9ffff</span>, <span class="term">afffe</span>, <span class="term">affff</span>, <span class="term">bfffe</span>, <span class="term">bffff</span>, <span class="term">cfffe</span>, <span class="term">cffff</span>, <span class="term">dfffe</span>, <span class="term">dffff</span>, <span class="term">efffe</span>, <span class="term">effff</span>, <span class="term">ffffe</span>, <span class="term">fffff</span>, <span class="term">10fffe</span>&#160;and <span class="term">10ffff</span><br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.2" id="s5.2"></a><span class="item-no">5.2</span>&#160; Valid attribute-element combinations
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Valid attribute-element combinations as per W3C specs.<br />
-<br />
-&#160; * &#160;includes deprecated attributes (marked <span class="term">^</span>), attributes for the non-standard <span class="term">embed</span>&#160;element (marked <span class="term">&#42;</span>), and the proprietary <span class="term">bordercolor</span>&#160;(marked <span class="term">~</span>)<br />
-&#160; * &#160;only non-frameset, HTML body elements<br />
-&#160; * &#160;<span class="term">name</span>&#160;for <span class="term">a</span>&#160;and <span class="term">map</span>, and <span class="term">lang</span>&#160;are invalid in XHTML 1.1<br />
-&#160; * &#160;<span class="term">target</span>&#160;is valid for <span class="term">a</span>&#160;in XHTML 1.1 and higher<br />
-&#160; * &#160;<span class="term">xml&#58;space</span>&#160;is only for XHTML 1.1<br />
-<br />
-&#160; abbr - td, th<br />
-&#160; accept - form, input<br />
-&#160; accept-charset - form<br />
-&#160; accesskey - a, area, button, input, label, legend, textarea<br />
-&#160; action - form<br />
-&#160; align - caption^, embed, applet, iframe, img^, input^, object^, legend^, table^, hr^, div^, h1^, h2^, h3^, h4^, h5^, h6^, p^, col, colgroup, tbody, td, tfoot, th, thead, tr<br />
-&#160; alt - applet, area, img, input<br />
-&#160; archive - applet, object<br />
-&#160; axis - td, th<br />
-&#160; bgcolor - embed, table^, tr^, td^, th^<br />
-&#160; border - table, img^, object^<br />
-&#160; bordercolor~ - table, td, tr<br />
-&#160; cellpadding - table<br />
-&#160; cellspacing - table<br />
-&#160; char - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
-&#160; charoff - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
-&#160; charset - a, script<br />
-&#160; checked - input<br />
-&#160; cite - blockquote, q, del, ins<br />
-&#160; classid - object<br />
-&#160; clear - br^<br />
-&#160; code - applet<br />
-&#160; codebase - object, applet<br />
-&#160; codetype - object<br />
-&#160; color - font<br />
-&#160; cols - textarea<br />
-&#160; colspan - td, th<br />
-&#160; compact - dir, dl^, menu, ol^, ul^<br />
-&#160; coords - area, a<br />
-&#160; data - object<br />
-&#160; datetime - del, ins<br />
-&#160; declare - object<br />
-&#160; defer - script<br />
-&#160; dir - bdo<br />
-&#160; disabled - button, input, optgroup, option, select, textarea<br />
-&#160; enctype - form<br />
-&#160; face - font<br />
-&#160; flashvars* - embed<br />
-&#160; for - label<br />
-&#160; frame - table<br />
-&#160; frameborder - iframe<br />
-&#160; headers - td, th<br />
-&#160; height - embed, iframe, td^, th^, img, object, applet<br />
-&#160; href - a, area<br />
-&#160; hreflang - a<br />
-&#160; hspace - applet, img^, object^<br />
-&#160; ismap - img, input<br />
-&#160; label - option, optgroup<br />
-&#160; language - script^<br />
-&#160; longdesc - img, iframe<br />
-&#160; marginheight - iframe<br />
-&#160; marginwidth - iframe<br />
-&#160; maxlength - input<br />
-&#160; method - form<br />
-&#160; model* - embed<br />
-&#160; multiple - select<br />
-&#160; name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param<br />
-&#160; nohref - area<br />
-&#160; noshade - hr^<br />
-&#160; nowrap - td^, th^<br />
-&#160; object - applet<br />
-&#160; onblur - a, area, button, input, label, select, textarea<br />
-&#160; onchange - input, select, textarea<br />
-&#160; onfocus - a, area, button, input, label, select, textarea<br />
-&#160; onreset - form<br />
-&#160; onselect - input, textarea<br />
-&#160; onsubmit - form<br />
-&#160; pluginspage* - embed<br />
-&#160; pluginurl* - embed<br />
-&#160; prompt - isindex<br />
-&#160; readonly - textarea, input<br />
-&#160; rel - a<br />
-&#160; rev - a<br />
-&#160; rows - textarea<br />
-&#160; rowspan - td, th<br />
-&#160; rules - table<br />
-&#160; scope - td, th<br />
-&#160; scrolling - iframe<br />
-&#160; selected - option<br />
-&#160; shape - area, a<br />
-&#160; size - hr^, font, input, select<br />
-&#160; span - col, colgroup<br />
-&#160; src - embed, script, input, iframe, img<br />
-&#160; standby - object<br />
-&#160; start - ol^<br />
-&#160; summary - table<br />
-&#160; tabindex - a, area, button, input, object, select, textarea<br />
-&#160; target - a^, area, form<br />
-&#160; type - a, embed, object, param, script, input, li^, ol^, ul^, button<br />
-&#160; usemap - img, input, object<br />
-&#160; valign - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
-&#160; value - input, option, param, button, li^<br />
-&#160; valuetype - param<br />
-&#160; vspace - applet, img^, object^<br />
-&#160; width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^<br />
-&#160; wmode - embed<br />
-&#160; xml:space - pre, script, style<br />
-<br />
-&#160; These are allowed in all but the shown elements:<br />
-<br />
-&#160; class - param, script<br />
-&#160; dir - applet, bdo, br, iframe, param, script<br />
-&#160; id - script<br />
-&#160; lang - applet, br, iframe, param, script<br />
-&#160; onclick - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; ondblclick - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onkeydown - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onkeypress - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onkeyup - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmousedown - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmousemove - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmouseout - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmouseover - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; onmouseup - applet, bdo, br, font, iframe, isindex, param, script<br />
-&#160; style - param, script<br />
-&#160; title - param, script<br />
-&#160; xml:lang - applet, br, iframe, param, script<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.3" id="s5.3"></a><span class="item-no">5.3</span>&#160; CSS 2.1 properties accepting URLs
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; background<br />
-&#160; background-image<br />
-&#160; content<br />
-&#160; cue-after<br />
-&#160; cue-before<br />
-&#160; cursor<br />
-&#160; list-style<br />
-&#160; list-style-image<br />
-&#160; play-during<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.4" id="s5.4"></a><span class="item-no">5.4</span>&#160; Microsoft Windows 1252 character replacements
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Key: <span class="term">d</span>&#160;double, <span class="term">l</span>&#160;left, <span class="term">q</span>&#160;quote, <span class="term">r</span>&#160;right, <span class="term">s.</span>&#160;single<br />
-<br />
-&#160; Code-point (decimal) - hexadecimal value - replacement entity - represented character<br />
-<br />
-&#160; 127 - 7f - (removed) - (not used)<br />
-&#160; 128 - 80 - &amp;#8364; - euro<br />
-&#160; 129 - 81 - (removed) - (not used)<br />
-&#160; 130 - 82 - &amp;#8218; - baseline s. q<br />
-&#160; 131 - 83 - &amp;#402; - florin<br />
-&#160; 132 - 84 - &amp;#8222; - baseline d q<br />
-&#160; 133 - 85 - &amp;#8230; - ellipsis<br />
-&#160; 134 - 86 - &amp;#8224; - dagger<br />
-&#160; 135 - 87 - &amp;#8225; - d dagger<br />
-&#160; 136 - 88 - &amp;#710; - circumflex accent<br />
-&#160; 137 - 89 - &amp;#8240; - permile<br />
-&#160; 138 - 8a - &amp;#352; - S Hacek<br />
-&#160; 139 - 8b - &amp;#8249; - l s. guillemet<br />
-&#160; 140 - 8c - &amp;#338; - OE ligature<br />
-&#160; 141 - 8d - (removed) - (not used)<br />
-&#160; 142 - 8e - &amp;#381; - Z dieresis<br />
-&#160; 143 - 8f - (removed) - (not used)<br />
-&#160; 144 - 90 - (removed) - (not used)<br />
-&#160; 145 - 91 - &amp;#8216; - l s. q<br />
-&#160; 146 - 92 - &amp;#8217; - r s. q<br />
-&#160; 147 - 93 - &amp;#8220; - l d q<br />
-&#160; 148 - 94 - &amp;#8221; - r d q<br />
-&#160; 149 - 95 - &amp;#8226; - bullet<br />
-&#160; 150 - 96 - &amp;#8211; - en dash<br />
-&#160; 151 - 97 - &amp;#8212; - em dash<br />
-&#160; 152 - 98 - &amp;#732; - tilde accent<br />
-&#160; 153 - 99 - &amp;#8482; - trademark<br />
-&#160; 154 - 9a - &amp;#353; - s Hacek<br />
-&#160; 155 - 9b - &amp;#8250; - r s. guillemet<br />
-&#160; 156 - 9c - &amp;#339; - oe ligature<br />
-&#160; 157 - 9d - (removed) - (not used)<br />
-&#160; 158 - 9e - &amp;#382; - z dieresis<br />
-&#160; 159 - 9f - &amp;#376; - Y dieresis<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.5" id="s5.5"></a><span class="item-no">5.5</span>&#160; URL format
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; An <em>absolute</em>&#160;URL has a <span class="term">protocol</span>&#160;or <span class="term">scheme</span>, a <span class="term">network location</span>&#160;or <span class="term">hostname</span>, and, optional <span class="term">path</span>, <span class="term">parameters</span>, <span class="term">query</span>&#160;and <span class="term">fragment</span>&#160;segments. Thus, an absolute URL has this generic structure:<br />
-<br />
-
-<code class="code">&#160; &#160; (scheme) &#58; (//network location) /(path) ;(parameters) ?(query) #(fragment)</code>
-<br />
-<br />
-&#160; The schemes can only contain letters, digits, <span class="term">+</span>, <span class="term">.</span>&#160;and <span class="term">-</span>. Hostname is the portion after the <span class="term">//</span>&#160;and up to the first <span class="term">/</span>&#160;(if any; else, up to the end) when <span class="term">&#58;</span>&#160;is followed by a <span class="term">//</span>&#160;(e.g., <span class="term">abc.com</span>&#160;in <span class="term">ftp&#58;//abc.com/def</span>); otherwise, it consists of everything after the <span class="term">&#58;</span>&#160;(e.g., <span class="term">def@abc.com</span>&#160;in mailto:def@abc.com').<br />
-<br />
-&#160; <em>Relative</em>&#160;URLs do not have explicit schemes and network locations; such values are inherited from a <em>base</em>&#160;URL.<br />
-
-</div>
-<div class="sub-section"><h3>
-<a name="s5.6" id="s5.6"></a><span class="item-no">5.6</span>&#160; Brief on htmLawed code
-</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
-<br />
-&#160; Much of the code's logic and reasoning can be understood from the documentation above.<br />
-<br />
-&#160; The <strong>output</strong>&#160;of htmLawed is a text string containing the processed input. There is no custom error tracking.<br />
-<br />
-&#160; <strong>Function arguments</strong>&#160;for htmLawed are:<br />
-<br />
-&#160; * &#160;<span class="term">$in</span>&#160;- 1st argument; a text string; the <strong>input text</strong>&#160;to be processed. Any extraneous slashes added by PHP when <em>magic quotes</em>&#160;are enabled should be removed beforehand using PHP's <span class="term">stripslashes()</span>&#160;function.<br />
-<br />
-&#160; * &#160;<span class="term">$config</span>&#160;- 2nd argument; an associative array; optional (named <span class="term">$C</span>&#160;in htmLawed code). The array has keys with names like <span class="term">balance</span>&#160;and <span class="term">keep_bad</span>, and the values, which can be boolean, string, or array, depending on the key, are read to accordingly set the <strong>configurable parameters</strong>&#160;(indicated by the keys). All configurable parameters receive some default value if the value to be used is not specified by the user through <span class="term">$config</span>. <em>Finalized</em>&#160;<span class="term">$config</span>&#160;is thus a filtered and possibly larger array.<br />
-<br />
-&#160; * &#160;<span class="term">$spec</span>&#160;- 3rd argument; a text string; optional. The string has rules, written in an htmLawed-designated format, <strong>specifying</strong>&#160;element-specific attribute and attribute value restrictions. Function <span class="term">hl_spec()</span>&#160;is used to convert the string to an associative-array for internal use. <em>Finalized</em>&#160;<span class="term">$spec</span>&#160;is thus an array.<br />
-<br />
-&#160; <em>Finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;are made <strong>global variables</strong>&#160;while htmLawed is at work. Values of any pre-existing global variables with same names are noted, and their values are restored after htmLawed finishes processing the input (to capture the <em>finalized</em>&#160;values, the <span class="term">show_settings</span>&#160;parameter of <span class="term">$config</span>&#160;should be used). Depending on <span class="term">$config</span>, another global variable <span class="term">hl_Ids</span>, to track <span class="term">id</span>&#160;attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.<br />
-<br />
-&#160; Except for the main function <span class="term">htmLawed()</span>&#160;and the functions <span class="term">kses()</span>&#160;and <span class="term">kses_hook()</span>, htmLawed's functions are <strong>name-spaced</strong>&#160;using the <span class="term">hl_</span>&#160;prefix. The <strong>functions</strong>&#160;and their roles are:<br />
-<br />
-&#160; * &#160;<span class="term">hl_attrval</span>&#160;- checking attribute values against $spec<br />
-&#160; * &#160;<span class="term">hl_bal</span>&#160;- tag balancing<br />
-&#160; * &#160;<span class="term">hl_cmtcd</span>&#160;- handling CDATA sections and HTML comments<br />
-&#160; * &#160;<span class="term">hl_ent</span>&#160;- entity handling<br />
-&#160; * &#160;<span class="term">hl_prot</span>&#160;- checking a URL scheme/protocol<br />
-&#160; * &#160;<span class="term">hl_regex</span>&#160;- checking syntax of a regular expression<br />
-&#160; * &#160;<span class="term">hl_spec</span>&#160;- converting user-supplied $spec value to one used by htmLawed internally<br />
-&#160; * &#160;<span class="term">hl_tag</span>&#160;- handling tags<br />
-&#160; * &#160;<span class="term">hl_tag2</span>&#160;- transforming tags<br />
-&#160; * &#160;<span class="term">hl_tidy</span>&#160;- compact/beautify HTML<br />
-&#160; * &#160;<span class="term">hl_version</span>&#160;- reporting htmLawed version<br />
-&#160; * &#160;<span class="term">htmLawed</span>&#160;- main function<br />
-&#160; * &#160;<span class="term">kses</span>&#160;- main function of <span class="term">kses</span><br />
-&#160; * &#160;<span class="term">kses_hook</span>&#160;- hook function of <span class="term">kses</span><br />
-<br />
-&#160; The last two are for compatibility with pre-existing code using the <span class="term">kses</span>&#160;script. htmLawed's <span class="term">kses()</span>&#160;basically passes on the filtering task to <span class="term">htmLawed()</span>&#160;function after deciphering <span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;from the argument values supplied to it. <span class="term">kses_hook()</span>&#160;is an empty function and is meant for being filled with custom code if the <span class="term">kses</span>&#160;script users were using one.<br />
-<br />
-&#160; <span class="term">htmLawed()</span>&#160;finalizes <span class="term">$spec</span>&#160;(with the help of <span class="term">hl_spec()</span>) and <span class="term">$config</span>, and globalizes them. Finalization of <span class="term">$config</span>&#160;involves setting default values if an inappropriate or invalid one is supplied. This includes calling <span class="term">hl_regex()</span>&#160;to check well-formedness of regular expression patterns if such expressions are user-supplied through <span class="term">$config</span>. <span class="term">htmLawed()</span>&#160;then removes invalid characters like nulls and <span class="term">x01</span>&#160;and appropriately handles entities using <span class="term">hl_ent()</span>. HTML comments and CDATA sections are identified and treated as per <span class="term">$config</span>&#160;with the help of <span class="term">hl_cmtcd()</span>. When retained, the <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;characters identifying them, and the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters inside them, are replaced with control characters (code-points <span class="term">1</span>&#160;to <span class="term">5</span>) till any tag balancing is completed.<br />
-<br />
-&#160; After this <em>initial processing</em>&#160;<span class="term">htmLawed()</span>&#160;identifies tags using regex and processes them with the help of <span class="term">hl_tag()</span>&#160;-- &#160;a large function that analyzes tag content, filtering it as per HTML standards, <span class="term">$config</span>&#160;and <span class="term">$spec</span>. Among other things, <span class="term">hl_tag()</span>&#160;transforms deprecated elements using <span class="term">hl_tag2()</span>, removes attributes from closing tags, checks attribute values as per <span class="term">$spec</span>&#160;rules using <span class="term">hl_attrval()</span>, and checks URL protocols using <span class="term">hl_prot()</span>. <span class="term">htmLawed()</span>&#160;performs tag balancing and nesting checks with a call to <span class="term">hl_bal()</span>, and optionally compacts/beautifies the output with proper white-spacing with a call to <span class="term">hl_tidy()</span>. The latter temporarily replaces white-space, and <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters inside <span class="term">pre</span>, <span class="term">script</span>&#160;and <span class="term">textarea</span>&#160;elements, and HTML comments and CDATA sections with control characters (code-points <span class="term">1</span>&#160;to <span class="term">5</span>, and <span class="term">7</span>).<br />
-<br />
-&#160; htmLawed permits the use of custom code or <strong>hook functions</strong>&#160;at two stages. The first, called inside <span class="term">htmLawed()</span>, allows the input text as well as the finalized $config and $spec values to be altered right after the initial processing (see <a href="#s3.7">section 3.7</a>). The second is called by <span class="term">hl_tag()</span>&#160;once the tag content is finalized (see <a href="#s3.4.9">section 3.4.9</a>).<br />
-<br />
-&#160; Being dictated by the external and stable HTML standard, htmLawed's objective is very clear-cut and less concerned with tweakability. The code is only minimally annotated with comments -- it is not meant to instruct; PHP developers familiar with the HTML specs will see the logic, and others can always refer to the htmLawed documentation. The compact structuring of the statements is meant to aid in quickly grasping the logic, at least when viewed with code syntax highlighted.
-</div>
-</div>
-<br />
-<hr /><br /><br /><span class="subtle"><small>HTM version of <em><a href="htmLawed_README.txt">htmLawed_README.txt</a></em> generated on 06 Jun, 2012 using <a href="http://www.bioinformatics.org/phplabware/internal_utilities">rTxt2htm</a> from PHP Labware</small></span>
-</div><!-- ended div body -->
-</div><!-- ended div top -->
-</body>
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+<meta http-equiv="Content-Language" content="en" />
+<meta name="description" content="htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter - htmLawed_README.txt - presented with rTxt2htm, a PHP Labware utility" />
+<meta name="keywords" content="htmLawed, HTM, HTML, HTML Tidy, converter, filter, formatter, purifier, sanitizer, XSS, input, PHP, software, code, script, security, cross-site scripting, hack, sanitize, remove, standards, tags, attributes, elements, htmLawed_README.txt, rTxt2htm, PHP Labware" />
+<style type="text/css" media="all">
+<!--/*--><![CDATA[/*><!--*/
+a {text-decoration:none; color: blue;}
+a:hover {color: red;}
+a:visited {color: blue;}
+body {margin: 0; padding: 0;}
+body, div, html, p {font-family: Georgia, 'Times new roman', Times;}
+code.code {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
+div.comment {padding: 5px; color: #999999; font-size: 80%;}
+div.comment a {color: #6699cc;}
+div#body {width: 70%; margin: 5px; padding: 5px;} /* holds non-toc content */
+div#toc {position: fixed; top: 5px; left: 73%; z-index: 2; margin-top: 5px; margin-left: 5px; border: 1px solid gray; padding: 5px; background-color: #ededed; width: 23%; overflow: auto; max-height:94%; font-size: 90%;} /* holds content table (toc) */
+div#top {font-size: 14px; margin: 5px; padding: 5px;} /* holds all content */
+div.monospace {overflow: auto; font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
+div.sub-section {padding-left: 15px;}
+div.sub-sub-section {padding-left: 30px;}
+h1 {font-size: 22px; margin-top: 5px; margin-bottom: 5px;}
+h2 {font-size: 20px; float: left; margin-top: 15px; margin-bottom: 5px;}
+h3 {font-size: 18px; float: left; margin-top: 15px; margin-bottom: 5px;}
+h4 {font-size: 16px; float: left; margin-top: 15px; margin-bottom: 5px;}
+hr {margin-top: 15px; margin-bottom: 5px;}
+input, textarea {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
+p.subtle {color: gray; padding: 0; padding-top: 10px; margin: 0;}
+p.subtle a, p.subtle a:visited {color: #6699cc;}
+span.item-no {color: black;}
+span.subtle {color: gray; margin: 0; padding:0;}
+span.subtle a, span.subtle a:visited {color: #6699cc;}
+span.term {font-family: 'Bitstream vera sans mono', 'Courier New', 'Courier', monospace;}
+span.toc-item {color: black;}
+span.totop {float: right; margin-top: 15px; margin-bottom: 5px;}
+span.totop a, span.totop a:visited {color: #6699cc;}
+@media screen { /* fixes for old IE */
+ * html, * html body {overflow-y: auto!important; height: 100%; margin: 0; padding: 0;}
+ * html div#body {height: 100%; overflow-y: auto; position: relative;}
+ * html div#toc {position: absolute;}
+}
+/*]]>*/-->
+</style>
+<title>htmLawed documentation | htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter</title>
+</head>
+<body>
+<div id="top">
+<h1><a id="peak" name="peak"></a>htmLawed documentation</h1>
+
+<div id="toc"><span class="toc-item"><a href="#s1"><span class="item-no">1</span>&#160; About htmLawed</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.1"><span class="item-no">1.1</span>&#160; Example uses</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.2"><span class="item-no">1.2</span>&#160; Features</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.3"><span class="item-no">1.3</span>&#160; History</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.4"><span class="item-no">1.4</span>&#160; License &amp; copyright</a></span><br />
+&#160; <span class="toc-item"><a href="#s1.5"><span class="item-no">1.5</span>&#160; Terms used here</a></span><br />
+<span class="toc-item"><a href="#s2"><span class="item-no">2</span>&#160; Usage</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.1"><span class="item-no">2.1</span>&#160; Simple</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.2"><span class="item-no">2.2</span>&#160; Configuring htmLawed using the <span class="term">$config</span>&#160;parameter</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.3"><span class="item-no">2.3</span>&#160; Extra HTML specifications using the <span class="term">$spec</span>&#160;parameter</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.4"><span class="item-no">2.4</span>&#160; Performance time &amp; memory usage</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.5"><span class="item-no">2.5</span>&#160; Some security risks to keep in mind</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.6"><span class="item-no">2.6</span>&#160; Use without modifying old <span class="term">kses()</span>&#160;code</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.7"><span class="item-no">2.7</span>&#160; Tolerance for ill-written HTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.8"><span class="item-no">2.8</span>&#160; Limitations &amp; work-arounds</a></span><br />
+&#160; <span class="toc-item"><a href="#s2.9"><span class="item-no">2.9</span>&#160; Examples of usage</a></span><br />
+<span class="toc-item"><a href="#s3"><span class="item-no">3</span>&#160; Details</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.1"><span class="item-no">3.1</span>&#160; Invalid/dangerous characters</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.2"><span class="item-no">3.2</span>&#160; Character references/entities</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.3"><span class="item-no">3.3</span>&#160; HTML elements</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.1"><span class="item-no">3.3.1</span>&#160; HTML comments and <span class="term">CDATA</span>&#160;sections</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.2"><span class="item-no">3.3.2</span>&#160; Tag-transformation for better XHTML-Strict</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.3"><span class="item-no">3.3.3</span>&#160; Tag balancing and proper nesting</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.4"><span class="item-no">3.3.4</span>&#160; Elements requiring child elements</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.3.5"><span class="item-no">3.3.5</span>&#160; Beautify or compact HTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.4"><span class="item-no">3.4</span>&#160; Attributes</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.1"><span class="item-no">3.4.1</span>&#160; Auto-addition of XHTML-required attributes</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.2"><span class="item-no">3.4.2</span>&#160; Duplicate/invalid <span class="term">id</span>&#160;values</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.3"><span class="item-no">3.4.3</span>&#160; URL schemes (protocols) and scripts in attribute values</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.4"><span class="item-no">3.4.4</span>&#160; Absolute &amp; relative URLs</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.5"><span class="item-no">3.4.5</span>&#160; Lower-cased, standard attribute values</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.6"><span class="item-no">3.4.6</span>&#160; Transformation of deprecated attributes</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.7"><span class="item-no">3.4.7</span>&#160; Anti-spam &amp; <span class="term">href</span></a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.8"><span class="item-no">3.4.8</span>&#160; Inline style properties</a></span><br />
+&#160; &#160; <span class="toc-item"><a href="#s3.4.9"><span class="item-no">3.4.9</span>&#160; Hook function for tag content</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.5"><span class="item-no">3.5</span>&#160; Simple configuration directive for most valid XHTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.6"><span class="item-no">3.6</span>&#160; Simple configuration directive for most <em>safe</em>&#160;HTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.7"><span class="item-no">3.7</span>&#160; Using a hook function</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.8"><span class="item-no">3.8</span>&#160; Obtaining <em>finalized</em>&#160;parameter values</a></span><br />
+&#160; <span class="toc-item"><a href="#s3.9"><span class="item-no">3.9</span>&#160; Retaining non-HTML tags in input with mixed markup</a></span><br />
+<span class="toc-item"><a href="#s4"><span class="item-no">4</span>&#160; Other</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.1"><span class="item-no">4.1</span>&#160; Support</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.2"><span class="item-no">4.2</span>&#160; Known issues</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.3"><span class="item-no">4.3</span>&#160; Change-log</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.4"><span class="item-no">4.4</span>&#160; Testing</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.5"><span class="item-no">4.5</span>&#160; Upgrade, &amp; old versions</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.6"><span class="item-no">4.6</span>&#160; Comparison with <span class="term">HTMLPurifier</span></a></span><br />
+&#160; <span class="toc-item"><a href="#s4.7"><span class="item-no">4.7</span>&#160; Use through application plug-ins/modules</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.8"><span class="item-no">4.8</span>&#160; Use in non-PHP applications</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.9"><span class="item-no">4.9</span>&#160; Donate</a></span><br />
+&#160; <span class="toc-item"><a href="#s4.10"><span class="item-no">4.10</span>&#160; Acknowledgements</a></span><br />
+<span class="toc-item"><a href="#s5"><span class="item-no">5</span>&#160; Appendices</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.1"><span class="item-no">5.1</span>&#160; Characters discouraged in HTML</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.2"><span class="item-no">5.2</span>&#160; Valid attribute-element combinations</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.3"><span class="item-no">5.3</span>&#160; CSS 2.1 properties accepting URLs</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.4"><span class="item-no">5.4</span>&#160; Microsoft Windows 1252 character replacements</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.5"><span class="item-no">5.5</span>&#160; URL format</a></span><br />
+&#160; <span class="toc-item"><a href="#s5.6"><span class="item-no">5.6</span>&#160; Brief on htmLawed code</a></span></div><!-- ended div toc -->
+
+<div id="body">
+<br />
+<div class="comment">htmLawed_README.txt, 29 August 2013<br />
+htmLawed 1.1.16, 29 August 2013<br />
+Copyright Santosh Patnaik<br />
+Dual licensed with LGPL 3 and GPL 2+<br />
+A PHP Labware internal utility &#45; <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a>&#160;</div>
+<br />
+
+<div class="section"><h2>
+<a name="s1" id="s1"></a><span class="item-no">1</span>&#160; About htmLawed
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed is a PHP script to process text with HTML markup to make it more compliant with HTML standards and administrative policies. It works by making HTML well-formed with balanced and properly nested tags, neutralizing code that may be used for cross-site scripting (XSS) attacks, allowing only specified HTML tags and attributes, and so on. Such <em>lawing in</em>&#160;of HTML in text used in (X)HTML or XML documents ensures that it is in accordance with the aesthetics, safety and usability requirements set by administrators.<br />
+<br />
+&#160; htmLawed is highly customizable, and fast with low memory usage. Its free and open-source code is in one small file, does not require extensions or libraries, and works in older versions of PHP as well. It is a good alternative to the HTML <a href="http://tidy.sourceforge.net">Tidy</a>&#160;application.<br />
+
+<div class="sub-section"><h3>
+<a name="s1.1" id="s1.1"></a><span class="item-no">1.1</span>&#160; Example uses
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; * &#160;Filtering of text submitted as comments on blogs to allow only certain HTML elements<br />
+<br />
+&#160; * &#160;Making RSS/Atom newsfeed item-content standard-compliant: often one uses an excerpt from an HTML document for the content, and with unbalanced tags, non-numerical entities, etc., such excerpts may not be XML-compliant<br />
+<br />
+&#160; * &#160;Text processing for stricter XML standard-compliance: e.g., to have lowercased <span class="term">x</span>&#160;in hexadecimal numeric entities becomes necessary if an XHTML document with MathML content needs to be served as <span class="term">application/xml</span><br />
+<br />
+&#160; * &#160;Scraping text or data from web-pages<br />
+<br />
+&#160; * &#160;Pretty-printing HTML code<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.2" id="s1.2"></a><span class="item-no">1.2</span>&#160; Features
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Key: <span class="term">&#42;</span>&#160;security feature, <span class="term">^</span>&#160;standard compliance, <span class="term">~</span>&#160;requires setting right options, <span class="term">&#96;</span>&#160;different from <span class="term">Kses</span><br />
+<br />
+&#160; * &#160;make input more <strong>secure</strong>&#160;and <strong>standard-compliant</strong><br />
+&#160; * &#160;use for HTML 4, XHTML 1.0 or 1.1, or even generic <strong>XML</strong>&#160;documents &#160;^~`<br />
+<br />
+&#160; * &#160;<strong>beautify</strong>&#160;or <strong>compact</strong>&#160;HTML &#160;^~`<br />
+<br />
+&#160; * &#160;can <strong>restrict elements</strong>&#160; ^~`<br />
+&#160; * &#160;ensures proper closure of empty elements like <span class="term">img</span>&#160; ^`<br />
+&#160; * &#160;<strong>transform deprecated elements</strong>&#160;like <span class="term">u</span>&#160; ^~`<br />
+&#160; * &#160;HTML <strong>comments</strong>&#160;and <span class="term">CDATA</span>&#160;sections can be permitted &#160;^~`<br />
+&#160; * &#160;elements like <span class="term">script</span>, <span class="term">object</span>&#160;and <span class="term">form</span>&#160;can be permitted &#160;~<br />
+<br />
+&#160; * &#160;<strong>restrict attributes</strong>, including <strong>element-specifically</strong>&#160; ^~`<br />
+&#160; * &#160;remove <strong>invalid attributes</strong>&#160; ^`<br />
+&#160; * &#160;element and attribute names are <strong>lower-cased</strong>&#160; ^<br />
+&#160; * &#160;provide <strong>required attributes</strong>, like <span class="term">alt</span>&#160;for <span class="term">image</span>&#160; ^`<br />
+&#160; * &#160;<strong>transforms deprecated attributes</strong>&#160; ^~`<br />
+&#160; * &#160;attributes <strong>declared only once</strong>&#160; ^`<br />
+<br />
+&#160; * &#160;<strong>restrict attribute values</strong>, including <strong>element-specifically</strong>&#160; ^~`<br />
+&#160; * &#160;a value is declared for <em>empty</em>&#160;(<em>minimized</em>) attributes like <span class="term">checked</span>&#160; ^<br />
+&#160; * &#160;check for potentially dangerous attribute values &#160;*~<br />
+&#160; * &#160;ensure <strong>unique</strong>&#160;<span class="term">id</span>&#160;attribute values &#160;^~`<br />
+&#160; * &#160;<strong>double-quote</strong>&#160;attribute values &#160;^<br />
+&#160; * &#160;lower-case <strong>standard attribute values</strong>&#160;like <span class="term">password</span>&#160; ^`<br />
+&#160; * &#160;permit custom, non-standard attributes as well as custom rules for standard attributes &#160;~`<br />
+<br />
+&#160; * &#160;<strong>attribute-specific URL protocol/scheme restriction</strong>&#160; *~`<br />
+&#160; * &#160;disable <strong>dynamic expressions</strong>&#160;in <span class="term">style</span>&#160;values &#160;*~`<br />
+<br />
+&#160; * &#160;neutralize invalid named character entities &#160;^`<br />
+&#160; * &#160;<strong>convert</strong>&#160;hexadecimal numeric entities to decimal ones, or vice versa &#160;^~`<br />
+&#160; * &#160;convert named entities to numeric ones for generic XML use &#160;^~`<br />
+<br />
+&#160; * &#160;remove <strong>null</strong>&#160;characters &#160;*<br />
+&#160; * &#160;neutralize potentially dangerous proprietary Netscape <strong>Javascript entities</strong>&#160; *<br />
+&#160; * &#160;replace potentially dangerous <strong>soft-hyphen</strong>&#160;character in URL-accepting attribute values with spaces &#160;*<br />
+<br />
+&#160; * &#160;remove common <strong>invalid characters</strong>&#160;not allowed in HTML or XML &#160;^`<br />
+&#160; * &#160;replace <strong>characters from Microsoft applications</strong>&#160;like <span class="term">Word</span>&#160;that are discouraged in HTML or XML &#160;^~`<br />
+&#160; * &#160;neutralize entities for characters invalid or discouraged in HTML or XML &#160;^`<br />
+&#160; * &#160;appropriately neutralize <span class="term">&lt;</span>, <span class="term">&amp;</span>, <span class="term">"</span>, and <span class="term">&gt;</span>&#160;characters &#160;^*`<br />
+<br />
+&#160; * &#160;understands improperly spaced tag content (like, spread over more than a line) and properly spaces them &#160;`<br />
+&#160; * &#160;attempts to <strong>balance tags</strong>&#160;for well-formedness &#160;^~`<br />
+&#160; * &#160;understands when <strong>omitable closing tags</strong>&#160;like <span class="term">&lt;/p&gt;</span>&#160;(allowed in HTML 4, transitional, e.g.) are missing &#160;^~`<br />
+&#160; * &#160;attempts to permit only <strong>validly nested tags</strong>&#160; ^~`<br />
+&#160; * &#160;option to <strong>remove or neutralize bad content</strong>&#160;^~`<br />
+&#160; * &#160;attempts to <strong>rectify common errors of plain-text misplacement</strong>&#160;(e.g., directly inside <span class="term">blockquote</span>) ^~`<br />
+<br />
+&#160; * &#160;fast, <strong>non-OOP</strong>&#160;code of ~45 kb incurring peak basal memory usage of ~0.5 MB<br />
+&#160; * &#160;<strong>compatible</strong>&#160;with pre-existing code using <span class="term">Kses</span>&#160;(the filter used by <span class="term">WordPress</span>)<br />
+<br />
+&#160; * &#160;optional <strong>anti-spam</strong>&#160;measures such as addition of <span class="term">rel="nofollow"</span>&#160;and link-disabling &#160;~`<br />
+&#160; * &#160;optionally makes <strong>relative URLs absolute</strong>, and vice versa &#160;~`<br />
+<br />
+&#160; * &#160;optionally mark <span class="term">&amp;</span>&#160;to identify the entities for <span class="term">&amp;</span>, <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;introduced by htmLawed &#160;~`<br />
+<br />
+&#160; * &#160;allows deployment of powerful <strong>hook functions</strong>&#160;to <strong>inject</strong>&#160;HTML, <strong>consolidate</strong>&#160;<span class="term">style</span>&#160;attributes to <span class="term">class</span>, finely check attribute values, etc. &#160;~`<br />
+<br />
+&#160; * &#160;<strong>independent of character encoding</strong>&#160;of input and does not affect it<br />
+<br />
+&#160; * &#160;<strong>tolerance for ill-written HTML</strong>&#160;to a certain degree<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.3" id="s1.3"></a><span class="item-no">1.3</span>&#160; History
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed was created in 2007 for use with <span class="term">LabWiki</span>, a wiki software developed at PHP Labware, as a suitable software could not be found. Existing PHP software like <span class="term">Kses</span>&#160;and <span class="term">HTMLPurifier</span>&#160;were deemed inadequate, slow, resource-intensive, or dependent on an extension or external application like <span class="term">HTML Tidy</span>. The core logic of htmLawed, that of identifying HTML elements and attributes, was based on the <span class="term">Kses</span>&#160;(version 0.2.2) HTML filter software of Ulf Harnhammar (it can still be used with code that uses <span class="term">Kses</span>; see <a href="#s2.6">section 2.6</a>.).<br />
+<br />
+&#160; See <a href="#s4.3">section 4.3</a>&#160;for a detailed log of changes in htmLawed over the years, and <a href="#s4.10">section 4.10</a>&#160;for acknowledgements.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.4" id="s1.4"></a><span class="item-no">1.4</span>&#160; License &amp; copyright
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed is free and open-source software dual copyrighted by Santosh Patnaik, MD, PhD, and licensed under LGPL license version <a href="http://www.gnu.org/licenses/lgpl-3.0.txt">3</a>, and GPL license version <a href="http://www.gnu.org/licenses/gpl-2.0.txt">2</a>&#160;(or later).<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.5" id="s1.5"></a><span class="item-no">1.5</span>&#160; Terms used here
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; In this document, only HTML body-level elements are considered. htmLawed does not have support for head-level elements, <span class="term">body</span>, and the frame-level elements, <span class="term">frameset</span>, <span class="term">frame</span>&#160;and <span class="term">noframes</span>, and these elements are ignored here.<br />
+<br />
+&#160; * &#160;<em>administrator</em>&#160;- or admin; person setting up the code that utilizes htmLawed; also, <em>user</em><br />
+&#160; * &#160;<em>attributes</em>&#160;- name-value pairs like <span class="term">href="http&#58;//x.com"</span>&#160;in opening tags<br />
+&#160; * &#160;<em>author</em>&#160;- see <em>writer</em><br />
+&#160; * &#160;<em>character</em>&#160;- atomic unit of text; internally represented by a numeric <em>code-point</em>&#160;as specified by the <em>encoding</em>&#160;or <em>charset</em>&#160;in use<br />
+&#160; * &#160;<em>entity</em>&#160;- markup like <span class="term">&amp;gt;</span>&#160;and <span class="term">&amp;#160;</span>&#160;used to refer to a character<br />
+&#160; * &#160;<em>element</em>&#160;- HTML element like <span class="term">a</span>&#160;and <span class="term">img</span><br />
+&#160; * &#160;<em>element content</em>&#160;- &#160;content between the opening and closing tags of an element, like <span class="term">click</span>&#160;of the <span class="term">&lt;a href="x"&gt;click&lt;/a&gt;</span>&#160;element<br />
+&#160; * &#160;<em>HTML</em>&#160;- implies XHTML unless specified otherwise<br />
+&#160; * &#160;<em>HTML body</em>&#160;- Complete HTML documents typically have a <em>head</em>&#160;and a <em>body</em>&#160;container. Information in <em>head</em>&#160;specifies title of the document, etc., whereas that in the body informs what is to be displayed on a web-page; it is only the elements for <em>body</em>, except <span class="term">frames</span>, <span class="term">frameset</span>&#160;and <span class="term">noframes</span>&#160;that htmLawed is concerned with<br />
+&#160; * &#160;<em>input</em>&#160;- text given to htmLawed to process<br />
+&#160; * &#160;<em>processing</em>&#160;- involves filtering, correction, etc., of input<br />
+&#160; * &#160;<em>safe</em>&#160;- absence or reduction of certain characters and HTML elements and attributes in HTML of text that can otherwise potentially, and circumstantially, expose text readers to security vulnerabilities like cross-site scripting attacks (XSS)<br />
+&#160; * &#160;<em>scheme</em>&#160;- a URL protocol like <span class="term">http</span>&#160;and <span class="term">ftp</span><br />
+&#160; * &#160;<em>specifications</em>&#160;- standard specifications, for HTML4, HTML5, Ruby, etc.<br />
+&#160; * &#160;<em>style property</em>&#160;- terms like <span class="term">border</span>&#160;and <span class="term">height</span>&#160;for which declarations are made in values for the <span class="term">style</span>&#160;attribute of elements<br />
+&#160; * &#160;<em>tag</em>&#160;- markers like <span class="term">&lt;a href="x"&gt;</span>&#160;and <span class="term">&lt;/a&gt;</span>&#160;delineating element content; the opening tag can contain attributes<br />
+&#160; * &#160;<em>tag content</em>&#160;- consists of tag markers <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, element names like <span class="term">div</span>, and possibly attributes<br />
+&#160; * &#160;<em>user</em>&#160;- administrator<br />
+&#160; * &#160;<em>writer</em>&#160;- end-user like a blog commenter providing the input that is to be processed; also, <em>author</em><br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.6" id="s1.6"></a><span class="item-no">1.6</span>&#160; Availability
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can be downloaded for free at its <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">website</a>. Besides the <span class="term">htmLawed.php</span>&#160;file, the download has the htmLawed documentation (this document) in plain <a href="htmLawed_README.txt">text</a>&#160;and <a href="htmLawed_README.htm">HTML</a>&#160;formats, a script for <a href="htmLawedTest.php">testing</a>, and a text file for <a href="htmLawed_TESTCASE.txt">test-cases</a>. htmLawed is also available as a PHP class (OOP code) on its website.<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s2" id="s2"></a><span class="item-no">2</span>&#160; Usage
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed works in PHP version 4.4 or higher. Either <span class="term">include()</span>&#160;the <span class="term">htmLawed.php</span>&#160;file, or copy-paste the entire code. To use with PHP 4.3, have the following code included:<br />
+<br />
+
+<code class="code">&#160; &#160; if(!function_exists(&#39;ctype_digit&#39;)){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;function ctype_digit($var){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; return ((int) $var == $var);</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;}</code>
+<br />
+
+<code class="code">&#160; &#160; }</code>
+<br />
+
+<div class="sub-section"><h3>
+<a name="s2.1" id="s2.1"></a><span class="item-no">2.1</span>&#160; Simple
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The input text to be processed, <span class="term">$text</span>, is passed as an argument of type string; <span class="term">htmLawed()</span>&#160;returns the processed string:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text);</code>
+<br />
+<br />
+&#160; With the <span class="term">htmLawed class</span>&#160;(<a href="#s1.6">section 1.6</a>), usage is:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed&#58;&#58;hl($text);</code>
+<br />
+<br />
+&#160; <strong>Notes</strong>: (1) If input is from a <span class="term">$_GET</span>&#160;or <span class="term">$_POST</span>&#160;value, and <span class="term">magic quotes</span>&#160;are enabled on the PHP setup, run <span class="term">stripslashes()</span>&#160;on the input before passing to htmLawed. (2) htmLawed does not have support for head-level elements, <span class="term">body</span>, and the frame-level elements, <span class="term">frameset</span>, <span class="term">frame</span>&#160;and <span class="term">noframes</span>.<br />
+<br />
+&#160; By default, htmLawed will process the text allowing all valid HTML elements/tags, secure URL scheme/CSS style properties, etc. It will allow <span class="term">CDATA</span>&#160;sections and HTML comments, balance tags, and ensure proper nesting of elements. Such actions can be configured using two other optional arguments -- <span class="term">$config</span>&#160;and <span class="term">$spec</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, $config, $spec);</code>
+<br />
+<br />
+&#160; The <span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;arguments are detailed below. Some examples are shown in <a href="#s2.9">section 2.9</a>. For maximum protection against <span class="term">XSS</span>&#160;and other scripting attacks (e.g., by disallowing Javascript code), consider using the <span class="term">safe</span>&#160;parameter; see <a href="#s3.6">section 3.6</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.2" id="s2.2"></a><span class="item-no">2.2</span>&#160; Configuring htmLawed using the <span class="term">$config</span>&#160;parameter
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; <span class="term">$config</span>&#160;instructs htmLawed on how to tackle certain tasks. When <span class="term">$config</span>&#160;is not specified, or not set as an array (e.g., <span class="term">$config = 1</span>), htmLawed will take default actions. One or many of the task-action or value-specification pairs can be specified in <span class="term">$config</span>&#160;as array key-value pairs. If a parameter is not specified, htmLawed will use the default value/action indicated further below.<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;comment&#39;=&gt;0, &#39;cdata&#39;=&gt;1);</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, $config);</code>
+<br />
+<br />
+&#160; Or,<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, array(&#39;comment&#39;=&gt;0, &#39;cdata&#39;=&gt;1));</code>
+<br />
+<br />
+&#160; Below are the possible value-specification combinations. In PHP code, values that are integers should not be quoted and should be used as numeric types (unless meant as string/text).<br />
+<br />
+&#160; Key: <span class="term">&#42;</span>&#160;default, <span class="term">^</span>&#160;different default when htmLawed is used in the Kses-compatible mode (see <a href="#s2.6">section 2.6</a>), <span class="term">~</span>&#160;different default when <span class="term">valid_xhtml</span>&#160;is set to <span class="term">1</span>&#160;(see <a href="#s3.5">section 3.5</a>), <span class="term">"</span>&#160;different default when <span class="term">safe</span>&#160;is set to <span class="term">1</span>&#160;(see <a href="#s3.6">section 3.6</a>)<br />
+<br />
+&#160; <strong>abs_url</strong><br />
+&#160; Make URLs absolute or relative; <span class="term">$config["base_url"]</span>&#160;needs to be set; see <a href="#s3.4.4">section 3.4.4</a><br />
+<br />
+&#160; <span class="term">-1</span>&#160;- make relative<br />
+&#160; <span class="term">0</span>&#160;- no action &#160;*<br />
+&#160; <span class="term">1</span>&#160;- make absolute<br />
+<br />
+&#160; <strong>and_mark</strong><br />
+&#160; Mark <span class="term">&amp;</span>&#160;characters in the original input; see <a href="#s3.2">section 3.2</a><br />
+<br />
+&#160; <strong>anti_link_spam</strong><br />
+&#160; Anti-link-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no measure taken &#160;*<br />
+&#160; <em>array("regex1", "regex2")</em>&#160;- will ensure a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;in its value in case the <span class="term">href</span>&#160;attribute value matches the regular expression pattern <span class="term">regex1</span>, and/or will remove <span class="term">href</span>&#160;if its value matches the regular expression pattern <span class="term">regex2</span>. E.g., <span class="term">array("/./", "/&#58;//\W&#42;(?!(abc\.com|xyz\.org))/")</span>; see <a href="#s3.4.7">section 3.4.7</a>&#160;for more.<br />
+<br />
+&#160; <strong>anti_mail_spam</strong><br />
+&#160; Anti-mail-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no measure taken &#160;*<br />
+&#160; <em>word</em>&#160;- <span class="term">@</span>&#160;in mail address in <span class="term">href</span>&#160;attribute value is replaced with specified <em>word</em><br />
+<br />
+&#160; <strong>balance</strong><br />
+&#160; Balance tags for well-formedness and proper nesting; see <a href="#s3.3.3">section 3.3.3</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no<br />
+&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
+<br />
+&#160; <strong>base_url</strong><br />
+&#160; Base URL value that needs to be set if <span class="term">$config["abs_url"]</span>&#160;is not <span class="term">0</span>; see <a href="#s3.4.4">section 3.4.4</a><br />
+<br />
+&#160; <strong>cdata</strong><br />
+&#160; Handling of <span class="term">CDATA</span>&#160;sections; see <a href="#s3.3.1">section 3.3.1</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- don't consider <span class="term">CDATA</span>&#160;sections as markup and proceed as if plain text &#160;^"<br />
+&#160; <span class="term">1</span>&#160;- remove<br />
+&#160; <span class="term">2</span>&#160;- allow, but neutralize any <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;inside by converting them to named entities<br />
+&#160; <span class="term">3</span>&#160;- allow &#160;*<br />
+<br />
+&#160; <strong>clean_ms_char</strong><br />
+&#160; Replace discouraged characters introduced by Microsoft Word, etc.; see <a href="#s3.1">section 3.1</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- yes<br />
+&#160; <span class="term">2</span>&#160;- yes, but replace special single &amp; double quotes with ordinary ones<br />
+<br />
+&#160; <strong>comment</strong><br />
+&#160; Handling of HTML comments; see <a href="#s3.3.1">section 3.3.1</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- don't consider comments as markup and proceed as if plain text &#160;^"<br />
+&#160; <span class="term">1</span>&#160;- remove<br />
+&#160; <span class="term">2</span>&#160;- allow, but neutralize any <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;inside by converting to named entities<br />
+&#160; <span class="term">3</span>&#160;- allow &#160;*<br />
+<br />
+&#160; <strong>css_expression</strong><br />
+&#160; Allow dynamic CSS expression by not removing the expression from CSS property values in <span class="term">style</span>&#160;attributes; see <a href="#s3.4.8">section 3.4.8</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- remove &#160;*<br />
+&#160; <span class="term">1</span>&#160;- allow<br />
+<br />
+&#160; <strong>deny_attribute</strong><br />
+&#160; Denied HTML attributes; see <a href="#s3.4">section 3.4</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- none &#160;*<br />
+&#160; <em>string</em>&#160;- dictated by values in <em>string</em><br />
+&#160; <span class="term">on&#42;</span>&#160;(like <span class="term">onfocus</span>) attributes not allowed - "<br />
+<br />
+&#160; <strong>direct_nest_list</strong><br />
+&#160; Allow direct nesting of a list within another without requiring it to be a list item; see <a href="#s3.3.4">section 3.3.4</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- yes<br />
+<br />
+&#160; <strong>elements</strong><br />
+&#160; Allowed HTML elements; see <a href="#s3.3">section 3.3</a><br />
+<br />
+&#160; <span class="term">&#42; -center -dir -font -isindex -menu -s -strike -u</span>&#160;- &#160;~<br />
+&#160; <span class="term">applet, embed, iframe, object, script</span>&#160;not allowed - "<br />
+<br />
+&#160; <strong>hexdec_entity</strong><br />
+&#160; Allow hexadecimal numeric entities and do not convert to the more widely accepted decimal ones, or convert decimal to hexadecimal ones; see <a href="#s3.2">section 3.2</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no<br />
+&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
+&#160; <span class="term">2</span>&#160;- convert decimal to hexadecimal ones<br />
+<br />
+&#160; <strong>hook</strong><br />
+&#160; Name of an optional hook function to alter the input string, <span class="term">$config</span>&#160;or <span class="term">$spec</span>&#160;before htmLawed starts its main work; see <a href="#s3.7">section 3.7</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no hook function &#160;*<br />
+&#160; <em>name</em>&#160;- <em>name</em>&#160;is name of the hook function (<span class="term">kses_hook</span>&#160; ^)<br />
+<br />
+&#160; <strong>hook_tag</strong><br />
+&#160; Name of an optional hook function to alter tag content finalized by htmLawed; see <a href="#s3.4.9">section 3.4.9</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no hook function &#160;*<br />
+&#160; <em>name</em>&#160;- <em>name</em>&#160;is name of the hook function<br />
+<br />
+&#160; <strong>keep_bad</strong><br />
+&#160; Neutralize bad tags by converting <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;to entities, or remove them; see <a href="#s3.3.3">section 3.3.3</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- remove &#160;^<br />
+&#160; <span class="term">1</span>&#160;- neutralize both tags and element content<br />
+&#160; <span class="term">2</span>&#160;- remove tags but neutralize element content<br />
+&#160; <span class="term">3</span>&#160;and <span class="term">4</span>&#160;- like <span class="term">1</span>&#160;and <span class="term">2</span>&#160;but remove if text (<span class="term">pcdata</span>) is invalid in parent element<br />
+&#160; <span class="term">5</span>&#160;and <span class="term">6</span>&#160;* - &#160;like <span class="term">3</span>&#160;and <span class="term">4</span>&#160;but line-breaks, tabs and spaces are left<br />
+<br />
+&#160; <strong>lc_std_val</strong><br />
+&#160; For XHTML compliance, predefined, standard attribute values, like <span class="term">get</span>&#160;for the <span class="term">method</span>&#160;attribute of <span class="term">form</span>, must be lowercased; see <a href="#s3.4.5">section 3.4.5</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no<br />
+&#160; <span class="term">1</span>&#160;- yes &#160;*<br />
+<br />
+&#160; <strong>make_tag_strict</strong><br />
+&#160; Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: <span class="term">applet</span>&#160;<span class="term">center</span>&#160;<span class="term">dir</span>&#160;<span class="term">embed</span>&#160;<span class="term">font</span>&#160;<span class="term">isindex</span>&#160;<span class="term">menu</span>&#160;<span class="term">s</span>&#160;<span class="term">strike</span>&#160;<span class="term">u</span>; see <a href="#s3.3.2">section 3.3.2</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;^<br />
+&#160; <span class="term">1</span>&#160;- yes, but leave <span class="term">applet</span>, <span class="term">embed</span>&#160;and <span class="term">isindex</span>&#160;elements that currently can't be transformed &#160;*<br />
+&#160; <span class="term">2</span>&#160;- yes, removing <span class="term">applet</span>, <span class="term">embed</span>&#160;and <span class="term">isindex</span>&#160;elements and their contents (nested elements remain) &#160;~<br />
+<br />
+&#160; <strong>named_entity</strong><br />
+&#160; Allow non-universal named HTML entities, or convert to numeric ones; see <a href="#s3.2">section 3.2</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- convert<br />
+&#160; <span class="term">1</span>&#160;- allow &#160;*<br />
+<br />
+&#160; <strong>no_deprecated_attr</strong><br />
+&#160; Allow deprecated attributes or transform them; see <a href="#s3.4.6">section 3.4.6</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- allow &#160;^<br />
+&#160; <span class="term">1</span>&#160;- transform, but <span class="term">name</span>&#160;attributes for <span class="term">a</span>&#160;and <span class="term">map</span>&#160;are retained &#160;*<br />
+&#160; <span class="term">2</span>&#160;- transform<br />
+<br />
+&#160; <strong>parent</strong><br />
+&#160; Name of the parent element, possibly imagined, that will hold the input; see <a href="#s3.3">section 3.3</a><br />
+<br />
+&#160; <strong>safe</strong><br />
+&#160; Magic parameter to make input the most secure against XSS without needing to specify other relevant <span class="term">$config</span>&#160;parameters; see <a href="#s3.6">section 3.6</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- will auto-adjust other relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">"</span>&#160;in this list)<br />
+<br />
+&#160; <strong>schemes</strong><br />
+&#160; Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or <span class="term">!</span>&#160;to <em>deny</em>&#160;any URL); <span class="term">&#42;</span>&#160;covers all unspecified attributes; see <a href="#s3.4.3">section 3.4.3</a><br />
+<br />
+&#160; <span class="term">href&#58; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; &#42;&#58;file, http, https</span>&#160; *<br />
+&#160; <span class="term">&#42;&#58; ftp, gopher, http, https, mailto, news, nntp, telnet</span>&#160; ^<br />
+&#160; <span class="term">href&#58; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style&#58; !; &#42;&#58;file, http, https</span>&#160; "<br />
+<br />
+&#160; <strong>show_setting</strong><br />
+&#160; Name of a PHP variable to assign the <em>finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values; see <a href="#s3.8">section 3.8</a><br />
+<br />
+&#160; <strong>style_pass</strong><br />
+&#160; Do not look at <span class="term">style</span>&#160;attribute values, letting them through without any alteration<br />
+<br />
+&#160; <span class="term">0</span>&#160;- no *<br />
+&#160; <span class="term">1</span>&#160;- htmLawed will let through any <span class="term">style</span>&#160;value; see <a href="#s3.4.8">section 3.4.8</a><br />
+<br />
+&#160; <strong>tidy</strong><br />
+&#160; Beautify or compact HTML code; see <a href="#s3.3.5">section 3.3.5</a><br />
+<br />
+&#160; <span class="term">-1</span>&#160;- compact<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;or <span class="term">string</span>&#160;- beautify (custom format specified by <span class="term">string</span>)<br />
+<br />
+&#160; <strong>unique_ids</strong><br />
+&#160; <span class="term">id</span>&#160;attribute value checks; see <a href="#s3.4.2">section 3.4.2</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;^<br />
+&#160; <span class="term">1</span>&#160;- remove duplicate and/or invalid ones &#160;*<br />
+&#160; <em>word</em>&#160;- remove invalid ones and replace duplicate ones with new and unique ones based on the <em>word</em>; the admin-specified <em>word</em>, like <span class="term">my_</span>, should begin with a letter (a-z) and can contain letters, digits, <span class="term">.</span>, <span class="term">_</span>, <span class="term">-</span>, and <span class="term">&#58;</span>.<br />
+<br />
+&#160; <strong>valid_xhtml</strong><br />
+&#160; Magic parameter to make input the most valid XHTML without needing to specify other relevant <span class="term">$config</span>&#160;parameters; see <a href="#s3.5">section 3.5</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- will auto-adjust other relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">~</span>&#160;in this list)<br />
+<br />
+&#160; <strong>xml:lang</strong><br />
+&#160; Auto-adding <span class="term">xml&#58;lang</span>&#160;attribute; see <a href="#s3.4.1">section 3.4.1</a><br />
+<br />
+&#160; <span class="term">0</span>&#160;- no &#160;*<br />
+&#160; <span class="term">1</span>&#160;- add if <span class="term">lang</span>&#160;attribute is present<br />
+&#160; <span class="term">2</span>&#160;- add if <span class="term">lang</span>&#160;attribute is present, and remove <span class="term">lang</span>&#160; ~<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.3" id="s2.3"></a><span class="item-no">2.3</span>&#160; Extra HTML specifications using the $spec parameter
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The <span class="term">$spec</span>&#160;argument of htmLawed can be used to disallow an otherwise legal attribute for an element, or to restrict the attribute's values. This can also be helpful as a security measure (e.g., in certain versions of browsers, certain values can cause buffer overflows and denial of service attacks), or in enforcing admin policies. <span class="term">$spec</span>&#160;is specified as a string of text containing one or more <em>rules</em>, with multiple rules separated from each other by a semi-colon (<span class="term">;</span>). E.g.,<br />
+<br />
+
+<code class="code">&#160; &#160; $spec = &#39;i=-&#42;; td, tr=style, id, -&#42;; a=id(match="/[a-z][a-z\d.&#58;\-&#96;"]&#42;/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, $config, $spec);</code>
+<br />
+<br />
+&#160; Or,<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, $config, &#39;i=-&#42;; td, tr=style, id, -&#42;; a=id(match="/[a-z][a-z\d.&#58;\-&#96;"]&#42;/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt&#39;);</code>
+<br />
+<br />
+&#160; A rule begins with an HTML <strong>element</strong>&#160;name(s) (<em>rule-element</em>), for which the rule applies, followed by an equal (<span class="term">=</span>) sign. A rule-element may represent multiple elements if comma (,)-separated element names are used. E.g., <span class="term">th,td,tr=</span>.<br />
+<br />
+&#160; Rest of the rule consists of comma-separated HTML <strong>attribute names</strong>. A minus (<span class="term">-</span>) character before an attribute means that the attribute is not permitted inside the rule-element. E.g., <span class="term">-width</span>. To deny all attributes, <span class="term">-&#42;</span>&#160;can be used.<br />
+<br />
+&#160; Following shows examples of rule excerpts with rule-element <span class="term">a</span>&#160;and the attributes that are being permitted:<br />
+<br />
+&#160; * &#160;<span class="term">a=</span>&#160;- all<br />
+&#160; * &#160;<span class="term">a=id</span>&#160;- all<br />
+&#160; * &#160;<span class="term">a=href, title, -id, -onclick</span>&#160;- all except <span class="term">id</span>&#160;and <span class="term">onclick</span><br />
+&#160; * &#160;<span class="term">a=&#42;, id, -id</span>&#160;- all except <span class="term">id</span><br />
+&#160; * &#160;<span class="term">a=-&#42;</span>&#160;- none<br />
+&#160; * &#160;<span class="term">a=-&#42;, href, title</span>&#160;- none except <span class="term">href</span>&#160;and <span class="term">title</span><br />
+&#160; * &#160;<span class="term">a=-&#42;, -id, href, title</span>&#160;- none except <span class="term">href</span>&#160;and <span class="term">title</span><br />
+<br />
+&#160; Rules regarding <strong>attribute values</strong>&#160;are optionally specified inside round brackets after attribute names in slash ('/')-separated <em>parameter = value</em>&#160;pairs. E.g., <span class="term">title(maxlen=30/minlen=5)</span>. None or one or more of the following parameters may be specified:<br />
+<br />
+&#160; * &#160;<span class="term">oneof</span>&#160;- one or more choices separated by <span class="term">|</span>&#160;that the value should match; if only one choice is provided, then the value must match that choice<br />
+<br />
+&#160; * &#160;<span class="term">noneof</span>&#160;- one or more choices separated by <span class="term">|</span>&#160;that the value should not match<br />
+<br />
+&#160; * &#160;<span class="term">maxlen</span>&#160;and <span class="term">minlen</span>&#160;- upper and lower limits for the number of characters in the attribute value; specified in numbers<br />
+<br />
+&#160; * &#160;<span class="term">maxval</span>&#160;and <span class="term">minval</span>&#160;- upper and lower limits for the numerical value specified in the attribute value; specified in numbers<br />
+<br />
+&#160; * &#160;<span class="term">match</span>&#160;and <span class="term">nomatch</span>&#160;- pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers<br />
+<br />
+&#160; * &#160;<span class="term">default</span>&#160;- a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters<br />
+<br />
+&#160; If <span class="term">default</span>&#160;is not set and the attribute value does not satisfy any of the specified parameters, then the attribute is removed. The <span class="term">default</span>&#160;value can also be used to force all attribute declarations to take the same value (by getting the values declared illegal by setting, e.g., <span class="term">maxlen</span>&#160;to <span class="term">-1</span>).<br />
+<br />
+&#160; Examples with <em>input</em>&#160;<span class="term">&lt;input title="WIDTH" value="10em" /&gt;&lt;input title="length" value="5" /&gt;</span>&#160;are shown below.<br />
+<br />
+&#160; <em>Rule</em>: <span class="term">input=title(maxlen=60/minlen=6), value</span><br />
+&#160; <em>Output</em>: <span class="term">&lt;input value="10em" /&gt;&lt;input title="length" value="5" /&gt;</span><br />
+<br />
+&#160; <em>Rule</em>: <span class="term">input=title(), value(maxval=8/default=6)</span><br />
+&#160; <em>Output</em>: <span class="term">&lt;input title="WIDTH" value="6" /&gt;&lt;input title="length" value="5" /&gt;</span><br />
+<br />
+&#160; <em>Rule</em>: <span class="term">input=title(nomatch=%w.d%i), value(match=%em%/default=6em)</span><br />
+&#160; <em>Output</em>: <span class="term">&lt;input value="10em" /&gt;&lt;input title="length" value="6em" /&gt;</span><br />
+<br />
+&#160; <em>Rule</em>: <span class="term">input=title(oneof=height|depth/default=depth), value(noneof=5|6)</span><br />
+&#160; <em>Output</em>: <span class="term">&lt;input title="depth" value="10em" /&gt;&lt;input title="depth" /&gt;</span><br />
+<br />
+&#160; <strong>Special characters</strong>: The characters <span class="term">;</span>, <span class="term">,</span>, <span class="term">/</span>, <span class="term">(</span>, <span class="term">)</span>, <span class="term">|</span>, <span class="term">~</span>&#160;and space have special meanings in the rules. Words in the rules that use such characters, or the characters themselves, should be <em>escaped</em>&#160;by enclosing in pairs of double-quotes (<span class="term">"</span>). A back-tick (<span class="term">&#96;</span>) can be used to escape a literal <span class="term">"</span>. An example rule illustrating this is <span class="term">input=value(maxlen=30/match="/^\w/"/default="your &#96;"ID&#96;"")</span>.<br />
+<br />
+&#160; <strong>Note</strong>: To deny an attribute for all elements for which it is legal, <span class="term">$config["deny_attribute"]</span>&#160;(see <a href="#s3.4">section 3.4</a>) can be used instead of <span class="term">$spec</span>. Also, attributes can be allowed element-specifically through <span class="term">$spec</span>&#160;while being denied globally through <span class="term">$config["deny_attribute"]</span>. The <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) can also be possibly used to implement a functionality like that achieved using <span class="term">$spec</span>&#160;functionality.<br />
+<br />
+&#160; <span class="term">$spec</span>&#160;can also be used to permit custom, non-standard attributes as well as custom rules for standard attributes. Thus, the following value of <span class="term">$spec</span>&#160;will permit the custom uses of the standard <span class="term">rel</span>&#160;attribute in <span class="term">input</span>&#160;(not permitted as per standards) and of a non-standard attribute, <span class="term">vFlag</span>, in <span class="term">img</span>.<br />
+<br />
+
+<code class="code">&#160; &#160; $spec = &#39;img=vFlag; input=rel&#39;</code>
+<br />
+<br />
+&#160; The attribute names can contain alphabets, colons (:) and hyphens (-), but they must start with an alphabet.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.4" id="s2.4"></a><span class="item-no">2.4</span>&#160; Performance time &amp; memory usage
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The time and memory consumed during text processing by htmLawed depends on its configuration, the size of the input, and the amount, nestedness and well-formedness of the HTML markup within the input. In particular, tag balancing and beautification each can increase the processing time by about a quarter.<br />
+<br />
+&#160; The htmLawed <a href="htmLawedTest.php">demo</a>&#160;can be used to evaluate the performance and effects of different types of input and <span class="term">$config</span>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.5" id="s2.5"></a><span class="item-no">2.5</span>&#160; Some security risks to keep in mind
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; When setting the parameters/arguments (like those to allow certain HTML elements) for use with htmLawed, one should bear in mind that the setting may let through potentially <em>dangerous</em>&#160;HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc. Unless end-users, either people or software, supplying the content are completely trusted, security issues arising from the degree of HTML usage permitted through htmLawed's setting should be considered. For example, following increase security risks:<br />
+<br />
+&#160; * &#160;Allowing <span class="term">script</span>, <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>&#160;or <span class="term">object</span>&#160;elements, or certain of their attributes like <span class="term">allowscriptaccess</span><br />
+<br />
+&#160; * &#160;Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., <span class="term">&lt;!--[if gte IE 4]&gt;&lt;script&gt;alert("xss");&lt;/script&gt;&lt;![endif]--&gt;</span><br />
+<br />
+&#160; * &#160;Allowing dynamic CSS expressions (some Internet Explorer versions are vulnerable)<br />
+<br />
+&#160; * &#160;Allowing the <span class="term">style</span>&#160;attribute<br />
+<br />
+&#160; To remove <em>unsecure</em>&#160;HTML, code-developers using htmLawed must set <span class="term">$config</span>&#160;appropriately. E.g., <span class="term">$config["elements"] = "&#42; -script"</span>&#160;to deny the <span class="term">script</span>&#160;element (<a href="#s3.3">section 3.3</a>), <span class="term">$config["safe"] = 1</span>&#160;to auto-configure ceratin htmLawed parameters for maximizing security (<a href="#s3.6">section 3.6</a>), etc.<br />
+<br />
+&#160; Permitting the <span class="term">&#42;style&#42;</span>&#160;attribute brings in risks of <em>click-jacking</em>, <em>phishing</em>, web-page overlays, etc., <em>even</em>&#160;when the <span class="term">safe</span>&#160;parameter is enabled (see <a href="#s3.6">section 3.6</a>). Except for URLs and a few other things like CSS dynamic expressions, htmLawed currently does not check every CSS style property. It does provide ways for the code-developer implementing htmLawed to do such checks through htmLawed's <span class="term">$spec</span>&#160;argument, and through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.8">section 3.4.8</a>&#160;for more). Disallowing <span class="term">style</span>&#160;completely and relying on CSS classes and stylesheet files is recommended.<br />
+<br />
+&#160; htmLawed does not check or correct the character <strong>encoding</strong>&#160;of the input it receives. In conjunction with permissive circumstances, such as when the character encoding is left undefined through HTTP headers or HTML <span class="term">meta</span>&#160;tags, this can allow for an exploit (like Google's <em>UTF-7/XSS</em>&#160;vulnerability of the past).<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.6" id="s2.6"></a><span class="item-no">2.6</span>&#160; Use without modifying old <span class="term">kses()</span>&#160;code
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The <span class="term">Kses</span>&#160;PHP script is used by many applications (like <span class="term">WordPress</span>). It is possible to have such applications use htmLawed instead, since it is compatible with code that calls the <span class="term">kses()</span>&#160;function declared in the <span class="term">Kses</span>&#160;file (usually named <span class="term">kses.php</span>). E.g., application code like this will continue to work after replacing <span class="term">Kses</span>&#160;with htmLawed:<br />
+<br />
+
+<code class="code">&#160; &#160; $comment_filtered = kses($comment_input, array(&#39;a&#39;=&gt;array(), &#39;b&#39;=&gt;array(), &#39;i&#39;=&gt;array()));</code>
+<br />
+<br />
+&#160; For some of the <span class="term">$config</span>&#160;parameters, htmLawed will use values other than the default ones. These are indicated by <span class="term">^</span>&#160;in <a href="#s2.2">section 2.2</a>. To force htmLawed to use other values, function <span class="term">kses()</span>&#160;in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.<br />
+<br />
+&#160; If the application uses a <span class="term">Kses</span>&#160;file that has the <span class="term">kses()</span>&#160;function declared, then, to have the application use htmLawed instead of <span class="term">Kses</span>, simply rename <span class="term">htmLawed.php</span>&#160;(to <span class="term">kses.php</span>, e.g.) and replace the <span class="term">Kses</span>&#160;file (or just replace the code in the <span class="term">Kses</span>&#160;file with the htmLawed code). If the <span class="term">kses()</span>&#160;function in the <span class="term">Kses</span>&#160;file had been renamed by the application developer (e.g., in <span class="term">WordPress</span>, it is named <span class="term">wp_kses()</span>), then appropriately rename the <span class="term">kses()</span>&#160;function in the htmLawed code.<br />
+<br />
+&#160; If the <span class="term">Kses</span>&#160;file used by the application has been highly altered by the application developers, then one may need a different approach. E.g., with <span class="term">WordPress</span>, it is best to copy the htmLawed code to <span class="term">wp_includes/kses.php</span>, rename the newly added function <span class="term">kses()</span>&#160;to <span class="term">wp_kses()</span>, and delete the code for the original <span class="term">wp_kses()</span>&#160;function.<br />
+<br />
+&#160; If the <span class="term">Kses</span>&#160;code has a non-empty hook function (e.g., <span class="term">wp_kses_hook()</span>&#160;in case of <span class="term">WordPress</span>), then the code for htmLawed's <span class="term">kses_hook()</span>&#160;function should be appropriately edited. However, the requirement of the hook function should be re-evaluated considering that htmLawed has extra capabilities. With <span class="term">WordPress</span>, the hook function is an essential one. The following code is suggested for the htmLawed <span class="term">kses_hook()</span>&#160;in case of <span class="term">WordPress</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; function kses_hook($string, &amp;$cf, &amp;$spec){</code>
+<br />
+
+<code class="code">&#160; &#160; // kses compatibility</code>
+<br />
+
+<code class="code">&#160; &#160; $allowed_html = $spec;</code>
+<br />
+
+<code class="code">&#160; &#160; $allowed_protocols = array();</code>
+<br />
+
+<code class="code">&#160; &#160; foreach($cf[&#39;schemes&#39;] as $v){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;foreach($v as $k2=&gt;$v2){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; if(!in_array($k2, $allowed_protocols)){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;$allowed_protocols[] = $k2;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;}</code>
+<br />
+
+<code class="code">&#160; &#160; }</code>
+<br />
+
+<code class="code">&#160; &#160; return wp_kses_hook($string, $allowed_html, $allowed_protocols);</code>
+<br />
+
+<code class="code">&#160; &#160; // eof</code>
+<br />
+
+<code class="code">&#160; &#160; }</code>
+<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.7" id="s2.7"></a><span class="item-no">2.7</span>&#160; Tolerance for ill-written HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be <em>read</em>&#160;as HTML, and may therefore get identified as mere plain text. Following statements indicate the degree of <em>looseness</em>&#160;that htmLawed can work with, and can be provided in instructions to writers:<br />
+<br />
+&#160; * &#160;Tags must be flanked by <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;with no <span class="term">&gt;</span>&#160;inside -- any needed <span class="term">&gt;</span>&#160;should be put in as <span class="term">&amp;gt;</span>. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and <span class="term">&gt;</span>, like <span class="term">&lt;div &gt;</span>&#160;and <span class="term">&lt;img / &gt;</span>, but not after the <span class="term">&lt;</span>.<br />
+<br />
+&#160; * &#160;Element and attribute names need not be lower-cased.<br />
+<br />
+&#160; * &#160;Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.<br />
+<br />
+&#160; * &#160;Attribute values may be single- and not double-quoted.<br />
+<br />
+&#160; * &#160;Left-padding of numeric entities (like, <span class="term">&amp;#0160;</span>, <span class="term">&amp;x07ff;</span>) with <span class="term">0</span>&#160;is okay as long as the number of characters between between the <span class="term">&amp;</span>&#160;and the <span class="term">;</span>&#160;does not exceed 8. All entities must end with <span class="term">;</span>&#160;though.<br />
+<br />
+&#160; * &#160;Named character entities must be properly cased. Thus, <span class="term">&amp;Lt;</span>&#160;or <span class="term">&amp;TILDE;</span>&#160;will not be recognized as entities and will be <em>neutralized</em>.<br />
+<br />
+&#160; * &#160;HTML comments should not be inside element tags (they can be between tags), and should begin with <span class="term">&lt;!--</span>&#160;and end with <span class="term">--&gt;</span>. Characters like <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">--&gt;</span>&#160;inside should be put in as <span class="term">--&amp;gt;</span>. Any <span class="term">--</span>&#160;inside will be automatically converted to <span class="term">-</span>, and a space will be added before the comment delimiter <span class="term">--&gt;</span>.<br />
+<br />
+&#160; * &#160;<span class="term">CDATA</span>&#160;sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with <span class="term">&lt;[CDATA[</span>&#160;and end with <span class="term">]]&gt;</span>. Characters like <span class="term">&lt;</span>, <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">]]&gt;</span>&#160;inside should be put in as <span class="term">]]&amp;gt;</span>.<br />
+<br />
+&#160; * &#160;For attribute values, character entities <span class="term">&amp;lt;</span>, <span class="term">&amp;gt;</span>&#160;and <span class="term">&amp;amp;</span>&#160;should be used instead of characters <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, and <span class="term">&amp;</span>&#160;(when <span class="term">&amp;</span>&#160;is not part of a character entity). This applies even for Javascript code in values of attributes like <span class="term">onclick</span>.<br />
+<br />
+&#160; * &#160;Characters <span class="term">&lt;</span>, <span class="term">&gt;</span>, <span class="term">&amp;</span>&#160;and <span class="term">"</span>&#160;that are part of actual Javascript, etc., code in <span class="term">script</span>&#160;elements should be used as such and not be put in as entities like <span class="term">&amp;gt;</span>. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside <span class="term">CDATA</span>&#160;sections.<br />
+<br />
+&#160; * &#160;Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on <span class="term">$config["keep_bad"]</span>, some code/text may be lost.<br />
+<br />
+&#160; * &#160;Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.<br />
+<br />
+&#160; * &#160;With <span class="term">$config["unique_ids"]</span>&#160;not <span class="term">0</span>&#160;and the <span class="term">id</span>&#160;attribute being permitted, writers should carefully avoid using duplicate or invalid <span class="term">id</span>&#160;values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when <span class="term">&lt;a id="home"&gt;&lt;/a&gt;&lt;input id="home" /&gt;&lt;label for="home"&gt;&lt;/label&gt;</span>&#160;is processed into<br />
+<span class="term">&lt;a id="home"&gt;&lt;/a&gt;&lt;input id="prefix_home" /&gt;&lt;label for="home"&gt;&lt;/label&gt;</span>.<br />
+<br />
+&#160; * &#160;Even if intended HTML is lost from an ill-written input, the processed output will be more secure and standard-compliant.<br />
+<br />
+&#160; * &#160;For URLs, unless <span class="term">$config["scheme"]</span>&#160;is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., <span class="term">htt&amp;#112;</span>&#160;(which many browsers will read as the harmless <span class="term">http</span>) may be considered bad by htmLawed.<br />
+<br />
+&#160; * &#160;htmLawed will attempt to put plain text present directly inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span>&#160;elements (illegal as per the specifications) inside auto-generated <span class="term">div</span>&#160;elements.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.8" id="s2.8"></a><span class="item-no">2.8</span>&#160; Limitations &amp; work-arounds
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed's main objective is to make the input text <em>more</em>&#160;standard-compliant, secure for readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.<br />
+<br />
+&#160; It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specifications (like asking for normalization of white-spacing within <span class="term">textarea</span>&#160;elements) are clearly wrong. Regarding security, note that <em>unsafe</em>&#160;HTML code is not legally invalid per se.<br />
+<br />
+&#160; * &#160;htmLawed is meant for input that goes into the <span class="term">body</span>&#160;of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements <span class="term">frameset</span>, <span class="term">frame</span>&#160;and <span class="term">noframes</span>. Content of the latter elements can, however, be individually filtered through htmLawed.<br />
+<br />
+&#160; * &#160;It cannot transform the non-standard <span class="term">embed</span>&#160;elements to the standard-compliant <span class="term">object</span>&#160;elements. Yet, it can allow <span class="term">embed</span>&#160;elements if permitted (<span class="term">embed</span>&#160;is widely used and supported). Admins can certainly use the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) to deploy a custom embed-to-object converter function.<br />
+<br />
+&#160; * &#160;The only non-standard element that may be permitted is <span class="term">embed</span>; others like <span class="term">noembed</span>&#160;and <span class="term">nobr</span>&#160;cannot be permitted without modifying the htmLawed code.<br />
+<br />
+&#160; * &#160;It cannot handle input that has non-HTML code like <span class="term">SVG</span>&#160;and <span class="term">MathML</span>. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in <a href="#s3.9">section 3.9</a>. A third way may be to some how take advantage of the <span class="term">$config["and_mark"]</span>&#160;parameter (see <a href="#s3.2">section 3.2</a>).<br />
+<br />
+&#160; * &#160;By default, htmLawed won't check many attribute values for standard compliance. E.g., <span class="term">width="20m"</span>&#160;with the dimension in non-standard <span class="term">m</span>&#160;is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span>&#160;to enforce finer checks.<br />
+<br />
+&#160; * &#160;The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specifications. Only a few of the proprietary attributes are supported.<br />
+<br />
+&#160; * &#160;Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span>&#160;for finer checks. Perhaps the best option is to disallow <span class="term">style</span>&#160;but allow <span class="term">class</span>&#160;attributes with the right <span class="term">oneof</span>&#160;or <span class="term">match</span>&#160;values for <span class="term">class</span>, and have the various class style properties in <span class="term">.css</span>&#160;CSS stylesheet files.<br />
+<br />
+&#160; * &#160;htmLawed does not parse emoticons, decode <em>BBcode</em>, or <em>wikify</em>, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to <span class="term">br</span>&#160;elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.<br />
+<br />
+&#160; * &#160;htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate <span class="term">target</span>&#160;and <span class="term">onclick</span>&#160;attributes to <span class="term">a</span>). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;Nesting-based checks are not possible. E.g., one cannot disallow <span class="term">p</span>&#160;elements specifically inside <span class="term">td</span>&#160;while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert <span class="term">http</span>&#160;to <span class="term">https</span>. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;Pairs of opening and closing tags that do not enclose any content (like <span class="term">&lt;em&gt;&lt;/em&gt;</span>) are not removed. This may be against the standard specifications for certain elements (e.g., <span class="term">table</span>). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.<br />
+<br />
+&#160; * &#160;htmLawed does not check for certain element orderings described in the standard specifications (e.g., in a <span class="term">table</span>, <span class="term">tbody</span>&#160;is allowed before <span class="term">tfoot</span>). Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;htmLawed does not check the number of nested elements. E.g., it will allow two <span class="term">caption</span>&#160;elements in a <span class="term">table</span>&#160;element, illegal as per the specifications. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span>&#160;parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; * &#160;htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers (<span class="term">/&#42;</span>) in <span class="term">style</span>&#160;attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like <span class="term">&amp;#101;xpression...</span>. If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>) to more specifically identify CSS expressions in the <span class="term">style</span>&#160;attribute values. Also, using <span class="term">$config["style_pass"]</span>, it is possible to have htmLawed pass <span class="term">style</span>&#160;attribute values without even looking at them (<a href="#s3.4.8">section 3.4.8</a>).<br />
+<br />
+&#160; * &#160;htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., <span class="term">&lt;a href="http&#58;//x%22+style=%22background-image&#58;xss"&gt;x&lt;/a&gt;</span>). These arise when browsers mis-identify markup in <em>escaped</em>&#160;text, defeating the very purpose of escaping text (a bad browser will read the given example as <span class="term">&lt;a href="http&#58;//x" style="background-image&#58;xss"&gt;x&lt;/a&gt;</span>).<br />
+<br />
+&#160; * &#160;Because of poor Unicode support in PHP, htmLawed does not remove the <em>high value</em>&#160;HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see <a href="#s3.1">section 3.1</a>).<br />
+<br />
+&#160; * &#160;htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML <span class="term">meta</span>&#160;tags, this can permit an exploit (like Google's <em>UTF-7/XSS</em>&#160;vulnerability of the past). Also, htmLawed can mangle input text if it is not well-formed in terms of character encoding. Administrators can consider using code available elsewhere to check well-formedness of input text characters to correct any defect.<br />
+<br />
+&#160; * &#160;htmLawed is expected to work with input texts in ASCII-compatible single byte encodings such as national variants of ASCII (like ISO-646-DE/German of the ISO 646 standard), extended ASCII variants (like ISO 8859-10/Turkish of the ISO 8859/ISO Latin standard), ISO 8859-based Windows variants (like Windows 1252), EBCDIC, Shift JIS (Japanese), GB-Roman (Chinese), and KS-Roman (Korean). It should also properly handle texts with variable byte encodings like UTF-7 (Unicode) and UTF-8 (Unicode). However, htmLawed may mangle input texts with double byte encodings like UTF-16 (Unicode), JIS X 0208:1997 (Japanese) and K SX 1001:1992 (Korean), or the UTF-32 (Unicode) quadruple byte encoding. If an input text has such an encoding, administrators can use PHP's <a href="http://php.net/manual/en/book.iconv.php">iconv</a>&#160;functions, or some other mean, to convert text to UTF-8 before passing it to htmLawed.<br />
+<br />
+&#160; * &#160;Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.9" id="s2.9"></a><span class="item-no">2.9</span>&#160; Examples of usage
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Safest, allowing only <em>safe</em>&#160;HTML markup --<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;safe&#39;=&gt;1);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in);</code>
+<br />
+<br />
+&#160; Simplest, allowing all valid HTML markup except <span class="term">javascript&#58;</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in);</code>
+<br />
+<br />
+&#160; Allowing all valid HTML markup including <span class="term">javascript&#58;</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;schemes&#39;=&gt;&#39;&#42;&#58;&#42;&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Allowing only <span class="term">safe</span>&#160;HTML and the elements <span class="term">a</span>, <span class="term">em</span>, and <span class="term">strong</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;safe&#39;=&gt;1, &#39;elements&#39;=&gt;&#39;a, em, strong&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Not allowing elements <span class="term">script</span>&#160;and <span class="term">object</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;elements&#39;=&gt;&#39;&#42; -script -object&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Not allowing attributes <span class="term">id</span>&#160;and <span class="term">style</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;id, style&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Permitting only attributes <span class="term">title</span>&#160;and <span class="term">href</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;&#42; -title -href&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Remove bad/disallowed tags altogether instead of converting them to entities --<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;keep_bad&#39;=&gt;0);</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config);</code>
+<br />
+<br />
+&#160; Allowing attribute <span class="term">title</span>&#160;only in <span class="term">a</span>&#160;and not allowing attributes <span class="term">id</span>, <span class="term">style</span>, or scriptable <em>on*</em>&#160;attributes like <span class="term">onclick</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $config = array(&#39;deny_attribute&#39;=&gt;&#39;title, id, style, on&#42;&#39;);</code>
+<br />
+
+<code class="code">&#160; &#160; $spec = &#39;a=title&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config, $spec);</code>
+<br />
+<br />
+&#160; Allowing a custom attribute, <span class="term">vFlag</span>, in <span class="term">img</span>&#160;and permitting custom use of the standard attribute, <span class="term">rel</span>, in <span class="term">input</span>&#160;--<br />
+<br />
+
+<code class="code">&#160; &#160; $spec = &#39;img=vFlag; input=rel&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; $out = htmLawed($in, $config, $spec);</code>
+<br />
+<br />
+&#160; Some case-studies are presented below.<br />
+<br />
+&#160; <strong>1.</strong>&#160;A blog administrator wants to allow only <span class="term">a</span>, <span class="term">em</span>, <span class="term">strike</span>, <span class="term">strong</span>&#160;and <span class="term">u</span>&#160;in comments, but needs <span class="term">strike</span>&#160;and <span class="term">u</span>&#160;transformed to <span class="term">span</span>&#160;for better XHTML 1-strict compliance, and, he wants the <span class="term">a</span>&#160;links to point only to <span class="term">http</span>&#160;or <span class="term">https</span>&#160;resources:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;elements&#39;=&gt;&#39;a, em, strike, strong, u&#39;, &#39;make_tag_strict&#39;=&gt;1, &#39;safe&#39;=&gt;1, &#39;schemes&#39;=&gt;&#39;&#42;&#58;http, https&#39;), &#39;a=href&#39;);</code>
+<br />
+<br />
+&#160; <strong>2.</strong>&#160;An author uses a custom-made web application to load content on his web-site. He is the only one using that application and the content he generates has all types of HTML, including scripts. The web application uses htmLawed primarily as a tool to correct errors that creep in while writing HTML and to take care of the occasional <em>bad</em>&#160;characters in copy-paste text introduced by Microsoft Office. The web application provides a preview before submitted input is added to the content. For the previewing process, htmLawed is set up as follows:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;css_expression&#39;=&gt;1, &#39;keep_bad&#39;=&gt;1, &#39;make_tag_strict&#39;=&gt;1, &#39;schemes&#39;=&gt;&#39;&#42;&#58;&#42;&#39;, &#39;valid_xhtml&#39;=&gt;1));</code>
+<br />
+<br />
+&#160; For the final submission process, <span class="term">keep_bad</span>&#160;is set to <span class="term">6</span>. A value of <span class="term">1</span>&#160;for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.<br />
+<br />
+&#160; <strong>3.</strong>&#160;A data-miner is scraping information in a specific table of similar web-pages and is collating the data rows, and uses htmLawed to reduce unnecessary markup and white-spaces:<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($in, array(&#39;elements&#39;=&gt;&#39;tr, td&#39;, &#39;tidy&#39;=&gt;-1), &#39;tr, td =&#39;);</code>
+<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s3" id="s3"></a><span class="item-no">3</span>&#160; Details
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<div class="sub-section"><h3>
+<a name="s3.1" id="s3.1"></a><span class="item-no">3.1</span>&#160; Invalid/dangerous characters
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Valid characters (more correctly, their code-points) in HTML or XML are, hexadecimally, <span class="term">9</span>, <span class="term">a</span>, <span class="term">d</span>, <span class="term">20</span>&#160;to <span class="term">d7ff</span>, and <span class="term">e000</span>&#160;to <span class="term">10ffff</span>, except <span class="term">fffe</span>&#160;and <span class="term">ffff</span>&#160;(decimally, <span class="term">9</span>, <span class="term">10</span>, <span class="term">13</span>, <span class="term">32</span>&#160;to <span class="term">55295</span>, and <span class="term">57344</span>&#160;to <span class="term">1114111</span>, except <span class="term">65534</span>&#160;and <span class="term">65535</span>). htmLawed removes the invalid characters <span class="term">0</span>&#160;to <span class="term">8</span>, <span class="term">b</span>, <span class="term">c</span>, and <span class="term">e</span>&#160;to <span class="term">1f</span>.<br />
+<br />
+&#160; Because of PHP's poor native support for multi-byte characters, htmLawed cannot check for the remaining invalid code-points. However, for various reasons, it is very unlikely for any of those characters to be in the input.<br />
+<br />
+&#160; Characters that are discouraged (see <a href="#s5.1">section 5.1</a>) but not invalid are not removed by htmLawed.<br />
+<br />
+&#160; It (function <span class="term">hl_tag()</span>) also replaces the potentially dangerous (in some Mozilla [Firefox] and Opera browsers) soft-hyphen character (code-point, hexadecimally, <span class="term">ad</span>, or decimally, <span class="term">173</span>) in attribute values with spaces. Where required, the characters <span class="term">&lt;</span>, <span class="term">&gt;</span>, <span class="term">&amp;</span>, and <span class="term">"</span>&#160;are converted to entities.<br />
+<br />
+&#160; With <span class="term">$config["clean_ms_char"]</span>&#160;set as <span class="term">1</span>&#160;or <span class="term">2</span>, many of the discouraged characters (decimal code-points <span class="term">127</span>&#160;to <span class="term">159</span>&#160;except <span class="term">133</span>) that many Microsoft applications incorrectly use (as per the <span class="term">Windows 1252</span>&#160;[<span class="term">Cp-1252</span>] or a similar encoding system), and the character for decimal code-point <span class="term">133</span>, are converted to appropriate decimal numerical entities (or removed for a few cases)-- see appendix in <a href="#s5.4">section 5.4</a>. This can help avoid some display issues arising from copying-pasting of content.<br />
+<br />
+&#160; With <span class="term">$config["clean_ms_char"]</span>&#160;set as <span class="term">2</span>, characters for the hexadecimal code-points <span class="term">82</span>, <span class="term">91</span>, and <span class="term">92</span>&#160;(for special single-quotes), and <span class="term">84</span>, <span class="term">93</span>, and <span class="term">94</span>&#160;(for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.<br />
+<br />
+&#160; The character values are replaced with entities/characters and not character values referred to by the entities/characters to keep this task independent of the character-encoding of input text.<br />
+<br />
+&#160; The <span class="term">$config["clean_ms_char"]</span>&#160;parameter should not be used if authors do not copy-paste Microsoft-created text, or if the input text is not believed to use the <span class="term">Windows 1252</span>&#160;(<span class="term">Cp-1252</span>) or a similar encoding like <span class="term">Cp-1251</span>&#160;(otherwise, for example when UTF-8 encoding is in use, Japanese or Korean characters can get mangled). Further, the input form and the web-pages displaying it or its content should have the character encoding appropriately marked-up.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.2" id="s3.2"></a><span class="item-no">3.2</span>&#160; Character references/entities
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Valid character entities take the form <span class="term">&amp;&#42;;</span>&#160;where <span class="term">&#42;</span>&#160;is <span class="term">#x</span>&#160;followed by a hexadecimal number (hexadecimal numeric entity; like <span class="term">&amp;#xA0;</span>&#160;for non-breaking space), or alphanumeric like <span class="term">gt</span>&#160;(external or named entity; like <span class="term">&amp;nbsp;</span>&#160;for non-breaking space), or <span class="term">#</span>&#160;followed by a number (decimal numeric entity; like <span class="term">&amp;#160;</span>&#160;for non-breaking space). Character entities referring to the soft-hyphen character (the <span class="term">&amp;shy;</span>&#160;or <span class="term">\xad</span>&#160;character; hexadecimal code-point <span class="term">ad</span>&#160;[decimal <span class="term">173</span>]) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.<br />
+<br />
+&#160; htmLawed (function <span class="term">hl_ent()</span>):<br />
+<br />
+&#160; * &#160;Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)<br />
+<br />
+&#160; * &#160;Lowercases the <span class="term">X</span>&#160;(for XML-compliance) and <span class="term">A-F</span>&#160;of hexadecimal numeric entities<br />
+<br />
+&#160; * &#160;Neutralizes entities referring to characters that are HTML-invalid (see <a href="#s3.1">section 3.1</a>)<br />
+<br />
+&#160; * &#160;Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, <span class="term">7f</span>&#160;to <span class="term">84</span>, <span class="term">86</span>&#160;to <span class="term">9f</span>, and <span class="term">fdd0</span>&#160;to <span class="term">fddf</span>, or decimally, <span class="term">127</span>&#160;to <span class="term">132</span>, <span class="term">134</span>&#160;to <span class="term">159</span>, and <span class="term">64991</span>&#160;to <span class="term">64976</span>). Entities referring to the remaining discouraged characters (see <a href="#s5.1">section 5.1</a>&#160;for a full list) are let through.<br />
+<br />
+&#160; * &#160;Neutralizes named entities that are not in the specs.<br />
+<br />
+&#160; * &#160;Optionally converts valid HTML-specific named entities except <span class="term">&amp;gt;</span>, <span class="term">&amp;lt;</span>, <span class="term">&amp;quot;</span>, and <span class="term">&amp;amp;</span>&#160;to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is <span class="term">2</span>) for generic XML-compliance. For this, <span class="term">$config["named_entity"]</span>&#160;should be <span class="term">1</span>.<br />
+<br />
+&#160; * &#160;Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, <span class="term">$config["hexdec_entity"]</span>&#160;should be <span class="term">0</span>.<br />
+<br />
+&#160; * &#160;Optionally converts decimal numeric entities to the hexadecimal ones. For this, <span class="term">$config["hexdec_entity"]</span>&#160;should be <span class="term">2</span>.<br />
+<br />
+&#160; <em>Neutralization</em>&#160;refers to the <em>entitification</em>&#160;of <span class="term">&amp;</span>&#160;to <span class="term">&amp;amp;</span>.<br />
+<br />
+&#160; <strong>Note</strong>: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's <span class="term">html_entity_decode</span>&#160;<a href="http://www.php.net/html_entity_decode">function</a>&#160;for that.<br />
+<br />
+&#160; <strong>Note</strong>: If <span class="term">$config["and_mark"]</span>&#160;is set, and set to a value other than <span class="term">0</span>, then the <span class="term">&amp;</span>&#160;characters in the original input are replaced with the control character for the hexadecimal code-point <span class="term">6</span>&#160;(<span class="term">\x06</span>; <span class="term">&amp;</span>&#160;characters introduced by htmLawed, e.g., after converting <span class="term">&lt;</span>&#160;to <span class="term">&amp;lt;</span>, are not affected). This allows one to distinguish, say, an <span class="term">&amp;gt;</span>&#160;introduced by htmLawed and an <span class="term">&amp;gt;</span>&#160;put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence <span class="term">o(&gt;&lt;)o</span>&#160;to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the <span class="term">\x06</span>&#160;can break documents. Before use in such documents, and preferably before any storage, any remaining <span class="term">\x06</span>&#160;should be changed back to <span class="term">&amp;</span>, e.g., with:<br />
+<br />
+
+<code class="code">&#160; &#160; $final = str_replace("\x06", &#39;&amp;&#39;, $prelim);</code>
+<br />
+<br />
+&#160; Also, see <a href="#s3.9">section 3.9</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.3" id="s3.3"></a><span class="item-no">3.3</span>&#160; HTML elements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on <span class="term">$config["keep_bad"]</span>, are either <em>neutralized</em>&#160;(converted to plain text by entitification of <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>) or removed.<br />
+<br />
+&#160; E.g., with only <span class="term">em</span>&#160;permitted:<br />
+<br />
+&#160; Input:<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is &lt;a href="http&#58;//a.com&gt;a.com&lt;/a&gt;.</code>
+<br />
+<br />
+&#160; Output, with <span class="term">$config["keep_bad"] = 0</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is a.com.</code>
+<br />
+<br />
+&#160; Output, with <span class="term">$config["keep_bad"]</span>&#160;not <span class="term">0</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;em&gt;My&lt;/em&gt; website is &amp;lt;a href=""&amp;gt;a.com&amp;lt;/a&amp;gt;.</code>
+<br />
+<br />
+&#160; See <a href="#s3.3.3">section 3.3.3</a>&#160;for differences between the various non-zero <span class="term">$config["keep_bad"]</span>&#160;values.<br />
+<br />
+&#160; htmLawed by default permits these 86 elements:<br />
+<br />
+
+<code class="code">&#160; &#160; a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var</code>
+<br />
+<br />
+&#160; Except for <span class="term">embed</span>&#160;(included because of its wide-spread use) and the Ruby elements (<span class="term">rb</span>, <span class="term">rbc</span>, <span class="term">rp</span>, <span class="term">rt</span>, <span class="term">rtc</span>, <span class="term">ruby</span>; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude <span class="term">center</span>, <span class="term">dir</span>, <span class="term">font</span>, <span class="term">isindex</span>, <span class="term">menu</span>, <span class="term">s</span>, <span class="term">strike</span>, and <span class="term">u</span>.<br />
+<br />
+&#160; With <span class="term">$config["safe"] = 1</span>, the default set will exclude <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>, <span class="term">object</span>&#160;and <span class="term">script</span>; see <a href="#s3.6">section 3.6</a>.<br />
+<br />
+&#160; When <span class="term">$config["elements"]</span>, which specifies allowed elements, is <em>properly</em>&#160;defined, and neither empty nor set to <span class="term">0</span>&#160;or <span class="term">&#42;</span>, the default set is not used. To have elements added to or removed from the default set, a <span class="term">+/-</span>&#160;notation is used. E.g., <span class="term">&#42;-script-object</span>&#160;implies that only <span class="term">script</span>&#160;and <span class="term">object</span>&#160;are disallowed, whereas <span class="term">&#42;+embed</span>&#160;means that <span class="term">noembed</span>&#160;is also allowed. Elements can also be specified as comma separated names. E.g., <span class="term">a, b, i</span>&#160;means only <span class="term">a</span>, <span class="term">b</span>&#160;and <span class="term">i</span>&#160;are permitted. In this notation, <span class="term">&#42;</span>, <span class="term">+</span>&#160;and <span class="term">-</span>&#160;have no significance and can actually cause a mis-reading.<br />
+<br />
+&#160; Some more examples of <span class="term">$config["elements"]</span>&#160;values indicating permitted elements (note that empty spaces are liberally allowed for clarity):<br />
+<br />
+&#160; * &#160;<span class="term">a, blockquote, code, em, strong</span>&#160;-- only <span class="term">a</span>, <span class="term">blockquote</span>, <span class="term">code</span>, <span class="term">em</span>, and <span class="term">strong</span><br />
+&#160; * &#160;<span class="term">&#42;-script</span>&#160;-- all excluding <span class="term">script</span><br />
+&#160; * &#160;<span class="term">&#42; -center -dir -font -isindex -menu -s -strike -u</span>&#160;-- only XHTML-Strict elements<br />
+&#160; * &#160;<span class="term">&#42;+noembed-script</span>&#160;-- all including <span class="term">noembed</span>&#160;excluding <span class="term">script</span><br />
+<br />
+&#160; Some mis-usages (and the resulting permitted elements) that can be avoided:<br />
+<br />
+&#160; * &#160;<span class="term">-&#42;</span>&#160;-- none; instead of htmLawed, one might just use, e.g., the <span class="term">htmlspecialchars()</span>&#160;PHP function<br />
+&#160; * &#160;<span class="term">&#42;, -script</span>&#160;-- all except <span class="term">script</span>; admin probably meant <span class="term">&#42;-script</span><br />
+&#160; * &#160;<span class="term">-&#42;, a, em, strong</span>&#160;-- all; admin probably meant <span class="term">a, em, strong</span><br />
+&#160; * &#160;<span class="term">&#42;</span>&#160;-- all; admin need not have set <span class="term">elements</span><br />
+&#160; * &#160;<span class="term">&#42;-form+form</span>&#160;-- all; a <span class="term">+</span>&#160;will always over-ride any <span class="term">-</span><br />
+&#160; * &#160;<span class="term">&#42;, noembed</span>&#160;-- only <span class="term">noembed</span>; admin probably meant <span class="term">&#42;+noembed</span><br />
+&#160; * &#160;<span class="term">a, +b, i</span>&#160;-- only <span class="term">a</span>&#160;and <span class="term">i</span>; admin probably meant <span class="term">a, b, i</span><br />
+<br />
+&#160; Basically, when using the <span class="term">+/-</span>&#160;notation, commas (<span class="term">,</span>) should not be used, and vice versa, and <span class="term">&#42;</span>&#160;should be used with the former but not the latter.<br />
+<br />
+&#160; <strong>Note</strong>: Even if an element that is not in the default set is allowed through <span class="term">$config["elements"]</span>, like <span class="term">noembed</span>&#160;in the last example, it will eventually be removed during tag balancing unless such balancing is turned off (<span class="term">$config["balance"]</span>&#160;set to <span class="term">0</span>). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function <span class="term">hl_bal()</span>&#160;to accommodate the element and its nesting properties.<br />
+<br />
+&#160; <strong>A possibly second way to specify allowed elements</strong>&#160;is to set <span class="term">$config["parent"]</span>&#160;to an element name that supposedly will hold the input, and to set <span class="term">$config["balance"]</span>&#160;to <span class="term">1</span>. During tag balancing (see <a href="#s3.3.3">section 3.3.3</a>), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to <span class="term">div</span>&#160;if <span class="term">$config["parent"]</span>&#160;is empty, <span class="term">body</span>, or an element not in htmLawed's default set of 86 elements.<br />
+<br />
+&#160; <em>Tag transformation</em>&#160;is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see <a href="#s3.3.2">section 3.3.2</a>.<br />
+
+<div class="sub-sub-section"><h4>
+<a name="s3.3.1" id="s3.3.1"></a><span class="item-no">3.3.1</span>&#160; Handling of comments and CDATA sections
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; <span class="term">CDATA</span>&#160;sections have the format <span class="term">&lt;![CDATA[...anything but not "]]&gt;"...]]&gt;</span>, and HTML comments, <span class="term">&lt;!--...anything but not "--&gt;"... --&gt;</span>. Neither HTML comments nor <span class="term">CDATA</span>&#160;sections can reside inside tags. HTML comments can exist anywhere else, but <span class="term">CDATA</span>&#160;sections can exist only where plain text is allowed (e.g., immediately inside <span class="term">td</span>&#160;element content but not immediately inside <span class="term">tr</span>&#160;element content).<br />
+<br />
+&#160; htmLawed (function <span class="term">hl_cmtcd()</span>) handles HTML comments or <span class="term">CDATA</span>&#160;sections depending on the values of <span class="term">$config["comment"]</span>&#160;or <span class="term">$config["cdata"]</span>. If <span class="term">0</span>, such markup is not looked for and the text is processed like plain text. If <span class="term">1</span>, it is removed completely. If <span class="term">2</span>, it is preserved but any <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;inside are changed to entities. If <span class="term">3</span>, they are left as such.<br />
+<br />
+&#160; Note that for the last two cases, HTML comments and <span class="term">CDATA</span>&#160;sections will always be removed from tag content (function <span class="term">hl_tag()</span>).<br />
+<br />
+&#160; Examples:<br />
+<br />
+&#160; Input:<br />
+
+<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 0, $config["cdata"] = 2</span>):<br />
+
+<code class="code">&#160; &#160; &amp;lt;-- home link --&amp;gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 1, $config["cdata"] = 2</span>):<br />
+
+<code class="code">&#160; &#160; &lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 2</span>):<br />
+
+<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 1</span>):<br />
+
+<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;Home&lt;/a&gt;</code>
+<br />
+&#160; Output (<span class="term">$config["comment"] = 3, $config["cdata"] = 3</span>):<br />
+
+<code class="code">&#160; &#160; &lt;!-- home link --&gt;&lt;a href="home.htm"&gt;&lt;![CDATA[x=&amp;y]]&gt;Home&lt;/a&gt;</code>
+<br />
+<br />
+&#160; For standard-compliance, comments are given the form <span class="term">&lt;!--comment --&gt;</span>, and any <span class="term">--</span>&#160;in the content is made <span class="term">-</span>.<br />
+<br />
+&#160; When <span class="term">$config["safe"] = 1</span>, CDATA sections and comments are considered plain text unless <span class="term">$config["comment"]</span>&#160;or <span class="term">$config["cdata"]</span>&#160;is explicitly specified; see <a href="#s3.6">section 3.6</a>.<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.3.2" id="s3.3.2"></a><span class="item-no">3.3.2</span>&#160; Tag-transformation for better XHTML-Strict
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["make_tag_strict"]</span>&#160;is set and not <span class="term">0</span>, following non-XHTML-Strict elements (and attributes), even if admin-permitted, are mutated as indicated (element content remains intact; function <span class="term">hl_tag2()</span>):<br />
+<br />
+&#160; * &#160;applet - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
+&#160; * &#160;center - <span class="term">div style="text-align&#58; center;"</span><br />
+&#160; * &#160;dir - <span class="term">ul</span><br />
+&#160; * &#160;embed - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
+&#160; * &#160;font (face, size, color) - &#160; &#160;<span class="term">span style="font-family&#58; ; font-size&#58; ; color&#58; ;"</span>&#160;(size transformation <a href="http://style.cleverchimp.com/font_size_intervals/altintervals.html">reference</a>)<br />
+&#160; * &#160;isindex - (based on <span class="term">$config["make_tag_strict"]</span>, unchanged (<span class="term">1</span>) or removed (<span class="term">2</span>))<br />
+&#160; * &#160;menu - <span class="term">ul</span><br />
+&#160; * &#160;s - <span class="term">span style="text-decoration&#58; line-through;"</span><br />
+&#160; * &#160;strike - <span class="term">span style="text-decoration&#58; line-through;"</span><br />
+&#160; * &#160;u - <span class="term">span style="text-decoration&#58; underline;"</span><br />
+<br />
+&#160; For an element with a pre-existing <span class="term">style</span>&#160;attribute value, the extra style properties are appended.<br />
+<br />
+&#160; Example input:<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;center&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;The PHP &lt;s&gt;software&lt;/s&gt; script used for this &lt;strike&gt;web-page&lt;/strike&gt; web-page is &lt;font style="font-weight&#58; bold " face=arial size=&#39;+3&#39; color &#160; = &#160;"red &#160;"&gt;htmLawedTest.php&lt;/font&gt;, from &lt;u style= &#39;color&#58;green&#39;&gt;PHP Labware&lt;/u&gt;.</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/center&gt;</code>
+<br />
+<br />
+&#160; The output:<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;div style="text-align&#58; center;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;The PHP &lt;span style="text-decoration&#58; line-through;"&gt;software&lt;/span&gt; script used for this &lt;span style="text-decoration&#58; line-through;"&gt;web-page&lt;/span&gt; web-page is &lt;span style="font-weight&#58; bold; font-family&#58; arial; color&#58; red; font-size&#58; 200%;"&gt;htmLawedTest.php&lt;/span&gt;, from &lt;span style="color&#58;green; text-decoration&#58; underline;"&gt;PHP Labware&lt;/span&gt;.</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/div&gt;</code>
+<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.3.3" id="s3.3.3"></a><span class="item-no">3.3.3</span>&#160; Tag balancing and proper nesting
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["balance"]</span>&#160;is set to <span class="term">1</span>, htmLawed (function <span class="term">hl_bal()</span>) checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).<br />
+<br />
+&#160; Depending on the value of <span class="term">$config["keep_bad"]</span>&#160;(see <a href="#s2.2">section 2.2</a>&#160;and <a href="#s3.3">section 3.3</a>), illegal content may be removed or neutralized to plain text by converting &lt; and &gt; to entities:<br />
+<br />
+&#160; <span class="term">0</span>&#160;- remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see <a href="#s2.6">section 2.6</a>)<br />
+&#160; <span class="term">1</span>&#160;- neutralize tags and keep element content<br />
+&#160; <span class="term">2</span>&#160;- remove tags but keep element content<br />
+&#160; <span class="term">3</span>&#160;and <span class="term">4</span>&#160;- like <span class="term">1</span>&#160;and <span class="term">2</span>, but keep element content only if text (<span class="term">pcdata</span>) is valid in parent element as per specs<br />
+&#160; <span class="term">5</span>&#160;and <span class="term">6</span>&#160;- &#160;like <span class="term">3</span>&#160;and <span class="term">4</span>, but line-breaks, tabs and spaces are left<br />
+<br />
+&#160; Example input (disallowing the <span class="term">p</span>&#160;element):<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;&#42;&gt; Pseudo-tags &lt;&#42;&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;p&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; Disallowed tag p</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/p&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;ul&gt;Bad&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
+<br />
+<br />
+&#160; The output with <span class="term">$config["keep_bad"] = 1</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;xml&amp;gt;Non-HTML tag xml&amp;lt;/xml&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;p&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; Disallowed tag p</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;/p&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;ul&gt;Bad&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
+<br />
+<br />
+&#160; The output with <span class="term">$config["keep_bad"] = 3</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;xml&amp;gt;Non-HTML tag xml&amp;lt;/xml&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;p&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; Disallowed tag p</code>
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;/p&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;ul&gt;&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
+<br />
+<br />
+&#160; The output with <span class="term">$config["keep_bad"] = 6</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &amp;lt;&#42;&amp;gt; Pseudo-tags &amp;lt;&#42;&amp;gt;</code>
+<br />
+
+<code class="code">&#160; &#160; Non-HTML tag xml</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; Disallowed tag p</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;ul&gt;&lt;li&gt;OK&lt;/li&gt;&lt;/ul&gt;</code>
+<br />
+<br />
+&#160; An option like <span class="term">1</span>&#160;is useful, e.g., when a writer previews his submission, whereas one like <span class="term">3</span>&#160;is useful before content is finalized and made available to all.<br />
+<br />
+&#160; <strong>Note:</strong>&#160;In the example above, unlike <span class="term">&lt;&#42;&gt;</span>, <span class="term">&lt;xml&gt;</span>&#160;gets considered as a tag (even though there is no HTML element named <span class="term">xml</span>). Thus, the <span class="term">keep_bad</span>&#160;parameter's value affects <span class="term">&lt;xml&gt;</span>&#160;but not <span class="term">&lt;&#42;&gt;</span>. In general, text matching the regular expression pattern <span class="term">&lt;(/?)([a-zA-Z][a-zA-Z1-6]&#42;)([^&gt;]&#42;?)\s?&gt;</span>&#160;is considered a tag (phrase enclosed by the angled brackets <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>, and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...), and is subjected to the <span class="term">keep_bad</span>&#160;value.<br />
+<br />
+&#160; Nesting/content rules for each of the 86 elements in htmLawed's default set (see <a href="#s3.3">section 3.3</a>) are defined in function <span class="term">hl_bal()</span>. This means that if a non-standard element besides <span class="term">embed</span>&#160;is being permitted through <span class="term">$config["elements"]</span>, the element's tag content will end up getting removed if <span class="term">$config["balance"]</span>&#160;is set to <span class="term">1</span>.<br />
+<br />
+&#160; Plain text and/or certain elements nested inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span>&#160;need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as <span class="term">form</span>, the input <span class="term">B&#58;&lt;input type="text" value="b" /&gt;C&#58;&lt;input type="text" value="c" /&gt;</span>&#160;is converted to <span class="term">&lt;div&gt;B&#58;&lt;input type="text" value="b" /&gt;C&#58;&lt;input type="text" value="c" /&gt;&lt;/div&gt;</span>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.3.4" id="s3.3.4"></a><span class="item-no">3.3.4</span>&#160; Elements requiring child elements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; As per specs, the following elements require legal child elements nested inside them:<br />
+<br />
+
+<code class="code">&#160; &#160; blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul</code>
+<br />
+<br />
+&#160; In some cases, the specs stipulate the number and/or the ordering of the child elements. A <span class="term">table</span>&#160;can have 0 or 1 <span class="term">caption</span>, <span class="term">tbody</span>, <span class="term">tfoot</span>, and <span class="term">thead</span>, but they must be in this order: <span class="term">caption</span>, <span class="term">thead</span>, <span class="term">tfoot</span>, <span class="term">tbody</span>.<br />
+<br />
+&#160; htmLawed currently does not check for conformance to these rules. Note that any non-compliance in this regard will not introduce security vulnerabilities, crash browser applications, or affect the rendering of web-pages.<br />
+<br />
+&#160; With <span class="term">$config["direct_list_nest"]</span>&#160;set to <span class="term">1</span>, htmLawed will allow direct nesting of an <span class="term">ol</span>&#160;or <span class="term">ul</span>&#160;list within another <span class="term">ol</span>&#160;or <span class="term">ul</span>&#160;without requiring the child list to be within an <span class="term">li</span>&#160;of the parent list. While this is not standard-compliant, directly nested lists are rendered properly by almost all browsers. The parameter <span class="term">$config["direct_list_nest"]</span>&#160;has no effect if tag-balancing (<a href="#s3.3.3">section 3.3.3</a>) is turned off.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.3.5" id="s3.3.5"></a><span class="item-no">3.3.5</span>&#160; Beautify or compact HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; By default, htmLawed will neither <em>beautify</em>&#160;HTML code by formatting it with indentations, etc., nor will it make it compact by removing un-needed white-space.(It does always properly white-space tag content.)<br />
+<br />
+&#160; As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside <span class="term">pre</span>&#160;elements) are all considered equivalent, and referred to as <em>white-spaces</em>. Browser applications are supposed to consider contiguous white-spaces as just a single space, and to disregard white-spaces trailing opening tags or preceding closing tags. This white-space <em>normalization</em>&#160;allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such <em>pretty</em>&#160;HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.<br />
+<br />
+&#160; With the <span class="term">$config</span>&#160;parameter <span class="term">tidy</span>, htmLawed can be used to beautify or compact the input text. Input with just plain text and no HTML markup is also subject to this. Besides <span class="term">pre</span>, the <span class="term">script</span>&#160;and <span class="term">textarea</span>&#160;elements, CDATA sections, and HTML comments are not subjected to the tidying process.<br />
+<br />
+&#160; To <em>compact</em>, use <span class="term">$config["tidy"] = -1</span>; single instances or runs of white-spaces are replaced with a single space, and white-spaces trailing and leading open and closing tags, respectively, are removed.<br />
+<br />
+&#160; To <em>beautify</em>, <span class="term">$config["tidy"]</span>&#160;is set as <span class="term">1</span>, or for customized tidying, as a string like <span class="term">2s2n</span>. The <span class="term">s</span>&#160;or <span class="term">t</span>&#160;character specifies the use of spaces or tabs for indentation. The first and third characters, any of the digits 0-9, specify the number of spaces or tabs per indentation, and any parental lead spacing (extra indenting of the whole block of input text). The <span class="term">r</span>&#160;and <span class="term">n</span>&#160;characters are used to specify line-break characters: <span class="term">n</span>&#160;for <span class="term">\n</span>&#160;(Unix/Mac OS X line-breaks), <span class="term">rn</span>&#160;or <span class="term">nr</span>&#160;for <span class="term">\r\n</span>&#160;(Windows/DOS line-breaks), or <span class="term">r</span>&#160;for <span class="term">\r</span>.<br />
+<br />
+&#160; The <span class="term">$config["tidy"]</span>&#160;value of <span class="term">1</span>&#160;is equivalent to <span class="term">2s0n</span>. Other <span class="term">$config["tidy"]</span>&#160;values are read loosely: a value of <span class="term">4</span>&#160;is equivalent to <span class="term">4s0n</span>; <span class="term">t2</span>, to <span class="term">1t2n</span>; <span class="term">s</span>, to <span class="term">2s0n</span>; <span class="term">2TR</span>, to <span class="term">2t0r</span>; <span class="term">T1</span>, to <span class="term">1t1n</span>; <span class="term">nr3</span>, to <span class="term">3s0nr</span>, and so on. Except in the indentations and line-spacings, runs of white-spaces are replaced with a single space during beautification.<br />
+<br />
+&#160; Input formatting using <span class="term">$config["tidy"]</span>&#160;is not recommended when input text has mixed markup (like HTML + PHP).<br />
+
+</div>
+</div>
+<div class="sub-section"><h3>
+<a name="s3.4" id="s3.4"></a><span class="item-no">3.4</span>&#160; Attributes
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed will only permit attributes described in the HTML specs (including deprecated ones). It also permits some attributes for use with the <span class="term">embed</span>&#160;element (the non-standard <span class="term">embed</span>&#160;element is supported in htmLawed because of its widespread use), and the the <span class="term">xml&#58;space</span>&#160;attribute (valid only in XHTML 1.1). A list of such 111 attributes and the elements they are allowed in is in <a href="#s5.2">section 5.2</a>. Using the <span class="term">$spec</span>&#160;argument, htmLawed can be forced to permit custom, non-standard attributes as well as custom rules for standard attributes (<a href="#s2.3">section 2.3</a>).<br />
+<br />
+&#160; When <span class="term">$config["deny_attribute"]</span>&#160;is not set, or set to <span class="term">0</span>, or empty (<span class="term">""</span>), all the 111 attributes are permitted. Otherwise, <span class="term">$config["deny_attribute"]</span>&#160;can be set as a list of comma-separated names of the denied attributes. <span class="term">on&#42;</span>&#160;can be used to refer to the group of potentially dangerous, script-accepting attributes: <span class="term">onblur</span>, <span class="term">onchange</span>, <span class="term">onclick</span>, <span class="term">ondblclick</span>, <span class="term">onfocus</span>, <span class="term">onkeydown</span>, <span class="term">onkeypress</span>, <span class="term">onkeyup</span>, <span class="term">onmousedown</span>, <span class="term">onmousemove</span>, <span class="term">onmouseout</span>, <span class="term">onmouseover</span>, <span class="term">onmouseup</span>, <span class="term">onreset</span>, <span class="term">onselect</span>&#160;and <span class="term">onsubmit</span>.<br />
+<br />
+&#160; Note that attributes specified in <span class="term">$config["deny_attribute"]</span>&#160;are denied globally, for all elements. To deny attributes for only specific elements, <span class="term">$spec</span>&#160;(see <a href="#s2.3">section 2.3</a>) can be used. <span class="term">$spec</span>&#160;can also be used to element-specifically permit an attribute otherwise denied through <span class="term">$config["deny_attribute"]</span>.<br />
+<br />
+&#160; With <span class="term">$config["safe"] = 1</span>&#160;(<a href="#s3.6">section 3.6</a>), the <span class="term">on&#42;</span>&#160;attributes are automatically disallowed.<br />
+<br />
+&#160; <strong>Note</strong>: To deny all but a few attributes globally, a simpler way to specify <span class="term">$config["deny_attribute"]</span>&#160;would be to use the notation <span class="term">&#42; -attribute1 -attribute2 ...</span>. Thus, a value of <span class="term">&#42; -title -href</span>&#160;implies that except <span class="term">href</span>&#160;and <span class="term">title</span>&#160;(where allowed as per standards) all other attributes are to be removed. With this notation, the value for the parameter <span class="term">safe</span>&#160;(<a href="#s3.6">section 3.6</a>) will have no effect on <span class="term">deny_attribute</span>.<br />
+<br />
+&#160; htmLawed (function <span class="term">hl_tag()</span>) also:<br />
+<br />
+&#160; * &#160;Lower-cases attribute names<br />
+&#160; * &#160;Removes duplicate attributes (last one stays)<br />
+&#160; * &#160;Gives attributes the form <span class="term">name="value"</span>&#160;and single-spaces them, removing unnecessary white-spacing<br />
+&#160; * &#160;Provides <em>required</em>&#160;attributes (see <a href="#s3.4.1">section 3.4.1</a>)<br />
+&#160; * &#160;Double-quotes values and escapes any <span class="term">"</span>&#160;inside them<br />
+&#160; * &#160;Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point <span class="term">ad</span>) in the values with spaces<br />
+&#160; * &#160;Allows custom function to additionally filter/modify attribute values (see <a href="#s3.4.9">section 3.4.9</a>)<br />
+
+<div class="sub-sub-section"><h4>
+<a name="s3.4.1" id="s3.4.1"></a><span class="item-no">3.4.1</span>&#160; Auto-addition of XHTML-required attributes
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If indicated attributes for the following elements are found missing, htmLawed (function <span class="term">hl_tag()</span>) will add them (with values same as attribute names unless indicated otherwise below):<br />
+<br />
+&#160; * &#160;area - alt (<span class="term">area</span>)<br />
+&#160; * &#160;area, img - src, alt (<span class="term">image</span>)<br />
+&#160; * &#160;bdo - dir (<span class="term">ltr</span>)<br />
+&#160; * &#160;form - action<br />
+&#160; * &#160;map - name<br />
+&#160; * &#160;optgroup - label<br />
+&#160; * &#160;param - name<br />
+&#160; * &#160;script - type (<span class="term">text/javascript</span>)<br />
+&#160; * &#160;textarea - rows (<span class="term">10</span>), cols (<span class="term">50</span>)<br />
+<br />
+&#160; Additionally, with <span class="term">$config["xml&#58;lang"]</span>&#160;set to <span class="term">1</span>&#160;or <span class="term">2</span>, if the <span class="term">lang</span>&#160;but not the <span class="term">xml&#58;lang</span>&#160;attribute is declared, then the latter is added too, with a value copied from that of <span class="term">lang</span>. This is for better standard-compliance. With <span class="term">$config["xml&#58;lang"]</span>&#160;set to <span class="term">2</span>, the <span class="term">lang</span>&#160;attribute is removed (XHTML 1.1 specs).<br />
+<br />
+&#160; Note that the <span class="term">name</span>&#160;attribute for <span class="term">map</span>, invalid in XHTML 1.1, is also transformed if required -- see <a href="#s3.4.6">section 3.4.6</a>.<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.2" id="s3.4.2"></a><span class="item-no">3.4.2</span>&#160; Duplicate/invalid <span class="term">id</span>&#160;values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["unique_ids"]</span>&#160;is <span class="term">1</span>, htmLawed (function <span class="term">hl_tag()</span>) removes <span class="term">id</span>&#160;attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, <span class="term">&#58;</span>, <span class="term">.</span>, <span class="term">-</span>&#160;and <span class="term">_</span>) or duplicate. If <span class="term">$config["unique_ids"]</span>&#160;is a word, any duplicate but otherwise valid value will be appropriately prefixed with the word to ensure its uniqueness. The word should begin with a letter and should contain only letters, numbers, <span class="term">&#58;</span>, <span class="term">.</span>, <span class="term">_</span>&#160;and <span class="term">-</span>.<br />
+<br />
+&#160; Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of <span class="term">id</span>&#160;values as it uses a global variable (<span class="term">$GLOBALS["hl_Ids"]</span>&#160;array). Further, an admin can restrict the use of certain <span class="term">id</span>&#160;values by presetting this variable before htmLawed is called into use. E.g.:<br />
+<br />
+
+<code class="code">&#160; &#160; $GLOBALS[&#39;hl_Ids&#39;] = array(&#39;top&#39;=&gt;1, &#39;bottom&#39;=&gt;1, &#39;myform&#39;=&gt;1); // id values not allowed in input</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text); // filter input</code>
+<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.3" id="s3.4.3"></a><span class="item-no">3.4.3</span>&#160; URL schemes (protocols) and scripts in attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed edits attributes that take URLs as values if they are found to contain un-permitted schemes. E.g., if the <span class="term">afp</span>&#160;scheme is not permitted, then <span class="term">&lt;a href="afp&#58;//domain.org"&gt;</span>&#160;becomes <span class="term">&lt;a href="denied&#58;afp&#58;//domain.org"&gt;</span>, and if Javascript is not permitted <span class="term">&lt;a onclick="javascript&#58;xss();"&gt;</span>&#160;becomes <span class="term">&lt;a onclick="denied&#58;javascript&#58;xss();"&gt;</span>.<br />
+<br />
+&#160; By default htmLawed permits these schemes in URLs for the <span class="term">href</span>&#160;attribute:<br />
+<br />
+
+<code class="code">&#160; &#160; aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet</code>
+<br />
+<br />
+&#160; Also, only <span class="term">file</span>, <span class="term">http</span>&#160;and <span class="term">https</span>&#160;are permitted in attributes whose names start with <span class="term">o</span>&#160;(like <span class="term">onmouseover</span>), and in these attributes that accept URLs:<br />
+<br />
+
+<code class="code">&#160; &#160; action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap</code>
+<br />
+<br />
+&#160; These default sets are used when <span class="term">$config["schemes"]</span>&#160;is not set (see <a href="#s2.2">section 2.2</a>). To over-ride the defaults, <span class="term">$config["schemes"]</span>&#160;is defined as a string of semi-colon-separated sub-strings of type <span class="term">attribute&#58; comma-separated schemes</span>. E.g., <span class="term">href&#58; mailto, http, https; onclick&#58; javascript; src&#58; http, https</span>. For unspecified attributes, <span class="term">file</span>, <span class="term">http</span>&#160;and <span class="term">https</span>&#160;are permitted. This can be changed by passing schemes for <span class="term">&#42;</span>&#160;in <span class="term">$config["schemes"]</span>. E.g., <span class="term">href&#58; mailto, http, https; &#42;&#58; https, https</span>.<br />
+<br />
+&#160; <span class="term">&#42;</span>&#160;can be put in the list of schemes to permit all protocols. E.g., <span class="term">style&#58; &#42;; img&#58; http, https</span>&#160;results in protocols not being checked in <span class="term">style</span>&#160;attribute values. However, in such cases, any relative-to-absolute URL conversion, or vice versa, (<a href="#s3.4.4">section 3.4.4</a>) is not done.<br />
+<br />
+&#160; Thus, <em>to allow Javascript</em>, one can set <span class="term">$config["schemes"]</span>&#160;as <span class="term">href&#58; mailto, http, https; &#42;&#58; http, https, javascript</span>, or <span class="term">href&#58; mailto, http, https, javascript; &#42;&#58; http, https, javascript</span>, or <span class="term">&#42;&#58; &#42;</span>, and so on.<br />
+<br />
+&#160; As a side-note, one may find <span class="term">style&#58; &#42;</span>&#160;useful as URLs in <span class="term">style</span>&#160;attributes can be specified in a variety of ways, and the patterns that htmLawed uses to identify URLs may mistakenly identify non-URL text.<br />
+<br />
+&#160; <span class="term">!</span>&#160;can be put in the list of schemes to disallow all protocols as well as <em>local</em>&#160;URLs. Thus, with <span class="term">href&#58; http, style&#58; !</span>, '&lt;a href="http://cnn.com" style="background-image: url('local.jpg');"&gt;CNN&lt;/a&gt;' will become '&lt;a href="http://cnn.com" style="background-image: url('denied:local.jpg');"&gt;CNN&lt;/a&gt;'.<br />
+<br />
+&#160; <strong>Note</strong>: If URL-accepting attributes other than those listed above are being allowed, then the scheme will not be checked unless the attribute name contains the string <span class="term">src</span>&#160;(e.g., <span class="term">dynsrc</span>) or starts with <span class="term">o</span>&#160;(e.g., <span class="term">onbeforecopy</span>).<br />
+<br />
+&#160; With <span class="term">$config["safe"] = 1</span>, all URLs are disallowed in the <span class="term">style</span>&#160;attribute values.<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.4" id="s3.4.4"></a><span class="item-no">3.4.4</span>&#160; Absolute &amp; relative URLs in attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can make absolute URLs in attributes like <span class="term">href</span>&#160;relative (<span class="term">$config["abs_url"]</span>&#160;is <span class="term">-1</span>), and vice versa (<span class="term">$config["abs_url"]</span>&#160;is <span class="term">1</span>). URLs in scripts are not considered for this, and so are URLs like <span class="term">#section_6</span>&#160;(fragment), <span class="term">?name=Tim#show</span>&#160;(starting with query string), and <span class="term">;var=1?name=Tim#show</span>&#160;(starting with parameters). Further, this requires that <span class="term">$config["base_url"]</span>&#160;be set properly, with the <span class="term">&#58;//</span>&#160;and a trailing slash (<span class="term">/</span>), with no query string, etc. E.g., <span class="term">file&#58;///D&#58;/page/</span>, <span class="term">https&#58;//abc.com/x/y/</span>, or <span class="term">http&#58;//localhost/demo/</span>&#160;are okay, but <span class="term">file&#58;///D&#58;/page/?help=1</span>, <span class="term">abc.com/x/y/</span>&#160;and <span class="term">http&#58;//localhost/demo/index.htm</span>&#160;are not.<br />
+<br />
+&#160; For making absolute URLs relative, only those URLs that have the <span class="term">$config["base_url"]</span>&#160;string at the beginning are converted. E.g., with <span class="term">$config["base_url"] = "https&#58;//abc.com/x/y/"</span>, <span class="term">https&#58;//abc.com/x/y/a.gif</span>&#160;and <span class="term">https&#58;//abc.com/x/y/z/b.gif</span>&#160;become <span class="term">a.gif</span>&#160;and <span class="term">z/b.gif</span>&#160;respectively, while <span class="term">https&#58;//abc.com/x/c.gif</span>&#160;is not changed.<br />
+<br />
+&#160; When making relative URLs absolute, only values for scheme, network location (host-name) and path values in the base URL are inherited. See <a href="#s5.5">section 5.5</a>&#160;for more about the URL specification as per RFC <a href="http://www.ietf.org/rfc/rfc1808.txt">1808</a>.<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.5" id="s3.4.5"></a><span class="item-no">3.4.5</span>&#160; Lower-cased, standard attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Optionally, for standard-compliance, htmLawed (function <span class="term">hl_tag()</span>) lower-cases standard attribute values to give, e.g., <span class="term">input type="password"</span>&#160;instead of <span class="term">input type="Password"</span>, if <span class="term">$config["lc_std_val"]</span>&#160;is <span class="term">1</span>. Attribute values matching those listed below for any of the elements (plus those for the <span class="term">type</span>&#160;attribute of <span class="term">button</span>&#160;or <span class="term">input</span>) are lower-cased:<br />
+<br />
+
+<code class="code">&#160; &#160; all, baseline, bottom, button, center, char, checkbox, circle, col, colgroup, cols, data, default, file, get, groups, hidden, image, justify, left, ltr, middle, none, object, password, poly, post, preserve, radio, rect, ref, reset, right, row, rowgroup, rows, rtl, submit, text, top</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml&#58;space</code>
+<br />
+<br />
+&#160; The following <em>empty</em>&#160;(<em>minimized</em>) attributes are always assigned lower-cased values (same as the names):<br />
+<br />
+
+<code class="code">&#160; &#160; checked, compact, declare, defer, disabled, ismap, multiple, nohref, noresize, noshade, nowrap, readonly, selected</code>
+<br />
+
+</div>
+<div class="sub-sub-section"><h4>
+<a name="s3.4.6" id="s3.4.6"></a><span class="item-no">3.4.6</span>&#160; Transformation of deprecated attributes
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["no_deprecated_attr"]</span>&#160;is <span class="term">0</span>, then deprecated attributes (see appendix in <a href="#s5.2">section 5.2</a>) are removed and, in most cases, their values are transformed to CSS style properties and added to the <span class="term">style</span>&#160;attributes (function <span class="term">hl_tag()</span>). Except for <span class="term">bordercolor</span>&#160;for <span class="term">table</span>, <span class="term">tr</span>&#160;and <span class="term">td</span>, the scores of proprietary attributes that were never part of any cross-browser standard are not supported.<br />
+<br />
+&#160; <strong>Note</strong>: The attribute <span class="term">target</span>&#160;for <span class="term">a</span>&#160;is allowed even though it is not in XHTML 1.0 specs. This is because of the attribute's wide-spread use and browser-support, and because the attribute is valid in XHTML 1.1 onwards.<br />
+<br />
+&#160; * &#160;align - for <span class="term">img</span>&#160;with value of <span class="term">left</span>&#160;or <span class="term">right</span>, becomes, e.g., <span class="term">float&#58; left</span>; for <span class="term">div</span>&#160;and <span class="term">table</span>&#160;with value <span class="term">center</span>, becomes <span class="term">margin&#58; auto</span>; all others become, e.g., <span class="term">text-align&#58; right</span><br />
+<br />
+&#160; * &#160;bgcolor - E.g., <span class="term">bgcolor="#ffffff"</span>&#160;becomes <span class="term">background-color&#58; #ffffff</span><br />
+&#160; * &#160;border - E.g., <span class="term">height= "10"</span>&#160;becomes <span class="term">height&#58; 10px</span><br />
+&#160; * &#160;bordercolor - E.g., <span class="term">bordercolor=#999999</span>&#160;becomes <span class="term">border-color&#58; #999999;</span><br />
+&#160; * &#160;compact - <span class="term">font-size&#58; 85%</span><br />
+&#160; * &#160;clear - E.g., 'clear="all" becomes <span class="term">clear&#58; both</span><br />
+<br />
+&#160; * &#160;height - E.g., <span class="term">height= "10"</span>&#160;becomes <span class="term">height&#58; 10px</span>&#160;and <span class="term">height="&#42;"</span>&#160;becomes <span class="term">height&#58; auto</span><br />
+<br />
+&#160; * &#160;hspace - E.g., <span class="term">hspace="10"</span>&#160;becomes <span class="term">margin-left&#58; 10px; margin-right&#58; 10px</span><br />
+&#160; * &#160;language - <span class="term">language="VBScript"</span>&#160;becomes <span class="term">type="text/vbscript"</span><br />
+&#160; * &#160;name - E.g., <span class="term">name="xx"</span>&#160;becomes <span class="term">id="xx"</span><br />
+&#160; * &#160;noshade - <span class="term">border-style&#58; none; border&#58; 0; background-color&#58; gray; color&#58; gray</span><br />
+&#160; * &#160;nowrap - <span class="term">white-space&#58; nowrap</span><br />
+&#160; * &#160;size - E.g., <span class="term">size="10"</span>&#160;becomes <span class="term">height&#58; 10px</span><br />
+&#160; * &#160;start - removed<br />
+&#160; * &#160;type - E.g., <span class="term">type="i"</span>&#160;becomes <span class="term">list-style-type&#58; lower-roman</span><br />
+&#160; * &#160;value - removed<br />
+&#160; * &#160;vspace - E.g., <span class="term">vspace="10"</span>&#160;becomes <span class="term">margin-top&#58; 10px; margin-bottom&#58; 10px</span><br />
+&#160; * &#160;width - like <span class="term">height</span><br />
+<br />
+&#160; Example input:<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;img src="j.gif" alt="image" name="dad&#39;s" /&gt;&lt;img src="k.gif" alt="image" id="dad_off" name="dad" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;br clear="left" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;hr noshade size="1" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding&#58;5px;" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;table width="50em" align="center" bgcolor="red"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;&lt;tr&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;td width="20%"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;div align="center"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;h3 align="right"&gt;Section&lt;/h3&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;p align="right"&gt;Para&lt;/p&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;ol type="a" start="e"&gt;&lt;li value="x"&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;/div&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;td width="&#42;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;ol type="1"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;&lt;/tr&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/table&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;br clear="all" /&gt;</code>
+<br />
+<br />
+&#160; And the output with <span class="term">$config["no_deprecated_attr"] = 1</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; &lt;img src="j.gif" alt="image" /&gt;&lt;img src="k.gif" alt="image" id="dad_off" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;br style="clear&#58; left;" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;hr style="border-style&#58; none; border&#58; 0; background-color&#58; gray; color&#58; gray; size&#58; 1px;" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;img src="i.gif" alt="image" width="10em" height="20" style="padding&#58;5px; float&#58; left; margin-left&#58; 10px; margin-right&#58; 10px; margin-top&#58; 10px; margin-bottom&#58; 10px; border&#58; 1px;" id="img" /&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;table width="50em" style="margin&#58; auto; background-color&#58; red;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;&lt;tr&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;td style="width&#58; 20%;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;div style="margin&#58; auto;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;h3 style="text-align&#58; right;"&gt;Section&lt;/h3&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;p style="text-align&#58; right;"&gt;Para&lt;/p&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; &lt;ol style="list-style-type&#58; lower-latin;"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;/div&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;td style="width&#58; auto;"&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160;&lt;ol style="list-style-type&#58; decimal;"&gt;&lt;li&gt;First item&lt;/li&gt;&lt;/ol&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &lt;/td&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160;&lt;/tr&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;/table&gt;</code>
+<br />
+
+<code class="code">&#160; &#160; &lt;br style="clear&#58; both;" /&gt;</code>
+<br />
+<br />
+&#160; For <span class="term">lang</span>, deprecated in XHTML 1.1, transformation is taken care of through <span class="term">$config["xml&#58;lang"]</span>; see <a href="#s3.4.1">section 3.4.1</a>.<br />
+<br />
+&#160; The attribute <span class="term">name</span>&#160;is deprecated in <span class="term">form</span>, <span class="term">iframe</span>, and <span class="term">img</span>, and is replaced with <span class="term">id</span>&#160;if an <span class="term">id</span>&#160;attribute doesn't exist and if the <span class="term">name</span>&#160;value is appropriate for <span class="term">id</span>. For such replacements for <span class="term">a</span>&#160;and <span class="term">map</span>, for which the <span class="term">name</span>&#160;attribute is deprecated in XHTML 1.1, <span class="term">$config["no_deprecated_attr"]</span>&#160;should be set to <span class="term">2</span>&#160;(when set to <span class="term">1</span>, for these two elements, the <span class="term">name</span>&#160;attribute is retained).<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.4.7" id="s3.4.7"></a><span class="item-no">3.4.7</span>&#160; Anti-spam &amp; <span class="term">href</span>
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed (function <span class="term">hl_tag()</span>) can check the <span class="term">href</span>&#160;attribute values (link addresses) as an anti-spam (email or link spam) measure.<br />
+<br />
+&#160; If <span class="term">$config["anti_mail_spam"]</span>&#160;is not <span class="term">0</span>, the <span class="term">@</span>&#160;of email addresses in <span class="term">href</span>&#160;values like <span class="term">mailto&#58;a@b.com</span>&#160;is replaced with text specified by <span class="term">$config["anti_mail_spam"]</span>. The text should be of a form that makes it clear to others that the address needs to be edited before a mail is sent; e.g., <span class="term">&lt;remove_this_antispam&gt;@</span>&#160;(makes the example address <span class="term">a&lt;remove_this_antispam&gt;@b.com</span>).<br />
+<br />
+&#160; For regular links, one can choose to have a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;in its value (which tells some search engines to not follow a link). This can discourage link spammers. Additionally, or as an alternative, one can choose to empty the <span class="term">href</span>&#160;value altogether (disable the link).<br />
+<br />
+&#160; For use of these options, <span class="term">$config["anti_link_spam"]</span>&#160;should be set as an array with values <span class="term">regex1</span>&#160;and <span class="term">regex2</span>, both or one of which can be empty (like <span class="term">array("", "regex2")</span>) to indicate that that option is not to be used. Otherwise, <span class="term">regex1</span>&#160;or <span class="term">regex2</span>&#160;should be PHP- and PCRE-compatible regular expression patterns: <span class="term">href</span>&#160;values will be matched against them and those matching the pattern will accordingly be treated.<br />
+<br />
+&#160; Note that the regular expressions should have <em>delimiters</em>, and be well-formed and preferably fast. Absolute efficiency/accuracy is often not needed.<br />
+<br />
+&#160; An example, to have a <span class="term">rel</span>&#160;attribute with <span class="term">nofollow</span>&#160;for all links, and to disable links that do not point to domains <span class="term">abc.com</span>&#160;and <span class="term">xyz.org</span>:<br />
+<br />
+
+<code class="code">&#160; &#160; $config["anti_link_spam"] = array(&#39;&#96;.&#96;&#39;, &#39;&#96;&#58;//\W&#42;(?!(abc\.com|xyz\.org))&#96;&#39;);</code>
+<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.4.8" id="s3.4.8"></a><span class="item-no">3.4.8</span>&#160; Inline style properties
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can check URL schemes and dynamic expressions (to guard against Javascript, etc., script-based insecurities) in inline CSS style property values in the <span class="term">style</span>&#160;attributes. (CSS properties like <span class="term">background-image</span>&#160;that accept URLs in their values are noted in <a href="#s5.3">section 5.3</a>.) Dynamic CSS expressions that allow scripting in the IE browser, and can be a vulnerability, can be removed from property values by setting <span class="term">$config["css_expression"]</span>&#160;to <span class="term">1</span>&#160;(default setting). Note that when <span class="term">$config["css_expression"]</span>&#160;is set to <span class="term">1</span>, htmLawed will remove <span class="term">/&#42;</span>&#160;from the <span class="term">style</span>&#160;values.<br />
+<br />
+&#160; <strong>Note</strong>: Because of the various ways of representing characters in attribute values (URL-escapement, entitification, etc.), htmLawed might alter the values of the <span class="term">style</span>&#160;attribute values, and may even falsely identify dynamic CSS expressions and URL schemes in them. If this is an important issue, checking of URLs and dynamic expressions can be turned off (<span class="term">$config["schemes"] = "...style&#58;&#42;..."</span>, see <a href="#s3.4.3">section 3.4.3</a>, and <span class="term">$config["css_expression"] = 0</span>). Alternately, admins can use their own custom function for finer handling of <span class="term">style</span>&#160;values through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; It is also possible to have htmLawed let through any <span class="term">style</span>&#160;value by setting <span class="term">$config["style_pass"]</span>&#160;to <span class="term">1</span>.<br />
+<br />
+&#160; As such, it is better to set up a CSS file with class declarations, disallow the <span class="term">style</span>&#160;attribute, set a <span class="term">$spec</span>&#160;rule (see <a href="#s2.3">section 2.3</a>) for <span class="term">class</span>&#160;for the <span class="term">oneof</span>&#160;or <span class="term">match</span>&#160;parameter, and ask writers to make use of the <span class="term">class</span>&#160;attribute.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.4.9" id="s3.4.9"></a><span class="item-no">3.4.9</span>&#160; Hook function for tag content
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; It is possible to utilize a custom hook function to alter the tag content htmLawed has finalized (i.e., after it has checked/corrected for required attributes, transformed attributes, lower-cased attribute names, etc.).<br />
+<br />
+&#160; When <span class="term">$config</span>&#160;parameter <span class="term">hook_tag</span>&#160;is set to the name of a function, htmLawed (function <span class="term">hl_tag()</span>) will pass on the element name, and, in the case of an opening tag, the <em>finalized</em>&#160;attribute name-value pairs as array elements to the function. The function, after completing a task such as filtering or tag transformation, will typically return an empty string, the full opening tag string like <span class="term">&lt;element_name attribute_1_name="attribute_1_value"...&gt;</span>&#160;(for empty elements like <span class="term">img</span>&#160;and <span class="term">input</span>, the element-closing slash <span class="term">/</span>&#160;should also be included), etc.<br />
+<br />
+&#160; Any <span class="term">hook_tag</span>&#160;function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as <span class="term">a</span>&#160;in the closing <span class="term">&lt;/a&gt;</span>&#160;tag of the element <span class="term">&lt;a href="http&#58;//cnn.com"&gt;CNN&lt;/a&gt;</span>. Unlike for opening tags, no other value (i.e., the attribute name-value array) is passed to the function since a closing tag contains only element names. Typically, the function will return an empty string or a full closing tag (like <span class="term">&lt;/a&gt;</span>).<br />
+<br />
+&#160; This is a <strong>powerful functionality</strong>&#160;that can be exploited for various objectives: consolidate-and-convert inline <span class="term">style</span>&#160;attributes to <span class="term">class</span>, convert <span class="term">embed</span>&#160;elements to <span class="term">object</span>, permit only one <span class="term">caption</span>&#160;element in a <span class="term">table</span>&#160;element, disallow embedding of certain types of media, <strong>inject HTML</strong>, use <a href="http://csstidy.sourceforge.net">CSSTidy</a>&#160;to sanitize <span class="term">style</span>&#160;attribute values, etc.<br />
+<br />
+&#160; As an example, the custom hook code below can be used to force a series of specifically ordered <span class="term">id</span>&#160;attributes on all elements, and a specific <span class="term">param</span>&#160;element inside all <span class="term">object</span>&#160;elements:<br />
+<br />
+
+<code class="code">&#160; &#160; function my_tag_function($element, $attribute_array=0){</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; // If second argument is not received, it means a closing tag is being handled</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; if(is_numeric($attribute_array)){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; return "&lt;/$element&gt;";</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; static $id = 0;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; // Remove any duplicate element</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; if($element == &#39;param&#39; &amp;&amp; isset($attribute_array[&#39;allowscriptaccess&#39;])){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; return &#39;&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; $new_element = &#39;&#39;;</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; // Force a serialized ID number</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; $attribute_array[&#39;id&#39;] = &#39;my_&#39;. $id;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; ++$id;</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; // Inject param for allowscriptaccess</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; if($element == &#39;object&#39;){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; $new_element = &#39;&lt;param id=&#39;my_&#39;. $id; allowscriptaccess="never" /&gt;&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; ++$id;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; $string = &#39;&#39;;</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; foreach($attribute_array as $k=&gt;$v){</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; &#160; $string .= " {$k}=\"{$v}\"";</code>
+<br />
+
+<code class="code">&#160; &#160; &#160; }</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; static $empty_elements = array(&#39;area&#39;=&gt;1, &#39;br&#39;=&gt;1, &#39;col&#39;=&gt;1, &#39;embed&#39;=&gt;1, &#39;hr&#39;=&gt;1, &#39;img&#39;=&gt;1, &#39;input&#39;=&gt;1, &#39;isindex&#39;=&gt;1, &#39;param&#39;=&gt;1);</code>
+<br />
+<br />
+
+<code class="code">&#160; &#160; &#160; return "&lt;{$element}{$string}". (isset($in_array($element, $empty_elements) ? &#39; /&#39; &#58; &#39;&#39;). &#39;&gt;&#39;. $new_element;</code>
+<br />
+
+<code class="code">&#160; &#160; }</code>
+<br />
+<br />
+&#160; The <span class="term">hook_tag</span>&#160;parameter is different from the <span class="term">hook</span>&#160;parameter (<a href="#s3.7">section 3.7</a>).<br />
+<br />
+&#160; Snippets of hook function code developed by others may be available on the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website.<br />
+
+</div>
+</div>
+<div class="sub-section"><h3>
+<a name="s3.5" id="s3.5"></a><span class="item-no">3.5</span>&#160; Simple configuration directive for most valid XHTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["valid_xhtml"]</span>&#160;is set to <span class="term">1</span>, some relevant <span class="term">$config</span>&#160;parameters (indicated by <span class="term">~</span>&#160;in <a href="#s2.2">section 2.2</a>) are auto-adjusted. This allows one to pass the <span class="term">$config</span>&#160;argument with a simpler value. If a value for a parameter auto-set through <span class="term">valid_xhtml</span>&#160;is still manually provided, then that value will over-ride the auto-set value.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.6" id="s3.6"></a><span class="item-no">3.6</span>&#160; Simple configuration directive for most <em>safe</em>&#160;HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; <em>Safe</em>&#160;HTML refers to HTML that is restricted to reduce the vulnerability for scripting attacks (such as XSS) based on HTML code which otherwise may still be legal and compliant with the HTML standard specs. When elements such as <span class="term">script</span>&#160;and <span class="term">object</span>, and attributes such as <span class="term">onmouseover</span>&#160;and <span class="term">style</span>&#160;are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered <span class="term">safe</span>&#160;depends on the nature of the web application and the trust-level accorded to its users.<br />
+<br />
+&#160; htmLawed allows an admin to use <span class="term">$config["safe"]</span>&#160;to auto-adjust multiple <span class="term">$config</span>&#160;parameters (such as <span class="term">elements</span>&#160;which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by <span class="term">"</span>&#160;in <a href="#s2.2">section 2.2</a>). Thus, one can pass the <span class="term">$config</span>&#160;argument with a simpler value.<br />
+<br />
+&#160; With the value of <span class="term">1</span>, htmLawed considers <span class="term">CDATA</span>&#160;sections and HTML comments as plain text, and prohibits the <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span>, <span class="term">object</span>&#160;and <span class="term">script</span>&#160;elements, and the <span class="term">on&#42;</span>&#160;attributes like <span class="term">onclick</span>. ( There are <span class="term">$config</span>&#160;parameters like <span class="term">css_expression</span>&#160;that are not affected by the value set for <span class="term">safe</span>&#160;but whose default values still contribute towards a more <em>safe</em>&#160;output.) Further, URLs with schemes (see <a href="#s3.4.3">section 3.4.3</a>) are neutralized so that, e.g., <span class="term">style="moz-binding&#58;url(http&#58;//danger)"</span>&#160;becomes <span class="term">style="moz-binding&#58;url(denied&#58;http&#58;//danger)"</span>.<br />
+<br />
+&#160; Admins, however, may still want to completely deny the <span class="term">style</span>&#160;attribute, e.g., with code like<br />
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text, array(&#39;safe&#39;=&gt;1, &#39;deny_attribute&#39;=&gt;&#39;style&#39;));</code>
+<br />
+<br />
+&#160; Permitting the <span class="term">style</span>&#160;attribute brings in risks of <em>click-jacking</em>, etc. CSS property values can render a page non-functional or be used to deface it. Except for URLs, dynamic expressions, and some other things, htmLawed does not completely check <span class="term">style</span>&#160;values. It does provide ways for the code-developer implementing htmLawed to do such checks through the <span class="term">$spec</span>&#160;argument, and through the <span class="term">hook_tag</span>&#160;parameter (see <a href="#s3.4.8">section 3.4.8</a>&#160;for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.<br />
+<br />
+&#160; If a value for a parameter auto-set through <span class="term">safe</span>&#160;is still manually provided, then that value can over-ride the auto-set value. E.g., with <span class="term">$config["safe"] = 1</span>&#160;and <span class="term">$config["elements"] = "&#42;+script"</span>, <span class="term">script</span>, but not <span class="term">applet</span>, is allowed.<br />
+<br />
+&#160; A page illustrating the efficacy of htmLawed's anti-XSS abilities with <span class="term">safe</span>&#160;set to <span class="term">1</span>&#160;against XSS vectors listed by <a href="http://ha.ckers.org/xss.html">RSnake</a>&#160;may be available <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/rsnake/RSnakeXSSTest.htm">here</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.7" id="s3.7"></a><span class="item-no">3.7</span>&#160; Using a hook function
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; If <span class="term">$config["hook"]</span>&#160;is not set to <span class="term">0</span>, then htmLawed will allow preliminarily processed input to be altered by a hook function named by <span class="term">$config["hook"]</span>&#160;before starting the main work (but after handling of characters, entities, HTML comments and <span class="term">CDATA</span>&#160;sections -- see code for function <span class="term">htmLawed()</span>).<br />
+<br />
+&#160; The hook function also allows one to alter the <em>finalized</em>&#160;values of <span class="term">$config</span>&#160;and <span class="term">$spec</span>.<br />
+<br />
+&#160; Note that the <span class="term">hook</span>&#160;parameter is different from the <span class="term">hook_tag</span>&#160;parameter (<a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; Snippets of hook function code developed by others may be available on the <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">htmLawed</a>&#160;website.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.8" id="s3.8"></a><span class="item-no">3.8</span>&#160; Obtaining <em>finalized</em>&#160;parameter values
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed can assign the <em>finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values to a variable named by <span class="term">$config["show_setting"]</span>. The variable, made global by htmLawed, is set as an array with three keys: <span class="term">config</span>, with the <span class="term">$config</span>&#160;value, <span class="term">spec</span>, with the <span class="term">$spec</span>&#160;value, and <span class="term">time</span>, with a value that is the Unix time (the output of PHP's <span class="term">microtime()</span>&#160;function) when the value was assigned. Admins should use a PHP-compliant variable name (e.g., one that does not begin with a numerical digit) that does not conflict with variable names in their non-htmLawed code.<br />
+<br />
+&#160; The values, which are also post-hook function (if any), can be used to auto-generate information (on, e.g., the elements that are permitted) for input writers.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.9" id="s3.9"></a><span class="item-no">3.9</span>&#160; Retaining non-HTML tags in input with mixed markup
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; htmLawed does not remove certain characters that, though invalid, are nevertheless <em>discouraged</em>&#160;in HTML documents as per the specifications (see <a href="#s5.1">section 5.1</a>). This can be utilized to deal with input that contains mixed markup. Input that may have HTML markup as well as some other markup that is based on the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters is considered to have mixed markup. The non-HTML markup can be rather proprietary (like markup for emoticons/smileys), or standard (like MathML or SVG). Or it can be programming code meant for execution/evaluation (such as embedded PHP code).<br />
+<br />
+&#160; To deal with such mixed markup, the input text can be pre-processed to hide the non-HTML markup by specifically replacing the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters with some of the HTML-discouraged characters (see <a href="#s3.1.2">section 3.1.2</a>). Post-htmLawed processing, the replacements are reverted.<br />
+<br />
+&#160; An example (mixed HTML and PHP code in input text):<br />
+<br />
+
+<code class="code">&#160; &#160; $text = preg_replace(&#39;&#96;&lt;\?php(.+?)\?&gt;&#96;sm&#39;, "\x83?php\\1?\x84", $text);</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = htmLawed($text);</code>
+<br />
+
+<code class="code">&#160; &#160; $processed = preg_replace(&#39;&#96;\x83\?php(.+?)\?\x84&#96;sm&#39;, &#39;&lt;?php$1?&gt;&#39;, $processed);</code>
+<br />
+<br />
+&#160; This code will not work if <span class="term">$config["clean_ms_char"]</span>&#160;is set to <span class="term">1</span>&#160;(<a href="#s3.1">section 3.1</a>), in which case one should instead deploy a hook function (<a href="#s3.7">section 3.7</a>). (htmLawed internally uses certain control characters, code-points <span class="term">1</span>&#160;to <span class="term">7</span>, and use of these characters as markers in the logic of hook functions may cause issues.)<br />
+<br />
+&#160; Admins may also be able to use <span class="term">$config["and_mark"]</span>&#160;to deal with such mixed markup; see <a href="#s3.2">section 3.2</a>.<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s4" id="s4"></a><span class="item-no">4</span>&#160; Other
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<div class="sub-section"><h3>
+<a name="s4.1" id="s4.1"></a><span class="item-no">4.1</span>&#160; Support
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; A careful reading of this documentation may provide an answer.<br />
+<br />
+&#160; Software updates and forum-based community-support may be found at <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a>. For general PHP issues (not htmLawed-specific), support may be found through internet searches and at <a href="http://php.net">http://php.net</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.2" id="s4.2"></a><span class="item-no">4.2</span>&#160; Known issues
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; See <a href="#s2.8">section 2.8</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.3" id="s4.3"></a><span class="item-no">4.3</span>&#160; Change-log
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the <span class="term">htmLawed.php</span>&#160;file, may be updated without a change-log entry if the secondary files, but not htmLawed per se, are revised.)<br />
+<br />
+&#160; <em>Version number - Release date. Notes</em><br />
+<br />
+&#160; 1.1.16 - 29 August 2013. Fix for a potential security vulnerability arising from specialy encoded space characters in URL schemes/protocols<br />
+<br />
+&#160; 1.1.15 - 11 August 2013. Improved tidying/prettifying functionality<br />
+<br />
+&#160; 1.1.14 - 8 August 2012. Fix for possible segmental loss of incremental indentation during <span class="term">tidying</span>&#160;when <span class="term">balance</span>&#160;is disabled; fix for non-effectuation under some circumstances of a corrective behavior to preserve plain text within elements like <span class="term">blockquote</span>.<br />
+<br />
+&#160; 1.1.13 - 22 July 2012. Added feature allowing use of custom, non-standard attributes or custom rules for standard attributes<br />
+<br />
+&#160; 1.1.12 - 5 July 2012. Fix for a bug in identifying an unquoted value of the <span class="term">face</span>&#160;attribute<br />
+<br />
+&#160; 1.1.11 - 5 June 2012. Fix for possible problem with handling of multi-byte characters in attribute values in an mbstring.func_overload enviroment. <span class="term">$config["hook_tag"]</span>, if specified, now receives names of elements in closing tags.<br />
+<br />
+&#160; 1.1.10 - 22 October 2011. Fix for a bug in the <span class="term">tidy</span>&#160;functionality that caused the entire input to be replaced with a single space; new parameter, <span class="term">$config["direct_list_nest"]</span>&#160;to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)<br />
+<br />
+&#160; 1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of <span class="term">li</span>&#160;within <span class="term">dir</span><br />
+<br />
+&#160; 1.1.9.4 - 3 July 2010. Parameter <span class="term">schemes</span>&#160;now accepts <span class="term">!</span>&#160;so any URL, even a local one, can be <em>denied</em>. An issue in which a second URL value in <span class="term">style</span>&#160;properties was not checked was fixed.<br />
+<br />
+&#160; 1.1.9.3 - 17 May 2010. Checks for correct nesting of <span class="term">param</span><br />
+<br />
+&#160; 1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes<br />
+<br />
+&#160; 1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for <span class="term">flashvars</span>&#160;attribute for <span class="term">embed</span><br />
+<br />
+&#160; 1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values<br />
+<br />
+&#160; 1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice<br />
+<br />
+&#160; 1.1.8 - 23 April 2009. Parameter <span class="term">deny_attribute</span>&#160;now accepts the wild-card <span class="term">&#42;</span>, making it simpler to specify its value when all but a few attributes are being denied; fixed a bug in interpreting <span class="term">$spec</span><br />
+<br />
+&#160; 1.1.7 - 11-12 March 2009. Attributes globally denied through <span class="term">deny_attribute</span>&#160;can be allowed element-specifically through <span class="term">$spec</span>; <span class="term">$config["style_pass"]</span>&#160;allowing letting through any <span class="term">style</span>&#160;value introduced; altered logic to catch certain types of dynamic crafted CSS expressions<br />
+<br />
+&#160; 1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions<br />
+<br />
+&#160; 1.1.2 - 22 January 2009. Fixed bug in parsing of <span class="term">font</span>&#160;attributes during tag transformation<br />
+<br />
+&#160; 1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent<br />
+<br />
+&#160; 1.1 - 29 June 2008. <span class="term">$config["hook_tag"]</span>&#160;and <span class="term">$config["tidy"]</span>&#160;introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug<br />
+<br />
+&#160; 1.0.9 - 11 June 2008. Fix for a bug in checks for invalid HTML code-point entities<br />
+<br />
+&#160; 1.0.8 - 15 May 2008. Permit <span class="term">bordercolor</span>&#160;attribute for <span class="term">table</span>, <span class="term">td</span>&#160;and <span class="term">tr</span><br />
+<br />
+&#160; 1.0.7 - 1 May 2008. Support for <span class="term">wmode</span>&#160;attribute for <span class="term">embed</span>; <span class="term">$config["show_setting"]</span>&#160;introduced; improved <span class="term">$config["elements"]</span>&#160;evaluation<br />
+<br />
+&#160; 1.0.6 - 20 April 2008. <span class="term">$config["and_mark"]</span>&#160;introduced<br />
+<br />
+&#160; 1.0.5 - 12 March 2008. <span class="term">style</span>&#160;URL schemes essentially disallowed when $config <span class="term">safe</span>&#160;is on; improved regex for CSS expression search<br />
+<br />
+&#160; 1.0.4 - 10 March 2008. Improved corrections for <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span>&#160;and <span class="term">noscript</span><br />
+<br />
+&#160; 1.0.3 - 3 March 2008. Character entities for soft-hyphens are now replaced with spaces (instead of being removed); fix for a bug allowing <span class="term">td</span>&#160;directly inside <span class="term">table</span>; <span class="term">$config["safe"]</span>&#160;introduced<br />
+<br />
+&#160; 1.0.2 - 13 February 2008. Improved implementation of <span class="term">$config["keep_bad"]</span><br />
+<br />
+&#160; 1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions (<span class="term">hl_tag()</span>&#160;and <span class="term">hl_prot()</span>); no error display with <span class="term">hl_regex()</span><br />
+<br />
+&#160; 1.0 - 2 November 2007. First release<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.4" id="s4.4"></a><span class="item-no">4.4</span>&#160; Testing
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; To test htmLawed using a form interface, a <a href="htmLawedTest.php">demo</a>&#160;web-page is provided with the htmLawed distribution (<span class="term">htmLawed.php</span>&#160;and <span class="term">htmLawedTest.php</span>&#160;should be in the same directory on the web-server). A file with <a href="htmLawed_TESTCASE.txt">test-cases</a>&#160;is also provided.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.5" id="s4.5"></a><span class="item-no">4.5</span>&#160; Upgrade, &amp; old versions
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Upgrading is as simple as replacing the previous version of <span class="term">htmLawed.php</span>&#160;(assuming it was not modified for customized features). As htmLawed output is almost always used in static documents, upgrading should not affect old, finalized content.<br />
+<br />
+&#160; <strong>Important</strong>&#160; The following upgrades may affect the functionality of a specific htmLawed installation:<br />
+<br />
+&#160; (1) From version 1.1-1.1.10 to 1.1.11 (or later), if a <span class="term">hook_tag</span>&#160;function is in use: In version 1.1.11, elements in closing tags (and not just the opening tags) are also passed to the function. There are no attribute names/values to pass, so a <span class="term">hook_tag</span>&#160;function receives only the element name. The <span class="term">hook_tag</span>&#160;function therefore may have to be edited. See <a href="#s3.4.9">section 3.4.9</a>.<br />
+<br />
+&#160; Old versions of htmLawed may be available online. E.g., for version 1.0, check <a href="http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip">http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip</a>, for 1.1.1, htmLawed111.zip, and for 1.1.10, htmLawed1110.zip.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.6" id="s4.6"></a><span class="item-no">4.6</span>&#160; Comparison with <span class="term">HTMLPurifier</span>
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; The HTMLPurifier PHP library by Edward Yang is a very good HTML filtering script that uses object oriented PHP code. Compared to htmLawed, it (as of year 2010):<br />
+<br />
+&#160; * &#160;does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)<br />
+<br />
+&#160; * &#160;is 15-20 times bigger (scores of files totalling more than 750 kb)<br />
+<br />
+&#160; * &#160;consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)<br />
+<br />
+&#160; * &#160;is expectedly slower<br />
+<br />
+&#160; * &#160;does not allow admins to fully allow all valid HTML (because of incomplete HTML support, it always considers elements like <span class="term">script</span>&#160;illegal)<br />
+<br />
+&#160; * &#160;lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)<br />
+<br />
+&#160; * &#160;has poor documentation<br />
+<br />
+&#160; However, HTMLPurifier has finer checks for character encodings and attribute values, and can log warnings and errors. Visit the HTMLPurifier <a href="http://htmlpurifier.org">website</a>&#160;for updated information.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.7" id="s4.7"></a><span class="item-no">4.7</span>&#160; Use through application plug-ins/modules
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Plug-ins/modules to implement htmLawed in applications such as Drupal and DokuWiki may have been developed. Please check the application websites and the forum on the htmLawed <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">site</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.8" id="s4.8"></a><span class="item-no">4.8</span>&#160; Use in non-PHP applications
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Non-PHP applications written in Python, Ruby, etc., may be able to use htmLawed through system calls to the PHP engine. Such code may have been documented on the internet. Also check the forum on the htmLawed <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">site</a>.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.9" id="s4.9"></a><span class="item-no">4.9</span>&#160; Donate
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; A donation in any currency and amount to appreciate or support this software can be sent by <a href="http://paypal.com">PayPal</a>&#160;to this email address: drpatnaik at yahoo dot com.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s4.10" id="s4.10"></a><span class="item-no">4.10</span>&#160; Acknowledgements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Nicholas Alipaz, Bryan Blakey, Pádraic Brady, Dac Chartrand, Ulf Harnhammer, Gareth Heyes, Klaus Leithoff, Lukasz Pilorz, Shelley Powers, Harro Verton, Edward Yang, and many anonymous users.<br />
+<br />
+&#160; Thank you!<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s5" id="s5"></a><span class="item-no">5</span>&#160; Appendices
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<div class="sub-section"><h3>
+<a name="s5.1" id="s5.1"></a><span class="item-no">5.1</span>&#160; Characters discouraged in XHTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Characters represented by the following hexadecimal code-points are <em>not</em>&#160;invalid, even though some validators may issue messages stating otherwise.<br />
+<br />
+&#160; <span class="term">7f</span>&#160;to <span class="term">84</span>, <span class="term">86</span>&#160;to <span class="term">9f</span>, <span class="term">fdd0</span>&#160;to <span class="term">fddf</span>, <span class="term">1fffe</span>, <span class="term">1ffff</span>, <span class="term">2fffe</span>, <span class="term">2ffff</span>, <span class="term">3fffe</span>, <span class="term">3ffff</span>, <span class="term">4fffe</span>, <span class="term">4ffff</span>, <span class="term">5fffe</span>, <span class="term">5ffff</span>, <span class="term">6fffe</span>, <span class="term">6ffff</span>, <span class="term">7fffe</span>, <span class="term">7ffff</span>, <span class="term">8fffe</span>, <span class="term">8ffff</span>, <span class="term">9fffe</span>, <span class="term">9ffff</span>, <span class="term">afffe</span>, <span class="term">affff</span>, <span class="term">bfffe</span>, <span class="term">bffff</span>, <span class="term">cfffe</span>, <span class="term">cffff</span>, <span class="term">dfffe</span>, <span class="term">dffff</span>, <span class="term">efffe</span>, <span class="term">effff</span>, <span class="term">ffffe</span>, <span class="term">fffff</span>, <span class="term">10fffe</span>&#160;and <span class="term">10ffff</span><br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.2" id="s5.2"></a><span class="item-no">5.2</span>&#160; Valid attribute-element combinations
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Valid attribute-element combinations as per <a href="http://www.w3c.org">W3C</a>&#160;specs.<br />
+<br />
+&#160; * &#160;includes deprecated attributes (marked <span class="term">^</span>), attributes for the non-standard <span class="term">embed</span>&#160;element (marked <span class="term">&#42;</span>), and the proprietary <span class="term">bordercolor</span>&#160;(marked <span class="term">~</span>)<br />
+&#160; * &#160;only non-frameset, HTML body elements<br />
+&#160; * &#160;<span class="term">name</span>&#160;for <span class="term">a</span>&#160;and <span class="term">map</span>, and <span class="term">lang</span>&#160;are invalid in XHTML 1.1<br />
+&#160; * &#160;<span class="term">target</span>&#160;is valid for <span class="term">a</span>&#160;in XHTML 1.1 and higher<br />
+&#160; * &#160;<span class="term">xml&#58;space</span>&#160;is only for XHTML 1.1<br />
+<br />
+&#160; abbr - td, th<br />
+&#160; accept - form, input<br />
+&#160; accept-charset - form<br />
+&#160; accesskey - a, area, button, input, label, legend, textarea<br />
+&#160; action - form<br />
+&#160; align - caption^, embed, applet, iframe, img^, input^, object^, legend^, table^, hr^, div^, h1^, h2^, h3^, h4^, h5^, h6^, p^, col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+&#160; alt - applet, area, img, input<br />
+&#160; archive - applet, object<br />
+&#160; axis - td, th<br />
+&#160; bgcolor - embed, table^, tr^, td^, th^<br />
+&#160; border - table, img^, object^<br />
+&#160; bordercolor~ - table, td, tr<br />
+&#160; cellpadding - table<br />
+&#160; cellspacing - table<br />
+&#160; char - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+&#160; charoff - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+&#160; charset - a, script<br />
+&#160; checked - input<br />
+&#160; cite - blockquote, q, del, ins<br />
+&#160; classid - object<br />
+&#160; clear - br^<br />
+&#160; code - applet<br />
+&#160; codebase - object, applet<br />
+&#160; codetype - object<br />
+&#160; color - font<br />
+&#160; cols - textarea<br />
+&#160; colspan - td, th<br />
+&#160; compact - dir, dl^, menu, ol^, ul^<br />
+&#160; coords - area, a<br />
+&#160; data - object<br />
+&#160; datetime - del, ins<br />
+&#160; declare - object<br />
+&#160; defer - script<br />
+&#160; dir - bdo<br />
+&#160; disabled - button, input, optgroup, option, select, textarea<br />
+&#160; enctype - form<br />
+&#160; face - font<br />
+&#160; flashvars* - embed<br />
+&#160; for - label<br />
+&#160; frame - table<br />
+&#160; frameborder - iframe<br />
+&#160; headers - td, th<br />
+&#160; height - embed, iframe, td^, th^, img, object, applet<br />
+&#160; href - a, area<br />
+&#160; hreflang - a<br />
+&#160; hspace - applet, img^, object^<br />
+&#160; ismap - img, input<br />
+&#160; label - option, optgroup<br />
+&#160; language - script^<br />
+&#160; longdesc - img, iframe<br />
+&#160; marginheight - iframe<br />
+&#160; marginwidth - iframe<br />
+&#160; maxlength - input<br />
+&#160; method - form<br />
+&#160; model* - embed<br />
+&#160; multiple - select<br />
+&#160; name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param<br />
+&#160; nohref - area<br />
+&#160; noshade - hr^<br />
+&#160; nowrap - td^, th^<br />
+&#160; object - applet<br />
+&#160; onblur - a, area, button, input, label, select, textarea<br />
+&#160; onchange - input, select, textarea<br />
+&#160; onfocus - a, area, button, input, label, select, textarea<br />
+&#160; onreset - form<br />
+&#160; onselect - input, textarea<br />
+&#160; onsubmit - form<br />
+&#160; pluginspage* - embed<br />
+&#160; pluginurl* - embed<br />
+&#160; prompt - isindex<br />
+&#160; readonly - textarea, input<br />
+&#160; rel - a<br />
+&#160; rev - a<br />
+&#160; rows - textarea<br />
+&#160; rowspan - td, th<br />
+&#160; rules - table<br />
+&#160; scope - td, th<br />
+&#160; scrolling - iframe<br />
+&#160; selected - option<br />
+&#160; shape - area, a<br />
+&#160; size - hr^, font, input, select<br />
+&#160; span - col, colgroup<br />
+&#160; src - embed, script, input, iframe, img<br />
+&#160; standby - object<br />
+&#160; start - ol^<br />
+&#160; summary - table<br />
+&#160; tabindex - a, area, button, input, object, select, textarea<br />
+&#160; target - a^, area, form<br />
+&#160; type - a, embed, object, param, script, input, li^, ol^, ul^, button<br />
+&#160; usemap - img, input, object<br />
+&#160; valign - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+&#160; value - input, option, param, button, li^<br />
+&#160; valuetype - param<br />
+&#160; vspace - applet, img^, object^<br />
+&#160; width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^<br />
+&#160; wmode - embed<br />
+&#160; xml:space - pre, script, style<br />
+<br />
+&#160; These are allowed in all but the shown elements:<br />
+<br />
+&#160; class - param, script<br />
+&#160; dir - applet, bdo, br, iframe, param, script<br />
+&#160; id - script<br />
+&#160; lang - applet, br, iframe, param, script<br />
+&#160; onclick - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; ondblclick - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onkeydown - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onkeypress - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onkeyup - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmousedown - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmousemove - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmouseout - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmouseover - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; onmouseup - applet, bdo, br, font, iframe, isindex, param, script<br />
+&#160; style - param, script<br />
+&#160; title - param, script<br />
+&#160; xml:lang - applet, br, iframe, param, script<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.3" id="s5.3"></a><span class="item-no">5.3</span>&#160; CSS 2.1 properties accepting URLs
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; background<br />
+&#160; background-image<br />
+&#160; content<br />
+&#160; cue-after<br />
+&#160; cue-before<br />
+&#160; cursor<br />
+&#160; list-style<br />
+&#160; list-style-image<br />
+&#160; play-during<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.4" id="s5.4"></a><span class="item-no">5.4</span>&#160; Microsoft Windows 1252 character replacements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Key: <span class="term">d</span>&#160;double, <span class="term">l</span>&#160;left, <span class="term">q</span>&#160;quote, <span class="term">r</span>&#160;right, <span class="term">s.</span>&#160;single<br />
+<br />
+&#160; Code-point (decimal) - hexadecimal value - replacement entity - represented character<br />
+<br />
+&#160; 127 - 7f - (removed) - (not used)<br />
+&#160; 128 - 80 - &amp;#8364; - euro<br />
+&#160; 129 - 81 - (removed) - (not used)<br />
+&#160; 130 - 82 - &amp;#8218; - baseline s. q<br />
+&#160; 131 - 83 - &amp;#402; - florin<br />
+&#160; 132 - 84 - &amp;#8222; - baseline d q<br />
+&#160; 133 - 85 - &amp;#8230; - ellipsis<br />
+&#160; 134 - 86 - &amp;#8224; - dagger<br />
+&#160; 135 - 87 - &amp;#8225; - d dagger<br />
+&#160; 136 - 88 - &amp;#710; - circumflex accent<br />
+&#160; 137 - 89 - &amp;#8240; - permile<br />
+&#160; 138 - 8a - &amp;#352; - S Hacek<br />
+&#160; 139 - 8b - &amp;#8249; - l s. guillemet<br />
+&#160; 140 - 8c - &amp;#338; - OE ligature<br />
+&#160; 141 - 8d - (removed) - (not used)<br />
+&#160; 142 - 8e - &amp;#381; - Z dieresis<br />
+&#160; 143 - 8f - (removed) - (not used)<br />
+&#160; 144 - 90 - (removed) - (not used)<br />
+&#160; 145 - 91 - &amp;#8216; - l s. q<br />
+&#160; 146 - 92 - &amp;#8217; - r s. q<br />
+&#160; 147 - 93 - &amp;#8220; - l d q<br />
+&#160; 148 - 94 - &amp;#8221; - r d q<br />
+&#160; 149 - 95 - &amp;#8226; - bullet<br />
+&#160; 150 - 96 - &amp;#8211; - en dash<br />
+&#160; 151 - 97 - &amp;#8212; - em dash<br />
+&#160; 152 - 98 - &amp;#732; - tilde accent<br />
+&#160; 153 - 99 - &amp;#8482; - trademark<br />
+&#160; 154 - 9a - &amp;#353; - s Hacek<br />
+&#160; 155 - 9b - &amp;#8250; - r s. guillemet<br />
+&#160; 156 - 9c - &amp;#339; - oe ligature<br />
+&#160; 157 - 9d - (removed) - (not used)<br />
+&#160; 158 - 9e - &amp;#382; - z dieresis<br />
+&#160; 159 - 9f - &amp;#376; - Y dieresis<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.5" id="s5.5"></a><span class="item-no">5.5</span>&#160; URL format
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; An <em>absolute</em>&#160;URL has a <span class="term">protocol</span>&#160;or <span class="term">scheme</span>, a <span class="term">network location</span>&#160;or <span class="term">hostname</span>, and, optional <span class="term">path</span>, <span class="term">parameters</span>, <span class="term">query</span>&#160;and <span class="term">fragment</span>&#160;segments. Thus, an absolute URL has this generic structure:<br />
+<br />
+
+<code class="code">&#160; &#160; (scheme) &#58; (//network location) /(path) ;(parameters) ?(query) #(fragment)</code>
+<br />
+<br />
+&#160; The schemes can only contain letters, digits, <span class="term">+</span>, <span class="term">.</span>&#160;and <span class="term">-</span>. Hostname is the portion after the <span class="term">//</span>&#160;and up to the first <span class="term">/</span>&#160;(if any; else, up to the end) when <span class="term">&#58;</span>&#160;is followed by a <span class="term">//</span>&#160;(e.g., <span class="term">abc.com</span>&#160;in <span class="term">ftp&#58;//abc.com/def</span>); otherwise, it consists of everything after the <span class="term">&#58;</span>&#160;(e.g., <span class="term">def@abc.com</span>&#160;in mailto:def@abc.com').<br />
+<br />
+&#160; <em>Relative</em>&#160;URLs do not have explicit schemes and network locations; such values are inherited from a <em>base</em>&#160;URL.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.6" id="s5.6"></a><span class="item-no">5.6</span>&#160; Brief on htmLawed code
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+&#160; Much of the code's logic and reasoning can be understood from the documentation above.<br />
+<br />
+&#160; The <strong>output</strong>&#160;of htmLawed is a text string containing the processed input. There is no custom error tracking.<br />
+<br />
+&#160; <strong>Function arguments</strong>&#160;for htmLawed are:<br />
+<br />
+&#160; * &#160;<span class="term">$in</span>&#160;- first argument; a text string; the <strong>input text</strong>&#160;to be processed. Any extraneous slashes added by PHP when <em>magic quotes</em>&#160;are enabled should be removed beforehand using PHP's <span class="term">stripslashes()</span>&#160;function.<br />
+<br />
+&#160; * &#160;<span class="term">$config</span>&#160;- second argument; an associative array; optional; named <span class="term">$C</span>&#160;within htmLawed code. The array has keys with names like <span class="term">balance</span>&#160;and <span class="term">keep_bad</span>, and the values, which can be boolean, string, or array, depending on the key, are read to accordingly set the <strong>configurable parameters</strong>&#160;(indicated by the keys). All configurable parameters receive some default value if the value to be used is not specified by the user through <span class="term">$config</span>. <em>Finalized</em>&#160;<span class="term">$config</span>&#160;is thus a filtered and possibly larger array.<br />
+<br />
+&#160; * &#160;<span class="term">$spec</span>&#160;- third argument; a text string; optional. The string has rules, written in an htmLawed-designated format, <strong>specifying</strong>&#160;element-specific attribute and attribute value restrictions. Function <span class="term">hl_spec()</span>&#160;is used to convert the string to an associative-array, named <span class="term">$S</span>&#160;within htmLawed code, for internal use. <em>Finalized</em>&#160;<span class="term">$spec</span>&#160;is thus an array.<br />
+<br />
+&#160; <em>Finalized</em>&#160;<span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;are made <strong>global variables</strong>&#160;while htmLawed is at work. Values of any pre-existing global variables with same names are noted, and their values are restored after htmLawed finishes processing the input (to capture the <em>finalized</em>&#160;values, the <span class="term">show_settings</span>&#160;parameter of <span class="term">$config</span>&#160;should be used). Depending on <span class="term">$config</span>, another global variable <span class="term">hl_Ids</span>, to track <span class="term">id</span>&#160;attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.<br />
+<br />
+&#160; Except for the main function <span class="term">htmLawed()</span>&#160;and the functions <span class="term">kses()</span>&#160;and <span class="term">kses_hook()</span>, htmLawed's functions are <strong>name-spaced</strong>&#160;using the <span class="term">hl_</span>&#160;prefix. The <strong>functions</strong>&#160;and their roles are:<br />
+<br />
+&#160; * &#160;<span class="term">hl_attrval</span>&#160;- checking attribute values against $spec<br />
+&#160; * &#160;<span class="term">hl_bal</span>&#160;- tag balancing<br />
+&#160; * &#160;<span class="term">hl_cmtcd</span>&#160;- handling CDATA sections and HTML comments<br />
+&#160; * &#160;<span class="term">hl_ent</span>&#160;- entity handling<br />
+&#160; * &#160;<span class="term">hl_prot</span>&#160;- checking a URL scheme/protocol<br />
+&#160; * &#160;<span class="term">hl_regex</span>&#160;- checking syntax of a regular expression<br />
+&#160; * &#160;<span class="term">hl_spec</span>&#160;- converting user-supplied $spec value to one used by htmLawed internally<br />
+&#160; * &#160;<span class="term">hl_tag</span>&#160;- handling tags<br />
+&#160; * &#160;<span class="term">hl_tag2</span>&#160;- transforming tags<br />
+&#160; * &#160;<span class="term">hl_tidy</span>&#160;- compact/beautify HTML<br />
+&#160; * &#160;<span class="term">hl_version</span>&#160;- reporting htmLawed version<br />
+&#160; * &#160;<span class="term">htmLawed</span>&#160;- main function<br />
+&#160; * &#160;<span class="term">kses</span>&#160;- main function of <span class="term">kses</span><br />
+&#160; * &#160;<span class="term">kses_hook</span>&#160;- hook function of <span class="term">kses</span><br />
+<br />
+&#160; The last two are for compatibility with pre-existing code using the <span class="term">kses</span>&#160;script. htmLawed's <span class="term">kses()</span>&#160;basically passes on the filtering task to <span class="term">htmLawed()</span>&#160;function after deciphering <span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;from the argument values supplied to it. <span class="term">kses_hook()</span>&#160;is an empty function and is meant for being filled with custom code if the <span class="term">kses</span>&#160;script users were using one.<br />
+<br />
+&#160; <span class="term">htmLawed()</span>&#160;finalizes <span class="term">$spec</span>&#160;(with the help of <span class="term">hl_spec()</span>) and <span class="term">$config</span>, and globalizes them. Finalization of <span class="term">$config</span>&#160;involves setting default values if an inappropriate or invalid one is supplied. This includes calling <span class="term">hl_regex()</span>&#160;to check well-formedness of regular expression patterns if such expressions are user-supplied through <span class="term">$config</span>. <span class="term">htmLawed()</span>&#160;then removes invalid characters like nulls and <span class="term">x01</span>&#160;and appropriately handles entities using <span class="term">hl_ent()</span>. HTML comments and CDATA sections are identified and treated as per <span class="term">$config</span>&#160;with the help of <span class="term">hl_cmtcd()</span>. When retained, the <span class="term">&lt;</span>&#160;and <span class="term">&gt;</span>&#160;characters identifying them, and the <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters inside them, are replaced with control characters (code-points <span class="term">1</span>&#160;to <span class="term">5</span>) till any tag balancing is completed.<br />
+<br />
+&#160; After this <em>initial processing</em>&#160;<span class="term">htmLawed()</span>&#160;identifies tags using regex and processes them with the help of <span class="term">hl_tag()</span>&#160;-- &#160;a large function that analyzes tag content, filtering it as per HTML standards, <span class="term">$config</span>&#160;and <span class="term">$spec</span>. Among other things, <span class="term">hl_tag()</span>&#160;transforms deprecated elements using <span class="term">hl_tag2()</span>, removes attributes from closing tags, checks attribute values as per <span class="term">$spec</span>&#160;rules using <span class="term">hl_attrval()</span>, and checks URL protocols using <span class="term">hl_prot()</span>. <span class="term">htmLawed()</span>&#160;performs tag balancing and nesting checks with a call to <span class="term">hl_bal()</span>, and optionally compacts/beautifies the output with proper white-spacing with a call to <span class="term">hl_tidy()</span>. The latter temporarily replaces white-space, and <span class="term">&lt;</span>, <span class="term">&gt;</span>&#160;and <span class="term">&amp;</span>&#160;characters inside <span class="term">pre</span>, <span class="term">script</span>&#160;and <span class="term">textarea</span>&#160;elements, and HTML comments and CDATA sections with control characters (code-points <span class="term">1</span>&#160;to <span class="term">5</span>, and <span class="term">7</span>).<br />
+<br />
+&#160; htmLawed permits the use of custom code or <strong>hook functions</strong>&#160;at two stages. The first, called inside <span class="term">htmLawed()</span>, allows the input text as well as the finalized <span class="term">$config</span>&#160;and <span class="term">$spec</span>&#160;values to be altered right after the initial processing (see <a href="#s3.7">section 3.7</a>). The second is called by <span class="term">hl_tag()</span>&#160;once the tag content is finalized (see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+&#160; The functionality of htmLawed is dictated by the external HTML standard. It is thus coded for a clear-cut objective with not much concern for tweakability. The code is only minimally annotated with comments -- it is not meant to instruct; PHP developers familiar with the HTML specifications will see the logic, and others can always refer to the htmLawed documentation. The compact structuring of the statements is meant to aid a quick grasp of the logic.
+</div>
+</div>
+<br />
+<hr /><br /><br /><span class="subtle"><small>HTM version of <em><a href="htmLawed_README.txt">htmLawed_README.txt</a></em> generated on 29 Aug, 2013 using <a href="http://www.bioinformatics.org/phplabware/internal_utilities">rTxt2htm</a> from PHP Labware</small></span>
+</div><!-- ended div body -->
+</div><!-- ended div top -->
+</body>
</html> \ No newline at end of file
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt b/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt
index e4027e465..5e19605e5 100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt
+++ b/mod/htmlawed/vendors/htmLawed/htmLawed_README.txt
@@ -1,1701 +1,1734 @@
-/*
-htmLawed_README.txt, 8 June 2012
-htmLawed 1.1.11, 5 June 2012
-Copyright Santosh Patnaik
-Dual licensed with LGPL 3 and GPL 2 or later
-A PHP Labware internal utility - http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed
-*/
-
-
-== Content ==========================================================
-
-
-1 About htmLawed
- 1.1 Example uses
- 1.2 Features
- 1.3 History
- 1.4 License & copyright
- 1.5 Terms used here
-2 Usage
- 2.1 Simple
- 2.2 Configuring htmLawed using the '$config' parameter
- 2.3 Extra HTML specifications using the '$spec' parameter
- 2.4 Performance time & memory usage
- 2.5 Some security risks to keep in mind
- 2.6 Use without modifying old 'kses()' code
- 2.7 Tolerance for ill-written HTML
- 2.8 Limitations & work-arounds
- 2.9 Examples of usage
-3 Details
- 3.1 Invalid/dangerous characters
- 3.2 Character references/entities
- 3.3 HTML elements
- 3.3.1 HTML comments and 'CDATA' sections
- 3.3.2 Tag-transformation for better XHTML-Strict
- 3.3.3 Tag balancing and proper nesting
- 3.3.4 Elements requiring child elements
- 3.3.5 Beautify or compact HTML
- 3.4 Attributes
- 3.4.1 Auto-addition of XHTML-required attributes
- 3.4.2 Duplicate/invalid 'id' values
- 3.4.3 URL schemes (protocols) and scripts in attribute values
- 3.4.4 Absolute & relative URLs
- 3.4.5 Lower-cased, standard attribute values
- 3.4.6 Transformation of deprecated attributes
- 3.4.7 Anti-spam & 'href'
- 3.4.8 Inline style properties
- 3.4.9 Hook function for tag content
- 3.5 Simple configuration directive for most valid XHTML
- 3.6 Simple configuration directive for most `safe` HTML
- 3.7 Using a hook function
- 3.8 Obtaining `finalized` parameter values
- 3.9 Retaining non-HTML tags in input with mixed markup
-4 Other
- 4.1 Support
- 4.2 Known issues
- 4.3 Change-log
- 4.4 Testing
- 4.5 Upgrade, & old versions
- 4.6 Comparison with 'HTMLPurifier'
- 4.7 Use through application plug-ins/modules
- 4.8 Use in non-PHP applications
- 4.9 Donate
- 4.10 Acknowledgements
-5 Appendices
- 5.1 Characters discouraged in HTML
- 5.2 Valid attribute-element combinations
- 5.3 CSS 2.1 properties accepting URLs
- 5.4 Microsoft Windows 1252 character replacements
- 5.5 URL format
- 5.6 Brief on htmLawed code
-
-
-== 1 About htmLawed ================================================
-
-
- htmLawed is a highly customizable single-file PHP script to make text secure, and standard- and admin policy-compliant for use in the body of HTML 4, XHTML 1 or 1.1, or generic XML documents. It is thus a configurable input (X)HTML filter, processor, purifier, sanitizer, beautifier, etc., and an alternative to the HTMLTidy:- http://tidy.sourceforge.net application.
-
- The `lawing in` of input text is needed to ensure that HTML code in the text is standard-compliant, does not introduce security vulnerabilities, and does not break the aesthetics, design or layout of web-pages. htmLawed tries to do this by, for example, making HTML well-formed with balanced and properly nested tags, neutralizing code that may be used for cross-site scripting ('XSS') attacks, and allowing only specified HTML elements/tags and attributes.
-
-
--- 1.1 Example uses ------------------------------------------------
-
-
- * Filtering of text submitted as comments on blogs to allow only certain HTML elements
-
- * Making RSS/Atom newsfeed item-content standard-compliant: often one uses an excerpt from an HTML document for the content, and with unbalanced tags, non-numerical entities, etc., such excerpts may not be XML-compliant
-
- * Text processing for stricter XML standard-compliance: e.g., to have lowercased 'x' in hexadecimal numeric entities becomes necessary if an XHTML document with MathML content needs to be served as 'application/xml'
-
- * Scraping text or data from web-pages
-
- * Pretty-printing HTML code
-
-
--- 1.2 Features ---------------------------------------------------o
-
-
- Key: '*' security feature, '^' standard compliance, '~' requires setting right options, '`' different from 'Kses'
-
- * make input more *secure* and *standard-compliant*
- * use for HTML 4, XHTML 1.0 or 1.1, or even generic *XML* documents ^~`
-
- * *beautify* or *compact* HTML ^~`
-
- * *restrict elements* ^~`
- * proper closure of empty elements like 'img' ^`
- * *transform deprecated elements* like 'u' ^~`
- * HTML *comments* and 'CDATA' sections can be permitted ^~`
- * elements like 'script', 'object' and 'form' can be permitted ~
-
- * *restrict attributes*, including *element-specifically* ^~`
- * remove *invalid attributes* ^`
- * element and attribute names are *lower-cased* ^
- * provide *required attributes*, like 'alt' for 'image' ^`
- * *transform deprecated attributes* ^~`
- * attributes *declared only once* ^`
-
- * *restrict attribute values*, including *element-specifically* ^~`
- * a value is declared for `empty` (`minimized`) attributes like 'checked' ^
- * check for potentially dangerous attribute values *~
- * ensure *unique* 'id' attribute values ^~`
- * *double-quote* attribute values ^
- * lower-case *standard attribute values* like 'password' ^`
-
- * *attribute-specific URL protocol/scheme restriction* *~`
- * disable *dynamic expressions* in 'style' values *~`
-
- * neutralize invalid named character entities ^`
- * *convert* hexadecimal numeric entities to decimal ones, or vice versa ^~`
- * convert named entities to numeric ones for generic XML use ^~`
-
- * remove *null* characters *
- * neutralize potentially dangerous proprietary Netscape *Javascript entities* *
- * replace potentially dangerous *soft-hyphen* character in URL-accepting attribute values with spaces *
-
- * remove common *invalid characters* not allowed in HTML or XML ^`
- * replace *characters from Microsoft applications* like 'Word' that are discouraged in HTML or XML ^~`
- * neutralize entities for characters invalid or discouraged in HTML or XML ^`
- * appropriately neutralize '<', '&', '"', and '>' characters ^*`
-
- * understands improperly spaced tag content (like, spread over more than a line) and properly spaces them `
- * attempts to *balance tags* for well-formedness ^~`
- * understands when *omitable closing tags* like '</p>' (allowed in HTML 4, transitional, e.g.) are missing ^~`
- * attempts to permit only *validly nested tags* ^~`
- * option to *remove or neutralize bad content* ^~`
- * attempts to *rectify common errors of plain-text misplacement* (e.g., directly inside 'blockquote') ^~`
-
- * fast, *non-OOP* code of ~45 kb incurring peak basal memory usage of ~0.5 MB
- * *compatible* with pre-existing code using 'Kses' (the filter used by 'WordPress')
-
- * optional *anti-spam* measures such as addition of 'rel="nofollow"' and link-disabling ~`
- * optionally makes *relative URLs absolute*, and vice versa ~`
-
- * optionally mark '&' to identify the entities for '&', '<' and '>' introduced by htmLawed ~`
-
- * allows deployment of powerful *hook functions* to *inject* HTML, *consolidate* 'style' attributes to 'class', finely check attribute values, etc. ~`
-
- * *independent of character encoding* of input and does not affect it
-
- * *tolerance for ill-written HTML* to a certain degree
-
-
--- 1.3 History ----------------------------------------------------o
-
-
- htmLawed was developed for use with 'LabWiki', a wiki software developed at PHP Labware, as a suitable software could not be found. Existing PHP software like 'Kses' and 'HTMLPurifier' were deemed inadequate, slow, resource-intensive, or dependent on external applications like 'HTML Tidy'.
-
- htmLawed started as a modification of Ulf Harnhammar's 'Kses' (version 0.2.2) software, and is compatible with code that uses 'Kses'; see section:- #2.6.
-
-
--- 1.4 License & copyright ----------------------------------------o
-
-
- htmLawed is free and open-source software dual licensed under LGPL license version 3:- http://www.gnu.org/licenses/lgpl-3.0.txt and GPL license version 2:- http://www.gnu.org/licenses/gpl-2.0.txt or later, and copyrighted by Santosh Patnaik, MD, PhD.
-
-
--- 1.5 Terms used here --------------------------------------------o
-
-
- * `administrator` - or admin; person setting up the code to pass input through htmLawed; also, `user`
- * `attributes` - name-value pairs like 'href="http://x.com"' in opening tags
- * `author` - `writer`
- * `character` - atomic unit of text; internally represented by a numeric `code-point` as specified by the `encoding` or `charset` in use
- * `entity` - markup like '&gt;' and '&#160;' used to refer to a character
- * `element` - HTML element like 'a' and 'img'
- * `element content` - content between the opening and closing tags of an element, like 'click' of '<a href="x">click</a>'
- * `HTML` - implies XHTML unless specified otherwise
- * `input` - text string given to htmLawed to process
- * `processing` - involves filtering, correction, etc., of input
- * `safe` - absence or reduction of certain characters and HTML elements and attributes in the input that can otherwise potentially and circumstantially expose web-site users to security vulnerabilities like cross-site scripting attacks (XSS)
- * `scheme` - URL protocol like 'http' and 'ftp'
- * `specs` - standard specifications
- * `style property` - terms like 'border' and 'height' for which declarations are made in values for the 'style' attribute of elements
- * `tag` - markers like '<a href="x">' and '</a>' delineating element content; the opening tag can contain attributes
- * `tag content` - consists of tag markers '<' and '>', element names like 'div', and possibly attributes
- * `user` - administrator
- * `writer` - end-user like a blog commenter providing the input that is to be processed; also, `author`
-
-
-== 2 Usage ========================================================oo
-
-
- htmLawed should work with PHP 4.4 and higher. Either 'include()' the 'htmLawed.php' file or copy-paste the entire code.
-
- To easily *test* htmLawed using a form-based interface, use the provided demo:- htmLawedTest.php ('htmLawed.php' and 'htmLawedTest.php' should be in the same directory on the web-server).
-
- *Note*: For code for usage of the htmLawed class (for htmLawed in OOP), please refer to the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website; the filtering itself can be configured, etc., as described here.
-
-
--- 2.1 Simple ------------------------------------------------------
-
-
- The input text to be processed, '$text', is passed as an argument of type string; 'htmLawed()' returns the processed string:
-
- $processed = htmLawed($text);
-
- *Note*: If input is from a '$_GET' or '$_POST' value, and 'magic quotes' are enabled on the PHP setup, run 'stripslashes()' on the input before passing to htmLawed.
-
- By default, htmLawed will process the text allowing all valid HTML elements/tags, secure URL scheme/CSS style properties, etc. It will allow 'CDATA' sections and HTML comments, balance tags, and ensure proper nesting of elements. Such actions can be configured using two other optional arguments -- '$config' and '$spec':
-
- $processed = htmLawed($text, $config, $spec);
-
- These extra parameters are detailed below. Some examples are shown in section:- #2.9.
-
- *Note*: For maximum protection against 'XSS' and other scripting attacks (e.g., by disallowing Javascript code), consider using the 'safe' parameter; see section:- #3.6.
-
-
--- 2.2 Configuring htmLawed using the '$config' parameter ---------o
-
-
- '$config' instructs htmLawed on how to tackle certain tasks. When '$config' is not specified, or not set as an array (e.g., '$config = 1'), htmLawed will take default actions. One or many of the task-action or value-specification pairs can be specified in '$config' as array key-value pairs. If a parameter is not specified, htmLawed will use the default value/action indicated further below.
-
- $config = array('comment'=>0, 'cdata'=>1);
- $processed = htmLawed($text, $config);
-
- Or,
-
- $processed = htmLawed($text, array('comment'=>0, 'cdata'=>1));
-
- Below are the possible value-specification combinations. In PHP code, values that are integers should not be quoted and should be used as numeric types (unless meant as string/text).
-
- Key: '*' default, '^' different default when htmLawed is used in the Kses-compatible mode (see section:- #2.6), '~' different default when 'valid_xhtml' is set to '1' (see section:- #3.5), '"' different default when 'safe' is set to '1' (see section:- #3.6)
-
- *abs_url*
- Make URLs absolute or relative; '$config["base_url"]' needs to be set; see section:- #3.4.4
-
- '-1' - make relative
- '0' - no action *
- '1' - make absolute
-
- *and_mark*
- Mark '&' characters in the original input; see section:- #3.2
-
- *anti_link_spam*
- Anti-link-spam measure; see section:- #3.4.7
-
- '0' - no measure taken *
- 'array("regex1", "regex2")' - will ensure a 'rel' attribute with 'nofollow' in its value in case the 'href' attribute value matches the regular expression pattern 'regex1', and/or will remove 'href' if its value matches the regular expression pattern 'regex2'. E.g., 'array("/./", "/://\W*(?!(abc\.com|xyz\.org))/")'; see section:- #3.4.7 for more.
-
- *anti_mail_spam*
- Anti-mail-spam measure; see section:- #3.4.7
-
- '0' - no measure taken *
- 'word' - '@' in mail address in 'href' attribute value is replaced with specified 'word'
-
- *balance*
- Balance tags for well-formedness and proper nesting; see section:- #3.3.3
-
- '0' - no
- '1' - yes *
-
- *base_url*
- Base URL value that needs to be set if '$config["abs_url"]' is not '0'; see section:- #3.4.4
-
- *cdata*
- Handling of 'CDATA' sections; see section:- #3.3.1
-
- '0' - don't consider 'CDATA' sections as markup and proceed as if plain text ^"
- '1' - remove
- '2' - allow, but neutralize any '<', '>', and '&' inside by converting them to named entities
- '3' - allow *
-
- *clean_ms_char*
- Replace discouraged characters introduced by Microsoft Word, etc.; see section:- #3.1
-
- '0' - no *
- '1' - yes
- '2' - yes, but replace special single & double quotes with ordinary ones
-
- *comment*
- Handling of HTML comments; see section:- #3.3.1
-
- '0' - don't consider comments as markup and proceed as if plain text ^"
- '1' - remove
- '2' - allow, but neutralize any '<', '>', and '&' inside by converting to named entities
- '3' - allow *
-
- *css_expression*
- Allow dynamic CSS expression by not removing the expression from CSS property values in 'style' attributes; see section:- #3.4.8
-
- '0' - remove *
- '1' - allow
-
- *deny_attribute*
- Denied HTML attributes; see section:- #3.4
-
- '0' - none *
- 'string' - dictated by values in 'string'
- 'on*' (like 'onfocus') attributes not allowed - "
-
- *direct_nest_list*
- Allow direct nesting of a list within another without requiring it to be a list item; see section:- #3.3.4
-
- '0' - no *
- '1' - yes
-
- *elements*
- Allowed HTML elements; see section:- #3.3
-
- '* -center -dir -font -isindex -menu -s -strike -u' - ~
- 'applet, embed, iframe, object, script' not allowed - "
-
- *hexdec_entity*
- Allow hexadecimal numeric entities and do not convert to the more widely accepted decimal ones, or convert decimal to hexadecimal ones; see section:- #3.2
-
- '0' - no
- '1' - yes *
- '2' - convert decimal to hexadecimal ones
-
- *hook*
- Name of an optional hook function to alter the input string, '$config' or '$spec' before htmLawed starts its main work; see section:- #3.7
-
- '0' - no hook function *
- 'name' - 'name' is name of the hook function ('kses_hook' ^)
-
- *hook_tag*
- Name of an optional hook function to alter tag content finalized by htmLawed; see section:- #3.4.9
-
- '0' - no hook function *
- 'name' - 'name' is name of the hook function
-
- *keep_bad*
- Neutralize bad tags by converting '<' and '>' to entities, or remove them; see section:- #3.3.3
-
- '0' - remove ^
- '1' - neutralize both tags and element content
- '2' - remove tags but neutralize element content
- '3' and '4' - like '1' and '2' but remove if text ('pcdata') is invalid in parent element
- '5' and '6' * - like '3' and '4' but line-breaks, tabs and spaces are left
-
- *lc_std_val*
- For XHTML compliance, predefined, standard attribute values, like 'get' for the 'method' attribute of 'form', must be lowercased; see section:- #3.4.5
-
- '0' - no
- '1' - yes *
-
- *make_tag_strict*
- Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: 'applet' 'center' 'dir' 'embed' 'font' 'isindex' 'menu' 's' 'strike' 'u'; see section:- #3.3.2
-
- '0' - no ^
- '1' - yes, but leave 'applet', 'embed' and 'isindex' elements that currently can't be transformed *
- '2' - yes, removing 'applet', 'embed' and 'isindex' elements and their contents (nested elements remain) ~
-
- *named_entity*
- Allow non-universal named HTML entities, or convert to numeric ones; see section:- #3.2
-
- '0' - convert
- '1' - allow *
-
- *no_deprecated_attr*
- Allow deprecated attributes or transform them; see section:- #3.4.6
-
- '0' - allow ^
- '1' - transform, but 'name' attributes for 'a' and 'map' are retained *
- '2' - transform
-
- *parent*
- Name of the parent element, possibly imagined, that will hold the input; see section:- #3.3
-
- *safe*
- Magic parameter to make input the most secure against XSS without needing to specify other relevant '$config' parameters; see section:- #3.6
-
- '0' - no *
- '1' - will auto-adjust other relevant '$config' parameters (indicated by '"' in this list)
-
- *schemes*
- Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or '!' to `deny` any URL); '*' covers all unspecified attributes; see section:- #3.4.3
-
- 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https' *
- '*: ftp, gopher, http, https, mailto, news, nntp, telnet' ^
- 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style: !; *:file, http, https' "
-
- *show_setting*
- Name of a PHP variable to assign the `finalized` '$config' and '$spec' values; see section:- #3.8
-
- *style_pass*
- Do not look at 'style' attribute values, letting them through without any alteration
-
- '0' - no *
- '1' - htmLawed will let through any 'style' value; see section:- #3.4.8
-
- *tidy*
- Beautify or compact HTML code; see section:- #3.3.5
-
- '-1' - compact
- '0' - no *
- '1' or 'string' - beautify (custom format specified by 'string')
-
- *unique_ids*
- 'id' attribute value checks; see section:- #3.4.2
-
- '0' - no ^
- '1' - remove duplicate and/or invalid ones *
- 'word' - remove invalid ones and replace duplicate ones with new and unique ones based on the 'word'; the admin-specified 'word', like 'my_', should begin with a letter (a-z) and can contain letters, digits, '.', '_', '-', and ':'.
-
- *valid_xhtml*
- Magic parameter to make input the most valid XHTML without needing to specify other relevant '$config' parameters; see section:- #3.5
-
- '0' - no *
- '1' - will auto-adjust other relevant '$config' parameters (indicated by '~' in this list)
-
- *xml:lang*
- Auto-adding 'xml:lang' attribute; see section:- #3.4.1
-
- '0' - no *
- '1' - add if 'lang' attribute is present
- '2' - add if 'lang' attribute is present, and remove 'lang' ~
-
-
--- 2.3 Extra HTML specifications using the $spec parameter --------o
-
-
- The '$spec' argument can be used to disallow an otherwise legal attribute for an element, or to restrict the attribute's values. This can also be helpful as a security measure (e.g., in certain versions of browsers, certain values can cause buffer overflows and denial of service attacks), or in enforcing admin policy compliance. '$spec' is specified as a string of text containing one or more `rules`, with multiple rules separated from each other by a semi-colon (';'). E.g.,
-
- $spec = 'i=-*; td, tr=style, id, -*; a=id(match="/[a-z][a-z\d.:\-`"]*/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt';
- $processed = htmLawed($text, $config, $spec);
-
- Or,
-
- $processed = htmLawed($text, $config, 'i=-*; td, tr=style, id, -*; a=id(match="/[a-z][a-z\d.:\-`"]*/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt');
-
- A rule begins with an HTML *element* name(s) (`rule-element`), for which the rule applies, followed by an equal ('=') sign. A rule-element may represent multiple elements if comma (,)-separated element names are used. E.g., 'th,td,tr='.
-
- Rest of the rule consists of comma-separated HTML *attribute names*. A minus ('-') character before an attribute means that the attribute is not permitted inside the rule-element. E.g., '-width'. To deny all attributes, '-*' can be used.
-
- Following shows examples of rule excerpts with rule-element 'a' and the attributes that are being permitted:
-
- * 'a=' - all
- * 'a=id' - all
- * 'a=href, title, -id, -onclick' - all except 'id' and 'onclick'
- * 'a=*, id, -id' - all except 'id'
- * 'a=-*' - none
- * 'a=-*, href, title' - none except 'href' and 'title'
- * 'a=-*, -id, href, title' - none except 'href' and 'title'
-
- Rules regarding *attribute values* are optionally specified inside round brackets after attribute names in slash ('/')-separated `parameter = value` pairs. E.g., 'title(maxlen=30/minlen=5)'. None, or one or more of the following parameters may be specified:
-
- * 'oneof' - one or more choices separated by '|' that the value should match; if only one choice is provided, then the value must match that choice
-
- * 'noneof' - one or more choices separated by '|' that the value should not match
-
- * 'maxlen' and 'minlen' - upper and lower limits for the number of characters in the attribute value; specified in numbers
-
- * 'maxval' and 'minval' - upper and lower limits for the numerical value specified in the attribute value; specified in numbers
-
- * 'match' and 'nomatch' - pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers
-
- * 'default' - a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters
-
- If 'default' is not set and the attribute value does not satisfy any of the specified parameters, then the attribute is removed. The 'default' value can also be used to force all attribute declarations to take the same value (by getting the values declared illegal by setting, e.g., 'maxlen' to '-1').
-
- Examples with `input` '<input title="WIDTH" value="10em" /><input title="length" value="5" />' are shown below.
-
- `Rule`: 'input=title(maxlen=60/minlen=6), value'
- `Output`: '<input value="10em" /><input title="length" value="5" />'
-
- `Rule`: 'input=title(), value(maxval=8/default=6)'
- `Output`: '<input title="WIDTH" value="6" /><input title="length" value="5" />'
-
- `Rule`: 'input=title(nomatch=%w.d%i), value(match=%em%/default=6em)'
- `Output`: '<input value="10em" /><input title="length" value="6em" />'
-
- `Rule`: 'input=title(oneof=height|depth/default=depth), value(noneof=5|6)'
- `Output`: '<input title="depth" value="10em" /><input title="depth" />'
-
- *Special characters*: The characters ';', ',', '/', '(', ')', '|', '~' and space have special meanings in the rules. Words in the rules that use such characters, or the characters themselves, should be `escaped` by enclosing in pairs of double-quotes ('"'). A back-tick ('`') can be used to escape a literal '"'. An example rule illustrating this is 'input=value(maxlen=30/match="/^\w/"/default="your `"ID`"")'.
-
- *Note*: To deny an attribute for all elements for which it is legal, '$config["deny_attribute"]' (see section:- #3.4) can be used instead of '$spec'. Also, attributes can be allowed element-specifically through '$spec' while being denied globally through '$config["deny_attribute"]'. The 'hook_tag' parameter (section:- #3.4.9) can also be used to implement the '$spec' functionality.
-
-
--- 2.4 Performance time & memory usage ----------------------------o
-
-
- The time and memory used by htmLawed depends on its configuration and the size of the input, and the amount, nestedness and well-formedness of the HTML markup within it. In particular, tag balancing and beautification each can increase the processing time by about a quarter.
-
- The htmLawed demo:- htmLawedTest.php can be used to evaluate the performance and effects of different types of input and '$config'.
-
-
--- 2.5 Some security risks to keep in mind ------------------------o
-
-
- When setting the parameters/arguments (like those to allow certain HTML elements) for use with htmLawed, one should bear in mind that the setting may let through potentially `dangerous` HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc.
-
- Unless end-users, either people or software, supplying the content are completely trusted, security issues arising from the degree of HTML usage permission has to be kept in mind. For example, following increase security risks:
-
- * Allowing 'script', 'applet', 'embed', 'iframe' or 'object' elements, or certain of their attributes like 'allowscriptaccess'
-
- * Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., '<!--[if gte IE 4]><script>alert("xss");</script><![endif]-->'
-
- * Allowing dynamic CSS expressions (a feature of the IE browser)
-
- * Allowing the 'style' attribute
-
- To remove `unsecure` HTML, code-developers using htmLawed must set '$config' appropriately. E.g., '$config["elements"] = "* -script"' to deny the 'script' element (section:- #3.3), '$config["safe"] = 1' to auto-configure ceratin htmLawed parameters for maximizing security (section:- #3.6), etc.
-
- Permitting the '*style*' attribute brings in risks of `click-jacking`, `phishing`, web-page overlays, etc., `even` when the 'safe' parameter is enabled (see section:- #3.6). Except for URLs and a few other things like CSS dynamic expressions, htmLawed currently does not check every CSS style property. It does provide ways for the code-developer implementing htmLawed to do such checks through htmLawed's '$spec' argument, and through the 'hook_tag' parameter (see section:- #3.4.8 for more). Disallowing 'style' completely and relying on CSS classes and stylesheet files is recommended.
-
- htmLawed does not check or correct the character *encoding* of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML 'meta' tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).
-
-
--- 2.6 Use without modifying old 'kses()' code --------------------o
-
-
- The 'Kses' PHP script is used by many applications (like 'WordPress'). It is possible to have such applications use htmLawed instead, since it is compatible with code that calls the 'kses()' function declared in the 'Kses' file (usually named 'kses.php'). E.g., application code like this will continue to work after replacing 'Kses' with htmLawed:
-
- $comment_filtered = kses($comment_input, array('a'=>array(), 'b'=>array(), 'i'=>array()));
-
- For some of the '$config' parameters, htmLawed will use values other than the default ones. These are indicated by '^' in section:- #2.2. To force htmLawed to use other values, function 'kses()' in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.
-
- If the application uses a 'Kses' file that has the 'kses()' function declared, then, to have the application use htmLawed instead of 'Kses', simply rename 'htmLawed.php' (to 'kses.php', e.g.) and replace the 'Kses' file (or just replace the code in the 'Kses' file with the htmLawed code). If the 'kses()' function in the 'Kses' file had been renamed by the application developer (e.g., in 'WordPress', it is named 'wp_kses()'), then appropriately rename the 'kses()' function in the htmLawed code.
-
- If the 'Kses' file used by the application has been highly altered by the application developers, then one may need a different approach. E.g., with 'WordPress', it is best to copy the htmLawed code to 'wp_includes/kses.php', rename the newly added function 'kses()' to 'wp_kses()', and delete the code for the original 'wp_kses()' function.
-
- If the 'Kses' code has a non-empty hook function (e.g., 'wp_kses_hook()' in case of 'WordPress'), then the code for htmLawed's 'kses_hook()' function should be appropriately edited. However, the requirement of the hook function should be re-evaluated considering that htmLawed has extra capabilities. With 'WordPress', the hook function is an essential one. The following code is suggested for the htmLawed 'kses_hook()' in case of 'WordPress':
-
- function kses_hook($string, &$cf, &$spec){
- // kses compatibility
- $allowed_html = $spec;
- $allowed_protocols = array();
- foreach($cf['schemes'] as $v){
- foreach($v as $k2=>$v2){
- if(!in_array($k2, $allowed_protocols)){
- $allowed_protocols[] = $k2;
- }
- }
- }
- return wp_kses_hook($string, $allowed_html, $allowed_protocols);
- // eof
- }
-
-
--- 2.7 Tolerance for ill-written HTML -----------------------------o
-
-
- htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be `read` as HTML, and be considered mere plain text instead. Following statements indicate the degree of `looseness` that htmLawed can work with, and can be provided in instructions to writers:
-
- * Tags must be flanked by '<' and '>' with no '>' inside -- any needed '>' should be put in as '&gt;'. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and '>', like '<div >' and '<img / >', but not after the '<'.
-
- * Element and attribute names need not be lower-cased.
-
- * Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.
-
- * Attribute values may not be double-quoted, or may be single-quoted.
-
- * Left-padding of numeric entities (like, '&#0160;', '&x07ff;') with '0' is okay as long as the number of characters between between the '&' and the ';' does not exceed 8. All entities must end with ';' though.
-
- * Named character entities must be properly cased. E.g., '&Lt;' or '&TILDE;' will not be let through without modification.
-
- * HTML comments should not be inside element tags (okay between tags), and should begin with '<!--' and end with '-->'. Characters like '<', '>', and '&' may be allowed inside depending on '$config', but any '-->' inside should be put in as '--&gt;'. Any '--' inside will be automatically converted to '-', and a space will be added before the comment delimiter '-->'.
-
- * 'CDATA' sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with '<[CDATA[' and end with ']]>'. Characters like '<', '>', and '&' may be allowed inside depending on '$config', but any ']]>' inside should be put in as ']]&gt;'.
-
- * For attribute values, character entities '&lt;', '&gt;' and '&amp;' should be used instead of characters '<' and '>', and '&' (when '&' is not part of a character entity). This applies even for Javascript code in values of attributes like 'onclick'.
-
- * Characters '<', '>', '&' and '"' that are part of actual Javascript, etc., code in 'script' elements should be used as such and not be put in as entities like '&gt;'. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside 'CDATA' sections.
-
- * Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on '$config["keep_bad"]', some code/text may be lost.
-
- * Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.
-
- * With '$config["unique_ids"]' not '0' and the 'id' attribute being permitted, writers should carefully avoid using duplicate or invalid 'id' values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when '<a id="home"></a><input id="home" /><label for="home"></label>' is processed into
-'<a id="home"></a><input id="prefix_home" /><label for="home"></label>'.
-
- * Note that even if intended HTML is lost in a highly ill-written input, the processed output will be more secure and standard-compliant.
-
- * For URLs, unless '$config["scheme"]' is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., 'htt&#112;' (which many browsers will read as the harmless 'http') may be considered bad by htmLawed.
-
- * htmLawed will attempt to put plain text present directly inside 'blockquote', 'form', 'map' and 'noscript' elements (illegal as per the specs) inside auto-generated 'div' elements.
-
-
--- 2.8 Limitations & work-arounds ---------------------------------o
-
-
- htmLawed's main objective is to make the input text `more` standard-compliant, secure for web-page readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.
-
- It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specs (like asking for normalization of white-spacing within 'textarea' elements) are clearly wrong. Regarding security, note that `unsafe` HTML code is not necessarily legally invalid.
-
- * htmLawed is meant for input that goes into the 'body' of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements 'frameset', 'frame' and 'noframes'.
-
- * It cannot transform the non-standard 'embed' elements to the standard-compliant 'object' elements. Yet, it can allow 'embed' elements if permitted ('embed' is widely used and supported). Admins can certainly use the 'hook_tag' parameter (section:- #3.4.9) to deploy a custom embed-to-object converter function.
-
- * The only non-standard element that may be permitted is 'embed'; others like 'noembed' and 'nobr' cannot be permitted without modifying the htmLawed code.
-
- * It cannot handle input that has non-HTML code like 'SVG' and 'MathML'. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in section:- #3.9. A third way may be to some how take advantage of the '$config["and_mark"]' parameter (see section:- #3.2).
-
- * By default, htmLawed won't check many attribute values for standard compliance. E.g., 'width="20m"' with the dimension in non-standard 'm' is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the 'hook_tag' parameter (section:- #3.4.9) or '$spec' to enforce finer checks.
-
- * The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specs. Only a few of the proprietary attributes are supported.
-
- * Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the 'hook_tag' parameter (section:- #3.4.9) or '$spec' for finer checks. Perhaps the best option is to disallow 'style' but allow 'class' attributes with the right 'oneof' or 'match' values for 'class', and have the various class style properties in '.css' CSS stylesheet files.
-
- * htmLawed does not parse emoticons, decode `BBcode`, or `wikify`, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to 'br' elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.
-
- * htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate 'target' and 'onclick' attributes to 'a'). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * Nesting-based checks are not possible. E.g., one cannot disallow 'p' elements specifically inside 'td' while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert 'http' to 'https'. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * Pairs of opening and closing tags that do not enclose any content (like '<em></em>') are not removed. This may be against the standard specs for certain elements (e.g., 'table'). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.
-
- * htmLawed does not check for certain element orderings described in the standard specs (e.g., in a 'table', 'tbody' is allowed before 'tfoot'). Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * htmLawed does not check the number of nested elements. E.g., it will allow two 'caption' elements in a 'table' element, illegal as per the specs. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
-
- * htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers ('/*') in 'style' attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like '&#101;xpression...'. If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the 'hook_tag' parameter (section:- #3.4.9) to more specifically identify CSS expressions in the 'style' attribute values. Also, using '$config["style_pass"]', it is possible to have htmLawed pass 'style' attribute values without even looking at them (section:- #3.4.8).
-
- * htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., '<a href="http://x%22+style=%22background-image:xss">x</a>'). These arise when browsers mis-identify markup in `escaped` text, defeating the very purpose of escaping text (a bad browser will read the given example as '<a href="http://x" style="background-image:xss">x</a>').
-
- * Because of poor Unicode support in PHP, htmLawed does not remove the `high value` HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see section:- #3.1).
-
- * htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML 'meta' tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).
-
- * Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.
-
-
--- 2.9 Examples of usage -------------------------------------------o
-
-
- Safest, allowing only `safe` HTML markup --
-
- $config = array('safe'=>1);
- $out = htmLawed($in);
-
- Simplest, allowing all valid HTML markup except 'javascript:' --
-
- $out = htmLawed($in);
-
- Allowing all valid HTML markup including 'javascript:' --
-
- $config = array('schemes'=>'*:*');
- $out = htmLawed($in, $config);
-
- Allowing only 'safe' HTML and the elements 'a', 'em', and 'strong' --
-
- $config = array('safe'=>1, 'elements'=>'a, em, strong');
- $out = htmLawed($in, $config);
-
- Not allowing elements 'script' and 'object' --
-
- $config = array('elements'=>'* -script -object');
- $out = htmLawed($in, $config);
-
- Not allowing attributes 'id' and 'style' --
-
- $config = array('deny_attribute'=>'id, style');
- $out = htmLawed($in, $config);
-
- Permitting only attributes 'title' and 'href' --
-
- $config = array('deny_attribute'=>'* -title -href');
- $out = htmLawed($in, $config);
-
- Remove bad/disallowed tags altogether instead of converting them to entities --
-
- $config = array('keep_bad'=>0);
- $out = htmLawed($in, $config);
-
- Allowing attribute 'title' only in 'a' and not allowing attributes 'id', 'style', or scriptable `on*` attributes like 'onclick' --
-
- $config = array('deny_attribute'=>'title, id, style, on*');
- $spec = 'a=title';
- $out = htmLawed($in, $config, $spec);
-
- Some case-studies are presented below.
-
- *1.* A blog administrator wants to allow only 'a', 'em', 'strike', 'strong' and 'u' in comments, but needs 'strike' and 'u' transformed to 'span' for better XHTML 1-strict compliance, and, he wants the 'a' links to be to 'http' or 'https' resources:
-
- $processed = htmLawed($in, array('elements'=>'a, em, strike, strong, u', 'make_tag_strict'=>1, 'safe'=>1, 'schemes'=>'*:http, https'), 'a=href');
-
- *2.* An author uses a custom-made web application to load content on his web-site. He is the only one using that application and the content he generates has all types of HTML, including scripts. The web application uses htmLawed primarily as a tool to correct errors that creep in while writing HTML and to take care of the occasional `bad` characters in copy-paste text introduced by Microsoft Office. The web application provides a preview before submitted input is added to the content. For the previewing process, htmLawed is set up as follows:
-
- $processed = htmLawed($in, array('css_expression'=>1, 'keep_bad'=>1, 'make_tag_strict'=>1, 'schemes'=>'*:*', 'valid_xhtml'=>1));
-
- For the final submission process, 'keep_bad' is set to '6'. A value of '1' for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.
-
- *3.* A data-miner is scraping information in a specific table of similar web-pages and is collating the data rows, and uses htmLawed to reduce unnecessary markup and white-spaces:
-
- $processed = htmLawed($in, array('elements'=>'tr, td', 'tidy'=>-1), 'tr, td =');
-
-
-== 3 Details =====================================================oo
-
-
--- 3.1 Invalid/dangerous characters --------------------------------
-
-
- Valid characters (more correctly, their code-points) in HTML or XML are, hexadecimally, '9', 'a', 'd', '20' to 'd7ff', and 'e000' to '10ffff', except 'fffe' and 'ffff' (decimally, '9', '10', '13', '32' to '55295', and '57344' to '1114111', except '65534' and '65535'). htmLawed removes the invalid characters '0' to '8', 'b', 'c', and 'e' to '1f'.
-
- Because of PHP's poor native support for multi-byte characters, htmLawed cannot check for the remaining invalid code-points. However, for various reasons, it is very unlikely for any of those characters to be in the input.
-
- Characters that are discouraged (see section:- #5.1) but not invalid are not removed by htmLawed.
-
- It (function 'hl_tag()') also replaces the potentially dangerous (in some Mozilla [Firefox] and Opera browsers) soft-hyphen character (code-point, hexadecimally, 'ad', or decimally, '173') in attribute values with spaces. Where required, the characters '<', '>', '&', and '"' are converted to entities.
-
- With '$config["clean_ms_char"]' set as '1' or '2', many of the discouraged characters (decimal code-points '127' to '159' except '133') that many Microsoft applications incorrectly use (as per the 'Windows 1252' ['Cp-1252'] or a similar encoding system), and the character for decimal code-point '133', are converted to appropriate decimal numerical entities (or removed for a few cases)-- see appendix in section:- #5.4. This can help avoid some display issues arising from copying-pasting of content.
-
- With '$config["clean_ms_char"]' set as '2', characters for the hexadecimal code-points '82', '91', and '92' (for special single-quotes), and '84', '93', and '94' (for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.
-
- The character values are replaced with entities/characters and not character values referred to by the entities/characters to keep this task independent of the character-encoding of input text.
-
- The '$config["clean_ms_char"]' parameter should not be used if authors do not copy-paste Microsoft-created text, or if the input text is not believed to use the 'Windows 1252' ('Cp-1252') or a similar encoding like 'Cp-1251'. Further, the input form and the web-pages displaying it or its content should have the character encoding appropriately marked-up.
-
-
--- 3.2 Character references/entities ------------------------------o
-
-
- Valid character entities take the form '&*;' where '*' is '#x' followed by a hexadecimal number (hexadecimal numeric entity; like '&#xA0;' for non-breaking space), or alphanumeric like 'gt' (external or named entity; like '&nbsp;' for non-breaking space), or '#' followed by a number (decimal numeric entity; like '&#160;' for non-breaking space). Character entities referring to the soft-hyphen character (the '&shy;' or '\xad' character; hexadecimal code-point 'ad' [decimal '173']) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.
-
- htmLawed (function 'hl_ent()'):
-
- * Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)
-
- * Lowercases the 'X' (for XML-compliance) and 'A-F' of hexadecimal numeric entities
-
- * Neutralizes entities referring to characters that are HTML-invalid (see section:- #3.1)
-
- * Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, '7f' to '84', '86' to '9f', and 'fdd0' to 'fddf', or decimally, '127' to '132', '134' to '159', and '64991' to '64976'). Entities referring to the remaining discouraged characters (see section:- #5.1 for a full list) are let through.
-
- * Neutralizes named entities that are not in the specs.
-
- * Optionally converts valid HTML-specific named entities except '&gt;', '&lt;', '&quot;', and '&amp;' to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is '2') for generic XML-compliance. For this, '$config["named_entity"]' should be '1'.
-
- * Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, '$config["hexdec_entity"]' should be '0'.
-
- * Optionally converts decimal numeric entities to the hexadecimal ones. For this, '$config["hexdec_entity"]' should be '2'.
-
- `Neutralization` refers to the `entitification` of '&' to '&amp;'.
-
- *Note*: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's 'html_entity_decode' function:- http://www.php.net/html_entity_decode for that.
-
- *Note*: If '$config["and_mark"]' is set, and set to a value other than '0', then the '&' characters in the original input are replaced with the control character for the hexadecimal code-point '6' ('\x06'; '&' characters introduced by htmLawed, e.g., after converting '<' to '&lt;', are not affected). This allows one to distinguish, say, an '&gt;' introduced by htmLawed and an '&gt;' put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence 'o(><)o' to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the '\x06' can break documents. Before use in such documents, and preferably before any storage, any remaining '\x06' should be changed back to '&', e.g., with:
-
- $final = str_replace("\x06", '&', $prelim);
-
- Also, see section:- #3.9.
-
-
--- 3.3 HTML elements ----------------------------------------------o
-
-
- htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on '$config["keep_bad"]', are either `neutralized` (converted to plain text by entitification of '<' and '>') or removed.
-
- E.g., with only 'em' permitted:
-
- Input:
-
- <em>My</em> website is <a href="http://a.com>a.com</a>.
-
- Output, with '$config["keep_bad"] = 0':
-
- <em>My</em> website is a.com.
-
- Output, with '$config["keep_bad"]' not '0':
-
- <em>My</em> website is &lt;a href=""&gt;a.com&lt;/a&gt;.
-
- See section:- #3.3.3 for differences between the various non-zero '$config["keep_bad"]' values.
-
- htmLawed by default permits these 86 elements:
-
- a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var
-
- Except for 'embed' (included because of its wide-spread use) and the Ruby elements ('rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby'; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude 'center', 'dir', 'font', 'isindex', 'menu', 's', 'strike', and 'u'.
-
- With '$config["safe"] = 1', the default set will exclude 'applet', 'embed', 'iframe', 'object' and 'script'; see section:- #3.6.
-
- When '$config["elements"]', which specifies allowed elements, is `properly` defined, and neither empty nor set to '0' or '*', the default set is not used. To have elements added to or removed from the default set, a '+/-' notation is used. E.g., '*-script-object' implies that only 'script' and 'object' are disallowed, whereas '*+embed' means that 'noembed' is also allowed. Elements can also be specified as comma separated names. E.g., 'a, b, i' means only 'a', 'b' and 'i' are permitted. In this notation, '*', '+' and '-' have no significance and can actually cause a mis-reading.
-
- Some more examples of '$config["elements"]' values indicating permitted elements (note that empty spaces are liberally allowed for clarity):
-
- * 'a, blockquote, code, em, strong' -- only 'a', 'blockquote', 'code', 'em', and 'strong'
- * '*-script' -- all excluding 'script'
- * '* -center -dir -font -isindex -menu -s -strike -u' -- only XHTML-Strict elements
- * '*+noembed-script' -- all including 'noembed' excluding 'script'
-
- Some mis-usages (and the resulting permitted elements) that can be avoided:
-
- * '-*' -- none; instead of htmLawed, one might just use, e.g., the 'htmlspecialchars()' PHP function
- * '*, -script' -- all except 'script'; admin probably meant '*-script'
- * '-*, a, em, strong' -- all; admin probably meant 'a, em, strong'
- * '*' -- all; admin need not have set 'elements'
- * '*-form+form' -- all; a '+' will always over-ride any '-'
- * '*, noembed' -- only 'noembed'; admin probably meant '*+noembed'
- * 'a, +b, i' -- only 'a' and 'i'; admin probably meant 'a, b, i'
-
- Basically, when using the '+/-' notation, commas (',') should not be used, and vice versa, and '*' should be used with the former but not the latter.
-
- *Note*: Even if an element that is not in the default set is allowed through '$config["elements"]', like 'noembed' in the last example, it will eventually be removed during tag balancing unless such balancing is turned off ('$config["balance"]' set to '0'). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function 'hl_bal()' to accommodate the element and its nesting properties.
-
- *A possibly second way to specify allowed elements* is to set '$config["parent"]' to an element name that supposedly will hold the input, and to set '$config["balance"]' to '1'. During tag balancing (see section:- #3.3.3), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to 'div' if '$config["parent"]' is empty, 'body', or an element not in htmLawed's default set of 86 elements.
-
- `Tag transformation` is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see section:- #3.3.2.
-
-
-.. 3.3.1 Handling of comments and CDATA sections ...................
-
-
- 'CDATA' sections have the format '<![CDATA[...anything but not "]]>"...]]>', and HTML comments, '<!--...anything but not "-->"... -->'. Neither HTML comments nor 'CDATA' sections can reside inside tags. HTML comments can exist anywhere else, but 'CDATA' sections can exist only where plain text is allowed (e.g., immediately inside 'td' element content but not immediately inside 'tr' element content).
-
- htmLawed (function 'hl_cmtcd()') handles HTML comments or 'CDATA' sections depending on the values of '$config["comment"]' or '$config["cdata"]'. If '0', such markup is not looked for and the text is processed like plain text. If '1', it is removed completely. If '2', it is preserved but any '<', '>' and '&' inside are changed to entities. If '3', they are left as such.
-
- Note that for the last two cases, HTML comments and 'CDATA' sections will always be removed from tag content (function 'hl_tag()').
-
- Examples:
-
- Input:
- <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
- Output ('$config["comment"] = 0, $config["cdata"] = 2'):
- &lt;-- home link --&gt;<a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
- Output ('$config["comment"] = 1, $config["cdata"] = 2'):
- <a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
- Output ('$config["comment"] = 2, $config["cdata"] = 2'):
- <!-- home link --><a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
- Output ('$config["comment"] = 2, $config["cdata"] = 1'):
- <!-- home link --><a href="home.htm">Home</a>
- Output ('$config["comment"] = 3, $config["cdata"] = 3'):
- <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
-
- For standard-compliance, comments are given the form '<!--comment -->', and any '--' in the content is made '-'.
-
- When '$config["safe"] = 1', CDATA sections and comments are considered plain text unless '$config["comment"]' or '$config["cdata"]' is explicitly specified; see section:- #3.6.
-
-
-.. 3.3.2 Tag-transformation for better XHTML-Strict ................o
-
-
- If '$config["make_tag_strict"]' is set and not '0', following non-XHTML-Strict elements (and attributes), even if admin-permitted, are mutated as indicated (element content remains intact; function 'hl_tag2()'):
-
- * applet - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
- * center - 'div style="text-align: center;"'
- * dir - 'ul'
- * embed - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
- * font (face, size, color) - 'span style="font-family: ; font-size: ; color: ;"' (size transformation reference:- http://style.cleverchimp.com/font_size_intervals/altintervals.html)
- * isindex - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
- * menu - 'ul'
- * s - 'span style="text-decoration: line-through;"'
- * strike - 'span style="text-decoration: line-through;"'
- * u - 'span style="text-decoration: underline;"'
-
- For an element with a pre-existing 'style' attribute value, the extra style properties are appended.
-
- Example input:
-
- <center>
- The PHP <s>software</s> script used for this <strike>web-page</strike> web-page is <font style="font-weight: bold " face=arial size='+3' color = "red ">htmLawedTest.php</font>, from <u style= 'color:green'>PHP Labware</u>.
- </center>
-
- The output:
-
- <div style="text-align: center;">
- The PHP <span style="text-decoration: line-through;">software</span> script used for this <span style="text-decoration: line-through;">web-page</span> web-page is <span style="font-weight: bold; font-family: arial; color: red; font-size: 200%;">htmLawedTest.php</span>, from <span style="color:green; text-decoration: underline;">PHP Labware</span>.
- </div>
-
-
--- 3.3.3 Tag balancing and proper nesting -------------------------o
-
-
- If '$config["balance"]' is set to '1', htmLawed (function 'hl_bal()') checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).
-
- Depending on the value of '$config["keep_bad"]' (see section:- #2.2 and section:- #3.3), illegal content may be removed or neutralized to plain text by converting < and > to entities:
-
- '0' - remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see section:- #2.6)
- '1' - neutralize tags and keep element content
- '2' - remove tags but keep element content
- '3' and '4' - like '1' and '2', but keep element content only if text ('pcdata') is valid in parent element as per specs
- '5' and '6' - like '3' and '4', but line-breaks, tabs and spaces are left
-
- Example input (disallowing the 'p' element):
-
- <*> Pseudo-tags <*>
- <xml>Non-HTML tag xml</xml>
- <p>
- Disallowed tag p
- </p>
- <ul>Bad<li>OK</li></ul>
-
- The output with '$config["keep_bad"] = 1':
-
- &lt;*&gt; Pseudo-tags &lt;*&gt;
- &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;
- &lt;p&gt;
- Disallowed tag p
- &lt;/p&gt;
- <ul>Bad<li>OK</li></ul>
-
- The output with '$config["keep_bad"] = 3':
-
- &lt;*&gt; Pseudo-tags &lt;*&gt;
- &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;
- &lt;p&gt;
- Disallowed tag p
- &lt;/p&gt;
- <ul><li>OK</li></ul>
-
- The output with '$config["keep_bad"] = 6':
-
- &lt;*&gt; Pseudo-tags &lt;*&gt;
- Non-HTML tag xml
-
- Disallowed tag p
-
- <ul><li>OK</li></ul>
-
- An option like '1' is useful, e.g., when a writer previews his submission, whereas one like '3' is useful before content is finalized and made available to all.
-
- *Note:* In the example above, unlike '<*>', '<xml>' gets considered as a tag (even though there is no HTML element named 'xml'). In general, text matching the regular expression pattern '<(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?>' is considered a tag (phrase enclosed by the angled brackets '<' and '>', and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...).
-
- Nesting/content rules for each of the 86 elements in htmLawed's default set (see section:- #3.3) are defined in function 'hl_bal()'. This means that if a non-standard element besides 'embed' is being permitted through '$config["elements"]', the element's tag content will end up getting removed if '$config["balance"]' is set to '1'.
-
- Plain text and/or certain elements nested inside 'blockquote', 'form', 'map' and 'noscript' need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as 'form', the input 'B:<input type="text" value="b" />C:<input type="text" value="c" />' is converted to '<div>B:<input type="text" value="b" />C:<input type="text" value="c" /></div>'.
-
-
--- 3.3.4 Elements requiring child elements ------------------------o
-
-
- As per specs, the following elements require legal child elements nested inside them:
-
- blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul
-
- In some cases, the specs stipulate the number and/or the ordering of the child elements. A 'table' can have 0 or 1 'caption', 'tbody', 'tfoot', and 'thead', but they must be in this order: 'caption', 'thead', 'tfoot', 'tbody'.
-
- htmLawed currently does not check for conformance to these rules. Note that any non-compliance in this regard will not introduce security vulnerabilities, crash browser applications, or affect the rendering of web-pages.
-
- With '$config["direct_list_nest"]' set to '1', htmLawed will allow direct nesting of an 'ol' or 'ul' list within another 'ol' or 'ul' without requiring the child list to be within an 'li' of the parent list. While this is not standard-compliant, directly nested lists are rendered properly by almost all browsers. The parameter '$config["direct_list_nest"]' has no effect if tag-balancing (section:- #3.3.3) is turned off.
-
-
--- 3.3.5 Beautify or compact HTML ---------------------------------o
-
-
- By default, htmLawed will neither `beautify` HTML code by formatting it with indentations, etc., nor will it make it compact by removing un-needed white-space.(It does always properly white-space tag content.)
-
- As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside 'pre' elements) are all considered equivalent, and referred to as `white-spaces`. Browser applications are supposed to consider contiguous white-spaces as just a single space, and to disregard white-spaces trailing opening tags or preceding closing tags. This white-space `normalization` allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such `pretty` HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.
-
- With the '$config' parameter 'tidy', htmLawed can be used to beautify or compact the input text. Input with just plain text and no HTML markup is also subject to this. Besides 'pre', the 'script' and 'textarea' elements, CDATA sections, and HTML comments are not subjected to the tidying process.
-
- To `compact`, use '$config["tidy"] = -1'; single instances or runs of white-spaces are replaced with a single space, and white-spaces trailing and leading open and closing tags, respectively, are removed.
-
- To `beautify`, '$config["tidy"]' is set as '1', or for customized tidying, as a string like '2s2n'. The 's' or 't' character specifies the use of spaces or tabs for indentation. The first and third characters, any of the digits 0-9, specify the number of spaces or tabs per indentation, and any parental lead spacing (extra indenting of the whole block of input text). The 'r' and 'n' characters are used to specify line-break characters: 'n' for '\n' (Unix/Mac OS X line-breaks), 'rn' or 'nr' for '\r\n' (Windows/DOS line-breaks), or 'r' for '\r'.
-
- The '$config["tidy"]' value of '1' is equivalent to '2s0n'. Other '$config["tidy"]' values are read loosely: a value of '4' is equivalent to '4s0n'; 't2', to '1t2n'; 's', to '2s0n'; '2TR', to '2t0r'; 'T1', to '1t1n'; 'nr3', to '3s0nr', and so on. Except in the indentations and line-spacings, runs of white-spaces are replaced with a single space during beautification.
-
- Input formatting using '$config["tidy"]' is not recommended when input text has mixed markup (like HTML + PHP).
-
-
--- 3.4 Attributes ------------------------------------------------oo
-
-
- htmLawed will only permit attributes described in the HTML specs (including deprecated ones). It also permits some attributes for use with the 'embed' element (the non-standard 'embed' element is supported in htmLawed because of its widespread use), and the the 'xml:space' attribute (valid only in XHTML 1.1). A list of such 111 attributes and the elements they are allowed in is in section:- #5.2.
-
- When '$config["deny_attribute"]' is not set, or set to '0', or empty ('""'), all the 111 attributes are permitted. Otherwise, '$config["deny_attribute"]' can be set as a list of comma-separated names of the denied attributes. 'on*' can be used to refer to the group of potentially dangerous, script-accepting attributes: 'onblur', 'onchange', 'onclick', 'ondblclick', 'onfocus', 'onkeydown', 'onkeypress', 'onkeyup', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onreset', 'onselect' and 'onsubmit'.
-
- Note that attributes specified in '$config["deny_attribute"]' are denied globally, for all elements. To deny attributes for only specific elements, '$spec' (see section:- #2.3) can be used. '$spec' can also be used to element-specifically permit an attribute otherwise denied through '$config["deny_attribute"]'.
-
- With '$config["safe"] = 1' (section:- #3.6), the 'on*' attributes are automatically disallowed.
-
- *Note*: To deny all but a few attributes globally, a simpler way to specify '$config["deny_attribute"]' would be to use the notation '* -attribute1 -attribute2 ...'. Thus, a value of '* -title -href' implies that except 'href' and 'title' (where allowed as per standards) all other attributes are to be removed. With this notation, the value for the parameter 'safe' (section:- #3.6) will have no effect on 'deny_attribute'.
-
- htmLawed (function 'hl_tag()') also:
-
- * Lower-cases attribute names
- * Removes duplicate attributes (last one stays)
- * Gives attributes the form 'name="value"' and single-spaces them, removing unnecessary white-spacing
- * Provides `required` attributes (see section:- #3.4.1)
- * Double-quotes values and escapes any '"' inside them
- * Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point 'ad') in the values with spaces
- * Allows custom function to additionally filter/modify attribute values (see section:- #3.4.9)
-
-
-.. 3.4.1 Auto-addition of XHTML-required attributes ................
-
-
- If indicated attributes for the following elements are found missing, htmLawed (function 'hl_tag()') will add them (with values same as attribute names unless indicated otherwise below):
-
- * area - alt ('area')
- * area, img - src, alt ('image')
- * bdo - dir ('ltr')
- * form - action
- * map - name
- * optgroup - label
- * param - name
- * script - type ('text/javascript')
- * textarea - rows ('10'), cols ('50')
-
- Additionally, with '$config["xml:lang"]' set to '1' or '2', if the 'lang' but not the 'xml:lang' attribute is declared, then the latter is added too, with a value copied from that of 'lang'. This is for better standard-compliance. With '$config["xml:lang"]' set to '2', the 'lang' attribute is removed (XHTML 1.1 specs).
-
- Note that the 'name' attribute for 'map', invalid in XHTML 1.1, is also transformed if required -- see section:- #3.4.6.
-
-
-.. 3.4.2 Duplicate/invalid 'id' values ............................o
-
-
- If '$config["unique_ids"]' is '1', htmLawed (function 'hl_tag()') removes 'id' attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, ':', '.', '-' and '_') or duplicate. If '$config["unique_ids"]' is a word, any duplicate but otherwise valid value will be appropriately prefixed with the word to ensure its uniqueness. The word should begin with a letter and should contain only letters, numbers, ':', '.', '_' and '-'.
-
- Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of 'id' values as it uses a global variable ('$GLOBALS["hl_Ids"]' array). Further, an admin can restrict the use of certain 'id' values by presetting this variable before htmLawed is called into use. E.g.:
-
- $GLOBALS['hl_Ids'] = array('top'=>1, 'bottom'=>1, 'myform'=>1); // id values not allowed in input
- $processed = htmLawed($text); // filter input
-
-
-.. 3.4.3 URL schemes (protocols) and scripts in attribute values ............o
-
-
- htmLawed edits attributes that take URLs as values if they are found to contain un-permitted schemes. E.g., if the 'afp' scheme is not permitted, then '<a href="afp://domain.org">' becomes '<a href="denied:afp://domain.org">', and if Javascript is not permitted '<a onclick="javascript:xss();">' becomes '<a onclick="denied:javascript:xss();">'.
-
- By default htmLawed permits these schemes in URLs for the 'href' attribute:
-
- aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet
-
- Also, only 'file', 'http' and 'https' are permitted in attributes whose names start with 'o' (like 'onmouseover'), and in these attributes that accept URLs:
-
- action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap
-
- These default sets are used when '$config["schemes"]' is not set (see section:- #2.2). To over-ride the defaults, '$config["schemes"]' is defined as a string of semi-colon-separated sub-strings of type 'attribute: comma-separated schemes'. E.g., 'href: mailto, http, https; onclick: javascript; src: http, https'. For unspecified attributes, 'file', 'http' and 'https' are permitted. This can be changed by passing schemes for '*' in '$config["schemes"]'. E.g., 'href: mailto, http, https; *: https, https'.
-
- '*' can be put in the list of schemes to permit all protocols. E.g., 'style: *; img: http, https' results in protocols not being checked in 'style' attribute values. However, in such cases, any relative-to-absolute URL conversion, or vice versa, (section:- #3.4.4) is not done.
-
- Thus, `to allow Javascript`, one can set '$config["schemes"]' as 'href: mailto, http, https; *: http, https, javascript', or 'href: mailto, http, https, javascript; *: http, https, javascript', or '*: *', and so on.
-
- As a side-note, one may find 'style: *' useful as URLs in 'style' attributes can be specified in a variety of ways, and the patterns that htmLawed uses to identify URLs may mistakenly identify non-URL text.
-
- '!' can be put in the list of schemes to disallow all protocols as well as `local` URLs. Thus, with 'href: http, style: !', '<a href="http://cnn.com" style="background-image: url('local.jpg');">CNN</a>' will become '<a href="http://cnn.com" style="background-image: url('denied:local.jpg');">CNN</a>'.
-
- *Note*: If URL-accepting attributes other than those listed above are being allowed, then the scheme will not be checked unless the attribute name contains the string 'src' (e.g., 'dynsrc') or starts with 'o' (e.g., 'onbeforecopy').
-
- With '$config["safe"] = 1', all URLs are disallowed in the 'style' attribute values.
-
-
-.. 3.4.4 Absolute & relative URLs in attribute values .............o
-
-
- htmLawed can make absolute URLs in attributes like 'href' relative ('$config["abs_url"]' is '-1'), and vice versa ('$config["abs_url"]' is '1'). URLs in scripts are not considered for this, and so are URLs like '#section_6' (fragment), '?name=Tim#show' (starting with query string), and ';var=1?name=Tim#show' (starting with parameters). Further, this requires that '$config["base_url"]' be set properly, with the '://' and a trailing slash ('/'), with no query string, etc. E.g., 'file:///D:/page/', 'https://abc.com/x/y/', or 'http://localhost/demo/' are okay, but 'file:///D:/page/?help=1', 'abc.com/x/y/' and 'http://localhost/demo/index.htm' are not.
-
- For making absolute URLs relative, only those URLs that have the '$config["base_url"]' string at the beginning are converted. E.g., with '$config["base_url"] = "https://abc.com/x/y/"', 'https://abc.com/x/y/a.gif' and 'https://abc.com/x/y/z/b.gif' become 'a.gif' and 'z/b.gif' respectively, while 'https://abc.com/x/c.gif' is not changed.
-
- When making relative URLs absolute, only values for scheme, network location (host-name) and path values in the base URL are inherited. See section:- #5.5 for more about the URL specification as per RFC 1808:- http://www.ietf.org/rfc/rfc1808.txt.
-
-
-.. 3.4.5 Lower-cased, standard attribute values ....................o
-
-
- Optionally, for standard-compliance, htmLawed (function 'hl_tag()') lower-cases standard attribute values to give, e.g., 'input type="password"' instead of 'input type="Password"', if '$config["lc_std_val"]' is '1'. Attribute values matching those listed below for any of the elements (plus those for the 'type' attribute of 'button' or 'input') are lower-cased:
-
- all, baseline, bottom, button, center, char, checkbox, circle, col, colgroup, cols, data, default, file, get, groups, hidden, image, justify, left, ltr, middle, none, object, password, poly, post, preserve, radio, rect, ref, reset, right, row, rowgroup, rows, rtl, submit, text, top
-
- a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml:space
-
- The following `empty` (`minimized`) attributes are always assigned lower-cased values (same as the names):
-
- checked, compact, declare, defer, disabled, ismap, multiple, nohref, noresize, noshade, nowrap, readonly, selected
-
-
-.. 3.4.6 Transformation of deprecated attributes ..................o
-
-
- If '$config["no_deprecated_attr"]' is '0', then deprecated attributes (see appendix in section:- #5.2) are removed and, in most cases, their values are transformed to CSS style properties and added to the 'style' attributes (function 'hl_tag()'). Except for 'bordercolor' for 'table', 'tr' and 'td', the scores of proprietary attributes that were never part of any cross-browser standard are not supported.
-
- *Note*: The attribute 'target' for 'a' is allowed even though it is not in XHTML 1.0 specs. This is because of the attribute's wide-spread use and browser-support, and because the attribute is valid in XHTML 1.1 onwards.
-
- * align - for 'img' with value of 'left' or 'right', becomes, e.g., 'float: left'; for 'div' and 'table' with value 'center', becomes 'margin: auto'; all others become, e.g., 'text-align: right'
-
- * bgcolor - E.g., 'bgcolor="#ffffff"' becomes 'background-color: #ffffff'
- * border - E.g., 'height= "10"' becomes 'height: 10px'
- * bordercolor - E.g., 'bordercolor=#999999' becomes 'border-color: #999999;'
- * compact - 'font-size: 85%'
- * clear - E.g., 'clear="all" becomes 'clear: both'
-
- * height - E.g., 'height= "10"' becomes 'height: 10px' and 'height="*"' becomes 'height: auto'
-
- * hspace - E.g., 'hspace="10"' becomes 'margin-left: 10px; margin-right: 10px'
- * language - 'language="VBScript"' becomes 'type="text/vbscript"'
- * name - E.g., 'name="xx"' becomes 'id="xx"'
- * noshade - 'border-style: none; border: 0; background-color: gray; color: gray'
- * nowrap - 'white-space: nowrap'
- * size - E.g., 'size="10"' becomes 'height: 10px'
- * start - removed
- * type - E.g., 'type="i"' becomes 'list-style-type: lower-roman'
- * value - removed
- * vspace - E.g., 'vspace="10"' becomes 'margin-top: 10px; margin-bottom: 10px'
- * width - like 'height'
-
- Example input:
-
- <img src="j.gif" alt="image" name="dad's" /><img src="k.gif" alt="image" id="dad_off" name="dad" />
- <br clear="left" />
- <hr noshade size="1" />
- <img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding:5px;" />
- <table width="50em" align="center" bgcolor="red">
- <tr>
- <td width="20%">
- <div align="center">
- <h3 align="right">Section</h3>
- <p align="right">Para</p>
- <ol type="a" start="e"><li value="x">First item</li></ol>
- </div>
- </td>
- <td width="*">
- <ol type="1"><li>First item</li></ol>
- </td>
- </tr>
- </table>
- <br clear="all" />
-
- And the output with '$config["no_deprecated_attr"] = 1':
-
- <img src="j.gif" alt="image" /><img src="k.gif" alt="image" id="dad_off" />
- <br style="clear: left;" />
- <hr style="border-style: none; border: 0; background-color: gray; color: gray; size: 1px;" />
- <img src="i.gif" alt="image" width="10em" height="20" style="padding:5px; float: left; margin-left: 10px; margin-right: 10px; margin-top: 10px; margin-bottom: 10px; border: 1px;" id="img" />
- <table width="50em" style="margin: auto; background-color: red;">
- <tr>
- <td style="width: 20%;">
- <div style="margin: auto;">
- <h3 style="text-align: right;">Section</h3>
- <p style="text-align: right;">Para</p>
- <ol style="list-style-type: lower-latin;"><li>First item</li></ol>
- </div>
- </td>
- <td style="width: auto;">
- <ol style="list-style-type: decimal;"><li>First item</li></ol>
- </td>
- </tr>
- </table>
- <br style="clear: both;" />
-
- For 'lang', deprecated in XHTML 1.1, transformation is taken care of through '$config["xml:lang"]'; see section:- #3.4.1.
-
- The attribute 'name' is deprecated in 'form', 'iframe', and 'img', and is replaced with 'id' if an 'id' attribute doesn't exist and if the 'name' value is appropriate for 'id'. For such replacements for 'a' and 'map', for which the 'name' attribute is deprecated in XHTML 1.1, '$config["no_deprecated_attr"]' should be set to '2' (when set to '1', for these two elements, the 'name' attribute is retained).
-
-
--- 3.4.7 Anti-spam & 'href' ---------------------------------------o
-
-
- htmLawed (function 'hl_tag()') can check the 'href' attribute values (link addresses) as an anti-spam (email or link spam) measure.
-
- If '$config["anti_mail_spam"]' is not '0', the '@' of email addresses in 'href' values like 'mailto:a@b.com' is replaced with text specified by '$config["anti_mail_spam"]'. The text should be of a form that makes it clear to others that the address needs to be edited before a mail is sent; e.g., '<remove_this_antispam>@' (makes the example address 'a<remove_this_antispam>@b.com').
-
- For regular links, one can choose to have a 'rel' attribute with 'nofollow' in its value (which tells some search engines to not follow a link). This can discourage link spammers. Additionally, or as an alternative, one can choose to empty the 'href' value altogether (disable the link).
-
- For use of these options, '$config["anti_link_spam"]' should be set as an array with values 'regex1' and 'regex2', both or one of which can be empty (like 'array("", "regex2")') to indicate that that option is not to be used. Otherwise, 'regex1' or 'regex2' should be PHP- and PCRE-compatible regular expression patterns: 'href' values will be matched against them and those matching the pattern will accordingly be treated.
-
- Note that the regular expressions should have `delimiters`, and be well-formed and preferably fast. Absolute efficiency/accuracy is often not needed.
-
- An example, to have a 'rel' attribute with 'nofollow' for all links, and to disable links that do not point to domains 'abc.com' and 'xyz.org':
-
- $config["anti_link_spam"] = array('`.`', '`://\W*(?!(abc\.com|xyz\.org))`');
-
-
--- 3.4.8 Inline style properties ----------------------------------o
-
-
- htmLawed can check URL schemes and dynamic expressions (to guard against Javascript, etc., script-based insecurities) in inline CSS style property values in the 'style' attributes. (CSS properties like 'background-image' that accept URLs in their values are noted in section:- #5.3.) Dynamic CSS expressions that allow scripting in the IE browser, and can be a vulnerability, can be removed from property values by setting '$config["css_expression"]' to '1' (default setting). Note that when '$config["css_expression"]' is set to '1', htmLawed will remove '/*' from the 'style' values.
-
- *Note*: Because of the various ways of representing characters in attribute values (URL-escapement, entitification, etc.), htmLawed might alter the values of the 'style' attribute values, and may even falsely identify dynamic CSS expressions and URL schemes in them. If this is an important issue, checking of URLs and dynamic expressions can be turned off ('$config["schemes"] = "...style:*..."', see section:- #3.4.3, and '$config["css_expression"] = 0'). Alternately, admins can use their own custom function for finer handling of 'style' values through the 'hook_tag' parameter (see section:- #3.4.9).
-
- It is also possible to have htmLawed let through any 'style' value by setting '$config["style_pass"]' to '1'.
-
- As such, it is better to set up a CSS file with class declarations, disallow the 'style' attribute, set a '$spec' rule (see section:- #2.3) for 'class' for the 'oneof' or 'match' parameter, and ask writers to make use of the 'class' attribute.
-
-
--- 3.4.9 Hook function for tag content ----------------------------o
-
-
- It is possible to utilize a custom hook function to alter the tag content htmLawed has finalized (i.e., after it has checked/corrected for required attributes, transformed attributes, lower-cased attribute names, etc.).
-
- When '$config' parameter 'hook_tag' is set to the name of a function, htmLawed (function 'hl_tag()') will pass on the element name, and, in the case of an opening tag, the `finalized` attribute name-value pairs as array elements to the function. The function, after completing a task such as filtering or tag transformation, will typically return an empty string, the full opening tag string like '<element_name attribute_1_name="attribute_1_value"...>' (for empty elements like 'img' and 'input', the element-closing slash '/' should also be included), etc.
-
- Any 'hook_tag' function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as 'a' in the closing '</a>' tag of the element '<a href="http://cnn.com">CNN</a>'. Unlike for opening tags, no other value (i.e., the attribute name-value array) is passed to the function since a closing tag contains only element names. Typically, the function will return an empty string or a full closing tag (like '</a>').
-
- This is a *powerful functionality* that can be exploited for various objectives: consolidate-and-convert inline 'style' attributes to 'class', convert 'embed' elements to 'object', permit only one 'caption' element in a 'table' element, disallow embedding of certain types of media, *inject HTML*, use CSSTidy:- http://csstidy.sourceforge.net to sanitize 'style' attribute values, etc.
-
- As an example, the custom hook code below can be used to force a series of specifically ordered 'id' attributes on all elements, and a specific 'param' element inside all 'object' elements:
-
- function my_tag_function($element, $attribute_array=0){
-
- // If second argument is not received, it means a closing tag is being handled
- if(is_numeric($attribute_array)){
- return "</$element>";
- }
-
- static $id = 0;
- // Remove any duplicate element
- if($element == 'param' && isset($attribute_array['allowscriptaccess'])){
- return '';
- }
-
- $new_element = '';
-
- // Force a serialized ID number
- $attribute_array['id'] = 'my_'. $id;
- ++$id;
-
- // Inject param for allowscriptaccess
- if($element == 'object'){
- $new_element = '<param id='my_'. $id; allowscriptaccess="never" />';
- ++$id;
- }
-
- $string = '';
- foreach($attribute_array as $k=>$v){
- $string .= " {$k}=\"{$v}\"";
- }
-
- static $empty_elements = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1);
-
- return "<{$element}{$string}". (isset($in_array($element, $empty_elements) ? ' /' : ''). '>'. $new_element;
- }
-
- The 'hook_tag' parameter is different from the 'hook' parameter (section:- #3.7).
-
- Snippets of hook function code developed by others may be available on the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website.
-
-
--- 3.5 Simple configuration directive for most valid XHTML -------oo
-
-
- If '$config["valid_xhtml"]' is set to '1', some relevant '$config' parameters (indicated by '~' in section:- #2.2) are auto-adjusted. This allows one to pass the '$config' argument with a simpler value. If a value for a parameter auto-set through 'valid_xhtml' is still manually provided, then that value will over-ride the auto-set value.
-
-
--- 3.6 Simple configuration directive for most `safe` HTML --------o
-
-
- `Safe` HTML refers to HTML that is restricted to reduce the vulnerability for scripting attacks (such as XSS) based on HTML code which otherwise may still be legal and compliant with the HTML standard specs. When elements such as 'script' and 'object', and attributes such as 'onmouseover' and 'style' are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered 'safe' depends on the nature of the web application and the trust-level accorded to its users.
-
- htmLawed allows an admin to use '$config["safe"]' to auto-adjust multiple '$config' parameters (such as 'elements' which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by '"' in section:- #2.2). Thus, one can pass the '$config' argument with a simpler value.
-
- With the value of '1', htmLawed considers 'CDATA' sections and HTML comments as plain text, and prohibits the 'applet', 'embed', 'iframe', 'object' and 'script' elements, and the 'on*' attributes like 'onclick'. ( There are '$config' parameters like 'css_expression' that are not affected by the value set for 'safe' but whose default values still contribute towards a more `safe` output.) Further, URLs with schemes (see section:- #3.4.3) are neutralized so that, e.g., 'style="moz-binding:url(http://danger)"' becomes 'style="moz-binding:url(denied:http://danger)"'.
-
- Admins, however, may still want to completely deny the 'style' attribute, e.g., with code like
-
- $processed = htmLawed($text, array('safe'=>1, 'deny_attribute'=>'style'));
-
- Permitting the 'style' attribute brings in risks of `click-jacking`, etc. CSS property values can render a page non-functional or be used to deface it. Except for URLs, dynamic expressions, and some other things, htmLawed does not completely check 'style' values. It does provide ways for the code-developer implementing htmLawed to do such checks through the '$spec' argument, and through the 'hook_tag' parameter (see section:- #3.4.8 for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.
-
- If a value for a parameter auto-set through 'safe' is still manually provided, then that value can over-ride the auto-set value. E.g., with '$config["safe"] = 1' and '$config["elements"] = "*+script"', 'script', but not 'applet', is allowed.
-
- A page illustrating the efficacy of htmLawed's anti-XSS abilities with 'safe' set to '1' against XSS vectors listed by RSnake:- http://ha.ckers.org/xss.html may be available here:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/rsnake/RSnakeXSSTest.htm.
-
-
--- 3.7 Using a hook function --------------------------------------o
-
-
- If '$config["hook"]' is not set to '0', then htmLawed will allow preliminarily processed input to be altered by a hook function named by '$config["hook"]' before starting the main work (but after handling of characters, entities, HTML comments and 'CDATA' sections -- see code for function 'htmLawed()').
-
- The hook function also allows one to alter the `finalized` values of '$config' and '$spec'.
-
- Note that the 'hook' parameter is different from the 'hook_tag' parameter (section:- #3.4.9).
-
- Snippets of hook function code developed by others may be available on the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website.
-
-
--- 3.8 Obtaining `finalized` parameter values ---------------------o
-
-
- htmLawed can assign the `finalized` '$config' and '$spec' values to a variable named by '$config["show_setting"]'. The variable, made global by htmLawed, is set as an array with three keys: 'config', with the '$config' value, 'spec', with the '$spec' value, and 'time', with a value that is the Unix time (the output of PHP's 'microtime()' function) when the value was assigned. Admins should use a PHP-compliant variable name (e.g., one that does not begin with a numerical digit) that does not conflict with variable names in their non-htmLawed code.
-
- The values, which are also post-hook function (if any), can be used to auto-generate information (on, e.g., the elements that are permitted) for input writers.
-
-
--- 3.9 Retaining non-HTML tags in input with mixed markup ---------o
-
-
- htmLawed does not remove certain characters that though invalid are nevertheless discouraged in HTML documents as per the specs (see section:- #5.1). This can be utilized to deal with input that contains mixed markup. Input that may have HTML markup as well as some other markup that is based on the '<', '>' and '&' characters is considered to have mixed markup. The non-HTML markup can be rather proprietary (like markup for emoticons/smileys), or standard (like MathML or SVG). Or it can be programming code meant for execution/evaluation (such as embedded PHP code).
-
- To deal with such mixed markup, the input text can be pre-processed to hide the non-HTML markup by specifically replacing the '<', '>' and '&' characters with some of the HTML-discouraged characters (see section:- #3.1.2). Post-htmLawed processing, the replacements are reverted.
-
- An example (mixed HTML and PHP code in input text):
-
- $text = preg_replace('`<\?php(.+?)\?>`sm', "\x83?php\\1?\x84", $text);
- $processed = htmLawed($text);
- $processed = preg_replace('`\x83\?php(.+?)\?\x84`sm', '<?php$1?>', $processed);
-
- This code will not work if '$config["clean_ms_char"]' is set to '1' (section:- #3.1), in which case one should instead deploy a hook function (section:- #3.7). (htmLawed internally uses certain control characters, code-points '1' to '7', and use of these characters as markers in the logic of hook functions may cause issues.)
-
- Admins may also be able to use '$config["and_mark"]' to deal with such mixed markup; see section:- #3.2.
-
-
-== 4 Other =======================================================oo
-
-
--- 4.1 Support -----------------------------------------------------
-
-
- A careful re-reading of this documentation will very likely answer your questions.
-
- Software updates and forum-based community-support may be found at http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed. For general PHP issues (not htmLawed-specific), support may be found through internet searches and at http://php.net.
-
-
--- 4.2 Known issues -----------------------------------------------o
-
-
- See section:- #2.8.
-
- Readers are advised to cross-check information given in this document.
-
-
--- 4.3 Change-log -------------------------------------------------o
-
-
- (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the 'htmLawed.php' file may be updated independently if the secondary files are revised.)
-
- `Version number - Release date. Notes`
-
- 1.1.11 - 5 June 2012. Fix for possible problem with handling of multi-byte characters in attribute values in an mbstring.func_overload enviroment. '$config["hook_tag"]', if specified, now receives names of elements in closing tags.
-
- 1.1.10 - 22 October 2011. Fix for a bug in the 'tidy' functionality that caused the entire input to be replaced with a single space; new parameter, '$config["direct_list_nest"]' to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)
-
- 1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of 'li' within 'dir'
-
- 1.1.9.4 - 3 July 2010. Parameter 'schemes' now accepts '!' so any URL, even a local one, can be `denied`. An issue in which a second URL value in 'style' properties was not checked was fixed.
-
- 1.1.9.3 - 17 May 2010. Checks for correct nesting of 'param'
-
- 1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes
-
- 1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for 'flashvars' attribute for 'embed'
-
- 1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values
-
- 1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice
-
- 1.1.8 - 23 April 2009. Parameter 'deny_attribute' now accepts the wild-card '*', making it simpler to specify its value when all but a few attributes are being denied; fixed a bug in interpreting '$spec'
-
- 1.1.7 - 11-12 March 2009. Attributes globally denied through 'deny_attribute' can be allowed element-specifically through '$spec'; '$config["style_pass"]' allowing letting through any 'style' value introduced; altered logic to catch certain types of dynamic crafted CSS expressions
-
- 1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions
-
- 1.1.2 - 22 January 2009. Fixed bug in parsing of 'font' attributes during tag transformation
-
- 1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent
-
- 1.1 - 29 June 2008. '$config["hook_tag"]' and '$config["format"]' introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug
-
- 1.0.9 - 11 June 2008. Fixed bug in invalid HTML code-point entity check
-
- 1.0.8 - 15 May 2008. 'bordercolor' attribute for 'table', 'td' and 'tr'
-
- 1.0.7 - 1 May 2008. Support for 'wmode' attribute for 'embed'; '$config["show_setting"]' introduced; improved '$config["elements"]' evaluation
-
- 1.0.6 - 20 April 2008. '$config["and_mark"]' introduced
-
- 1.0.5 - 12 March 2008. 'style' URL schemes essentially disallowed when $config 'safe' is on; improved regex for CSS expression search
-
- 1.0.4 - 10 March 2008. Improved corrections for 'blockquote', 'form', 'map' and 'noscript'
-
- 1.0.3 - 3 March 2008. Character entities for soft-hyphens are now replaced with spaces (instead of being removed); a bug allowing 'td' directly inside 'table' fixed; 'safe' '$config' parameter added
-
- 1.0.2 - 13 February 2008. Improved implementation of '$config["keep_bad"]'
-
- 1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions ('hl_tag()' and 'hl_prot()'); no error display with 'hl_regex()'
-
- 1.0 - 2 November 2007. First release
-
-
--- 4.4 Testing ----------------------------------------------------o
-
-
- To test htmLawed using a form interface, a demo:- htmLawedTest.php web-page is provided with the htmLawed distribution ('htmLawed.php' and 'htmLawedTest.php' should be in the same directory on the web-server). A file with test-cases:- htmLawed_TESTCASE.txt is also provided.
-
-
--- 4.5 Upgrade, & old versions ------------------------------------o
-
-
- Upgrading is as simple as replacing the previous version of 'htmLawed.php' (assuming it was not modified for customized features). As htmLawed output is almost always used in static documents, upgrading should not affect old, finalized content.
-
- *Important* The following upgrades may affect the functionality of a specific htmLawed as indicated by their corresponding notes:
-
- (1) From version 1.1-1.1.10 to 1.1.11, if a 'hook_tag' function is in use: In version 1.1.11, elements in closing tags (and not just the opening tags) are also passed to the function. There are no attribute names/values to pass, so a 'hook_tag' function receives only the element name. The 'hook_tag' function therefore may have to be edited. See section:- #3.4.9.
-
- Old versions of htmLawed may be available online. E.g., for version 1.0, check http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip, for 1.1.1, htmLawed111.zip, and for 1.1.10, htmLawed1110.zip.
-
-
--- 4.6 Comparison with 'HTMLPurifier' -----------------------------o
-
-
- The HTMLPurifier PHP library by Edward Yang is a very good HTML filtering script that uses object oriented PHP code. Compared to htmLawed, it (as of mid-2009):
-
- * does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)
-
- * is 15-20 times bigger (scores of files totalling more than 750 kb)
-
- * consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)
-
- * is expectedly slower
-
- * does not allow admins to fully allow all valid HTML (because of incomplete HTML support, it always considers elements like 'script' illegal)
-
- * lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)
-
- * has poor documentation
-
- However, HTMLPurifier has finer checks for character encodings and attribute values, and can log warnings and errors. Visit the HTMLPurifier website:- http://htmlpurifier.org for updated information.
-
-
--- 4.7 Use through application plug-ins/modules -------------------o
-
-
- Plug-ins/modules to implement htmLawed in applications such as Drupal and DokuWiki may have been developed. Please check the application websites and the forum on the htmLawed site:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed.
-
-
--- 4.8 Use in non-PHP applications --------------------------------o
-
-
- Non-PHP applications written in Python, Ruby, etc., may be able to use htmLawed through system calls to the PHP engine. Such code may have been documented on the internet. Also check the forum on the htmLawed site:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed.
-
-
--- 4.9 Donate -----------------------------------------------------o
-
-
- A donation in any currency and amount to appreciate or support this software can be sent by PayPal:- http://paypal.com to this email address: drpatnaik at yahoo dot com.
-
-
--- 4.10 Acknowledgements ------------------------------------------o
-
-
- Nicholas Alipaz, Bryan Blakey, Pádraic Brady, Ulf Harnhammer, Gareth Heyes, Klaus Leithoff, Lukasz Pilorz, Shelley Powers, Edward Yang, and many anonymous users.
-
- Thank you!
-
-
-== 5 Appendices ==================================================oo
-
-
--- 5.1 Characters discouraged in XHTML -----------------------------
-
-
- Characters represented by the following hexadecimal code-points are `not` invalid, even though some validators may issue messages stating otherwise.
-
- '7f' to '84', '86' to '9f', 'fdd0' to 'fddf', '1fffe', '1ffff', '2fffe', '2ffff', '3fffe', '3ffff', '4fffe', '4ffff', '5fffe', '5ffff', '6fffe', '6ffff', '7fffe', '7ffff', '8fffe', '8ffff', '9fffe', '9ffff', 'afffe', 'affff', 'bfffe', 'bffff', 'cfffe', 'cffff', 'dfffe', 'dffff', 'efffe', 'effff', 'ffffe', 'fffff', '10fffe' and '10ffff'
-
-
--- 5.2 Valid attribute-element combinations -----------------------o
-
-
- Valid attribute-element combinations as per W3C specs.
-
- * includes deprecated attributes (marked '^'), attributes for the non-standard 'embed' element (marked '*'), and the proprietary 'bordercolor' (marked '~')
- * only non-frameset, HTML body elements
- * 'name' for 'a' and 'map', and 'lang' are invalid in XHTML 1.1
- * 'target' is valid for 'a' in XHTML 1.1 and higher
- * 'xml:space' is only for XHTML 1.1
-
- abbr - td, th
- accept - form, input
- accept-charset - form
- accesskey - a, area, button, input, label, legend, textarea
- action - form
- align - caption^, embed, applet, iframe, img^, input^, object^, legend^, table^, hr^, div^, h1^, h2^, h3^, h4^, h5^, h6^, p^, col, colgroup, tbody, td, tfoot, th, thead, tr
- alt - applet, area, img, input
- archive - applet, object
- axis - td, th
- bgcolor - embed, table^, tr^, td^, th^
- border - table, img^, object^
- bordercolor~ - table, td, tr
- cellpadding - table
- cellspacing - table
- char - col, colgroup, tbody, td, tfoot, th, thead, tr
- charoff - col, colgroup, tbody, td, tfoot, th, thead, tr
- charset - a, script
- checked - input
- cite - blockquote, q, del, ins
- classid - object
- clear - br^
- code - applet
- codebase - object, applet
- codetype - object
- color - font
- cols - textarea
- colspan - td, th
- compact - dir, dl^, menu, ol^, ul^
- coords - area, a
- data - object
- datetime - del, ins
- declare - object
- defer - script
- dir - bdo
- disabled - button, input, optgroup, option, select, textarea
- enctype - form
- face - font
- flashvars* - embed
- for - label
- frame - table
- frameborder - iframe
- headers - td, th
- height - embed, iframe, td^, th^, img, object, applet
- href - a, area
- hreflang - a
- hspace - applet, img^, object^
- ismap - img, input
- label - option, optgroup
- language - script^
- longdesc - img, iframe
- marginheight - iframe
- marginwidth - iframe
- maxlength - input
- method - form
- model* - embed
- multiple - select
- name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param
- nohref - area
- noshade - hr^
- nowrap - td^, th^
- object - applet
- onblur - a, area, button, input, label, select, textarea
- onchange - input, select, textarea
- onfocus - a, area, button, input, label, select, textarea
- onreset - form
- onselect - input, textarea
- onsubmit - form
- pluginspage* - embed
- pluginurl* - embed
- prompt - isindex
- readonly - textarea, input
- rel - a
- rev - a
- rows - textarea
- rowspan - td, th
- rules - table
- scope - td, th
- scrolling - iframe
- selected - option
- shape - area, a
- size - hr^, font, input, select
- span - col, colgroup
- src - embed, script, input, iframe, img
- standby - object
- start - ol^
- summary - table
- tabindex - a, area, button, input, object, select, textarea
- target - a^, area, form
- type - a, embed, object, param, script, input, li^, ol^, ul^, button
- usemap - img, input, object
- valign - col, colgroup, tbody, td, tfoot, th, thead, tr
- value - input, option, param, button, li^
- valuetype - param
- vspace - applet, img^, object^
- width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^
- wmode - embed
- xml:space - pre, script, style
-
- These are allowed in all but the shown elements:
-
- class - param, script
- dir - applet, bdo, br, iframe, param, script
- id - script
- lang - applet, br, iframe, param, script
- onclick - applet, bdo, br, font, iframe, isindex, param, script
- ondblclick - applet, bdo, br, font, iframe, isindex, param, script
- onkeydown - applet, bdo, br, font, iframe, isindex, param, script
- onkeypress - applet, bdo, br, font, iframe, isindex, param, script
- onkeyup - applet, bdo, br, font, iframe, isindex, param, script
- onmousedown - applet, bdo, br, font, iframe, isindex, param, script
- onmousemove - applet, bdo, br, font, iframe, isindex, param, script
- onmouseout - applet, bdo, br, font, iframe, isindex, param, script
- onmouseover - applet, bdo, br, font, iframe, isindex, param, script
- onmouseup - applet, bdo, br, font, iframe, isindex, param, script
- style - param, script
- title - param, script
- xml:lang - applet, br, iframe, param, script
-
-
--- 5.3 CSS 2.1 properties accepting URLs ------------------------o
-
-
- background
- background-image
- content
- cue-after
- cue-before
- cursor
- list-style
- list-style-image
- play-during
-
-
--- 5.4 Microsoft Windows 1252 character replacements --------------o
-
-
- Key: 'd' double, 'l' left, 'q' quote, 'r' right, 's.' single
-
- Code-point (decimal) - hexadecimal value - replacement entity - represented character
-
- 127 - 7f - (removed) - (not used)
- 128 - 80 - &#8364; - euro
- 129 - 81 - (removed) - (not used)
- 130 - 82 - &#8218; - baseline s. q
- 131 - 83 - &#402; - florin
- 132 - 84 - &#8222; - baseline d q
- 133 - 85 - &#8230; - ellipsis
- 134 - 86 - &#8224; - dagger
- 135 - 87 - &#8225; - d dagger
- 136 - 88 - &#710; - circumflex accent
- 137 - 89 - &#8240; - permile
- 138 - 8a - &#352; - S Hacek
- 139 - 8b - &#8249; - l s. guillemet
- 140 - 8c - &#338; - OE ligature
- 141 - 8d - (removed) - (not used)
- 142 - 8e - &#381; - Z dieresis
- 143 - 8f - (removed) - (not used)
- 144 - 90 - (removed) - (not used)
- 145 - 91 - &#8216; - l s. q
- 146 - 92 - &#8217; - r s. q
- 147 - 93 - &#8220; - l d q
- 148 - 94 - &#8221; - r d q
- 149 - 95 - &#8226; - bullet
- 150 - 96 - &#8211; - en dash
- 151 - 97 - &#8212; - em dash
- 152 - 98 - &#732; - tilde accent
- 153 - 99 - &#8482; - trademark
- 154 - 9a - &#353; - s Hacek
- 155 - 9b - &#8250; - r s. guillemet
- 156 - 9c - &#339; - oe ligature
- 157 - 9d - (removed) - (not used)
- 158 - 9e - &#382; - z dieresis
- 159 - 9f - &#376; - Y dieresis
-
-
--- 5.5 URL format -------------------------------------------------o
-
-
- An `absolute` URL has a 'protocol' or 'scheme', a 'network location' or 'hostname', and, optional 'path', 'parameters', 'query' and 'fragment' segments. Thus, an absolute URL has this generic structure:
-
- (scheme) : (//network location) /(path) ;(parameters) ?(query) #(fragment)
-
- The schemes can only contain letters, digits, '+', '.' and '-'. Hostname is the portion after the '//' and up to the first '/' (if any; else, up to the end) when ':' is followed by a '//' (e.g., 'abc.com' in 'ftp://abc.com/def'); otherwise, it consists of everything after the ':' (e.g., 'def@abc.com' in mailto:def@abc.com').
-
- `Relative` URLs do not have explicit schemes and network locations; such values are inherited from a `base` URL.
-
-
--- 5.6 Brief on htmLawed code -------------------------------------o
-
-
- Much of the code's logic and reasoning can be understood from the documentation above.
-
- The *output* of htmLawed is a text string containing the processed input. There is no custom error tracking.
-
- *Function arguments* for htmLawed are:
-
- * '$in' - 1st argument; a text string; the *input text* to be processed. Any extraneous slashes added by PHP when `magic quotes` are enabled should be removed beforehand using PHP's 'stripslashes()' function.
-
- * '$config' - 2nd argument; an associative array; optional (named '$C' in htmLawed code). The array has keys with names like 'balance' and 'keep_bad', and the values, which can be boolean, string, or array, depending on the key, are read to accordingly set the *configurable parameters* (indicated by the keys). All configurable parameters receive some default value if the value to be used is not specified by the user through '$config'. `Finalized` '$config' is thus a filtered and possibly larger array.
-
- * '$spec' - 3rd argument; a text string; optional. The string has rules, written in an htmLawed-designated format, *specifying* element-specific attribute and attribute value restrictions. Function 'hl_spec()' is used to convert the string to an associative-array for internal use. `Finalized` '$spec' is thus an array.
-
- `Finalized` '$config' and '$spec' are made *global variables* while htmLawed is at work. Values of any pre-existing global variables with same names are noted, and their values are restored after htmLawed finishes processing the input (to capture the `finalized` values, the 'show_settings' parameter of '$config' should be used). Depending on '$config', another global variable 'hl_Ids', to track 'id' attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.
-
- Except for the main function 'htmLawed()' and the functions 'kses()' and 'kses_hook()', htmLawed's functions are *name-spaced* using the 'hl_' prefix. The *functions* and their roles are:
-
- * 'hl_attrval' - checking attribute values against $spec
- * 'hl_bal' - tag balancing
- * 'hl_cmtcd' - handling CDATA sections and HTML comments
- * 'hl_ent' - entity handling
- * 'hl_prot' - checking a URL scheme/protocol
- * 'hl_regex' - checking syntax of a regular expression
- * 'hl_spec' - converting user-supplied $spec value to one used by htmLawed internally
- * 'hl_tag' - handling tags
- * 'hl_tag2' - transforming tags
- * 'hl_tidy' - compact/beautify HTML
- * 'hl_version' - reporting htmLawed version
- * 'htmLawed' - main function
- * 'kses' - main function of 'kses'
- * 'kses_hook' - hook function of 'kses'
-
- The last two are for compatibility with pre-existing code using the 'kses' script. htmLawed's 'kses()' basically passes on the filtering task to 'htmLawed()' function after deciphering '$config' and '$spec' from the argument values supplied to it. 'kses_hook()' is an empty function and is meant for being filled with custom code if the 'kses' script users were using one.
-
- 'htmLawed()' finalizes '$spec' (with the help of 'hl_spec()') and '$config', and globalizes them. Finalization of '$config' involves setting default values if an inappropriate or invalid one is supplied. This includes calling 'hl_regex()' to check well-formedness of regular expression patterns if such expressions are user-supplied through '$config'. 'htmLawed()' then removes invalid characters like nulls and 'x01' and appropriately handles entities using 'hl_ent()'. HTML comments and CDATA sections are identified and treated as per '$config' with the help of 'hl_cmtcd()'. When retained, the '<' and '>' characters identifying them, and the '<', '>' and '&' characters inside them, are replaced with control characters (code-points '1' to '5') till any tag balancing is completed.
-
- After this `initial processing` 'htmLawed()' identifies tags using regex and processes them with the help of 'hl_tag()' -- a large function that analyzes tag content, filtering it as per HTML standards, '$config' and '$spec'. Among other things, 'hl_tag()' transforms deprecated elements using 'hl_tag2()', removes attributes from closing tags, checks attribute values as per '$spec' rules using 'hl_attrval()', and checks URL protocols using 'hl_prot()'. 'htmLawed()' performs tag balancing and nesting checks with a call to 'hl_bal()', and optionally compacts/beautifies the output with proper white-spacing with a call to 'hl_tidy()'. The latter temporarily replaces white-space, and '<', '>' and '&' characters inside 'pre', 'script' and 'textarea' elements, and HTML comments and CDATA sections with control characters (code-points '1' to '5', and '7').
-
- htmLawed permits the use of custom code or *hook functions* at two stages. The first, called inside 'htmLawed()', allows the input text as well as the finalized $config and $spec values to be altered right after the initial processing (see section:- #3.7). The second is called by 'hl_tag()' once the tag content is finalized (see section:- #3.4.9).
-
- Being dictated by the external and stable HTML standard, htmLawed's objective is very clear-cut and less concerned with tweakability. The code is only minimally annotated with comments -- it is not meant to instruct; PHP developers familiar with the HTML specs will see the logic, and others can always refer to the htmLawed documentation. The compact structuring of the statements is meant to aid in quickly grasping the logic, at least when viewed with code syntax highlighted.
-
-___________________________________________________________________oo
-
-
-@@description: htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter
-@@encoding: utf-8
-@@keywords: htmLawed, HTM, HTML, HTML Tidy, converter, filter, formatter, purifier, sanitizer, XSS, input, PHP, software, code, script, security, cross-site scripting, hack, sanitize, remove, standards, tags, attributes, elements
-@@language: en
+/*
+htmLawed_README.txt, 29 August 2013
+htmLawed 1.1.16, 29 August 2013
+Copyright Santosh Patnaik
+Dual licensed with LGPL 3 and GPL 2+
+A PHP Labware internal utility - http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed
+*/
+
+
+== Content ==========================================================
+
+
+1 About htmLawed
+ 1.1 Example uses
+ 1.2 Features
+ 1.3 History
+ 1.4 License & copyright
+ 1.5 Terms used here
+2 Usage
+ 2.1 Simple
+ 2.2 Configuring htmLawed using the '$config' parameter
+ 2.3 Extra HTML specifications using the '$spec' parameter
+ 2.4 Performance time & memory usage
+ 2.5 Some security risks to keep in mind
+ 2.6 Use without modifying old 'kses()' code
+ 2.7 Tolerance for ill-written HTML
+ 2.8 Limitations & work-arounds
+ 2.9 Examples of usage
+3 Details
+ 3.1 Invalid/dangerous characters
+ 3.2 Character references/entities
+ 3.3 HTML elements
+ 3.3.1 HTML comments and 'CDATA' sections
+ 3.3.2 Tag-transformation for better XHTML-Strict
+ 3.3.3 Tag balancing and proper nesting
+ 3.3.4 Elements requiring child elements
+ 3.3.5 Beautify or compact HTML
+ 3.4 Attributes
+ 3.4.1 Auto-addition of XHTML-required attributes
+ 3.4.2 Duplicate/invalid 'id' values
+ 3.4.3 URL schemes (protocols) and scripts in attribute values
+ 3.4.4 Absolute & relative URLs
+ 3.4.5 Lower-cased, standard attribute values
+ 3.4.6 Transformation of deprecated attributes
+ 3.4.7 Anti-spam & 'href'
+ 3.4.8 Inline style properties
+ 3.4.9 Hook function for tag content
+ 3.5 Simple configuration directive for most valid XHTML
+ 3.6 Simple configuration directive for most `safe` HTML
+ 3.7 Using a hook function
+ 3.8 Obtaining `finalized` parameter values
+ 3.9 Retaining non-HTML tags in input with mixed markup
+4 Other
+ 4.1 Support
+ 4.2 Known issues
+ 4.3 Change-log
+ 4.4 Testing
+ 4.5 Upgrade, & old versions
+ 4.6 Comparison with 'HTMLPurifier'
+ 4.7 Use through application plug-ins/modules
+ 4.8 Use in non-PHP applications
+ 4.9 Donate
+ 4.10 Acknowledgements
+5 Appendices
+ 5.1 Characters discouraged in HTML
+ 5.2 Valid attribute-element combinations
+ 5.3 CSS 2.1 properties accepting URLs
+ 5.4 Microsoft Windows 1252 character replacements
+ 5.5 URL format
+ 5.6 Brief on htmLawed code
+
+
+== 1 About htmLawed ================================================
+
+
+ htmLawed is a PHP script to process text with HTML markup to make it more compliant with HTML standards and administrative policies. It works by making HTML well-formed with balanced and properly nested tags, neutralizing code that may be used for cross-site scripting (XSS) attacks, allowing only specified HTML tags and attributes, and so on. Such `lawing in` of HTML in text used in (X)HTML or XML documents ensures that it is in accordance with the aesthetics, safety and usability requirements set by administrators.
+
+ htmLawed is highly customizable, and fast with low memory usage. Its free and open-source code is in one small file, does not require extensions or libraries, and works in older versions of PHP as well. It is a good alternative to the HTML Tidy:- http://tidy.sourceforge.net application.
+
+
+-- 1.1 Example uses ------------------------------------------------
+
+
+ * Filtering of text submitted as comments on blogs to allow only certain HTML elements
+
+ * Making RSS/Atom newsfeed item-content standard-compliant: often one uses an excerpt from an HTML document for the content, and with unbalanced tags, non-numerical entities, etc., such excerpts may not be XML-compliant
+
+ * Text processing for stricter XML standard-compliance: e.g., to have lowercased 'x' in hexadecimal numeric entities becomes necessary if an XHTML document with MathML content needs to be served as 'application/xml'
+
+ * Scraping text or data from web-pages
+
+ * Pretty-printing HTML code
+
+
+-- 1.2 Features ---------------------------------------------------o
+
+
+ Key: '*' security feature, '^' standard compliance, '~' requires setting right options, '`' different from 'Kses'
+
+ * make input more *secure* and *standard-compliant*
+ * use for HTML 4, XHTML 1.0 or 1.1, or even generic *XML* documents ^~`
+
+ * *beautify* or *compact* HTML ^~`
+
+ * can *restrict elements* ^~`
+ * ensures proper closure of empty elements like 'img' ^`
+ * *transform deprecated elements* like 'u' ^~`
+ * HTML *comments* and 'CDATA' sections can be permitted ^~`
+ * elements like 'script', 'object' and 'form' can be permitted ~
+
+ * *restrict attributes*, including *element-specifically* ^~`
+ * remove *invalid attributes* ^`
+ * element and attribute names are *lower-cased* ^
+ * provide *required attributes*, like 'alt' for 'image' ^`
+ * *transforms deprecated attributes* ^~`
+ * attributes *declared only once* ^`
+
+ * *restrict attribute values*, including *element-specifically* ^~`
+ * a value is declared for `empty` (`minimized`) attributes like 'checked' ^
+ * check for potentially dangerous attribute values *~
+ * ensure *unique* 'id' attribute values ^~`
+ * *double-quote* attribute values ^
+ * lower-case *standard attribute values* like 'password' ^`
+ * permit custom, non-standard attributes as well as custom rules for standard attributes ~`
+
+ * *attribute-specific URL protocol/scheme restriction* *~`
+ * disable *dynamic expressions* in 'style' values *~`
+
+ * neutralize invalid named character entities ^`
+ * *convert* hexadecimal numeric entities to decimal ones, or vice versa ^~`
+ * convert named entities to numeric ones for generic XML use ^~`
+
+ * remove *null* characters *
+ * neutralize potentially dangerous proprietary Netscape *Javascript entities* *
+ * replace potentially dangerous *soft-hyphen* character in URL-accepting attribute values with spaces *
+
+ * remove common *invalid characters* not allowed in HTML or XML ^`
+ * replace *characters from Microsoft applications* like 'Word' that are discouraged in HTML or XML ^~`
+ * neutralize entities for characters invalid or discouraged in HTML or XML ^`
+ * appropriately neutralize '<', '&', '"', and '>' characters ^*`
+
+ * understands improperly spaced tag content (like, spread over more than a line) and properly spaces them `
+ * attempts to *balance tags* for well-formedness ^~`
+ * understands when *omitable closing tags* like '</p>' (allowed in HTML 4, transitional, e.g.) are missing ^~`
+ * attempts to permit only *validly nested tags* ^~`
+ * option to *remove or neutralize bad content* ^~`
+ * attempts to *rectify common errors of plain-text misplacement* (e.g., directly inside 'blockquote') ^~`
+
+ * fast, *non-OOP* code of ~45 kb incurring peak basal memory usage of ~0.5 MB
+ * *compatible* with pre-existing code using 'Kses' (the filter used by 'WordPress')
+
+ * optional *anti-spam* measures such as addition of 'rel="nofollow"' and link-disabling ~`
+ * optionally makes *relative URLs absolute*, and vice versa ~`
+
+ * optionally mark '&' to identify the entities for '&', '<' and '>' introduced by htmLawed ~`
+
+ * allows deployment of powerful *hook functions* to *inject* HTML, *consolidate* 'style' attributes to 'class', finely check attribute values, etc. ~`
+
+ * *independent of character encoding* of input and does not affect it
+
+ * *tolerance for ill-written HTML* to a certain degree
+
+
+-- 1.3 History ----------------------------------------------------o
+
+
+ htmLawed was created in 2007 for use with 'LabWiki', a wiki software developed at PHP Labware, as a suitable software could not be found. Existing PHP software like 'Kses' and 'HTMLPurifier' were deemed inadequate, slow, resource-intensive, or dependent on an extension or external application like 'HTML Tidy'. The core logic of htmLawed, that of identifying HTML elements and attributes, was based on the 'Kses' (version 0.2.2) HTML filter software of Ulf Harnhammar (it can still be used with code that uses 'Kses'; see section:- #2.6.).
+
+ See section:- #4.3 for a detailed log of changes in htmLawed over the years, and section:- #4.10 for acknowledgements.
+
+
+-- 1.4 License & copyright ----------------------------------------o
+
+
+ htmLawed is free and open-source software dual copyrighted by Santosh Patnaik, MD, PhD, and licensed under LGPL license version 3:- http://www.gnu.org/licenses/lgpl-3.0.txt, and GPL license version 2:- http://www.gnu.org/licenses/gpl-2.0.txt (or later).
+
+
+-- 1.5 Terms used here --------------------------------------------o
+
+
+ In this document, only HTML body-level elements are considered. htmLawed does not have support for head-level elements, 'body', and the frame-level elements, 'frameset', 'frame' and 'noframes', and these elements are ignored here.
+
+ * `administrator` - or admin; person setting up the code that utilizes htmLawed; also, `user`
+ * `attributes` - name-value pairs like 'href="http://x.com"' in opening tags
+ * `author` - see `writer`
+ * `character` - atomic unit of text; internally represented by a numeric `code-point` as specified by the `encoding` or `charset` in use
+ * `entity` - markup like '&gt;' and '&#160;' used to refer to a character
+ * `element` - HTML element like 'a' and 'img'
+ * `element content` - content between the opening and closing tags of an element, like 'click' of the '<a href="x">click</a>' element
+ * `HTML` - implies XHTML unless specified otherwise
+ * `HTML body` - Complete HTML documents typically have a `head` and a `body` container. Information in `head` specifies title of the document, etc., whereas that in the body informs what is to be displayed on a web-page; it is only the elements for `body`, except 'frames', 'frameset' and 'noframes' that htmLawed is concerned with
+ * `input` - text given to htmLawed to process
+ * `processing` - involves filtering, correction, etc., of input
+ * `safe` - absence or reduction of certain characters and HTML elements and attributes in HTML of text that can otherwise potentially, and circumstantially, expose text readers to security vulnerabilities like cross-site scripting attacks (XSS)
+ * `scheme` - a URL protocol like 'http' and 'ftp'
+ * `specifications` - standard specifications, for HTML4, HTML5, Ruby, etc.
+ * `style property` - terms like 'border' and 'height' for which declarations are made in values for the 'style' attribute of elements
+ * `tag` - markers like '<a href="x">' and '</a>' delineating element content; the opening tag can contain attributes
+ * `tag content` - consists of tag markers '<' and '>', element names like 'div', and possibly attributes
+ * `user` - administrator
+ * `writer` - end-user like a blog commenter providing the input that is to be processed; also, `author`
+
+
+-- 1.6 Availability ------------------------------------------------o
+
+
+ htmLawed can be downloaded for free at its website:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed. Besides the 'htmLawed.php' file, the download has the htmLawed documentation (this document) in plain text:- htmLawed_README.txt and HTML:- htmLawed_README.htm formats, a script for testing:- htmLawedTest.php, and a text file for test-cases:- htmLawed_TESTCASE.txt. htmLawed is also available as a PHP class (OOP code) on its website.
+
+
+== 2 Usage ========================================================oo
+
+
+ htmLawed works in PHP version 4.4 or higher. Either 'include()' the 'htmLawed.php' file, or copy-paste the entire code. To use with PHP 4.3, have the following code included:
+
+ if(!function_exists('ctype_digit')){
+ function ctype_digit($var){
+ return ((int) $var == $var);
+ }
+ }
+
+
+-- 2.1 Simple ------------------------------------------------------
+
+
+ The input text to be processed, '$text', is passed as an argument of type string; 'htmLawed()' returns the processed string:
+
+ $processed = htmLawed($text);
+
+ With the 'htmLawed class' (section:- #1.6), usage is:
+
+ $processed = htmLawed::hl($text);
+
+ *Notes*: (1) If input is from a '$_GET' or '$_POST' value, and 'magic quotes' are enabled on the PHP setup, run 'stripslashes()' on the input before passing to htmLawed. (2) htmLawed does not have support for head-level elements, 'body', and the frame-level elements, 'frameset', 'frame' and 'noframes'.
+
+ By default, htmLawed will process the text allowing all valid HTML elements/tags, secure URL scheme/CSS style properties, etc. It will allow 'CDATA' sections and HTML comments, balance tags, and ensure proper nesting of elements. Such actions can be configured using two other optional arguments -- '$config' and '$spec':
+
+ $processed = htmLawed($text, $config, $spec);
+
+ The '$config' and '$spec' arguments are detailed below. Some examples are shown in section:- #2.9. For maximum protection against 'XSS' and other scripting attacks (e.g., by disallowing Javascript code), consider using the 'safe' parameter; see section:- #3.6.
+
+
+-- 2.2 Configuring htmLawed using the '$config' parameter ---------o
+
+
+ '$config' instructs htmLawed on how to tackle certain tasks. When '$config' is not specified, or not set as an array (e.g., '$config = 1'), htmLawed will take default actions. One or many of the task-action or value-specification pairs can be specified in '$config' as array key-value pairs. If a parameter is not specified, htmLawed will use the default value/action indicated further below.
+
+ $config = array('comment'=>0, 'cdata'=>1);
+ $processed = htmLawed($text, $config);
+
+ Or,
+
+ $processed = htmLawed($text, array('comment'=>0, 'cdata'=>1));
+
+ Below are the possible value-specification combinations. In PHP code, values that are integers should not be quoted and should be used as numeric types (unless meant as string/text).
+
+ Key: '*' default, '^' different default when htmLawed is used in the Kses-compatible mode (see section:- #2.6), '~' different default when 'valid_xhtml' is set to '1' (see section:- #3.5), '"' different default when 'safe' is set to '1' (see section:- #3.6)
+
+ *abs_url*
+ Make URLs absolute or relative; '$config["base_url"]' needs to be set; see section:- #3.4.4
+
+ '-1' - make relative
+ '0' - no action *
+ '1' - make absolute
+
+ *and_mark*
+ Mark '&' characters in the original input; see section:- #3.2
+
+ *anti_link_spam*
+ Anti-link-spam measure; see section:- #3.4.7
+
+ '0' - no measure taken *
+ `array("regex1", "regex2")` - will ensure a 'rel' attribute with 'nofollow' in its value in case the 'href' attribute value matches the regular expression pattern 'regex1', and/or will remove 'href' if its value matches the regular expression pattern 'regex2'. E.g., 'array("/./", "/://\W*(?!(abc\.com|xyz\.org))/")'; see section:- #3.4.7 for more.
+
+ *anti_mail_spam*
+ Anti-mail-spam measure; see section:- #3.4.7
+
+ '0' - no measure taken *
+ `word` - '@' in mail address in 'href' attribute value is replaced with specified `word`
+
+ *balance*
+ Balance tags for well-formedness and proper nesting; see section:- #3.3.3
+
+ '0' - no
+ '1' - yes *
+
+ *base_url*
+ Base URL value that needs to be set if '$config["abs_url"]' is not '0'; see section:- #3.4.4
+
+ *cdata*
+ Handling of 'CDATA' sections; see section:- #3.3.1
+
+ '0' - don't consider 'CDATA' sections as markup and proceed as if plain text ^"
+ '1' - remove
+ '2' - allow, but neutralize any '<', '>', and '&' inside by converting them to named entities
+ '3' - allow *
+
+ *clean_ms_char*
+ Replace discouraged characters introduced by Microsoft Word, etc.; see section:- #3.1
+
+ '0' - no *
+ '1' - yes
+ '2' - yes, but replace special single & double quotes with ordinary ones
+
+ *comment*
+ Handling of HTML comments; see section:- #3.3.1
+
+ '0' - don't consider comments as markup and proceed as if plain text ^"
+ '1' - remove
+ '2' - allow, but neutralize any '<', '>', and '&' inside by converting to named entities
+ '3' - allow *
+
+ *css_expression*
+ Allow dynamic CSS expression by not removing the expression from CSS property values in 'style' attributes; see section:- #3.4.8
+
+ '0' - remove *
+ '1' - allow
+
+ *deny_attribute*
+ Denied HTML attributes; see section:- #3.4
+
+ '0' - none *
+ `string` - dictated by values in `string`
+ 'on*' (like 'onfocus') attributes not allowed - "
+
+ *direct_nest_list*
+ Allow direct nesting of a list within another without requiring it to be a list item; see section:- #3.3.4
+
+ '0' - no *
+ '1' - yes
+
+ *elements*
+ Allowed HTML elements; see section:- #3.3
+
+ '* -center -dir -font -isindex -menu -s -strike -u' - ~
+ 'applet, embed, iframe, object, script' not allowed - "
+
+ *hexdec_entity*
+ Allow hexadecimal numeric entities and do not convert to the more widely accepted decimal ones, or convert decimal to hexadecimal ones; see section:- #3.2
+
+ '0' - no
+ '1' - yes *
+ '2' - convert decimal to hexadecimal ones
+
+ *hook*
+ Name of an optional hook function to alter the input string, '$config' or '$spec' before htmLawed starts its main work; see section:- #3.7
+
+ '0' - no hook function *
+ `name` - `name` is name of the hook function ('kses_hook' ^)
+
+ *hook_tag*
+ Name of an optional hook function to alter tag content finalized by htmLawed; see section:- #3.4.9
+
+ '0' - no hook function *
+ `name` - `name` is name of the hook function
+
+ *keep_bad*
+ Neutralize bad tags by converting '<' and '>' to entities, or remove them; see section:- #3.3.3
+
+ '0' - remove ^
+ '1' - neutralize both tags and element content
+ '2' - remove tags but neutralize element content
+ '3' and '4' - like '1' and '2' but remove if text ('pcdata') is invalid in parent element
+ '5' and '6' * - like '3' and '4' but line-breaks, tabs and spaces are left
+
+ *lc_std_val*
+ For XHTML compliance, predefined, standard attribute values, like 'get' for the 'method' attribute of 'form', must be lowercased; see section:- #3.4.5
+
+ '0' - no
+ '1' - yes *
+
+ *make_tag_strict*
+ Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: 'applet' 'center' 'dir' 'embed' 'font' 'isindex' 'menu' 's' 'strike' 'u'; see section:- #3.3.2
+
+ '0' - no ^
+ '1' - yes, but leave 'applet', 'embed' and 'isindex' elements that currently can't be transformed *
+ '2' - yes, removing 'applet', 'embed' and 'isindex' elements and their contents (nested elements remain) ~
+
+ *named_entity*
+ Allow non-universal named HTML entities, or convert to numeric ones; see section:- #3.2
+
+ '0' - convert
+ '1' - allow *
+
+ *no_deprecated_attr*
+ Allow deprecated attributes or transform them; see section:- #3.4.6
+
+ '0' - allow ^
+ '1' - transform, but 'name' attributes for 'a' and 'map' are retained *
+ '2' - transform
+
+ *parent*
+ Name of the parent element, possibly imagined, that will hold the input; see section:- #3.3
+
+ *safe*
+ Magic parameter to make input the most secure against XSS without needing to specify other relevant '$config' parameters; see section:- #3.6
+
+ '0' - no *
+ '1' - will auto-adjust other relevant '$config' parameters (indicated by '"' in this list)
+
+ *schemes*
+ Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or '!' to `deny` any URL); '*' covers all unspecified attributes; see section:- #3.4.3
+
+ 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https' *
+ '*: ftp, gopher, http, https, mailto, news, nntp, telnet' ^
+ 'href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style: !; *:file, http, https' "
+
+ *show_setting*
+ Name of a PHP variable to assign the `finalized` '$config' and '$spec' values; see section:- #3.8
+
+ *style_pass*
+ Do not look at 'style' attribute values, letting them through without any alteration
+
+ '0' - no *
+ '1' - htmLawed will let through any 'style' value; see section:- #3.4.8
+
+ *tidy*
+ Beautify or compact HTML code; see section:- #3.3.5
+
+ '-1' - compact
+ '0' - no *
+ '1' or 'string' - beautify (custom format specified by 'string')
+
+ *unique_ids*
+ 'id' attribute value checks; see section:- #3.4.2
+
+ '0' - no ^
+ '1' - remove duplicate and/or invalid ones *
+ `word` - remove invalid ones and replace duplicate ones with new and unique ones based on the `word`; the admin-specified `word`, like 'my_', should begin with a letter (a-z) and can contain letters, digits, '.', '_', '-', and ':'.
+
+ *valid_xhtml*
+ Magic parameter to make input the most valid XHTML without needing to specify other relevant '$config' parameters; see section:- #3.5
+
+ '0' - no *
+ '1' - will auto-adjust other relevant '$config' parameters (indicated by '~' in this list)
+
+ *xml:lang*
+ Auto-adding 'xml:lang' attribute; see section:- #3.4.1
+
+ '0' - no *
+ '1' - add if 'lang' attribute is present
+ '2' - add if 'lang' attribute is present, and remove 'lang' ~
+
+
+-- 2.3 Extra HTML specifications using the $spec parameter --------o
+
+
+ The '$spec' argument of htmLawed can be used to disallow an otherwise legal attribute for an element, or to restrict the attribute's values. This can also be helpful as a security measure (e.g., in certain versions of browsers, certain values can cause buffer overflows and denial of service attacks), or in enforcing admin policies. '$spec' is specified as a string of text containing one or more `rules`, with multiple rules separated from each other by a semi-colon (';'). E.g.,
+
+ $spec = 'i=-*; td, tr=style, id, -*; a=id(match="/[a-z][a-z\d.:\-`"]*/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt';
+ $processed = htmLawed($text, $config, $spec);
+
+ Or,
+
+ $processed = htmLawed($text, $config, 'i=-*; td, tr=style, id, -*; a=id(match="/[a-z][a-z\d.:\-`"]*/i"/minval=2), href(maxlen=100/minlen=34); img=-width,-alt');
+
+ A rule begins with an HTML *element* name(s) (`rule-element`), for which the rule applies, followed by an equal ('=') sign. A rule-element may represent multiple elements if comma (,)-separated element names are used. E.g., 'th,td,tr='.
+
+ Rest of the rule consists of comma-separated HTML *attribute names*. A minus ('-') character before an attribute means that the attribute is not permitted inside the rule-element. E.g., '-width'. To deny all attributes, '-*' can be used.
+
+ Following shows examples of rule excerpts with rule-element 'a' and the attributes that are being permitted:
+
+ * 'a=' - all
+ * 'a=id' - all
+ * 'a=href, title, -id, -onclick' - all except 'id' and 'onclick'
+ * 'a=*, id, -id' - all except 'id'
+ * 'a=-*' - none
+ * 'a=-*, href, title' - none except 'href' and 'title'
+ * 'a=-*, -id, href, title' - none except 'href' and 'title'
+
+ Rules regarding *attribute values* are optionally specified inside round brackets after attribute names in slash ('/')-separated `parameter = value` pairs. E.g., 'title(maxlen=30/minlen=5)'. None or one or more of the following parameters may be specified:
+
+ * 'oneof' - one or more choices separated by '|' that the value should match; if only one choice is provided, then the value must match that choice
+
+ * 'noneof' - one or more choices separated by '|' that the value should not match
+
+ * 'maxlen' and 'minlen' - upper and lower limits for the number of characters in the attribute value; specified in numbers
+
+ * 'maxval' and 'minval' - upper and lower limits for the numerical value specified in the attribute value; specified in numbers
+
+ * 'match' and 'nomatch' - pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers
+
+ * 'default' - a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters
+
+ If 'default' is not set and the attribute value does not satisfy any of the specified parameters, then the attribute is removed. The 'default' value can also be used to force all attribute declarations to take the same value (by getting the values declared illegal by setting, e.g., 'maxlen' to '-1').
+
+ Examples with `input` '<input title="WIDTH" value="10em" /><input title="length" value="5" />' are shown below.
+
+ `Rule`: 'input=title(maxlen=60/minlen=6), value'
+ `Output`: '<input value="10em" /><input title="length" value="5" />'
+
+ `Rule`: 'input=title(), value(maxval=8/default=6)'
+ `Output`: '<input title="WIDTH" value="6" /><input title="length" value="5" />'
+
+ `Rule`: 'input=title(nomatch=%w.d%i), value(match=%em%/default=6em)'
+ `Output`: '<input value="10em" /><input title="length" value="6em" />'
+
+ `Rule`: 'input=title(oneof=height|depth/default=depth), value(noneof=5|6)'
+ `Output`: '<input title="depth" value="10em" /><input title="depth" />'
+
+ *Special characters*: The characters ';', ',', '/', '(', ')', '|', '~' and space have special meanings in the rules. Words in the rules that use such characters, or the characters themselves, should be `escaped` by enclosing in pairs of double-quotes ('"'). A back-tick ('`') can be used to escape a literal '"'. An example rule illustrating this is 'input=value(maxlen=30/match="/^\w/"/default="your `"ID`"")'.
+
+ *Note*: To deny an attribute for all elements for which it is legal, '$config["deny_attribute"]' (see section:- #3.4) can be used instead of '$spec'. Also, attributes can be allowed element-specifically through '$spec' while being denied globally through '$config["deny_attribute"]'. The 'hook_tag' parameter (section:- #3.4.9) can also be possibly used to implement a functionality like that achieved using '$spec' functionality.
+
+ '$spec' can also be used to permit custom, non-standard attributes as well as custom rules for standard attributes. Thus, the following value of '$spec' will permit the custom uses of the standard 'rel' attribute in 'input' (not permitted as per standards) and of a non-standard attribute, 'vFlag', in 'img'.
+
+ $spec = 'img=vFlag; input=rel'
+
+ The attribute names can contain alphabets, colons (:) and hyphens (-), but they must start with an alphabet.
+
+
+-- 2.4 Performance time & memory usage ----------------------------o
+
+
+ The time and memory consumed during text processing by htmLawed depends on its configuration, the size of the input, and the amount, nestedness and well-formedness of the HTML markup within the input. In particular, tag balancing and beautification each can increase the processing time by about a quarter.
+
+ The htmLawed demo:- htmLawedTest.php can be used to evaluate the performance and effects of different types of input and '$config'.
+
+
+-- 2.5 Some security risks to keep in mind ------------------------o
+
+
+ When setting the parameters/arguments (like those to allow certain HTML elements) for use with htmLawed, one should bear in mind that the setting may let through potentially `dangerous` HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc. Unless end-users, either people or software, supplying the content are completely trusted, security issues arising from the degree of HTML usage permitted through htmLawed's setting should be considered. For example, following increase security risks:
+
+ * Allowing 'script', 'applet', 'embed', 'iframe' or 'object' elements, or certain of their attributes like 'allowscriptaccess'
+
+ * Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., '<!--[if gte IE 4]><script>alert("xss");</script><![endif]-->'
+
+ * Allowing dynamic CSS expressions (some Internet Explorer versions are vulnerable)
+
+ * Allowing the 'style' attribute
+
+ To remove `unsecure` HTML, code-developers using htmLawed must set '$config' appropriately. E.g., '$config["elements"] = "* -script"' to deny the 'script' element (section:- #3.3), '$config["safe"] = 1' to auto-configure ceratin htmLawed parameters for maximizing security (section:- #3.6), etc.
+
+ Permitting the '*style*' attribute brings in risks of `click-jacking`, `phishing`, web-page overlays, etc., `even` when the 'safe' parameter is enabled (see section:- #3.6). Except for URLs and a few other things like CSS dynamic expressions, htmLawed currently does not check every CSS style property. It does provide ways for the code-developer implementing htmLawed to do such checks through htmLawed's '$spec' argument, and through the 'hook_tag' parameter (see section:- #3.4.8 for more). Disallowing 'style' completely and relying on CSS classes and stylesheet files is recommended.
+
+ htmLawed does not check or correct the character *encoding* of the input it receives. In conjunction with permissive circumstances, such as when the character encoding is left undefined through HTTP headers or HTML 'meta' tags, this can allow for an exploit (like Google's `UTF-7/XSS` vulnerability of the past).
+
+
+-- 2.6 Use without modifying old 'kses()' code --------------------o
+
+
+ The 'Kses' PHP script is used by many applications (like 'WordPress'). It is possible to have such applications use htmLawed instead, since it is compatible with code that calls the 'kses()' function declared in the 'Kses' file (usually named 'kses.php'). E.g., application code like this will continue to work after replacing 'Kses' with htmLawed:
+
+ $comment_filtered = kses($comment_input, array('a'=>array(), 'b'=>array(), 'i'=>array()));
+
+ For some of the '$config' parameters, htmLawed will use values other than the default ones. These are indicated by '^' in section:- #2.2. To force htmLawed to use other values, function 'kses()' in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.
+
+ If the application uses a 'Kses' file that has the 'kses()' function declared, then, to have the application use htmLawed instead of 'Kses', simply rename 'htmLawed.php' (to 'kses.php', e.g.) and replace the 'Kses' file (or just replace the code in the 'Kses' file with the htmLawed code). If the 'kses()' function in the 'Kses' file had been renamed by the application developer (e.g., in 'WordPress', it is named 'wp_kses()'), then appropriately rename the 'kses()' function in the htmLawed code.
+
+ If the 'Kses' file used by the application has been highly altered by the application developers, then one may need a different approach. E.g., with 'WordPress', it is best to copy the htmLawed code to 'wp_includes/kses.php', rename the newly added function 'kses()' to 'wp_kses()', and delete the code for the original 'wp_kses()' function.
+
+ If the 'Kses' code has a non-empty hook function (e.g., 'wp_kses_hook()' in case of 'WordPress'), then the code for htmLawed's 'kses_hook()' function should be appropriately edited. However, the requirement of the hook function should be re-evaluated considering that htmLawed has extra capabilities. With 'WordPress', the hook function is an essential one. The following code is suggested for the htmLawed 'kses_hook()' in case of 'WordPress':
+
+ function kses_hook($string, &$cf, &$spec){
+ // kses compatibility
+ $allowed_html = $spec;
+ $allowed_protocols = array();
+ foreach($cf['schemes'] as $v){
+ foreach($v as $k2=>$v2){
+ if(!in_array($k2, $allowed_protocols)){
+ $allowed_protocols[] = $k2;
+ }
+ }
+ }
+ return wp_kses_hook($string, $allowed_html, $allowed_protocols);
+ // eof
+ }
+
+
+-- 2.7 Tolerance for ill-written HTML -----------------------------o
+
+
+ htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be `read` as HTML, and may therefore get identified as mere plain text. Following statements indicate the degree of `looseness` that htmLawed can work with, and can be provided in instructions to writers:
+
+ * Tags must be flanked by '<' and '>' with no '>' inside -- any needed '>' should be put in as '&gt;'. It is possible for tag content (element name and attributes) to be spread over many lines instead of being on one. A space may be present between the tag content and '>', like '<div >' and '<img / >', but not after the '<'.
+
+ * Element and attribute names need not be lower-cased.
+
+ * Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.
+
+ * Attribute values may be single- and not double-quoted.
+
+ * Left-padding of numeric entities (like, '&#0160;', '&x07ff;') with '0' is okay as long as the number of characters between between the '&' and the ';' does not exceed 8. All entities must end with ';' though.
+
+ * Named character entities must be properly cased. Thus, '&Lt;' or '&TILDE;' will not be recognized as entities and will be `neutralized`.
+
+ * HTML comments should not be inside element tags (they can be between tags), and should begin with '<!--' and end with '-->'. Characters like '<', '>', and '&' may be allowed inside depending on '$config', but any '-->' inside should be put in as '--&gt;'. Any '--' inside will be automatically converted to '-', and a space will be added before the comment delimiter '-->'.
+
+ * 'CDATA' sections should not be inside element tags, and can be in element content only if plain text is allowed for that element. They should begin with '<[CDATA[' and end with ']]>'. Characters like '<', '>', and '&' may be allowed inside depending on '$config', but any ']]>' inside should be put in as ']]&gt;'.
+
+ * For attribute values, character entities '&lt;', '&gt;' and '&amp;' should be used instead of characters '<' and '>', and '&' (when '&' is not part of a character entity). This applies even for Javascript code in values of attributes like 'onclick'.
+
+ * Characters '<', '>', '&' and '"' that are part of actual Javascript, etc., code in 'script' elements should be used as such and not be put in as entities like '&gt;'. Otherwise, though the HTML will be valid, the code may fail to work. Further, if such characters have to be used, then they should be put inside 'CDATA' sections.
+
+ * Simple instructions like "an opening tag cannot be present between two closing tags" and "nested elements should be closed in the reverse order of how they were opened" can help authors write balanced HTML. If tags are imbalanced, htmLawed will try to balance them, but in the process, depending on '$config["keep_bad"]', some code/text may be lost.
+
+ * Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.
+
+ * With '$config["unique_ids"]' not '0' and the 'id' attribute being permitted, writers should carefully avoid using duplicate or invalid 'id' values as even though htmLawed will correct/remove the values, the final output may not be the one desired. E.g., when '<a id="home"></a><input id="home" /><label for="home"></label>' is processed into
+'<a id="home"></a><input id="prefix_home" /><label for="home"></label>'.
+
+ * Even if intended HTML is lost from an ill-written input, the processed output will be more secure and standard-compliant.
+
+ * For URLs, unless '$config["scheme"]' is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., 'htt&#112;' (which many browsers will read as the harmless 'http') may be considered bad by htmLawed.
+
+ * htmLawed will attempt to put plain text present directly inside 'blockquote', 'form', 'map' and 'noscript' elements (illegal as per the specifications) inside auto-generated 'div' elements.
+
+
+-- 2.8 Limitations & work-arounds ---------------------------------o
+
+
+ htmLawed's main objective is to make the input text `more` standard-compliant, secure for readers, and free of HTML elements and attributes considered undesirable by the administrator. Some of its current limitations, regardless of this objective, are noted below along with work-arounds.
+
+ It should be borne in mind that no browser application is 100% standard-compliant, and that some of the standard specifications (like asking for normalization of white-spacing within 'textarea' elements) are clearly wrong. Regarding security, note that `unsafe` HTML code is not legally invalid per se.
+
+ * htmLawed is meant for input that goes into the 'body' of HTML documents. HTML's head-level elements are not supported, nor are the frameset elements 'frameset', 'frame' and 'noframes'. Content of the latter elements can, however, be individually filtered through htmLawed.
+
+ * It cannot transform the non-standard 'embed' elements to the standard-compliant 'object' elements. Yet, it can allow 'embed' elements if permitted ('embed' is widely used and supported). Admins can certainly use the 'hook_tag' parameter (section:- #3.4.9) to deploy a custom embed-to-object converter function.
+
+ * The only non-standard element that may be permitted is 'embed'; others like 'noembed' and 'nobr' cannot be permitted without modifying the htmLawed code.
+
+ * It cannot handle input that has non-HTML code like 'SVG' and 'MathML'. One way around is to break the input into pieces and passing only those without non-HTML code to htmLawed. Another is described in section:- #3.9. A third way may be to some how take advantage of the '$config["and_mark"]' parameter (see section:- #3.2).
+
+ * By default, htmLawed won't check many attribute values for standard compliance. E.g., 'width="20m"' with the dimension in non-standard 'm' is let through. Implementing universal and strict attribute value checks can make htmLawed slow and resource-intensive. Admins should look at the 'hook_tag' parameter (section:- #3.4.9) or '$spec' to enforce finer checks.
+
+ * The attributes, deprecated (which can be transformed too) or not, that it supports are largely those that are in the specifications. Only a few of the proprietary attributes are supported.
+
+ * Except for contained URLs and dynamic expressions (also optional), htmLawed does not check CSS style property values. Admins should look at using the 'hook_tag' parameter (section:- #3.4.9) or '$spec' for finer checks. Perhaps the best option is to disallow 'style' but allow 'class' attributes with the right 'oneof' or 'match' values for 'class', and have the various class style properties in '.css' CSS stylesheet files.
+
+ * htmLawed does not parse emoticons, decode `BBcode`, or `wikify`, auto-converting text to proper HTML. Similarly, it won't convert line-breaks to 'br' elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.
+
+ * htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate 'target' and 'onclick' attributes to 'a'). Admins should look at Javascript-based DOM-modifying solutions for this. Admins may also be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * Nesting-based checks are not possible. E.g., one cannot disallow 'p' elements specifically inside 'td' while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * Except for optionally converting absolute or relative URLs to the other type, htmLawed will not alter URLs (e.g., to change the value of query strings or to convert 'http' to 'https'. Having absolute URLs may be a standard-requirement, e.g., when HTML is embedded in email messages, whereas altering URLs for other purposes is beyond htmLawed's goals. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * Pairs of opening and closing tags that do not enclose any content (like '<em></em>') are not removed. This may be against the standard specifications for certain elements (e.g., 'table'). However, presence of such standard-incompliant code will not break the display or layout of content. Admins can also use simple regex-based code to filter out such code.
+
+ * htmLawed does not check for certain element orderings described in the standard specifications (e.g., in a 'table', 'tbody' is allowed before 'tfoot'). Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * htmLawed does not check the number of nested elements. E.g., it will allow two 'caption' elements in a 'table' element, illegal as per the specifications. Admins may be able to use a custom hook function to enforce such checks ('hook_tag' parameter; see section:- #3.4.9).
+
+ * htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers ('/*') in 'style' attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like '&#101;xpression...'. If this is too harsh, admins can allow CSS expressions through htmLawed core but then use a custom function through the 'hook_tag' parameter (section:- #3.4.9) to more specifically identify CSS expressions in the 'style' attribute values. Also, using '$config["style_pass"]', it is possible to have htmLawed pass 'style' attribute values without even looking at them (section:- #3.4.8).
+
+ * htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., '<a href="http://x%22+style=%22background-image:xss">x</a>'). These arise when browsers mis-identify markup in `escaped` text, defeating the very purpose of escaping text (a bad browser will read the given example as '<a href="http://x" style="background-image:xss">x</a>').
+
+ * Because of poor Unicode support in PHP, htmLawed does not remove the `high value` HTML-invalid characters with multi-byte code-points. Such characters however are extremely unlikely to be in the input. (see section:- #3.1).
+
+ * htmLawed does not check or correct the character encoding of the input it receives. In conjunction with permitting circumstances such as when the character encoding is left undefined through HTTP headers or HTML 'meta' tags, this can permit an exploit (like Google's `UTF-7/XSS` vulnerability of the past). Also, htmLawed can mangle input text if it is not well-formed in terms of character encoding. Administrators can consider using code available elsewhere to check well-formedness of input text characters to correct any defect.
+
+ * htmLawed is expected to work with input texts in ASCII-compatible single byte encodings such as national variants of ASCII (like ISO-646-DE/German of the ISO 646 standard), extended ASCII variants (like ISO 8859-10/Turkish of the ISO 8859/ISO Latin standard), ISO 8859-based Windows variants (like Windows 1252), EBCDIC, Shift JIS (Japanese), GB-Roman (Chinese), and KS-Roman (Korean). It should also properly handle texts with variable byte encodings like UTF-7 (Unicode) and UTF-8 (Unicode). However, htmLawed may mangle input texts with double byte encodings like UTF-16 (Unicode), JIS X 0208:1997 (Japanese) and K SX 1001:1992 (Korean), or the UTF-32 (Unicode) quadruple byte encoding. If an input text has such an encoding, administrators can use PHP's iconv:- http://php.net/manual/en/book.iconv.php functions, or some other mean, to convert text to UTF-8 before passing it to htmLawed.
+
+ * Like any script using PHP's PCRE regex functions, PHP setup-specific low PCRE limit values can cause htmLawed to at least partially fail with very long input texts.
+
+
+-- 2.9 Examples of usage -------------------------------------------o
+
+
+ Safest, allowing only `safe` HTML markup --
+
+ $config = array('safe'=>1);
+ $out = htmLawed($in);
+
+ Simplest, allowing all valid HTML markup except 'javascript:' --
+
+ $out = htmLawed($in);
+
+ Allowing all valid HTML markup including 'javascript:' --
+
+ $config = array('schemes'=>'*:*');
+ $out = htmLawed($in, $config);
+
+ Allowing only 'safe' HTML and the elements 'a', 'em', and 'strong' --
+
+ $config = array('safe'=>1, 'elements'=>'a, em, strong');
+ $out = htmLawed($in, $config);
+
+ Not allowing elements 'script' and 'object' --
+
+ $config = array('elements'=>'* -script -object');
+ $out = htmLawed($in, $config);
+
+ Not allowing attributes 'id' and 'style' --
+
+ $config = array('deny_attribute'=>'id, style');
+ $out = htmLawed($in, $config);
+
+ Permitting only attributes 'title' and 'href' --
+
+ $config = array('deny_attribute'=>'* -title -href');
+ $out = htmLawed($in, $config);
+
+ Remove bad/disallowed tags altogether instead of converting them to entities --
+
+ $config = array('keep_bad'=>0);
+ $out = htmLawed($in, $config);
+
+ Allowing attribute 'title' only in 'a' and not allowing attributes 'id', 'style', or scriptable `on*` attributes like 'onclick' --
+
+ $config = array('deny_attribute'=>'title, id, style, on*');
+ $spec = 'a=title';
+ $out = htmLawed($in, $config, $spec);
+
+ Allowing a custom attribute, 'vFlag', in 'img' and permitting custom use of the standard attribute, 'rel', in 'input' --
+
+ $spec = 'img=vFlag; input=rel';
+ $out = htmLawed($in, $config, $spec);
+
+ Some case-studies are presented below.
+
+ *1.* A blog administrator wants to allow only 'a', 'em', 'strike', 'strong' and 'u' in comments, but needs 'strike' and 'u' transformed to 'span' for better XHTML 1-strict compliance, and, he wants the 'a' links to point only to 'http' or 'https' resources:
+
+ $processed = htmLawed($in, array('elements'=>'a, em, strike, strong, u', 'make_tag_strict'=>1, 'safe'=>1, 'schemes'=>'*:http, https'), 'a=href');
+
+ *2.* An author uses a custom-made web application to load content on his web-site. He is the only one using that application and the content he generates has all types of HTML, including scripts. The web application uses htmLawed primarily as a tool to correct errors that creep in while writing HTML and to take care of the occasional `bad` characters in copy-paste text introduced by Microsoft Office. The web application provides a preview before submitted input is added to the content. For the previewing process, htmLawed is set up as follows:
+
+ $processed = htmLawed($in, array('css_expression'=>1, 'keep_bad'=>1, 'make_tag_strict'=>1, 'schemes'=>'*:*', 'valid_xhtml'=>1));
+
+ For the final submission process, 'keep_bad' is set to '6'. A value of '1' for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.
+
+ *3.* A data-miner is scraping information in a specific table of similar web-pages and is collating the data rows, and uses htmLawed to reduce unnecessary markup and white-spaces:
+
+ $processed = htmLawed($in, array('elements'=>'tr, td', 'tidy'=>-1), 'tr, td =');
+
+
+== 3 Details =====================================================oo
+
+
+-- 3.1 Invalid/dangerous characters --------------------------------
+
+
+ Valid characters (more correctly, their code-points) in HTML or XML are, hexadecimally, '9', 'a', 'd', '20' to 'd7ff', and 'e000' to '10ffff', except 'fffe' and 'ffff' (decimally, '9', '10', '13', '32' to '55295', and '57344' to '1114111', except '65534' and '65535'). htmLawed removes the invalid characters '0' to '8', 'b', 'c', and 'e' to '1f'.
+
+ Because of PHP's poor native support for multi-byte characters, htmLawed cannot check for the remaining invalid code-points. However, for various reasons, it is very unlikely for any of those characters to be in the input.
+
+ Characters that are discouraged (see section:- #5.1) but not invalid are not removed by htmLawed.
+
+ It (function 'hl_tag()') also replaces the potentially dangerous (in some Mozilla [Firefox] and Opera browsers) soft-hyphen character (code-point, hexadecimally, 'ad', or decimally, '173') in attribute values with spaces. Where required, the characters '<', '>', '&', and '"' are converted to entities.
+
+ With '$config["clean_ms_char"]' set as '1' or '2', many of the discouraged characters (decimal code-points '127' to '159' except '133') that many Microsoft applications incorrectly use (as per the 'Windows 1252' ['Cp-1252'] or a similar encoding system), and the character for decimal code-point '133', are converted to appropriate decimal numerical entities (or removed for a few cases)-- see appendix in section:- #5.4. This can help avoid some display issues arising from copying-pasting of content.
+
+ With '$config["clean_ms_char"]' set as '2', characters for the hexadecimal code-points '82', '91', and '92' (for special single-quotes), and '84', '93', and '94' (for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.
+
+ The character values are replaced with entities/characters and not character values referred to by the entities/characters to keep this task independent of the character-encoding of input text.
+
+ The '$config["clean_ms_char"]' parameter should not be used if authors do not copy-paste Microsoft-created text, or if the input text is not believed to use the 'Windows 1252' ('Cp-1252') or a similar encoding like 'Cp-1251' (otherwise, for example when UTF-8 encoding is in use, Japanese or Korean characters can get mangled). Further, the input form and the web-pages displaying it or its content should have the character encoding appropriately marked-up.
+
+
+-- 3.2 Character references/entities ------------------------------o
+
+
+ Valid character entities take the form '&*;' where '*' is '#x' followed by a hexadecimal number (hexadecimal numeric entity; like '&#xA0;' for non-breaking space), or alphanumeric like 'gt' (external or named entity; like '&nbsp;' for non-breaking space), or '#' followed by a number (decimal numeric entity; like '&#160;' for non-breaking space). Character entities referring to the soft-hyphen character (the '&shy;' or '\xad' character; hexadecimal code-point 'ad' [decimal '173']) in URL-accepting attribute values are always replaced with spaces; soft-hyphens in attribute values introduce vulnerabilities in some older versions of the Opera and Mozilla [Firefox] browsers.
+
+ htmLawed (function 'hl_ent()'):
+
+ * Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)
+
+ * Lowercases the 'X' (for XML-compliance) and 'A-F' of hexadecimal numeric entities
+
+ * Neutralizes entities referring to characters that are HTML-invalid (see section:- #3.1)
+
+ * Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, '7f' to '84', '86' to '9f', and 'fdd0' to 'fddf', or decimally, '127' to '132', '134' to '159', and '64991' to '64976'). Entities referring to the remaining discouraged characters (see section:- #5.1 for a full list) are let through.
+
+ * Neutralizes named entities that are not in the specs.
+
+ * Optionally converts valid HTML-specific named entities except '&gt;', '&lt;', '&quot;', and '&amp;' to decimal numeric ones (hexadecimal if $config["hexdec_entity"] is '2') for generic XML-compliance. For this, '$config["named_entity"]' should be '1'.
+
+ * Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, '$config["hexdec_entity"]' should be '0'.
+
+ * Optionally converts decimal numeric entities to the hexadecimal ones. For this, '$config["hexdec_entity"]' should be '2'.
+
+ `Neutralization` refers to the `entitification` of '&' to '&amp;'.
+
+ *Note*: htmLawed does not convert entities to the actual characters represented by them; one can pass the htmLawed output through PHP's 'html_entity_decode' function:- http://www.php.net/html_entity_decode for that.
+
+ *Note*: If '$config["and_mark"]' is set, and set to a value other than '0', then the '&' characters in the original input are replaced with the control character for the hexadecimal code-point '6' ('\x06'; '&' characters introduced by htmLawed, e.g., after converting '<' to '&lt;', are not affected). This allows one to distinguish, say, an '&gt;' introduced by htmLawed and an '&gt;' put in by the input writer, and can be helpful in further processing of the htmLawed-processed text (e.g., to identify the character sequence 'o(><)o' to generate an emoticon image). When this feature is active, admins should ensure that the htmLawed output is not directly used in web pages or XML documents as the presence of the '\x06' can break documents. Before use in such documents, and preferably before any storage, any remaining '\x06' should be changed back to '&', e.g., with:
+
+ $final = str_replace("\x06", '&', $prelim);
+
+ Also, see section:- #3.9.
+
+
+-- 3.3 HTML elements ----------------------------------------------o
+
+
+ htmLawed can be configured to allow only certain HTML elements (tags) in the input. Disallowed elements (just tag-content, and not element-content), based on '$config["keep_bad"]', are either `neutralized` (converted to plain text by entitification of '<' and '>') or removed.
+
+ E.g., with only 'em' permitted:
+
+ Input:
+
+ <em>My</em> website is <a href="http://a.com>a.com</a>.
+
+ Output, with '$config["keep_bad"] = 0':
+
+ <em>My</em> website is a.com.
+
+ Output, with '$config["keep_bad"]' not '0':
+
+ <em>My</em> website is &lt;a href=""&gt;a.com&lt;/a&gt;.
+
+ See section:- #3.3.3 for differences between the various non-zero '$config["keep_bad"]' values.
+
+ htmLawed by default permits these 86 elements:
+
+ a, abbr, acronym, address, applet, area, b, bdo, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, embed, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, iframe, img, input, ins, isindex, kbd, label, legend, li, map, menu, noscript, object, ol, optgroup, option, p, param, pre, q, rb, rbc, rp, rt, rtc, ruby, s, samp, script, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var
+
+ Except for 'embed' (included because of its wide-spread use) and the Ruby elements ('rb', 'rbc', 'rp', 'rt', 'rtc', 'ruby'; part of XHTML 1.1), these are all the elements in the HTML 4/XHTML 1 specs. Strict-specific specs. exclude 'center', 'dir', 'font', 'isindex', 'menu', 's', 'strike', and 'u'.
+
+ With '$config["safe"] = 1', the default set will exclude 'applet', 'embed', 'iframe', 'object' and 'script'; see section:- #3.6.
+
+ When '$config["elements"]', which specifies allowed elements, is `properly` defined, and neither empty nor set to '0' or '*', the default set is not used. To have elements added to or removed from the default set, a '+/-' notation is used. E.g., '*-script-object' implies that only 'script' and 'object' are disallowed, whereas '*+embed' means that 'noembed' is also allowed. Elements can also be specified as comma separated names. E.g., 'a, b, i' means only 'a', 'b' and 'i' are permitted. In this notation, '*', '+' and '-' have no significance and can actually cause a mis-reading.
+
+ Some more examples of '$config["elements"]' values indicating permitted elements (note that empty spaces are liberally allowed for clarity):
+
+ * 'a, blockquote, code, em, strong' -- only 'a', 'blockquote', 'code', 'em', and 'strong'
+ * '*-script' -- all excluding 'script'
+ * '* -center -dir -font -isindex -menu -s -strike -u' -- only XHTML-Strict elements
+ * '*+noembed-script' -- all including 'noembed' excluding 'script'
+
+ Some mis-usages (and the resulting permitted elements) that can be avoided:
+
+ * '-*' -- none; instead of htmLawed, one might just use, e.g., the 'htmlspecialchars()' PHP function
+ * '*, -script' -- all except 'script'; admin probably meant '*-script'
+ * '-*, a, em, strong' -- all; admin probably meant 'a, em, strong'
+ * '*' -- all; admin need not have set 'elements'
+ * '*-form+form' -- all; a '+' will always over-ride any '-'
+ * '*, noembed' -- only 'noembed'; admin probably meant '*+noembed'
+ * 'a, +b, i' -- only 'a' and 'i'; admin probably meant 'a, b, i'
+
+ Basically, when using the '+/-' notation, commas (',') should not be used, and vice versa, and '*' should be used with the former but not the latter.
+
+ *Note*: Even if an element that is not in the default set is allowed through '$config["elements"]', like 'noembed' in the last example, it will eventually be removed during tag balancing unless such balancing is turned off ('$config["balance"]' set to '0'). Currently, the only way around this, which actually is simple, is to edit the various arrays in the function 'hl_bal()' to accommodate the element and its nesting properties.
+
+ *A possibly second way to specify allowed elements* is to set '$config["parent"]' to an element name that supposedly will hold the input, and to set '$config["balance"]' to '1'. During tag balancing (see section:- #3.3.3), all elements that cannot legally nest inside the parent element will be removed. The parent element is auto-reset to 'div' if '$config["parent"]' is empty, 'body', or an element not in htmLawed's default set of 86 elements.
+
+ `Tag transformation` is possible for improving XHTML-Strict compliance -- most of the deprecated elements are removed or converted to valid XHTML-Strict ones; see section:- #3.3.2.
+
+
+.. 3.3.1 Handling of comments and CDATA sections ...................
+
+
+ 'CDATA' sections have the format '<![CDATA[...anything but not "]]>"...]]>', and HTML comments, '<!--...anything but not "-->"... -->'. Neither HTML comments nor 'CDATA' sections can reside inside tags. HTML comments can exist anywhere else, but 'CDATA' sections can exist only where plain text is allowed (e.g., immediately inside 'td' element content but not immediately inside 'tr' element content).
+
+ htmLawed (function 'hl_cmtcd()') handles HTML comments or 'CDATA' sections depending on the values of '$config["comment"]' or '$config["cdata"]'. If '0', such markup is not looked for and the text is processed like plain text. If '1', it is removed completely. If '2', it is preserved but any '<', '>' and '&' inside are changed to entities. If '3', they are left as such.
+
+ Note that for the last two cases, HTML comments and 'CDATA' sections will always be removed from tag content (function 'hl_tag()').
+
+ Examples:
+
+ Input:
+ <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
+ Output ('$config["comment"] = 0, $config["cdata"] = 2'):
+ &lt;-- home link --&gt;<a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
+ Output ('$config["comment"] = 1, $config["cdata"] = 2'):
+ <a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
+ Output ('$config["comment"] = 2, $config["cdata"] = 2'):
+ <!-- home link --><a href="home.htm"><![CDATA[x=&amp;y]]>Home</a>
+ Output ('$config["comment"] = 2, $config["cdata"] = 1'):
+ <!-- home link --><a href="home.htm">Home</a>
+ Output ('$config["comment"] = 3, $config["cdata"] = 3'):
+ <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
+
+ For standard-compliance, comments are given the form '<!--comment -->', and any '--' in the content is made '-'.
+
+ When '$config["safe"] = 1', CDATA sections and comments are considered plain text unless '$config["comment"]' or '$config["cdata"]' is explicitly specified; see section:- #3.6.
+
+
+.. 3.3.2 Tag-transformation for better XHTML-Strict ................o
+
+
+ If '$config["make_tag_strict"]' is set and not '0', following non-XHTML-Strict elements (and attributes), even if admin-permitted, are mutated as indicated (element content remains intact; function 'hl_tag2()'):
+
+ * applet - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
+ * center - 'div style="text-align: center;"'
+ * dir - 'ul'
+ * embed - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
+ * font (face, size, color) - 'span style="font-family: ; font-size: ; color: ;"' (size transformation reference:- http://style.cleverchimp.com/font_size_intervals/altintervals.html)
+ * isindex - (based on '$config["make_tag_strict"]', unchanged ('1') or removed ('2'))
+ * menu - 'ul'
+ * s - 'span style="text-decoration: line-through;"'
+ * strike - 'span style="text-decoration: line-through;"'
+ * u - 'span style="text-decoration: underline;"'
+
+ For an element with a pre-existing 'style' attribute value, the extra style properties are appended.
+
+ Example input:
+
+ <center>
+ The PHP <s>software</s> script used for this <strike>web-page</strike> web-page is <font style="font-weight: bold " face=arial size='+3' color = "red ">htmLawedTest.php</font>, from <u style= 'color:green'>PHP Labware</u>.
+ </center>
+
+ The output:
+
+ <div style="text-align: center;">
+ The PHP <span style="text-decoration: line-through;">software</span> script used for this <span style="text-decoration: line-through;">web-page</span> web-page is <span style="font-weight: bold; font-family: arial; color: red; font-size: 200%;">htmLawedTest.php</span>, from <span style="color:green; text-decoration: underline;">PHP Labware</span>.
+ </div>
+
+
+-- 3.3.3 Tag balancing and proper nesting -------------------------o
+
+
+ If '$config["balance"]' is set to '1', htmLawed (function 'hl_bal()') checks and corrects the input to have properly balanced tags and legal element content (i.e., any element nesting should be valid, and plain text may be present only in the content of elements that allow them).
+
+ Depending on the value of '$config["keep_bad"]' (see section:- #2.2 and section:- #3.3), illegal content may be removed or neutralized to plain text by converting < and > to entities:
+
+ '0' - remove; this option is available only to maintain Kses-compatibility and should not be used otherwise (see section:- #2.6)
+ '1' - neutralize tags and keep element content
+ '2' - remove tags but keep element content
+ '3' and '4' - like '1' and '2', but keep element content only if text ('pcdata') is valid in parent element as per specs
+ '5' and '6' - like '3' and '4', but line-breaks, tabs and spaces are left
+
+ Example input (disallowing the 'p' element):
+
+ <*> Pseudo-tags <*>
+ <xml>Non-HTML tag xml</xml>
+ <p>
+ Disallowed tag p
+ </p>
+ <ul>Bad<li>OK</li></ul>
+
+ The output with '$config["keep_bad"] = 1':
+
+ &lt;*&gt; Pseudo-tags &lt;*&gt;
+ &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;
+ &lt;p&gt;
+ Disallowed tag p
+ &lt;/p&gt;
+ <ul>Bad<li>OK</li></ul>
+
+ The output with '$config["keep_bad"] = 3':
+
+ &lt;*&gt; Pseudo-tags &lt;*&gt;
+ &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;
+ &lt;p&gt;
+ Disallowed tag p
+ &lt;/p&gt;
+ <ul><li>OK</li></ul>
+
+ The output with '$config["keep_bad"] = 6':
+
+ &lt;*&gt; Pseudo-tags &lt;*&gt;
+ Non-HTML tag xml
+
+ Disallowed tag p
+
+ <ul><li>OK</li></ul>
+
+ An option like '1' is useful, e.g., when a writer previews his submission, whereas one like '3' is useful before content is finalized and made available to all.
+
+ *Note:* In the example above, unlike '<*>', '<xml>' gets considered as a tag (even though there is no HTML element named 'xml'). Thus, the 'keep_bad' parameter's value affects '<xml>' but not '<*>'. In general, text matching the regular expression pattern '<(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?>' is considered a tag (phrase enclosed by the angled brackets '<' and '>', and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...), and is subjected to the 'keep_bad' value.
+
+ Nesting/content rules for each of the 86 elements in htmLawed's default set (see section:- #3.3) are defined in function 'hl_bal()'. This means that if a non-standard element besides 'embed' is being permitted through '$config["elements"]', the element's tag content will end up getting removed if '$config["balance"]' is set to '1'.
+
+ Plain text and/or certain elements nested inside 'blockquote', 'form', 'map' and 'noscript' need to be in block-level elements. This point is often missed during manual writing of HTML code. htmLawed attempts to address this during balancing. E.g., if the parent container is set as 'form', the input 'B:<input type="text" value="b" />C:<input type="text" value="c" />' is converted to '<div>B:<input type="text" value="b" />C:<input type="text" value="c" /></div>'.
+
+
+-- 3.3.4 Elements requiring child elements ------------------------o
+
+
+ As per specs, the following elements require legal child elements nested inside them:
+
+ blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul
+
+ In some cases, the specs stipulate the number and/or the ordering of the child elements. A 'table' can have 0 or 1 'caption', 'tbody', 'tfoot', and 'thead', but they must be in this order: 'caption', 'thead', 'tfoot', 'tbody'.
+
+ htmLawed currently does not check for conformance to these rules. Note that any non-compliance in this regard will not introduce security vulnerabilities, crash browser applications, or affect the rendering of web-pages.
+
+ With '$config["direct_list_nest"]' set to '1', htmLawed will allow direct nesting of an 'ol' or 'ul' list within another 'ol' or 'ul' without requiring the child list to be within an 'li' of the parent list. While this is not standard-compliant, directly nested lists are rendered properly by almost all browsers. The parameter '$config["direct_list_nest"]' has no effect if tag-balancing (section:- #3.3.3) is turned off.
+
+
+-- 3.3.5 Beautify or compact HTML ---------------------------------o
+
+
+ By default, htmLawed will neither `beautify` HTML code by formatting it with indentations, etc., nor will it make it compact by removing un-needed white-space.(It does always properly white-space tag content.)
+
+ As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside 'pre' elements) are all considered equivalent, and referred to as `white-spaces`. Browser applications are supposed to consider contiguous white-spaces as just a single space, and to disregard white-spaces trailing opening tags or preceding closing tags. This white-space `normalization` allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such `pretty` HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.
+
+ With the '$config' parameter 'tidy', htmLawed can be used to beautify or compact the input text. Input with just plain text and no HTML markup is also subject to this. Besides 'pre', the 'script' and 'textarea' elements, CDATA sections, and HTML comments are not subjected to the tidying process.
+
+ To `compact`, use '$config["tidy"] = -1'; single instances or runs of white-spaces are replaced with a single space, and white-spaces trailing and leading open and closing tags, respectively, are removed.
+
+ To `beautify`, '$config["tidy"]' is set as '1', or for customized tidying, as a string like '2s2n'. The 's' or 't' character specifies the use of spaces or tabs for indentation. The first and third characters, any of the digits 0-9, specify the number of spaces or tabs per indentation, and any parental lead spacing (extra indenting of the whole block of input text). The 'r' and 'n' characters are used to specify line-break characters: 'n' for '\n' (Unix/Mac OS X line-breaks), 'rn' or 'nr' for '\r\n' (Windows/DOS line-breaks), or 'r' for '\r'.
+
+ The '$config["tidy"]' value of '1' is equivalent to '2s0n'. Other '$config["tidy"]' values are read loosely: a value of '4' is equivalent to '4s0n'; 't2', to '1t2n'; 's', to '2s0n'; '2TR', to '2t0r'; 'T1', to '1t1n'; 'nr3', to '3s0nr', and so on. Except in the indentations and line-spacings, runs of white-spaces are replaced with a single space during beautification.
+
+ Input formatting using '$config["tidy"]' is not recommended when input text has mixed markup (like HTML + PHP).
+
+
+-- 3.4 Attributes ------------------------------------------------oo
+
+
+ htmLawed will only permit attributes described in the HTML specs (including deprecated ones). It also permits some attributes for use with the 'embed' element (the non-standard 'embed' element is supported in htmLawed because of its widespread use), and the the 'xml:space' attribute (valid only in XHTML 1.1). A list of such 111 attributes and the elements they are allowed in is in section:- #5.2. Using the '$spec' argument, htmLawed can be forced to permit custom, non-standard attributes as well as custom rules for standard attributes (section:- #2.3).
+
+ When '$config["deny_attribute"]' is not set, or set to '0', or empty ('""'), all the 111 attributes are permitted. Otherwise, '$config["deny_attribute"]' can be set as a list of comma-separated names of the denied attributes. 'on*' can be used to refer to the group of potentially dangerous, script-accepting attributes: 'onblur', 'onchange', 'onclick', 'ondblclick', 'onfocus', 'onkeydown', 'onkeypress', 'onkeyup', 'onmousedown', 'onmousemove', 'onmouseout', 'onmouseover', 'onmouseup', 'onreset', 'onselect' and 'onsubmit'.
+
+ Note that attributes specified in '$config["deny_attribute"]' are denied globally, for all elements. To deny attributes for only specific elements, '$spec' (see section:- #2.3) can be used. '$spec' can also be used to element-specifically permit an attribute otherwise denied through '$config["deny_attribute"]'.
+
+ With '$config["safe"] = 1' (section:- #3.6), the 'on*' attributes are automatically disallowed.
+
+ *Note*: To deny all but a few attributes globally, a simpler way to specify '$config["deny_attribute"]' would be to use the notation '* -attribute1 -attribute2 ...'. Thus, a value of '* -title -href' implies that except 'href' and 'title' (where allowed as per standards) all other attributes are to be removed. With this notation, the value for the parameter 'safe' (section:- #3.6) will have no effect on 'deny_attribute'.
+
+ htmLawed (function 'hl_tag()') also:
+
+ * Lower-cases attribute names
+ * Removes duplicate attributes (last one stays)
+ * Gives attributes the form 'name="value"' and single-spaces them, removing unnecessary white-spacing
+ * Provides `required` attributes (see section:- #3.4.1)
+ * Double-quotes values and escapes any '"' inside them
+ * Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point 'ad') in the values with spaces
+ * Allows custom function to additionally filter/modify attribute values (see section:- #3.4.9)
+
+
+.. 3.4.1 Auto-addition of XHTML-required attributes ................
+
+
+ If indicated attributes for the following elements are found missing, htmLawed (function 'hl_tag()') will add them (with values same as attribute names unless indicated otherwise below):
+
+ * area - alt ('area')
+ * area, img - src, alt ('image')
+ * bdo - dir ('ltr')
+ * form - action
+ * map - name
+ * optgroup - label
+ * param - name
+ * script - type ('text/javascript')
+ * textarea - rows ('10'), cols ('50')
+
+ Additionally, with '$config["xml:lang"]' set to '1' or '2', if the 'lang' but not the 'xml:lang' attribute is declared, then the latter is added too, with a value copied from that of 'lang'. This is for better standard-compliance. With '$config["xml:lang"]' set to '2', the 'lang' attribute is removed (XHTML 1.1 specs).
+
+ Note that the 'name' attribute for 'map', invalid in XHTML 1.1, is also transformed if required -- see section:- #3.4.6.
+
+
+.. 3.4.2 Duplicate/invalid 'id' values ............................o
+
+
+ If '$config["unique_ids"]' is '1', htmLawed (function 'hl_tag()') removes 'id' attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, ':', '.', '-' and '_') or duplicate. If '$config["unique_ids"]' is a word, any duplicate but otherwise valid value will be appropriately prefixed with the word to ensure its uniqueness. The word should begin with a letter and should contain only letters, numbers, ':', '.', '_' and '-'.
+
+ Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of 'id' values as it uses a global variable ('$GLOBALS["hl_Ids"]' array). Further, an admin can restrict the use of certain 'id' values by presetting this variable before htmLawed is called into use. E.g.:
+
+ $GLOBALS['hl_Ids'] = array('top'=>1, 'bottom'=>1, 'myform'=>1); // id values not allowed in input
+ $processed = htmLawed($text); // filter input
+
+
+.. 3.4.3 URL schemes (protocols) and scripts in attribute values ............o
+
+
+ htmLawed edits attributes that take URLs as values if they are found to contain un-permitted schemes. E.g., if the 'afp' scheme is not permitted, then '<a href="afp://domain.org">' becomes '<a href="denied:afp://domain.org">', and if Javascript is not permitted '<a onclick="javascript:xss();">' becomes '<a onclick="denied:javascript:xss();">'.
+
+ By default htmLawed permits these schemes in URLs for the 'href' attribute:
+
+ aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet
+
+ Also, only 'file', 'http' and 'https' are permitted in attributes whose names start with 'o' (like 'onmouseover'), and in these attributes that accept URLs:
+
+ action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap
+
+ These default sets are used when '$config["schemes"]' is not set (see section:- #2.2). To over-ride the defaults, '$config["schemes"]' is defined as a string of semi-colon-separated sub-strings of type 'attribute: comma-separated schemes'. E.g., 'href: mailto, http, https; onclick: javascript; src: http, https'. For unspecified attributes, 'file', 'http' and 'https' are permitted. This can be changed by passing schemes for '*' in '$config["schemes"]'. E.g., 'href: mailto, http, https; *: https, https'.
+
+ '*' can be put in the list of schemes to permit all protocols. E.g., 'style: *; img: http, https' results in protocols not being checked in 'style' attribute values. However, in such cases, any relative-to-absolute URL conversion, or vice versa, (section:- #3.4.4) is not done.
+
+ Thus, `to allow Javascript`, one can set '$config["schemes"]' as 'href: mailto, http, https; *: http, https, javascript', or 'href: mailto, http, https, javascript; *: http, https, javascript', or '*: *', and so on.
+
+ As a side-note, one may find 'style: *' useful as URLs in 'style' attributes can be specified in a variety of ways, and the patterns that htmLawed uses to identify URLs may mistakenly identify non-URL text.
+
+ '!' can be put in the list of schemes to disallow all protocols as well as `local` URLs. Thus, with 'href: http, style: !', '<a href="http://cnn.com" style="background-image: url('local.jpg');">CNN</a>' will become '<a href="http://cnn.com" style="background-image: url('denied:local.jpg');">CNN</a>'.
+
+ *Note*: If URL-accepting attributes other than those listed above are being allowed, then the scheme will not be checked unless the attribute name contains the string 'src' (e.g., 'dynsrc') or starts with 'o' (e.g., 'onbeforecopy').
+
+ With '$config["safe"] = 1', all URLs are disallowed in the 'style' attribute values.
+
+
+.. 3.4.4 Absolute & relative URLs in attribute values .............o
+
+
+ htmLawed can make absolute URLs in attributes like 'href' relative ('$config["abs_url"]' is '-1'), and vice versa ('$config["abs_url"]' is '1'). URLs in scripts are not considered for this, and so are URLs like '#section_6' (fragment), '?name=Tim#show' (starting with query string), and ';var=1?name=Tim#show' (starting with parameters). Further, this requires that '$config["base_url"]' be set properly, with the '://' and a trailing slash ('/'), with no query string, etc. E.g., 'file:///D:/page/', 'https://abc.com/x/y/', or 'http://localhost/demo/' are okay, but 'file:///D:/page/?help=1', 'abc.com/x/y/' and 'http://localhost/demo/index.htm' are not.
+
+ For making absolute URLs relative, only those URLs that have the '$config["base_url"]' string at the beginning are converted. E.g., with '$config["base_url"] = "https://abc.com/x/y/"', 'https://abc.com/x/y/a.gif' and 'https://abc.com/x/y/z/b.gif' become 'a.gif' and 'z/b.gif' respectively, while 'https://abc.com/x/c.gif' is not changed.
+
+ When making relative URLs absolute, only values for scheme, network location (host-name) and path values in the base URL are inherited. See section:- #5.5 for more about the URL specification as per RFC 1808:- http://www.ietf.org/rfc/rfc1808.txt.
+
+
+.. 3.4.5 Lower-cased, standard attribute values ....................o
+
+
+ Optionally, for standard-compliance, htmLawed (function 'hl_tag()') lower-cases standard attribute values to give, e.g., 'input type="password"' instead of 'input type="Password"', if '$config["lc_std_val"]' is '1'. Attribute values matching those listed below for any of the elements (plus those for the 'type' attribute of 'button' or 'input') are lower-cased:
+
+ all, baseline, bottom, button, center, char, checkbox, circle, col, colgroup, cols, data, default, file, get, groups, hidden, image, justify, left, ltr, middle, none, object, password, poly, post, preserve, radio, rect, ref, reset, right, row, rowgroup, rows, rtl, submit, text, top
+
+ a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml:space
+
+ The following `empty` (`minimized`) attributes are always assigned lower-cased values (same as the names):
+
+ checked, compact, declare, defer, disabled, ismap, multiple, nohref, noresize, noshade, nowrap, readonly, selected
+
+
+.. 3.4.6 Transformation of deprecated attributes ..................o
+
+
+ If '$config["no_deprecated_attr"]' is '0', then deprecated attributes (see appendix in section:- #5.2) are removed and, in most cases, their values are transformed to CSS style properties and added to the 'style' attributes (function 'hl_tag()'). Except for 'bordercolor' for 'table', 'tr' and 'td', the scores of proprietary attributes that were never part of any cross-browser standard are not supported.
+
+ *Note*: The attribute 'target' for 'a' is allowed even though it is not in XHTML 1.0 specs. This is because of the attribute's wide-spread use and browser-support, and because the attribute is valid in XHTML 1.1 onwards.
+
+ * align - for 'img' with value of 'left' or 'right', becomes, e.g., 'float: left'; for 'div' and 'table' with value 'center', becomes 'margin: auto'; all others become, e.g., 'text-align: right'
+
+ * bgcolor - E.g., 'bgcolor="#ffffff"' becomes 'background-color: #ffffff'
+ * border - E.g., 'height= "10"' becomes 'height: 10px'
+ * bordercolor - E.g., 'bordercolor=#999999' becomes 'border-color: #999999;'
+ * compact - 'font-size: 85%'
+ * clear - E.g., 'clear="all" becomes 'clear: both'
+
+ * height - E.g., 'height= "10"' becomes 'height: 10px' and 'height="*"' becomes 'height: auto'
+
+ * hspace - E.g., 'hspace="10"' becomes 'margin-left: 10px; margin-right: 10px'
+ * language - 'language="VBScript"' becomes 'type="text/vbscript"'
+ * name - E.g., 'name="xx"' becomes 'id="xx"'
+ * noshade - 'border-style: none; border: 0; background-color: gray; color: gray'
+ * nowrap - 'white-space: nowrap'
+ * size - E.g., 'size="10"' becomes 'height: 10px'
+ * start - removed
+ * type - E.g., 'type="i"' becomes 'list-style-type: lower-roman'
+ * value - removed
+ * vspace - E.g., 'vspace="10"' becomes 'margin-top: 10px; margin-bottom: 10px'
+ * width - like 'height'
+
+ Example input:
+
+ <img src="j.gif" alt="image" name="dad's" /><img src="k.gif" alt="image" id="dad_off" name="dad" />
+ <br clear="left" />
+ <hr noshade size="1" />
+ <img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding:5px;" />
+ <table width="50em" align="center" bgcolor="red">
+ <tr>
+ <td width="20%">
+ <div align="center">
+ <h3 align="right">Section</h3>
+ <p align="right">Para</p>
+ <ol type="a" start="e"><li value="x">First item</li></ol>
+ </div>
+ </td>
+ <td width="*">
+ <ol type="1"><li>First item</li></ol>
+ </td>
+ </tr>
+ </table>
+ <br clear="all" />
+
+ And the output with '$config["no_deprecated_attr"] = 1':
+
+ <img src="j.gif" alt="image" /><img src="k.gif" alt="image" id="dad_off" />
+ <br style="clear: left;" />
+ <hr style="border-style: none; border: 0; background-color: gray; color: gray; size: 1px;" />
+ <img src="i.gif" alt="image" width="10em" height="20" style="padding:5px; float: left; margin-left: 10px; margin-right: 10px; margin-top: 10px; margin-bottom: 10px; border: 1px;" id="img" />
+ <table width="50em" style="margin: auto; background-color: red;">
+ <tr>
+ <td style="width: 20%;">
+ <div style="margin: auto;">
+ <h3 style="text-align: right;">Section</h3>
+ <p style="text-align: right;">Para</p>
+ <ol style="list-style-type: lower-latin;"><li>First item</li></ol>
+ </div>
+ </td>
+ <td style="width: auto;">
+ <ol style="list-style-type: decimal;"><li>First item</li></ol>
+ </td>
+ </tr>
+ </table>
+ <br style="clear: both;" />
+
+ For 'lang', deprecated in XHTML 1.1, transformation is taken care of through '$config["xml:lang"]'; see section:- #3.4.1.
+
+ The attribute 'name' is deprecated in 'form', 'iframe', and 'img', and is replaced with 'id' if an 'id' attribute doesn't exist and if the 'name' value is appropriate for 'id'. For such replacements for 'a' and 'map', for which the 'name' attribute is deprecated in XHTML 1.1, '$config["no_deprecated_attr"]' should be set to '2' (when set to '1', for these two elements, the 'name' attribute is retained).
+
+
+-- 3.4.7 Anti-spam & 'href' ---------------------------------------o
+
+
+ htmLawed (function 'hl_tag()') can check the 'href' attribute values (link addresses) as an anti-spam (email or link spam) measure.
+
+ If '$config["anti_mail_spam"]' is not '0', the '@' of email addresses in 'href' values like 'mailto:a@b.com' is replaced with text specified by '$config["anti_mail_spam"]'. The text should be of a form that makes it clear to others that the address needs to be edited before a mail is sent; e.g., '<remove_this_antispam>@' (makes the example address 'a<remove_this_antispam>@b.com').
+
+ For regular links, one can choose to have a 'rel' attribute with 'nofollow' in its value (which tells some search engines to not follow a link). This can discourage link spammers. Additionally, or as an alternative, one can choose to empty the 'href' value altogether (disable the link).
+
+ For use of these options, '$config["anti_link_spam"]' should be set as an array with values 'regex1' and 'regex2', both or one of which can be empty (like 'array("", "regex2")') to indicate that that option is not to be used. Otherwise, 'regex1' or 'regex2' should be PHP- and PCRE-compatible regular expression patterns: 'href' values will be matched against them and those matching the pattern will accordingly be treated.
+
+ Note that the regular expressions should have `delimiters`, and be well-formed and preferably fast. Absolute efficiency/accuracy is often not needed.
+
+ An example, to have a 'rel' attribute with 'nofollow' for all links, and to disable links that do not point to domains 'abc.com' and 'xyz.org':
+
+ $config["anti_link_spam"] = array('`.`', '`://\W*(?!(abc\.com|xyz\.org))`');
+
+
+-- 3.4.8 Inline style properties ----------------------------------o
+
+
+ htmLawed can check URL schemes and dynamic expressions (to guard against Javascript, etc., script-based insecurities) in inline CSS style property values in the 'style' attributes. (CSS properties like 'background-image' that accept URLs in their values are noted in section:- #5.3.) Dynamic CSS expressions that allow scripting in the IE browser, and can be a vulnerability, can be removed from property values by setting '$config["css_expression"]' to '1' (default setting). Note that when '$config["css_expression"]' is set to '1', htmLawed will remove '/*' from the 'style' values.
+
+ *Note*: Because of the various ways of representing characters in attribute values (URL-escapement, entitification, etc.), htmLawed might alter the values of the 'style' attribute values, and may even falsely identify dynamic CSS expressions and URL schemes in them. If this is an important issue, checking of URLs and dynamic expressions can be turned off ('$config["schemes"] = "...style:*..."', see section:- #3.4.3, and '$config["css_expression"] = 0'). Alternately, admins can use their own custom function for finer handling of 'style' values through the 'hook_tag' parameter (see section:- #3.4.9).
+
+ It is also possible to have htmLawed let through any 'style' value by setting '$config["style_pass"]' to '1'.
+
+ As such, it is better to set up a CSS file with class declarations, disallow the 'style' attribute, set a '$spec' rule (see section:- #2.3) for 'class' for the 'oneof' or 'match' parameter, and ask writers to make use of the 'class' attribute.
+
+
+-- 3.4.9 Hook function for tag content ----------------------------o
+
+
+ It is possible to utilize a custom hook function to alter the tag content htmLawed has finalized (i.e., after it has checked/corrected for required attributes, transformed attributes, lower-cased attribute names, etc.).
+
+ When '$config' parameter 'hook_tag' is set to the name of a function, htmLawed (function 'hl_tag()') will pass on the element name, and, in the case of an opening tag, the `finalized` attribute name-value pairs as array elements to the function. The function, after completing a task such as filtering or tag transformation, will typically return an empty string, the full opening tag string like '<element_name attribute_1_name="attribute_1_value"...>' (for empty elements like 'img' and 'input', the element-closing slash '/' should also be included), etc.
+
+ Any 'hook_tag' function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as 'a' in the closing '</a>' tag of the element '<a href="http://cnn.com">CNN</a>'. Unlike for opening tags, no other value (i.e., the attribute name-value array) is passed to the function since a closing tag contains only element names. Typically, the function will return an empty string or a full closing tag (like '</a>').
+
+ This is a *powerful functionality* that can be exploited for various objectives: consolidate-and-convert inline 'style' attributes to 'class', convert 'embed' elements to 'object', permit only one 'caption' element in a 'table' element, disallow embedding of certain types of media, *inject HTML*, use CSSTidy:- http://csstidy.sourceforge.net to sanitize 'style' attribute values, etc.
+
+ As an example, the custom hook code below can be used to force a series of specifically ordered 'id' attributes on all elements, and a specific 'param' element inside all 'object' elements:
+
+ function my_tag_function($element, $attribute_array=0){
+
+ // If second argument is not received, it means a closing tag is being handled
+ if(is_numeric($attribute_array)){
+ return "</$element>";
+ }
+
+ static $id = 0;
+ // Remove any duplicate element
+ if($element == 'param' && isset($attribute_array['allowscriptaccess'])){
+ return '';
+ }
+
+ $new_element = '';
+
+ // Force a serialized ID number
+ $attribute_array['id'] = 'my_'. $id;
+ ++$id;
+
+ // Inject param for allowscriptaccess
+ if($element == 'object'){
+ $new_element = '<param id='my_'. $id; allowscriptaccess="never" />';
+ ++$id;
+ }
+
+ $string = '';
+ foreach($attribute_array as $k=>$v){
+ $string .= " {$k}=\"{$v}\"";
+ }
+
+ static $empty_elements = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1);
+
+ return "<{$element}{$string}". (isset($in_array($element, $empty_elements) ? ' /' : ''). '>'. $new_element;
+ }
+
+ The 'hook_tag' parameter is different from the 'hook' parameter (section:- #3.7).
+
+ Snippets of hook function code developed by others may be available on the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website.
+
+
+-- 3.5 Simple configuration directive for most valid XHTML -------oo
+
+
+ If '$config["valid_xhtml"]' is set to '1', some relevant '$config' parameters (indicated by '~' in section:- #2.2) are auto-adjusted. This allows one to pass the '$config' argument with a simpler value. If a value for a parameter auto-set through 'valid_xhtml' is still manually provided, then that value will over-ride the auto-set value.
+
+
+-- 3.6 Simple configuration directive for most `safe` HTML --------o
+
+
+ `Safe` HTML refers to HTML that is restricted to reduce the vulnerability for scripting attacks (such as XSS) based on HTML code which otherwise may still be legal and compliant with the HTML standard specs. When elements such as 'script' and 'object', and attributes such as 'onmouseover' and 'style' are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered 'safe' depends on the nature of the web application and the trust-level accorded to its users.
+
+ htmLawed allows an admin to use '$config["safe"]' to auto-adjust multiple '$config' parameters (such as 'elements' which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by '"' in section:- #2.2). Thus, one can pass the '$config' argument with a simpler value.
+
+ With the value of '1', htmLawed considers 'CDATA' sections and HTML comments as plain text, and prohibits the 'applet', 'embed', 'iframe', 'object' and 'script' elements, and the 'on*' attributes like 'onclick'. ( There are '$config' parameters like 'css_expression' that are not affected by the value set for 'safe' but whose default values still contribute towards a more `safe` output.) Further, URLs with schemes (see section:- #3.4.3) are neutralized so that, e.g., 'style="moz-binding:url(http://danger)"' becomes 'style="moz-binding:url(denied:http://danger)"'.
+
+ Admins, however, may still want to completely deny the 'style' attribute, e.g., with code like
+
+ $processed = htmLawed($text, array('safe'=>1, 'deny_attribute'=>'style'));
+
+ Permitting the 'style' attribute brings in risks of `click-jacking`, etc. CSS property values can render a page non-functional or be used to deface it. Except for URLs, dynamic expressions, and some other things, htmLawed does not completely check 'style' values. It does provide ways for the code-developer implementing htmLawed to do such checks through the '$spec' argument, and through the 'hook_tag' parameter (see section:- #3.4.8 for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.
+
+ If a value for a parameter auto-set through 'safe' is still manually provided, then that value can over-ride the auto-set value. E.g., with '$config["safe"] = 1' and '$config["elements"] = "*+script"', 'script', but not 'applet', is allowed.
+
+ A page illustrating the efficacy of htmLawed's anti-XSS abilities with 'safe' set to '1' against XSS vectors listed by RSnake:- http://ha.ckers.org/xss.html may be available here:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed/rsnake/RSnakeXSSTest.htm.
+
+
+-- 3.7 Using a hook function --------------------------------------o
+
+
+ If '$config["hook"]' is not set to '0', then htmLawed will allow preliminarily processed input to be altered by a hook function named by '$config["hook"]' before starting the main work (but after handling of characters, entities, HTML comments and 'CDATA' sections -- see code for function 'htmLawed()').
+
+ The hook function also allows one to alter the `finalized` values of '$config' and '$spec'.
+
+ Note that the 'hook' parameter is different from the 'hook_tag' parameter (section:- #3.4.9).
+
+ Snippets of hook function code developed by others may be available on the htmLawed:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed website.
+
+
+-- 3.8 Obtaining `finalized` parameter values ---------------------o
+
+
+ htmLawed can assign the `finalized` '$config' and '$spec' values to a variable named by '$config["show_setting"]'. The variable, made global by htmLawed, is set as an array with three keys: 'config', with the '$config' value, 'spec', with the '$spec' value, and 'time', with a value that is the Unix time (the output of PHP's 'microtime()' function) when the value was assigned. Admins should use a PHP-compliant variable name (e.g., one that does not begin with a numerical digit) that does not conflict with variable names in their non-htmLawed code.
+
+ The values, which are also post-hook function (if any), can be used to auto-generate information (on, e.g., the elements that are permitted) for input writers.
+
+
+-- 3.9 Retaining non-HTML tags in input with mixed markup ---------o
+
+
+ htmLawed does not remove certain characters that, though invalid, are nevertheless `discouraged` in HTML documents as per the specifications (see section:- #5.1). This can be utilized to deal with input that contains mixed markup. Input that may have HTML markup as well as some other markup that is based on the '<', '>' and '&' characters is considered to have mixed markup. The non-HTML markup can be rather proprietary (like markup for emoticons/smileys), or standard (like MathML or SVG). Or it can be programming code meant for execution/evaluation (such as embedded PHP code).
+
+ To deal with such mixed markup, the input text can be pre-processed to hide the non-HTML markup by specifically replacing the '<', '>' and '&' characters with some of the HTML-discouraged characters (see section:- #3.1.2). Post-htmLawed processing, the replacements are reverted.
+
+ An example (mixed HTML and PHP code in input text):
+
+ $text = preg_replace('`<\?php(.+?)\?>`sm', "\x83?php\\1?\x84", $text);
+ $processed = htmLawed($text);
+ $processed = preg_replace('`\x83\?php(.+?)\?\x84`sm', '<?php$1?>', $processed);
+
+ This code will not work if '$config["clean_ms_char"]' is set to '1' (section:- #3.1), in which case one should instead deploy a hook function (section:- #3.7). (htmLawed internally uses certain control characters, code-points '1' to '7', and use of these characters as markers in the logic of hook functions may cause issues.)
+
+ Admins may also be able to use '$config["and_mark"]' to deal with such mixed markup; see section:- #3.2.
+
+
+== 4 Other =======================================================oo
+
+
+-- 4.1 Support -----------------------------------------------------
+
+
+ A careful reading of this documentation may provide an answer.
+
+ Software updates and forum-based community-support may be found at http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed. For general PHP issues (not htmLawed-specific), support may be found through internet searches and at http://php.net.
+
+
+-- 4.2 Known issues -----------------------------------------------o
+
+
+ See section:- #2.8.
+
+
+-- 4.3 Change-log -------------------------------------------------o
+
+
+ (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the 'htmLawed.php' file, may be updated without a change-log entry if the secondary files, but not htmLawed per se, are revised.)
+
+ `Version number - Release date. Notes`
+
+ 1.1.16 - 29 August 2013. Fix for a potential security vulnerability arising from specialy encoded space characters in URL schemes/protocols
+
+ 1.1.15 - 11 August 2013. Improved tidying/prettifying functionality
+
+ 1.1.14 - 8 August 2012. Fix for possible segmental loss of incremental indentation during 'tidying' when 'balance' is disabled; fix for non-effectuation under some circumstances of a corrective behavior to preserve plain text within elements like 'blockquote'.
+
+ 1.1.13 - 22 July 2012. Added feature allowing use of custom, non-standard attributes or custom rules for standard attributes
+
+ 1.1.12 - 5 July 2012. Fix for a bug in identifying an unquoted value of the 'face' attribute
+
+ 1.1.11 - 5 June 2012. Fix for possible problem with handling of multi-byte characters in attribute values in an mbstring.func_overload enviroment. '$config["hook_tag"]', if specified, now receives names of elements in closing tags.
+
+ 1.1.10 - 22 October 2011. Fix for a bug in the 'tidy' functionality that caused the entire input to be replaced with a single space; new parameter, '$config["direct_list_nest"]' to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)
+
+ 1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of 'li' within 'dir'
+
+ 1.1.9.4 - 3 July 2010. Parameter 'schemes' now accepts '!' so any URL, even a local one, can be `denied`. An issue in which a second URL value in 'style' properties was not checked was fixed.
+
+ 1.1.9.3 - 17 May 2010. Checks for correct nesting of 'param'
+
+ 1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes
+
+ 1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for 'flashvars' attribute for 'embed'
+
+ 1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values
+
+ 1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice
+
+ 1.1.8 - 23 April 2009. Parameter 'deny_attribute' now accepts the wild-card '*', making it simpler to specify its value when all but a few attributes are being denied; fixed a bug in interpreting '$spec'
+
+ 1.1.7 - 11-12 March 2009. Attributes globally denied through 'deny_attribute' can be allowed element-specifically through '$spec'; '$config["style_pass"]' allowing letting through any 'style' value introduced; altered logic to catch certain types of dynamic crafted CSS expressions
+
+ 1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions
+
+ 1.1.2 - 22 January 2009. Fixed bug in parsing of 'font' attributes during tag transformation
+
+ 1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent
+
+ 1.1 - 29 June 2008. '$config["hook_tag"]' and '$config["tidy"]' introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug
+
+ 1.0.9 - 11 June 2008. Fix for a bug in checks for invalid HTML code-point entities
+
+ 1.0.8 - 15 May 2008. Permit 'bordercolor' attribute for 'table', 'td' and 'tr'
+
+ 1.0.7 - 1 May 2008. Support for 'wmode' attribute for 'embed'; '$config["show_setting"]' introduced; improved '$config["elements"]' evaluation
+
+ 1.0.6 - 20 April 2008. '$config["and_mark"]' introduced
+
+ 1.0.5 - 12 March 2008. 'style' URL schemes essentially disallowed when $config 'safe' is on; improved regex for CSS expression search
+
+ 1.0.4 - 10 March 2008. Improved corrections for 'blockquote', 'form', 'map' and 'noscript'
+
+ 1.0.3 - 3 March 2008. Character entities for soft-hyphens are now replaced with spaces (instead of being removed); fix for a bug allowing 'td' directly inside 'table'; '$config["safe"]' introduced
+
+ 1.0.2 - 13 February 2008. Improved implementation of '$config["keep_bad"]'
+
+ 1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions ('hl_tag()' and 'hl_prot()'); no error display with 'hl_regex()'
+
+ 1.0 - 2 November 2007. First release
+
+
+-- 4.4 Testing ----------------------------------------------------o
+
+
+ To test htmLawed using a form interface, a demo:- htmLawedTest.php web-page is provided with the htmLawed distribution ('htmLawed.php' and 'htmLawedTest.php' should be in the same directory on the web-server). A file with test-cases:- htmLawed_TESTCASE.txt is also provided.
+
+
+-- 4.5 Upgrade, & old versions ------------------------------------o
+
+
+ Upgrading is as simple as replacing the previous version of 'htmLawed.php' (assuming it was not modified for customized features). As htmLawed output is almost always used in static documents, upgrading should not affect old, finalized content.
+
+ *Important* The following upgrades may affect the functionality of a specific htmLawed installation:
+
+ (1) From version 1.1-1.1.10 to 1.1.11 (or later), if a 'hook_tag' function is in use: In version 1.1.11, elements in closing tags (and not just the opening tags) are also passed to the function. There are no attribute names/values to pass, so a 'hook_tag' function receives only the element name. The 'hook_tag' function therefore may have to be edited. See section:- #3.4.9.
+
+ Old versions of htmLawed may be available online. E.g., for version 1.0, check http://www.bioinformatics.org/phplabware/downloads/htmLawed1.zip, for 1.1.1, htmLawed111.zip, and for 1.1.10, htmLawed1110.zip.
+
+
+-- 4.6 Comparison with 'HTMLPurifier' -----------------------------o
+
+
+ The HTMLPurifier PHP library by Edward Yang is a very good HTML filtering script that uses object oriented PHP code. Compared to htmLawed, it (as of year 2010):
+
+ * does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)
+
+ * is 15-20 times bigger (scores of files totalling more than 750 kb)
+
+ * consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)
+
+ * is expectedly slower
+
+ * does not allow admins to fully allow all valid HTML (because of incomplete HTML support, it always considers elements like 'script' illegal)
+
+ * lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)
+
+ * has poor documentation
+
+ However, HTMLPurifier has finer checks for character encodings and attribute values, and can log warnings and errors. Visit the HTMLPurifier website:- http://htmlpurifier.org for updated information.
+
+
+-- 4.7 Use through application plug-ins/modules -------------------o
+
+
+ Plug-ins/modules to implement htmLawed in applications such as Drupal and DokuWiki may have been developed. Please check the application websites and the forum on the htmLawed site:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed.
+
+
+-- 4.8 Use in non-PHP applications --------------------------------o
+
+
+ Non-PHP applications written in Python, Ruby, etc., may be able to use htmLawed through system calls to the PHP engine. Such code may have been documented on the internet. Also check the forum on the htmLawed site:- http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed.
+
+
+-- 4.9 Donate -----------------------------------------------------o
+
+
+ A donation in any currency and amount to appreciate or support this software can be sent by PayPal:- http://paypal.com to this email address: drpatnaik at yahoo dot com.
+
+
+-- 4.10 Acknowledgements ------------------------------------------o
+
+
+ Nicholas Alipaz, Bryan Blakey, Pádraic Brady, Dac Chartrand, Ulf Harnhammer, Gareth Heyes, Klaus Leithoff, Lukasz Pilorz, Shelley Powers, Harro Verton, Edward Yang, and many anonymous users.
+
+ Thank you!
+
+
+== 5 Appendices ==================================================oo
+
+
+-- 5.1 Characters discouraged in XHTML -----------------------------
+
+
+ Characters represented by the following hexadecimal code-points are `not` invalid, even though some validators may issue messages stating otherwise.
+
+ '7f' to '84', '86' to '9f', 'fdd0' to 'fddf', '1fffe', '1ffff', '2fffe', '2ffff', '3fffe', '3ffff', '4fffe', '4ffff', '5fffe', '5ffff', '6fffe', '6ffff', '7fffe', '7ffff', '8fffe', '8ffff', '9fffe', '9ffff', 'afffe', 'affff', 'bfffe', 'bffff', 'cfffe', 'cffff', 'dfffe', 'dffff', 'efffe', 'effff', 'ffffe', 'fffff', '10fffe' and '10ffff'
+
+
+-- 5.2 Valid attribute-element combinations -----------------------o
+
+
+ Valid attribute-element combinations as per W3C:- http://www.w3c.org specs.
+
+ * includes deprecated attributes (marked '^'), attributes for the non-standard 'embed' element (marked '*'), and the proprietary 'bordercolor' (marked '~')
+ * only non-frameset, HTML body elements
+ * 'name' for 'a' and 'map', and 'lang' are invalid in XHTML 1.1
+ * 'target' is valid for 'a' in XHTML 1.1 and higher
+ * 'xml:space' is only for XHTML 1.1
+
+ abbr - td, th
+ accept - form, input
+ accept-charset - form
+ accesskey - a, area, button, input, label, legend, textarea
+ action - form
+ align - caption^, embed, applet, iframe, img^, input^, object^, legend^, table^, hr^, div^, h1^, h2^, h3^, h4^, h5^, h6^, p^, col, colgroup, tbody, td, tfoot, th, thead, tr
+ alt - applet, area, img, input
+ archive - applet, object
+ axis - td, th
+ bgcolor - embed, table^, tr^, td^, th^
+ border - table, img^, object^
+ bordercolor~ - table, td, tr
+ cellpadding - table
+ cellspacing - table
+ char - col, colgroup, tbody, td, tfoot, th, thead, tr
+ charoff - col, colgroup, tbody, td, tfoot, th, thead, tr
+ charset - a, script
+ checked - input
+ cite - blockquote, q, del, ins
+ classid - object
+ clear - br^
+ code - applet
+ codebase - object, applet
+ codetype - object
+ color - font
+ cols - textarea
+ colspan - td, th
+ compact - dir, dl^, menu, ol^, ul^
+ coords - area, a
+ data - object
+ datetime - del, ins
+ declare - object
+ defer - script
+ dir - bdo
+ disabled - button, input, optgroup, option, select, textarea
+ enctype - form
+ face - font
+ flashvars* - embed
+ for - label
+ frame - table
+ frameborder - iframe
+ headers - td, th
+ height - embed, iframe, td^, th^, img, object, applet
+ href - a, area
+ hreflang - a
+ hspace - applet, img^, object^
+ ismap - img, input
+ label - option, optgroup
+ language - script^
+ longdesc - img, iframe
+ marginheight - iframe
+ marginwidth - iframe
+ maxlength - input
+ method - form
+ model* - embed
+ multiple - select
+ name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param
+ nohref - area
+ noshade - hr^
+ nowrap - td^, th^
+ object - applet
+ onblur - a, area, button, input, label, select, textarea
+ onchange - input, select, textarea
+ onfocus - a, area, button, input, label, select, textarea
+ onreset - form
+ onselect - input, textarea
+ onsubmit - form
+ pluginspage* - embed
+ pluginurl* - embed
+ prompt - isindex
+ readonly - textarea, input
+ rel - a
+ rev - a
+ rows - textarea
+ rowspan - td, th
+ rules - table
+ scope - td, th
+ scrolling - iframe
+ selected - option
+ shape - area, a
+ size - hr^, font, input, select
+ span - col, colgroup
+ src - embed, script, input, iframe, img
+ standby - object
+ start - ol^
+ summary - table
+ tabindex - a, area, button, input, object, select, textarea
+ target - a^, area, form
+ type - a, embed, object, param, script, input, li^, ol^, ul^, button
+ usemap - img, input, object
+ valign - col, colgroup, tbody, td, tfoot, th, thead, tr
+ value - input, option, param, button, li^
+ valuetype - param
+ vspace - applet, img^, object^
+ width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^
+ wmode - embed
+ xml:space - pre, script, style
+
+ These are allowed in all but the shown elements:
+
+ class - param, script
+ dir - applet, bdo, br, iframe, param, script
+ id - script
+ lang - applet, br, iframe, param, script
+ onclick - applet, bdo, br, font, iframe, isindex, param, script
+ ondblclick - applet, bdo, br, font, iframe, isindex, param, script
+ onkeydown - applet, bdo, br, font, iframe, isindex, param, script
+ onkeypress - applet, bdo, br, font, iframe, isindex, param, script
+ onkeyup - applet, bdo, br, font, iframe, isindex, param, script
+ onmousedown - applet, bdo, br, font, iframe, isindex, param, script
+ onmousemove - applet, bdo, br, font, iframe, isindex, param, script
+ onmouseout - applet, bdo, br, font, iframe, isindex, param, script
+ onmouseover - applet, bdo, br, font, iframe, isindex, param, script
+ onmouseup - applet, bdo, br, font, iframe, isindex, param, script
+ style - param, script
+ title - param, script
+ xml:lang - applet, br, iframe, param, script
+
+
+-- 5.3 CSS 2.1 properties accepting URLs ------------------------o
+
+
+ background
+ background-image
+ content
+ cue-after
+ cue-before
+ cursor
+ list-style
+ list-style-image
+ play-during
+
+
+-- 5.4 Microsoft Windows 1252 character replacements --------------o
+
+
+ Key: 'd' double, 'l' left, 'q' quote, 'r' right, 's.' single
+
+ Code-point (decimal) - hexadecimal value - replacement entity - represented character
+
+ 127 - 7f - (removed) - (not used)
+ 128 - 80 - &#8364; - euro
+ 129 - 81 - (removed) - (not used)
+ 130 - 82 - &#8218; - baseline s. q
+ 131 - 83 - &#402; - florin
+ 132 - 84 - &#8222; - baseline d q
+ 133 - 85 - &#8230; - ellipsis
+ 134 - 86 - &#8224; - dagger
+ 135 - 87 - &#8225; - d dagger
+ 136 - 88 - &#710; - circumflex accent
+ 137 - 89 - &#8240; - permile
+ 138 - 8a - &#352; - S Hacek
+ 139 - 8b - &#8249; - l s. guillemet
+ 140 - 8c - &#338; - OE ligature
+ 141 - 8d - (removed) - (not used)
+ 142 - 8e - &#381; - Z dieresis
+ 143 - 8f - (removed) - (not used)
+ 144 - 90 - (removed) - (not used)
+ 145 - 91 - &#8216; - l s. q
+ 146 - 92 - &#8217; - r s. q
+ 147 - 93 - &#8220; - l d q
+ 148 - 94 - &#8221; - r d q
+ 149 - 95 - &#8226; - bullet
+ 150 - 96 - &#8211; - en dash
+ 151 - 97 - &#8212; - em dash
+ 152 - 98 - &#732; - tilde accent
+ 153 - 99 - &#8482; - trademark
+ 154 - 9a - &#353; - s Hacek
+ 155 - 9b - &#8250; - r s. guillemet
+ 156 - 9c - &#339; - oe ligature
+ 157 - 9d - (removed) - (not used)
+ 158 - 9e - &#382; - z dieresis
+ 159 - 9f - &#376; - Y dieresis
+
+
+-- 5.5 URL format -------------------------------------------------o
+
+
+ An `absolute` URL has a 'protocol' or 'scheme', a 'network location' or 'hostname', and, optional 'path', 'parameters', 'query' and 'fragment' segments. Thus, an absolute URL has this generic structure:
+
+ (scheme) : (//network location) /(path) ;(parameters) ?(query) #(fragment)
+
+ The schemes can only contain letters, digits, '+', '.' and '-'. Hostname is the portion after the '//' and up to the first '/' (if any; else, up to the end) when ':' is followed by a '//' (e.g., 'abc.com' in 'ftp://abc.com/def'); otherwise, it consists of everything after the ':' (e.g., 'def@abc.com' in mailto:def@abc.com').
+
+ `Relative` URLs do not have explicit schemes and network locations; such values are inherited from a `base` URL.
+
+
+-- 5.6 Brief on htmLawed code -------------------------------------o
+
+
+ Much of the code's logic and reasoning can be understood from the documentation above.
+
+ The *output* of htmLawed is a text string containing the processed input. There is no custom error tracking.
+
+ *Function arguments* for htmLawed are:
+
+ * '$in' - first argument; a text string; the *input text* to be processed. Any extraneous slashes added by PHP when `magic quotes` are enabled should be removed beforehand using PHP's 'stripslashes()' function.
+
+ * '$config' - second argument; an associative array; optional; named '$C' within htmLawed code. The array has keys with names like 'balance' and 'keep_bad', and the values, which can be boolean, string, or array, depending on the key, are read to accordingly set the *configurable parameters* (indicated by the keys). All configurable parameters receive some default value if the value to be used is not specified by the user through '$config'. `Finalized` '$config' is thus a filtered and possibly larger array.
+
+ * '$spec' - third argument; a text string; optional. The string has rules, written in an htmLawed-designated format, *specifying* element-specific attribute and attribute value restrictions. Function 'hl_spec()' is used to convert the string to an associative-array, named '$S' within htmLawed code, for internal use. `Finalized` '$spec' is thus an array.
+
+ `Finalized` '$config' and '$spec' are made *global variables* while htmLawed is at work. Values of any pre-existing global variables with same names are noted, and their values are restored after htmLawed finishes processing the input (to capture the `finalized` values, the 'show_settings' parameter of '$config' should be used). Depending on '$config', another global variable 'hl_Ids', to track 'id' attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.
+
+ Except for the main function 'htmLawed()' and the functions 'kses()' and 'kses_hook()', htmLawed's functions are *name-spaced* using the 'hl_' prefix. The *functions* and their roles are:
+
+ * 'hl_attrval' - checking attribute values against $spec
+ * 'hl_bal' - tag balancing
+ * 'hl_cmtcd' - handling CDATA sections and HTML comments
+ * 'hl_ent' - entity handling
+ * 'hl_prot' - checking a URL scheme/protocol
+ * 'hl_regex' - checking syntax of a regular expression
+ * 'hl_spec' - converting user-supplied $spec value to one used by htmLawed internally
+ * 'hl_tag' - handling tags
+ * 'hl_tag2' - transforming tags
+ * 'hl_tidy' - compact/beautify HTML
+ * 'hl_version' - reporting htmLawed version
+ * 'htmLawed' - main function
+ * 'kses' - main function of 'kses'
+ * 'kses_hook' - hook function of 'kses'
+
+ The last two are for compatibility with pre-existing code using the 'kses' script. htmLawed's 'kses()' basically passes on the filtering task to 'htmLawed()' function after deciphering '$config' and '$spec' from the argument values supplied to it. 'kses_hook()' is an empty function and is meant for being filled with custom code if the 'kses' script users were using one.
+
+ 'htmLawed()' finalizes '$spec' (with the help of 'hl_spec()') and '$config', and globalizes them. Finalization of '$config' involves setting default values if an inappropriate or invalid one is supplied. This includes calling 'hl_regex()' to check well-formedness of regular expression patterns if such expressions are user-supplied through '$config'. 'htmLawed()' then removes invalid characters like nulls and 'x01' and appropriately handles entities using 'hl_ent()'. HTML comments and CDATA sections are identified and treated as per '$config' with the help of 'hl_cmtcd()'. When retained, the '<' and '>' characters identifying them, and the '<', '>' and '&' characters inside them, are replaced with control characters (code-points '1' to '5') till any tag balancing is completed.
+
+ After this `initial processing` 'htmLawed()' identifies tags using regex and processes them with the help of 'hl_tag()' -- a large function that analyzes tag content, filtering it as per HTML standards, '$config' and '$spec'. Among other things, 'hl_tag()' transforms deprecated elements using 'hl_tag2()', removes attributes from closing tags, checks attribute values as per '$spec' rules using 'hl_attrval()', and checks URL protocols using 'hl_prot()'. 'htmLawed()' performs tag balancing and nesting checks with a call to 'hl_bal()', and optionally compacts/beautifies the output with proper white-spacing with a call to 'hl_tidy()'. The latter temporarily replaces white-space, and '<', '>' and '&' characters inside 'pre', 'script' and 'textarea' elements, and HTML comments and CDATA sections with control characters (code-points '1' to '5', and '7').
+
+ htmLawed permits the use of custom code or *hook functions* at two stages. The first, called inside 'htmLawed()', allows the input text as well as the finalized '$config' and '$spec' values to be altered right after the initial processing (see section:- #3.7). The second is called by 'hl_tag()' once the tag content is finalized (see section:- #3.4.9).
+
+ The functionality of htmLawed is dictated by the external HTML standard. It is thus coded for a clear-cut objective with not much concern for tweakability. The code is only minimally annotated with comments -- it is not meant to instruct; PHP developers familiar with the HTML specifications will see the logic, and others can always refer to the htmLawed documentation. The compact structuring of the statements is meant to aid a quick grasp of the logic.
+
+___________________________________________________________________oo
+
+
+@@description: htmLawed PHP software is a free, open-source, customizable HTML input purifier and filter
+@@encoding: utf-8
+@@keywords: htmLawed, HTM, HTML, HTML Tidy, converter, filter, formatter, purifier, sanitizer, XSS, input, PHP, software, code, script, security, cross-site scripting, hack, sanitize, remove, standards, tags, attributes, elements
+@@language: en
@@title: htmLawed documentation \ No newline at end of file
diff --git a/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt b/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt
index 793a5a6a7..c5cccaaba 100755
--- a/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt
+++ b/mod/htmlawed/vendors/htmLawed/htmLawed_TESTCASE.txt
@@ -1,8 +1,8 @@
/*
-htmLawed_TESTCASE.txt, 22 October 2011
-htmLawed 1.1.11, 5 June 2012
+htmLawed_TESTCASE.txt, 27 August 2013
+htmLawed 1.1.16, 29 August 2013
Copyright Santosh Patnaik
-Dual licensed with LGPL 3 and GPL 2 or later
+Dual licensed with LGPL 3 and GPL 2+
A PHP Labware internal utility - http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed
*/
@@ -27,6 +27,8 @@ character encoding to Unicode/UTF-8
<strong>Duplicated:</strong> <a id="id5" id="id6">a</a><br />
<strong>Deprecated:</strong> <a id="id7" target="self" name="n">a</a>, <hr noshade="noshade" /><br />
<strong>Casing:</strong> <a HREF=""></a><br />
+<strong>Custom:</strong> <img alt="image" my:data="portrait" /><br />
+<strong>Data-*:</strong> <a data-xml="x" data-xmnt="x" data-xmlnt="x" data-xmn:t="x" data-xmxm="x">a</a><br />
<strong>Admin-restricted?:</strong> <a href="x" onclick="alert();"></a>
<h6>Attribute values</h6>
@@ -46,6 +48,11 @@ character encoding to Unicode/UTF-8
<blockquote><div>abc</div>def</blockquote><br />
<blockquote>abc<div>def</div>ghi</blockquote><br />
abc<div>def</div>ghi<br />
+<blockquote>QQQ<div>x</div><!-- comment --></blockquote><br />
+<blockquote><div>x</div><!-- comment -->QQQ</blockquote><br />
+<blockquote><!-- comment --><div>x</div>QQQ<div>x</div></blockquote><br />
+<blockquote><div>x<!-- comment --></div>QQQ</blockquote><p>x</p><br />
+<br />
(try with blockquote parent)
<h6>CDATA sections</h6>
@@ -128,8 +135,9 @@ Disallowed tag p
<strong>Invalid:</strong> <image src="s" alt="a" /><br />
<strong>Empty:</strong> <img src="s" alt="a" />, <img src="s" alt="a"></img>, <img src="s" alt="a">text</img><br />
<strong>Content invalid:</strong> <a href="h">1<a>2</a></a><br />
-<strong>Content invalid?:</strong> <form></form><br /> (try setting 'form' as parent)
-<strong>Casing:</strong> <A href=""></a>
+<strong>Content invalid?:</strong> <form></form><br /> (try setting 'form' as parent)<br />
+<strong>Casing:</strong> <A href=""></a><br />
+<strong>Check for tidy:</strong> <br /><hr /></div><hr /></div><hr /></div><div>hi</div>
<h6>Entities</h6>
@@ -198,6 +206,13 @@ text <img src="none" alt="none" /> <b>t<em> e <strong> x </strong> t</em></b>
<strong>Malformed:</strong> <![cdata check ]]>, < ![CDATA check ]]>, < ![CDATA check ] ]><br />
Invalid:</strong> <em <!-- check -->>comment in tag content</em>, <!--check-->
+<h6>HTML5</h6>
+
+<strong>figure and figcaption:</strong> <figure><img src="picture.jpg" alt="picture"><figcaption>Caption for the awesome picture</figcaption></figure>
+<strong>article:</strong> <h1>A</h1><p>B</p><article><h2>C</h2></article><article><h2>E</h2><p>F</p><p>G</p></article>
+<strong>meter</strong>: <p>Heat <meter min="100" max="200" value="150">150</meter>.</p>
+<strong>datalist</strong>: <input list="b" /><datalist id="b"><option value="c"><option value="d"></datalist>
+
<h6>Ins-Del</h6>
(depending on context, these elements can be of either block or inline type)<br />
@@ -258,6 +273,10 @@ Invalid:</strong> <em <!-- check -->>comment in tag content</em>, <!--check-->
</form>
</li></ul>
</td></tr></table></li></ol>
+<strong>Menu</strong>: <menu type="toolbar"><li><menu label="File">
+ <button type="button" onclick="new()">New...</button>
+ </menu></li><li><menu label="Edit"><button type="button" onclick="cut()">Cut...</button></menu></li>
+ </menu>
<h6>Microdata</h6>
@@ -266,6 +285,16 @@ I am <span itemprop="name">X</span> but people call me <span itemprop="nickname"
Find me at <a href="http://www.xy.com" itemprop="url">www.xy.com</a>
</div>
+<h6>Microsoft Word</h6>
+
+<strong>Proprietary tag</strong>: <p class=3DMsoNormal><o:p>&nbsp;</o:p></p><br />
+<strong>XML declaration</strong>: <?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><br />
+<strong>XML-invalid character code-point (may not replicate)</strong>: <p class=3DMsoNormal>“Where is he?†asked both Mary – the one so lovely – and Jane.</p>
+
+<h6>Nesting</h6>
+
+<strong>Block or inline a</strong>: <p><a href="link">text</a></p><a href="link"><div>hi</div></a><br />
+
<h6>Non-English text-1</h6>
Inscrieţi-vă acum la a Zecea Conferinţă Internaţională<br />
@@ -313,6 +342,7 @@ na Alemanha.
<rp>(</rp><rt>aaa</rt><rp>)</rp>
</ruby>
+
<h6>Tables</h6>
<strong>Omitted closing tags:</strong> <table>
@@ -339,6 +369,14 @@ na Alemanha.
<tr><td>r2c1<td>r2c2
</table><br />
+<h6>Tag transformation</h6>
+<strong>Font element intended as 'inline' element:</strong> <p><font color='red'>hi</font></p><br />
+<strong>Font element intended as 'block' element:</strong> <div><font color='red'><div>hi</div></font></div><br />
+<strong>Font element intended as 'block' element:</strong> <center><font color='red'><div>hi</div><div>QQQ</div></font></center><br />
+
+<h6>Tidy</h6>
+<strong>White-space handling:</strong> abc<em> def </em> ghi abc <em>def</em> ghi
+
<h6>URLs</h6>
<strong>Relative and absolute:</strong> <a href="mailto:x"></a>, <a href="http://a.com/b/c/d.f"></a>, <a href="./../d.f"></a>, <a href="./d.f"></a>, <a href="d.f"></a>, <a href="#s"></a>, <a href="./../../d.f#s"></a><br />
@@ -364,6 +402,7 @@ src=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#1
<a style=";-moz-binding:url(http://lukasz.pilorz.net/xss/xss.xml#xss)" href="http://example.com">test</a><br />
<strong>Bad IE7:</strong> <a href="http://x&x=%22+style%3d%22background-image%3a+expression%28alert
%28%27xss%3f%29%29">x</a><br />
+<strong>Opera:</strong> <a href="\xE2\x80\x83javascript:alert(123)">link</a>
<strong>Bad IE7:</strong> <a style=color:expr/*comment*/ession(alert(document.domain))>xxx</a><br />
<strong>Bad IE7:</strong> <a href="xxx" style="background: exp&#x72;ession(alert('xss'));">xxx</a><br />
<strong>Bad IE7:</strong> <a href="xxx" style="background: &#101;xpression(alert('xss'));">xxx</a><br />
@@ -393,4 +432,19 @@ script:eval(document.all.mycode.expr)')">hi</a><br />
3 < 4 <br />
3 > 4 <br />
- > 3 <br /> \ No newline at end of file
+ > 3 <br />
+<._.> hi! <br />
+<<< ALERT >>> <br />
+<![if !vml]> some stuff <![endif]> <br />
+<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> <br />
+<uml:ns ns = "urn:www"> <br />
+<uml:ns ns = 'urn:www'> <br />
+if(13<age AND 21>age){say 'teen'} <br />
+age >51 and a smoking history of >51 pack-years <b>was</b> <br />
+age > 51 and a smoking history of >51 pack-years <b>was</b> <br />
+age <51 and a smoking history of <51 pack-years <b>was</b> <br />
+age < 51 and a smoking history of < 51 pack-years <b>was</b> <br />
+<b>age >51 and a smoking history of >51 pack-years</b> <br />
+<b>age > 51 and a smoking history of >51 pack-years</b> <br />
+<b>age <51 and a smoking history of <51 pack-years</b> <br />
+<b>age < 51 and a smoking history of < 51 pack-years</b> <br /> \ No newline at end of file
diff --git a/mod/logbrowser/views/default/forms/logbrowser/refine.php b/mod/logbrowser/views/default/forms/logbrowser/refine.php
index ebf7f10ed..3d081c9c2 100644
--- a/mod/logbrowser/views/default/forms/logbrowser/refine.php
+++ b/mod/logbrowser/views/default/forms/logbrowser/refine.php
@@ -9,12 +9,12 @@
* @uses $vars['timeupper']
*/
-if (isset($vars['timelower'])) {
+if (isset($vars['timelower']) && $vars['timelower']) {
$lowerval = date('r', $vars['timelower']);
} else {
$lowerval = "";
}
-if (isset($vars['timeupper'])) {
+if (isset($vars['timeupper']) && $vars['timeupper']) {
$upperval = date('r', $vars['timeupper']);
} else {
$upperval = "";
diff --git a/mod/logbrowser/views/default/logbrowser/refine.php b/mod/logbrowser/views/default/logbrowser/refine.php
index 86460c79e..b40f23fa3 100644
--- a/mod/logbrowser/views/default/logbrowser/refine.php
+++ b/mod/logbrowser/views/default/logbrowser/refine.php
@@ -19,7 +19,7 @@ $toggle_link = elgg_view('output/url', array(
));
$form_class = 'elgg-module elgg-module-inline';
-if (!isset($vars['user_guid'])) {
+if (!isset($vars['user_guid']) && !isset($vars['username'])) {
$form_class .= ' hidden';
}
diff --git a/mod/logbrowser/views/default/logbrowser/table.php b/mod/logbrowser/views/default/logbrowser/table.php
index 1223c1456..b08a0c428 100644
--- a/mod/logbrowser/views/default/logbrowser/table.php
+++ b/mod/logbrowser/views/default/logbrowser/table.php
@@ -35,7 +35,7 @@ $log_entries = $vars['log_entries'];
'is_trusted' => true,
));
$user_guid_link = elgg_view('output/url', array(
- 'href' => "admin/overview/logbrowser?user_guid=$user->guid",
+ 'href' => "admin/administer_utilities/logbrowser?user_guid={$user->guid}",
'text' => $user->getGUID(),
'is_trusted' => true,
));
diff --git a/mod/logrotate/languages/en.php b/mod/logrotate/languages/en.php
index 27731d732..d785ad50d 100644
--- a/mod/logrotate/languages/en.php
+++ b/mod/logrotate/languages/en.php
@@ -20,9 +20,10 @@ $english = array(
'logrotate:week' => 'week',
'logrotate:month' => 'month',
'logrotate:year' => 'year',
+ 'logrotate:never' => 'never',
'logrotate:logdeleted' => "Log deleted\n",
- 'logrotate:lognotdeleted' => "Error deleting log\n",
+ 'logrotate:lognotdeleted' => "No logs deleted\n",
);
add_translation("en", $english);
diff --git a/mod/logrotate/start.php b/mod/logrotate/start.php
index 28f14ad14..f67e419bc 100644
--- a/mod/logrotate/start.php
+++ b/mod/logrotate/start.php
@@ -21,8 +21,11 @@ function logrotate_init() {
// Register cron hook for archival of logs
elgg_register_plugin_hook_handler('cron', $period, 'logrotate_archive_cron');
- // Register cron hook for deletion of selected archived logs
- elgg_register_plugin_hook_handler('cron', $delete, 'logrotate_delete_cron');
+
+ if ($delete != 'never') {
+ // Register cron hook for deletion of selected archived logs
+ elgg_register_plugin_hook_handler('cron', $delete, 'logrotate_delete_cron');
+ }
}
/**
@@ -88,34 +91,32 @@ function logrotate_delete_cron($hook, $entity_type, $returnvalue, $params) {
/**
* This function deletes archived copies of the system logs that are older than specified.
*
- * @param int $time_of_delete An offset in seconds from now to delete (useful for log deletion)
+ * @param int $time_of_delete An offset in seconds from now to delete log tables
+ * @return bool Were any log tables deleted
*/
function log_browser_delete_log($time_of_delete) {
global $CONFIG;
- $offset = (int)$time_of_delete;
- $now = time();
-
- $ts = $now - $offset;
-
- $FLAG = 1;
- $result = mysql_query("SHOW TABLES like '{$CONFIG->dbprefix}system_log_%'");
- while ($showtablerow = mysql_fetch_array($result)) {
- //To obtain time of archival
- $log_time = explode("{$CONFIG->dbprefix}system_log_", $showtablerow[0]);
- if ($log_time < $ts) {
- //If the time of archival is before the required offset then delete
- if (!mysql_query("DROP TABLE $showtablerow[0]")) {
- $FLAG = 0;
- }
+ $cutoff = time() - (int)$time_of_delete;
+
+ $deleted_tables = false;
+ $results = get_data("SHOW TABLES like '{$CONFIG->dbprefix}system_log_%'");
+ if ($results) {
+ foreach ($results as $result) {
+ $data = (array)$result;
+ $table_name = array_shift($data);
+ // extract log table rotation time
+ $log_time = str_replace("{$CONFIG->dbprefix}system_log_", '', $table_name);
+ if ($log_time < $cutoff) {
+ if (delete_data("DROP TABLE $table_name") !== false) {
+ // delete_data returns 0 when dropping a table (false for failure)
+ $deleted_tables = true;
+ } else {
+ elgg_log("Failed to delete the log table $table_name", 'ERROR');
+ }
+ }
}
}
- //Check if the appropriate tables have been deleted and return true if yes
- if ($FLAG) {
- return true;
- } else {
- return false;
- }
-
+ return $deleted_tables;
}
diff --git a/mod/logrotate/views/default/plugins/logrotate/settings.php b/mod/logrotate/views/default/plugins/logrotate/settings.php
index bef8b308d..9fd3e08df 100644
--- a/mod/logrotate/views/default/plugins/logrotate/settings.php
+++ b/mod/logrotate/views/default/plugins/logrotate/settings.php
@@ -40,6 +40,7 @@ if (!$delete) {
'weekly' => elgg_echo('logrotate:week'),
'monthly' => elgg_echo('logrotate:month'),
'yearly' => elgg_echo('logrotate:year'),
+ 'never' => elgg_echo('logrotate:never'),
),
'value' => $delete,
));
diff --git a/mod/members/pages/members/search.php b/mod/members/pages/members/search.php
index 1f0444d67..5466a8246 100644
--- a/mod/members/pages/members/search.php
+++ b/mod/members/pages/members/search.php
@@ -7,7 +7,9 @@
if ($vars['search_type'] == 'tag') {
$tag = get_input('tag');
- $title = elgg_echo('members:title:searchtag', array($tag));
+ $display_query = _elgg_get_display_query($tag);
+
+ $title = elgg_echo('members:title:searchtag', array($display_query));
$options = array();
$options['query'] = $tag;
@@ -28,7 +30,9 @@ if ($vars['search_type'] == 'tag') {
} else {
$name = sanitize_string(get_input('name'));
- $title = elgg_echo('members:title:searchname', array($name));
+ $display_query = _elgg_get_display_query($name);
+
+ $title = elgg_echo('members:title:searchname', array($display_query));
$db_prefix = elgg_get_config('dbprefix');
$params = array(
diff --git a/mod/messageboard/pages/messageboard/owner.php b/mod/messageboard/pages/messageboard/owner.php
index 2c854d4f3..b3e9f45b0 100644
--- a/mod/messageboard/pages/messageboard/owner.php
+++ b/mod/messageboard/pages/messageboard/owner.php
@@ -16,7 +16,6 @@ elgg_push_breadcrumb($page_owner->name, $page_owner->getURL());
$options = array(
'annotations_name' => 'messageboard',
'guid' => $page_owner_guid,
- 'limit' => 10,
'reverse_order_by' => true,
);
diff --git a/mod/messages/start.php b/mod/messages/start.php
index e17640098..6d0e82744 100644
--- a/mod/messages/start.php
+++ b/mod/messages/start.php
@@ -51,6 +51,9 @@ function messages_init() {
elgg_register_plugin_hook_handler('notify:entity:message', 'object', 'messages_notification_msg');
register_notification_object('object', 'messages', elgg_echo('messages:new'));
+ // delete messages sent by a user when user is deleted
+ elgg_register_event_handler('delete', 'user', 'messages_purge');
+
// ecml
elgg_register_plugin_hook_handler('get_views', 'ecml', 'messages_ecml_views_hook');
@@ -74,23 +77,30 @@ function messages_init() {
*/
function messages_page_handler($page) {
+ $current_user = elgg_get_logged_in_user_entity();
+ if (!$current_user) {
+ register_error(elgg_echo('noaccess'));
+ $_SESSION['last_forward_from'] = current_page_url();
+ forward('');
+ }
+
elgg_load_library('elgg:messages');
- elgg_push_breadcrumb(elgg_echo('messages'), 'messages/inbox/' . elgg_get_logged_in_user_entity()->username);
+ elgg_push_breadcrumb(elgg_echo('messages'), 'messages/inbox/' . $current_user->username);
if (!isset($page[0])) {
$page[0] = 'inbox';
}
- // supporting the old inbox url /messages/<username>
- $user = get_user_by_username($page[0]);
- if ($user) {
+ // Support the old inbox url /messages/<username>, but only if it matches the logged in user.
+ // Otherwise having a username like "read" on the system could confuse this function.
+ if ($current_user->username === $page[0]) {
$page[1] = $page[0];
$page[0] = 'inbox';
}
if (!isset($page[1])) {
- $page[1] = elgg_get_logged_in_user_entity()->username;
+ $page[1] = $current_user->username;
}
$base_dir = elgg_get_plugins_path() . 'messages/pages/messages';
@@ -418,6 +428,39 @@ function messages_user_hover_menu($hook, $type, $return, $params) {
return $return;
}
+/**
+ * Delete messages from a user who is being deleted
+ *
+ * @param string $event Event name
+ * @param string $type Event type
+ * @param ElggUser $user User being deleted
+ */
+function messages_purge($event, $type, $user) {
+
+ if (!$user->getGUID()) {
+ return;
+ }
+
+ // make sure we delete them all
+ $entity_disable_override = access_get_show_hidden_status();
+ access_show_hidden_entities(true);
+ $ia = elgg_set_ignore_access(true);
+
+ $options = array(
+ 'type' => 'object',
+ 'subtype' => 'messages',
+ 'metadata_name' => 'fromId',
+ 'metadata_value' => $user->getGUID(),
+ 'limit' => 0,
+ );
+ $batch = new ElggBatch('elgg_get_entities_from_metadata', $options);
+ foreach ($batch as $e) {
+ $e->delete();
+ }
+
+ elgg_set_ignore_access($ia);
+ access_show_hidden_entities($entity_disable_override);
+}
/**
* Register messages with ECML.
diff --git a/mod/notifications/actions/groupsave.php b/mod/notifications/actions/groupsave.php
index 2dd2a6db3..e79dae5cc 100644
--- a/mod/notifications/actions/groupsave.php
+++ b/mod/notifications/actions/groupsave.php
@@ -21,8 +21,8 @@ $groups = array();
$options = array(
'relationship' => 'member',
'relationship_guid' => $user->guid,
- 'types' => 'group',
- 'limit' => 9999,
+ 'type' => 'group',
+ 'limit' => false,
);
if ($groupmemberships = elgg_get_entities_from_relationship($options)) {
foreach($groupmemberships as $groupmembership) {
diff --git a/mod/notifications/groups.php b/mod/notifications/groups.php
index 3347d4054..973f3493c 100644
--- a/mod/notifications/groups.php
+++ b/mod/notifications/groups.php
@@ -28,8 +28,8 @@ $people = array();
$groupmemberships = elgg_get_entities_from_relationship(array(
'relationship' => 'member',
'relationship_guid' => $user->guid,
- 'types' => 'group',
- 'limit' => 9999,
+ 'type' => 'group',
+ 'limit' => false,
));
$body = elgg_view_form('notificationsettings/groupsave', array(), array(
diff --git a/mod/notifications/index.php b/mod/notifications/index.php
index cd1857f04..a99622efd 100644
--- a/mod/notifications/index.php
+++ b/mod/notifications/index.php
@@ -27,8 +27,8 @@ $people = array();
if ($people_ents = elgg_get_entities_from_relationship(array(
'relationship' => 'notify',
'relationship_guid' => $user->guid,
- 'types' => 'user',
- 'limit' => 99999,
+ 'type' => 'user',
+ 'limit' => false,
))) {
foreach($people_ents as $ent) {
diff --git a/mod/notifications/views/default/forms/notificationsettings/groupsave.php b/mod/notifications/views/default/forms/notificationsettings/groupsave.php
index 168639ab2..f3e5f693a 100644
--- a/mod/notifications/views/default/forms/notificationsettings/groupsave.php
+++ b/mod/notifications/views/default/forms/notificationsettings/groupsave.php
@@ -15,8 +15,8 @@ foreach ($NOTIFICATION_HANDLERS as $method => $foo) {
$subsbig[$method] = elgg_get_entities_from_relationship(array(
'relationship' => 'notify' . $method,
'relationship_guid' => $user->guid,
- 'types' => 'group',
- 'limit' => 99999,
+ 'type' => 'group',
+ 'limit' => false,
));
$tmparray = array();
if ($subsbig[$method]) {
diff --git a/mod/notifications/views/default/notifications/subscriptions/forminternals.php b/mod/notifications/views/default/notifications/subscriptions/forminternals.php
index 11f266303..79a7959ac 100644
--- a/mod/notifications/views/default/notifications/subscriptions/forminternals.php
+++ b/mod/notifications/views/default/notifications/subscriptions/forminternals.php
@@ -31,8 +31,8 @@ foreach($NOTIFICATION_HANDLERS as $method => $foo) {
$subsbig[$method] = elgg_get_entities_from_relationship(array(
'relationship' => 'notify' . $method,
'relationship_guid' => $user->guid,
- 'types' => 'user',
- 'limit' => 99999,
+ 'type' => 'user',
+ 'limit' => false,
));
}
diff --git a/mod/oauth_api/manifest.xml b/mod/oauth_api/manifest.xml
deleted file mode 100644
index 991be6a22..000000000
--- a/mod/oauth_api/manifest.xml
+++ /dev/null
@@ -1,25 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
- <name>OAuth API</name>
- <author>Core developers</author>
- <version>1.8</version>
- <description>Provides OAuth libraries and API support.</description>
- <category>bundled</category>
- <category>api</category>
- <website>http://www.elgg.org/</website>
- <copyright>See COPYRIGHT.txt</copyright>
- <license>GNU General Public License version 2</license>
- <requires>
- <type>elgg_release</type>
- <version>1.8</version>
- </requires>
-
- <conflicts>
- <type>plugin</type>
- <name>oauth_lib</name>
- </conflicts>
- <conflicts>
- <type>php_extension</type>
- <name>oauth</name>
- </conflicts>
-</plugin_manifest>
diff --git a/mod/oauth_api/start.php b/mod/oauth_api/start.php
deleted file mode 100644
index d087a13d1..000000000
--- a/mod/oauth_api/start.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-/**
- * OAuth libs
- *
- * @todo Pull these out into an elgg_oauth lib and use elgg_register_library().
- * @package oauth_api
- */
-
-// require all vendor libraries
-$plugin_path = dirname(__FILE__) . '/vendors/oauth/library';
-require_once "$plugin_path/OAuthDiscovery.php";
-require_once "$plugin_path/OAuthRequest.php";
-require_once "$plugin_path/OAuthRequester.php";
-require_once "$plugin_path/OAuthRequestVerifier.php";
-require_once "$plugin_path/OAuthServer.php";
-
-require_once "$plugin_path/body/OAuthBodyMultipartFormdata.php";
-
-require_once "$plugin_path/store/OAuthStoreAbstract.class.php";
-
-require_once "$plugin_path/signature_method/OAuthSignatureMethod_HMAC_SHA1.php";
-require_once "$plugin_path/signature_method/OAuthSignatureMethod_MD5.php";
-require_once "$plugin_path/signature_method/OAuthSignatureMethod_PLAINTEXT.php";
-require_once "$plugin_path/signature_method/OAuthSignatureMethod_RSA_SHA1.php";
diff --git a/mod/oauth_api/vendors/oauth/LICENSE b/mod/oauth_api/vendors/oauth/LICENSE
deleted file mode 100644
index f64bcd50f..000000000
--- a/mod/oauth_api/vendors/oauth/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License
-
-Copyright (c) 2007-2008 Mediamatic Lab
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE. \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/INSTALL b/mod/oauth_api/vendors/oauth/example/server/INSTALL
deleted file mode 100644
index 249c85e9d..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/INSTALL
+++ /dev/null
@@ -1,53 +0,0 @@
-In this example I assume that oauth-php lives in /home/john/src/oauth-php
-
-
-1) Create a virtual host and set the DB_DSN VARIABLE to the DSN of your (mysql) database.
-
-Example
-<VirtualHost *>
- ServerAdmin admin@localhost
- ServerName hello.local
- DocumentRoot /home/john/src/oauth-php/example/server/www
-
- UseCanonicalName Off
- ServerSignature On
-
- SetEnv DB_DSN mysql://foo:bar@localhost/oauth_example_server_db
-
- <Directory "home/john/src/oauth-php/example/server/www">
- Options Indexes FollowSymLinks MultiViews
- AllowOverride None
- Order allow,deny
- Allow from all
-
- <IfModule mod_php5.c>
- php_value magic_quotes_gpc 0
- php_value register_globals 0
- php_value session.auto_start 0
- </IfModule>
-
- </Directory>
-</VirtualHost>
-
-
-2) Create the database structure for the server:
-
-# mysql -u foo -p bar -h localhost < /home/john/src/oauth-php/library/store/mysql/mysql.sql
-
-
-
-3) Download and install smarty into the smarty/core/smarty directory:
-
-# cd /home/john/src/oauth-php/example/server/core
-# wget 'http://www.smarty.net/do_download.php?download_file=Smarty-2.6.19.tar.gz'
-# tar zxf Smarty-2.6.19.tar.gz
-# mv Smarty-2.6.19 smarty
-
-
-4) That's it! Point your browser to
-
- http://hello.local/
-
-To get started.
-
-Arjan Scherpenisse <arjan@mediamatic.nl>, July 2008
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/init.php b/mod/oauth_api/vendors/oauth/example/server/core/init.php
deleted file mode 100644
index e5bb9de35..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/init.php
+++ /dev/null
@@ -1,127 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * Global initialization file for the server, defines some helper
- * functions, required includes, and starts the session.
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-/*
- * Simple 'user management'
- */
-define ('USERNAME', 'sysadmin');
-define ('PASSWORD', 'sysadmin');
-
-
-/*
- * Always announce XRDS OAuth discovery
- */
-header('X-XRDS-Location: http://' . $_SERVER['SERVER_NAME'] . '/services.xrds');
-
-
-/*
- * Initialize the database connection
- */
-$info = parse_url(getenv('DB_DSN'));
-($GLOBALS['db_conn'] = mysql_connect($info['host'], $info['user'], $info['pass'])) || die(mysql_error());
-mysql_select_db(basename($info['path']), $GLOBALS['db_conn']) || die(mysql_error());
-unset($info);
-
-
-require_once '../../../library/OAuthServer.php';
-
-/*
- * Initialize OAuth store
- */
-require_once '../../../library/OAuthStore.php';
-OAuthStore::instance('MySQL', array('conn' => $GLOBALS['db_conn']));
-
-
-/*
- * Session
- */
-session_start();
-
-
-/*
- * Template handling
- */
-require_once 'smarty/libs/Smarty.class.php';
-function session_smarty()
-{
- if (!isset($GLOBALS['smarty']))
- {
- $GLOBALS['smarty'] = new Smarty;
- $GLOBALS['smarty']->template_dir = dirname(__FILE__) . '/templates/';
- $GLOBALS['smarty']->compile_dir = dirname(__FILE__) . '/../cache/templates_c';
- }
-
- return $GLOBALS['smarty'];
-}
-
-function assert_logged_in()
-{
- if (empty($_SESSION['authorized']))
- {
- $uri = $_SERVER['REQUEST_URI'];
- header('Location: /logon?goto=' . urlencode($uri));
- }
-}
-
-function assert_request_vars()
-{
- foreach(func_get_args() as $a)
- {
- if (!isset($_REQUEST[$a]))
- {
- header('HTTP/1.1 400 Bad Request');
- echo 'Bad request.';
- exit;
- }
- }
-}
-
-function assert_request_vars_all()
-{
- foreach($_REQUEST as $row)
- {
- foreach(func_get_args() as $a)
- {
- if (!isset($row[$a]))
- {
- header('HTTP/1.1 400 Bad Request');
- echo 'Bad request.';
- exit;
- }
- }
- }
-}
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl
deleted file mode 100644
index 308b1d01b..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/footer.tpl
+++ /dev/null
@@ -1,2 +0,0 @@
-</body>
-</html>
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl
deleted file mode 100644
index 5046f54b0..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/inc/header.tpl
+++ /dev/null
@@ -1,2 +0,0 @@
-<html>
- <body>
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl
deleted file mode 100644
index 7b065537d..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/index.tpl
+++ /dev/null
@@ -1,13 +0,0 @@
-{include file='inc/header.tpl'}
-
-<h1>OAuth server</h1>
-Go to:
-
-<ul>
- <li><a href="/logon">Logon</a></li>
- <li><a href="/register">Register your consumer</a></li>
-</ul>
-
-Afterwards, make an OAuth test request to <strong>http://{$smarty.server.name}/hello</strong> to test your connection.</p>
-
-{include file='inc/footer.tpl'}
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl
deleted file mode 100644
index 5ccd432b5..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/logon.tpl
+++ /dev/null
@@ -1,21 +0,0 @@
-{include file='inc/header.tpl'}
-
-<h1>Login</h1>
-
-<form method="post">
- <input type="hidden" name="goto" value="{$smarty.request.goto}" />
-
- <label for="username">User name</label><br />
- <input type="text" name="username" id="username" />
-
- <br /><br />
-
- <label for="password">Password</label><br />
- <input type="text" name="password" id="password" />
-
- <br /><br />
-
- <input type="submit" value="Login" />
-</form>
-
-{include file='inc/footer.tpl'}
diff --git a/mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl b/mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl
deleted file mode 100644
index 0e28c1584..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/core/templates/register.tpl
+++ /dev/null
@@ -1,41 +0,0 @@
-{include file='inc/header.tpl'}
-
-<h1>Register server</h1>
-
-<p>Register a server which is gonna act as an identity client.</p>
-
-<form method="post">
-
- <fieldset>
- <legend>About You</legend>
-
- <p>
- <label for="requester_name">Your name</label><br/>
- <input class="text" id="requester_name" name="requester_name" type="text" value="{$consumer.requester_name|default:$smarty.request.requester_name|escape}" />
- </p>
-
- <p>
- <label for="requester_email">Your email address</label><br/>
- <input class="text" id="requester_email" name="requester_email" type="text" value="{$consumer.requester_email|default:$smarty.request.requester_email|escape}" />
- </p>
- </fieldset>
-
- <fieldset>
- <legend>Location Of Your Application Or Site</legend>
-
- <p>
- <label for="application_uri">URL of your application or site</label><br/>
- <input id="application_uri" class="text" name="application_uri" type="text" value="{$consumer.application_uri|default:$smarty.request.application_uri|escape}" />
- </p>
-
- <p>
- <label for="callback_uri">Callback URL</label><br/>
- <input id="callback_uri" class="text" name="callback_uri" type="text" value="{$consumer.callback_uri|default:$smarty.request.callback_uri|escape}" />
- </p>
- </fieldset>
-
- <br />
- <input type="submit" value="Register server" />
-</form>
-
-{include file='inc/footer.tpl'}
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/hello.php b/mod/oauth_api/vendors/oauth/example/server/www/hello.php
deleted file mode 100644
index 8cb94bb1e..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/hello.php
+++ /dev/null
@@ -1,65 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * An example service, http://hostname/hello. You will only get the
- * 'Hello, world!' string back if you have signed your request with
- * oauth.
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once '../core/init.php';
-
-$authorized = false;
-$server = new OAuthServer();
-try
-{
- if ($server->verifyIfSigned())
- {
- $authorized = true;
- }
-}
-catch (OAuthException $e)
-{
-}
-
-if (!$authorized)
-{
- header('HTTP/1.1 401 Unauthorized');
- header('Content-Type: text/plain');
-
- echo "OAuth Verification Failed: " . $e->getMessage();
- die;
-}
-
-// From here on we are authenticated with OAuth.
-
-header('Content-type: text/plain');
-echo 'Hello, world!';
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/index.php b/mod/oauth_api/vendors/oauth/example/server/www/index.php
deleted file mode 100644
index f5cadbe61..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/index.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require '../core/init.php';
-
-$smarty = session_smarty();
-$smarty->display('index.tpl');
-
-?>
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/logon.php b/mod/oauth_api/vendors/oauth/example/server/www/logon.php
deleted file mode 100644
index 5c937b719..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/logon.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * Simple logon for consumer registration at this server.
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once '../core/init.php';
-
-if (isset($_POST['username']) && isset($_POST['password']))
-{
- if ($_POST['username'] == USERNAME && $_POST['password'] == PASSWORD)
- {
- $_SESSION['authorized'] = true;
- if (!empty($_REQUEST['goto']))
- {
- header('Location: ' . $_REQUEST['goto']);
- die;
- }
-
- echo "Logon succesfull.";
- die;
- }
-}
-
-$smarty = session_smarty();
-$smarty->display('logon.tpl');
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/oauth.php b/mod/oauth_api/vendors/oauth/example/server/www/oauth.php
deleted file mode 100644
index e0badcc39..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/oauth.php
+++ /dev/null
@@ -1,77 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * This file implements the OAuth server endpoints. The most basic
- * implementation of an OAuth server.
- *
- * Call with: /oauth/request_token, /oauth/authorize, /oauth/access_token
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once '../core/init.php';
-
-$server = new OAuthServer();
-
-switch($_SERVER['PATH_INFO'])
-{
-case '/request_token':
- $server->requestToken();
- exit;
-
-case '/access_token':
- $server->accessToken();
- exit;
-
-case '/authorize':
- # logon
-
- assert_logged_in();
-
- try
- {
- $server->authorizeVerify();
- $server->authorizeFinish(true, 1);
- }
- catch (OAuthException $e)
- {
- header('HTTP/1.1 400 Bad Request');
- header('Content-Type: text/plain');
-
- echo "Failed OAuth Request: " . $e->getMessage();
- }
- exit;
-
-
-default:
- header('HTTP/1.1 500 Internal Server Error');
- header('Content-Type: text/plain');
- echo "Unknown request";
-}
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/register.php b/mod/oauth_api/vendors/oauth/example/server/www/register.php
deleted file mode 100644
index c5785c2c8..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/register.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-require_once '../core/init.php';
-
-assert_logged_in();
-
-if ($_SERVER['REQUEST_METHOD'] == 'POST')
-{
- try
- {
- $store = OAuthStore::instance();
- $key = $store->updateConsumer($_POST, 1, true);
-
- $c = $store->getConsumer($key);
- echo 'Your consumer key is: <strong>' . $c['consumer_key'] . '</strong><br />';
- echo 'Your consumer secret is: <strong>' . $c['consumer_secret'] . '</strong><br />';
- }
- catch (OAuthException $e)
- {
- echo '<strong>Error: ' . $e->getMessage() . '</strong><br />';
- }
-}
-
-
-$smarty = session_smarty();
-$smarty->display('register.tpl');
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php b/mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php
deleted file mode 100644
index 4c50aa12b..000000000
--- a/mod/oauth_api/vendors/oauth/example/server/www/services.xrds.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * oauth-php: Example OAuth server
- *
- * XRDS discovery for OAuth. This file helps the consumer program to
- * discover where the OAuth endpoints for this server are.
- *
- * @author Arjan Scherpenisse <arjan@scherpenisse.net>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-header('Content-Type: application/xrds+xml');
-
-$server = $_SERVER['SERVER_NAME'];
-
-echo '<?xml version="1.0" encoding="utf-8"?>' . "\n";
-
-?>
-<XRDS xmlns="xri://$xrds">
- <XRD xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" xmlns:openid="http://openid.net/xmlns/1.0" version="2.0" xml:id="main">
- <Type>xri://$xrds*simple</Type>
- <Service>
- <Type>http://oauth.net/discovery/1.0</Type>
- <URI>#main</URI>
- </Service>
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/request</Type>
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
- <URI>http://<?=$server?>/oauth/request_token</URI>
- </Service>
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <URI>http://<?=$server?>/oauth/authorize</URI>
- </Service>
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/access</Type>
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
- <URI>http://<?=$server?>/oauth/access_token</URI>
- </Service>
- </XRD>
-</XRDS>
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php b/mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php
deleted file mode 100644
index d097756dd..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthDiscovery.php
+++ /dev/null
@@ -1,226 +0,0 @@
-<?php
-
-/**
- * Handle the discovery of OAuth service provider endpoints and static consumer identity.
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 4, 2008 5:05:19 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once dirname(__FILE__).'/discovery/xrds_parse.php';
-
-require_once dirname(__FILE__).'/OAuthException.php';
-require_once dirname(__FILE__).'/OAuthRequestLogger.php';
-
-
-class OAuthDiscovery
-{
- /**
- * Return a description how we can do a consumer allocation. Prefers static allocation if
- * possible. If static allocation is possible
- *
- * See also: http://oauth.net/discovery/#consumer_identity_types
- *
- * @param string uri
- * @return array provider description
- */
- static function discover ( $uri )
- {
- // See what kind of consumer allocations are available
- $xrds_file = self::discoverXRDS($uri);
- if (!empty($xrds_file))
- {
- $xrds = xrds_parse($xrds_file);
- if (empty($xrds))
- {
- throw new OAuthException('Could not discover OAuth information for '.$uri);
- }
- }
- else
- {
- throw new OAuthException('Could not discover XRDS file at '.$uri);
- }
-
- // Fill an OAuthServer record for the uri found
- $ps = parse_url($uri);
- $host = isset($ps['host']) ? $ps['host'] : 'localhost';
- $server_uri = $ps['scheme'].'://'.$host.'/';
-
- $p = array(
- 'user_id' => null,
- 'consumer_key' => '',
- 'consumer_secret' => '',
- 'signature_methods' => '',
- 'server_uri' => $server_uri,
- 'request_token_uri' => '',
- 'authorize_uri' => '',
- 'access_token_uri' => ''
- );
-
-
- // Consumer identity (out of bounds or static)
- if (isset($xrds['consumer_identity']))
- {
- // Try to find a static consumer allocation, we like those :)
- foreach ($xrds['consumer_identity'] as $ci)
- {
- if ($ci['method'] == 'static' && !empty($ci['consumer_key']))
- {
- $p['consumer_key'] = $ci['consumer_key'];
- $p['consumer_secret'] = '';
- }
- else if ($ci['method'] == 'oob' && !empty($ci['uri']))
- {
- // TODO: Keep this uri somewhere for the user?
- $p['consumer_oob_uri'] = $ci['uri'];
- }
- }
- }
-
- // The token uris
- if (isset($xrds['request'][0]['uri']))
- {
- $p['request_token_uri'] = $xrds['request'][0]['uri'];
- if (!empty($xrds['request'][0]['signature_method']))
- {
- $p['signature_methods'] = $xrds['request'][0]['signature_method'];
- }
- }
- if (isset($xrds['authorize'][0]['uri']))
- {
- $p['authorize_uri'] = $xrds['authorize'][0]['uri'];
- if (!empty($xrds['authorize'][0]['signature_method']))
- {
- $p['signature_methods'] = $xrds['authorize'][0]['signature_method'];
- }
- }
- if (isset($xrds['access'][0]['uri']))
- {
- $p['access_token_uri'] = $xrds['access'][0]['uri'];
- if (!empty($xrds['access'][0]['signature_method']))
- {
- $p['signature_methods'] = $xrds['access'][0]['signature_method'];
- }
- }
- return $p;
- }
-
-
- /**
- * Discover the XRDS file at the uri. This is a bit primitive, you should overrule
- * this function so that the XRDS file can be cached for later referral.
- *
- * @param string uri
- * @return string false when no XRDS file found
- */
- static protected function discoverXRDS ( $uri, $recur = 0 )
- {
- // Bail out when we are following redirects
- if ($recur > 10)
- {
- return false;
- }
-
- $data = self::curl($uri);
-
- // Check what we got back, could be:
- // 1. The XRDS discovery file itself (check content-type)
- // 2. The X-XRDS-Location header
-
- if (is_string($data) && !empty($data))
- {
- list($head,$body) = explode("\r\n\r\n", $data);
- $body = trim($body);
- $m = false;
-
- // See if we got the XRDS file itself or we have to follow a location header
- if ( preg_match('/^Content-Type:\s*application\/xrds+xml/im', $head)
- || preg_match('/^<\?xml[^>]*\?>\s*<xrds\s/i', $body)
- || preg_match('/^<xrds\s/i', $body)
- )
- {
- $xrds = $body;
- }
- else if ( preg_match('/^X-XRDS-Location:\s*(.*)$/im', $head, $m)
- || preg_match('/^Location:\s*(.*)$/im', $head, $m))
- {
- // Recurse to the given location
- if ($uri != $m[1])
- {
- $xrds = self::discoverXRDS($m[1], $recur+1);
- }
- else
- {
- // Referring to the same uri, bail out
- $xrds = false;
- }
- }
- else
- {
- // Not an XRDS file an nowhere else to check
- $xrds = false;
- }
- }
- else
- {
- $xrds = false;
- }
- return $xrds;
- }
-
-
- /**
- * Try to fetch an XRDS file at the given location. Sends an accept header preferring the xrds file.
- *
- * @param string uri
- * @return array (head,body), false on an error
- */
- static protected function curl ( $uri )
- {
- $ch = curl_init();
-
- curl_setopt($ch, CURLOPT_HTTPHEADER, array('Accept: application/xrds+xml, */*;q=0.1'));
- curl_setopt($ch, CURLOPT_USERAGENT, 'anyMeta/OAuth 1.0 - (OAuth Discovery $LastChangedRevision: 45 $)');
- curl_setopt($ch, CURLOPT_URL, $uri);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HEADER, true);
-
- $txt = curl_exec($ch);
- curl_close($ch);
-
- // Tell the logger what we requested and what we received back
- $data = "GET $uri";
- OAuthRequestLogger::setSent($data, "");
- OAuthRequestLogger::setReceived($txt);
-
- return $txt;
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthException.php b/mod/oauth_api/vendors/oauth/library/OAuthException.php
deleted file mode 100644
index cadd1d032..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthException.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-/**
- * Simple exception wrapper for OAuth
- *
- * @version $Id: OAuthException.php 49 2008-10-01 09:43:19Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 29, 2007 5:33:54 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-// TODO: something with the HTTP return code matching to the problem
-
-require_once dirname(__FILE__) . '/OAuthRequestLogger.php';
-
-class OAuthException extends Exception
-{
- function __construct ( $message )
- {
- Exception::__construct($message);
- OAuthRequestLogger::addNote('OAuthException: '.$message);
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequest.php b/mod/oauth_api/vendors/oauth/library/OAuthRequest.php
deleted file mode 100644
index c0d6ddbc7..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequest.php
+++ /dev/null
@@ -1,801 +0,0 @@
-<?php
-
-/**
- * Request wrapper class. Prepares a request for consumption by the OAuth routines
- *
- * @version $Id: OAuthRequest.php 50 2008-10-01 15:11:08Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 12:20:31 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-require_once dirname(__FILE__) . '/OAuthException.php';
-
-/**
- * Object to parse an incoming OAuth request or prepare an outgoing OAuth request
- */
-class OAuthRequest
-{
- /* the realm for this request */
- protected $realm;
-
- /* all the parameters, RFC3986 encoded name/value pairs */
- protected $param = array();
-
- /* the parsed request uri */
- protected $uri_parts;
-
- /* the raw request uri */
- protected $uri;
-
- /* the request headers */
- protected $headers;
-
- /* the request method */
- protected $method;
-
- /* the body of the OAuth request */
- protected $body;
-
-
- /**
- * Construct from the current request. Useful for checking the signature of a request.
- * When not supplied with any parameters this will use the current request.
- *
- * @param string uri might include parameters
- * @param string method GET, PUT, POST etc.
- * @param string parameters additional post parameters, urlencoded (RFC1738)
- * @param array headers headers for request
- * @param string body optional body of the OAuth request (POST or PUT)
- */
- function __construct ( $uri = null, $method = 'GET', $parameters = '', $headers = array(), $body = null )
- {
- if (empty($uri))
- {
- if (is_object($_SERVER))
- {
- // Tainted arrays - the normal stuff in anyMeta
- $method = $_SERVER->REQUEST_METHOD->getRawUnsafe();
- $uri = $_SERVER->REQUEST_URI->getRawUnsafe();
- }
- else
- {
- // non anyMeta systems
- $method = $_SERVER['REQUEST_METHOD'];
- $uri = $_SERVER['REQUEST_URI'];
- }
- $headers = getallheaders();
- $parameters = '';
- $this->method = strtoupper($method);
-
- // If this is a post then also check the posted variables
- if (strcasecmp($method, 'POST') == 0)
- {
- /*
- // TODO: what to do with 'multipart/form-data'?
- if ($this->getRequestContentType() == 'multipart/form-data')
- {
- throw new OAuthException('Unsupported POST content type, expected "application/x-www-form-urlencoded" got "'.@$_SERVER['CONTENT_TYPE'].'"');
- }
- */
- if ($this->getRequestContentType() == 'application/x-www-form-urlencoded')
- {
- // Get the posted body (when available)
- if (!isset($headers['X-OAuth-Test']))
- {
- $parameters .= $this->getRequestBody();
- }
- }
- else
- {
- $body = $this->getRequestBody();
- }
- }
- else if (strcasecmp($method, 'PUT') == 0)
- {
- $body = $this->getRequestBody();
- }
- }
-
- $this->method = strtoupper($method);
- $this->headers = $headers;
- // Store the values, prepare for oauth
- $this->uri = $uri;
- $this->body = $body;
- $this->parseUri($parameters);
- $this->parseHeaders();
- $this->transcodeParams();
- }
-
-
- /**
- * Return the signature base string.
- * Note that we can't use rawurlencode due to specified use of RFC3986.
- *
- * @return string
- */
- function signatureBaseString ()
- {
- $sig = array();
- $sig[] = $this->method;
- $sig[] = $this->getRequestUrl();
- $sig[] = $this->getNormalizedParams();
-
- return implode('&', array_map(array($this, 'urlencode'), $sig));
- }
-
-
- /**
- * Calculate the signature of the request, using the method in oauth_signature_method.
- * The signature is returned encoded in the form as used in the url. So the base64 and
- * urlencoding has been done.
- *
- * @param string consumer_secret
- * @param string token_secret
- * @exception when not all parts available
- * @return string
- */
- function calculateSignature ( $consumer_secret, $token_secret, $token_type = 'access' )
- {
- $required = array(
- 'oauth_consumer_key',
- 'oauth_signature_method',
- 'oauth_timestamp',
- 'oauth_nonce'
- );
-
- if ($token_type !== false)
- {
- $required[] = 'oauth_token';
- }
-
- foreach ($required as $req)
- {
- if (!isset($this->param[$req]))
- {
- throw new OAuthException('Can\'t sign request, missing parameter "'.$req.'"');
- }
- }
-
- $this->checks();
-
- $base = $this->signatureBaseString();
- $signature = $this->calculateDataSignature($base, $consumer_secret, $token_secret, $this->param['oauth_signature_method']);
- return $signature;
- }
-
-
- /**
- * Calculate the signature of a string.
- * Uses the signature method from the current parameters.
- *
- * @param string data
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature_method
- * @exception OAuthException thrown when the signature method is unknown
- * @return string signature
- */
- function calculateDataSignature ( $data, $consumer_secret, $token_secret, $signature_method )
- {
- if (is_null($data))
- {
- $data = '';
- }
-
- $sig = $this->getSignatureMethod($signature_method);
- return $sig->signature($this, $data, $consumer_secret, $token_secret);
- }
-
-
- /**
- * Select a signature method from the list of available methods.
- * We try to check the most secure methods first.
- *
- * @todo Let the signature method tell us how secure it is
- * @param array methods
- * @exception OAuthException when we don't support any method in the list
- * @return string
- */
- public function selectSignatureMethod ( $methods )
- {
- if (in_array('HMAC-SHA1', $methods))
- {
- $method = 'HMAC-SHA1';
- }
- else if (in_array('MD5', $methods))
- {
- $method = 'MD5';
- }
- else
- {
- $method = false;
- foreach ($methods as $m)
- {
- $m = strtoupper($m);
- $m = preg_replace('/[^A-Z0-9]/', '_', $m);
- if (file_exists(dirname(__FILE__).'/signature_method/OAuthSignatureMethod_'.$m.'.php'))
- {
- $method = $m;
- break;
- }
- }
-
- if (empty($method))
- {
- throw new OAuthException('None of the signing methods is supported.');
- }
- }
- return $method;
- }
-
-
- /**
- * Fetch the signature object used for calculating and checking the signature base string
- *
- * @param string method
- * @return OAuthSignatureMethod object
- */
- function getSignatureMethod ( $method )
- {
- $m = strtoupper($method);
- $m = preg_replace('/[^A-Z0-9]/', '_', $m);
- $class = 'OAuthSignatureMethod_'.$m;
-
- if (file_exists(dirname(__FILE__).'/signature_method/'.$class.'.php'))
- {
- require_once dirname(__FILE__).'/signature_method/'.$class.'.php';
- $sig = new $class();
- }
- else
- {
- throw new OAuthException('Unsupported signature method "'.$m.'".');
- }
- return $sig;
- }
-
-
- /**
- * Perform some sanity checks.
- *
- * @exception OAuthException thrown when sanity checks failed
- */
- function checks ()
- {
- if (isset($this->param['oauth_version']))
- {
- $version = $this->urldecode($this->param['oauth_version']);
- if ($version != '1.0')
- {
- throw new OAuthException('Expected OAuth version 1.0, got "'.$this->param['oauth_version'].'"');
- }
- }
- }
-
-
- /**
- * Return the request method
- *
- * @return string
- */
- function getMethod ()
- {
- return $this->method;
- }
-
- /**
- * Return the complete parameter string for the signature check.
- * All parameters are correctly urlencoded and sorted on name and value
- *
- * @return string
- */
- function getNormalizedParams ()
- {
- /*
- // sort by name, then by value
- // (needed when we start allowing multiple values with the same name)
- $keys = array_keys($this->param);
- $values = array_values($this->param);
- array_multisort($keys, SORT_ASC, $values, SORT_ASC);
- */
- $params = $this->param;
- $normalized = array();
-
- ksort($params);
- foreach ($params as $key => $value)
- {
- // all names and values are already urlencoded, exclude the oauth signature
- if ($key != 'oauth_signature')
- {
- if (is_array($value))
- {
- $value_sort = $value;
- sort($value_sort);
- foreach ($value_sort as $v)
- {
- $normalized[] = $key.'='.$v;
- }
- }
- else
- {
- $normalized[] = $key.'='.$value;
- }
- }
- }
- return implode('&', $normalized);
- }
-
-
- /**
- * Return the normalised url for signature checks
- */
- function getRequestUrl ()
- {
- $url = $this->uri_parts['scheme'] . '://'
- . $this->uri_parts['user'] . (!empty($this->uri_parts['pass']) ? ':' : '')
- . $this->uri_parts['pass'] . (!empty($this->uri_parts['user']) ? '@' : '')
- . $this->uri_parts['host'];
-
- if ( $this->uri_parts['port']
- && $this->uri_parts['port'] != $this->defaultPortForScheme($this->uri_parts['scheme']))
- {
- $url .= ':'.$this->uri_parts['port'];
- }
- if (!empty($this->uri_parts['path']))
- {
- $url .= $this->uri_parts['path'];
- }
- return $url;
- }
-
-
- /**
- * Get a parameter, value is always urlencoded
- *
- * @param string name
- * @param boolean urldecode set to true to decode the value upon return
- * @return string value false when not found
- */
- function getParam ( $name, $urldecode = false )
- {
- if (isset($this->param[$name]))
- {
- $s = $this->param[$name];
- }
- else if (isset($this->param[$this->urlencode($name)]))
- {
- $s = $this->param[$this->urlencode($name)];
- }
- else
- {
- $s = false;
- }
- if (!empty($s) && $urldecode)
- {
- if (is_array($s))
- {
- $s = array_map(array($this,'urldecode'), $s);
- }
- else
- {
- $s = $this->urldecode($s);
- }
- }
- return $s;
- }
-
- /**
- * Set a parameter
- *
- * @param string name
- * @param string value
- * @param boolean encoded set to true when the values are already encoded
- */
- function setParam ( $name, $value, $encoded = false )
- {
- if (!$encoded)
- {
- $name_encoded = $this->urlencode($name);
- if (is_array($value))
- {
- foreach ($value as $v)
- {
- $this->param[$name_encoded][] = $this->urlencode($v);
- }
- }
- else
- {
- $this->param[$name_encoded] = $this->urlencode($value);
- }
- }
- else
- {
- $this->param[$name] = $value;
- }
- }
-
-
- /**
- * Re-encode all parameters so that they are encoded using RFC3986.
- * Updates the $this->param attribute.
- */
- protected function transcodeParams ()
- {
- $params = $this->param;
- $this->param = array();
-
- foreach ($params as $name=>$value)
- {
- if (is_array($value))
- {
- $this->param[$this->urltranscode($name)] = array_map(array($this,'urltranscode'), $value);
- }
- else
- {
- $this->param[$this->urltranscode($name)] = $this->urltranscode($value);
- }
- }
- }
-
-
-
- /**
- * Return the body of the OAuth request.
- *
- * @return string null when no body
- */
- function getBody ()
- {
- return $this->body;
- }
-
-
- /**
- * Return the body of the OAuth request.
- *
- * @return string null when no body
- */
- function setBody ( $body )
- {
- $this->body = $body;
- }
-
-
- /**
- * Parse the uri into its parts. Fill in the missing parts.
- *
- * @todo check for the use of https, right now we default to http
- * @todo support for multiple occurences of parameters
- * @param string $parameters optional extra parameters (from eg the http post)
- */
- protected function parseUri ( $parameters )
- {
- $ps = parse_url($this->uri);
-
- // Get the current/requested method
- if (empty($ps['scheme']))
- {
- $ps['scheme'] = 'http';
- }
- else
- {
- $ps['scheme'] = strtolower($ps['scheme']);
- }
-
- // Get the current/requested host
- if (empty($ps['host']))
- {
- if (isset($_SERVER['HTTP_HOST']))
- {
- $ps['host'] = $_SERVER['HTTP_HOST'];
- }
- else
- {
- $ps['host'] = '';
- }
- }
- $ps['host'] = mb_strtolower($ps['host']);
- if (!preg_match('/^[a-z0-9\.\-]+$/', $ps['host']))
- {
- throw new OAuthException('Unsupported characters in host name');
- }
-
- // Get the port we are talking on
- if (empty($ps['port']))
- {
- $ps['port'] = $this->defaultPortForScheme($ps['scheme']);
- }
-
- if (empty($ps['user']))
- {
- $ps['user'] = '';
- }
- if (empty($ps['pass']))
- {
- $ps['pass'] = '';
- }
- if (empty($ps['path']))
- {
- $ps['path'] = '/';
- }
- if (empty($ps['query']))
- {
- $ps['query'] = '';
- }
- if (empty($ps['fragment']))
- {
- $ps['fragment'] = '';
- }
-
- // Now all is complete - parse all parameters
- foreach (array($ps['query'], $parameters) as $params)
- {
- if (strlen($params) > 0)
- {
- $params = explode('&', $params);
- foreach ($params as $p)
- {
- @list($name, $value) = explode('=', $p, 2);
- $this->param[$name] = $value;
- }
- }
- }
- $this->uri_parts = $ps;
- }
-
-
- /**
- * Return the default port for a scheme
- *
- * @param string scheme
- * @return int
- */
- protected function defaultPortForScheme ( $scheme )
- {
- switch ($scheme)
- {
- case 'http': return 80;
- case 'https': return 43;
- default:
- throw new OAuthException('Unsupported scheme type, expected http or https, got "'.$scheme.'"');
- break;
- }
- }
-
-
- /**
- * Encode a string according to the RFC3986
- *
- * @param string s
- * @return string
- */
- function urlencode ( $s )
- {
- if ($s === false)
- {
- return $s;
- }
- else
- {
- return str_replace('%7E', '~', rawurlencode($s));
- }
- }
-
- /**
- * Decode a string according to RFC3986.
- * Also correctly decodes RFC1738 urls.
- *
- * @param string s
- * @return string
- */
- function urldecode ( $s )
- {
- if ($s === false)
- {
- return $s;
- }
- else
- {
- return rawurldecode($s);
- }
- }
-
- /**
- * urltranscode - make sure that a value is encoded using RFC3986.
- * We use a basic urldecode() function so that any use of '+' as the
- * encoding of the space character is correctly handled.
- *
- * @param string s
- * @return string
- */
- function urltranscode ( $s )
- {
- if ($s === false)
- {
- return $s;
- }
- else
- {
- return $this->urlencode(urldecode($s));
- }
- }
-
-
- /**
- * Parse the oauth parameters from the request headers
- * Looks for something like:
- *
- * Authorization: OAuth realm="http://photos.example.net/authorize",
- * oauth_consumer_key="dpf43f3p2l4k3l03",
- * oauth_token="nnch734d00sl2jdk",
- * oauth_signature_method="HMAC-SHA1",
- * oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",
- * oauth_timestamp="1191242096",
- * oauth_nonce="kllo9940pd9333jh",
- * oauth_version="1.0"
- */
- private function parseHeaders ()
- {
-/*
- $this->headers['Authorization'] = 'OAuth realm="http://photos.example.net/authorize",
- oauth_consumer_key="dpf43f3p2l4k3l03",
- oauth_token="nnch734d00sl2jdk",
- oauth_signature_method="HMAC-SHA1",
- oauth_signature="tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D",
- oauth_timestamp="1191242096",
- oauth_nonce="kllo9940pd9333jh",
- oauth_version="1.0"';
-*/
- if (isset($this->headers['Authorization']))
- {
- $auth = trim($this->headers['Authorization']);
- if (strncasecmp($auth, 'OAuth', 4) == 0)
- {
- $vs = explode(',', substr($auth, 6));
- foreach ($vs as $v)
- {
- if (strpos($v, '='))
- {
- $v = trim($v);
- list($name,$value) = explode('=', $v, 2);
- if (!empty($value) && $value{0} == '"' && substr($value, -1) == '"')
- {
- $value = substr(substr($value, 1), 0, -1);
- }
-
- if (strcasecmp($name, 'realm') == 0)
- {
- $this->realm = $value;
- }
- else
- {
- $this->param[$name] = $value;
- }
- }
- }
- }
- }
- }
-
-
- /**
- * Fetch the content type of the current request
- *
- * @return string
- */
- private function getRequestContentType ()
- {
- $content_type = 'application/octet-stream';
- if (!empty($_SERVER) && array_key_exists('CONTENT_TYPE', $_SERVER))
- {
- list($content_type) = explode(';', $_SERVER['CONTENT_TYPE']);
- }
- return trim($content_type);
- }
-
-
- /**
- * Get the body of a POST or PUT.
- *
- * Used for fetching the post parameters and to calculate the body signature.
- *
- * @return string null when no body present (or wrong content type for body)
- */
- private function getRequestBody ()
- {
- $body = null;
- if ($this->method == 'POST' || $this->method == 'PUT')
- {
- $body = '';
- $fh = @fopen('php://input', 'r');
- if ($fh)
- {
- while (!feof($fh))
- {
- $s = fread($fh, 1024);
- if (is_string($s))
- {
- $body .= $s;
- }
- }
- fclose($fh);
- }
- }
- return $body;
- }
-
-
- /**
- * Simple function to perform a redirect (GET).
- * Redirects the User-Agent, does not return.
- *
- * @param string uri
- * @param array params parameters, urlencoded
- * @exception OAuthException when redirect uri is illegal
- */
- public function redirect ( $uri, $params )
- {
- if (!empty($params))
- {
- $q = array();
- foreach ($params as $name=>$value)
- {
- $q[] = $name.'='.$value;
- }
- $q_s = implode('&', $q);
-
- if (strpos($uri, '?'))
- {
- $uri .= '&'.$q_s;
- }
- else
- {
- $uri .= '?'.$q_s;
- }
- }
-
- // simple security - multiline location headers can inject all kinds of extras
- $uri = preg_replace('/\s/', '%20', $uri);
- if (strncasecmp($uri, 'http://', 7) && strncasecmp($uri, 'https://', 8))
- {
- if (strpos($uri, '://'))
- {
- throw new OAuthException('Illegal protocol in redirect uri '.$uri);
- }
- $uri = 'http://'.$uri;
- }
-
- header('HTTP/1.1 302 Found');
- header('Location: '.$uri);
- echo '';
- exit();
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php b/mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php
deleted file mode 100644
index 934c1c53c..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequestLogger.php
+++ /dev/null
@@ -1,274 +0,0 @@
-<?php
-
-/**
- * Log OAuth requests
- *
- * @version $Id: OAuthRequestLogger.php 55 2009-01-14 15:27:36Z scherpenisse $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Dec 7, 2007 12:22:43 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-class OAuthRequestLogger
-{
- static private $logging = 0;
- static private $enable_logging = null;
- static private $store_log = null;
- static private $note = '';
- static private $user_id = null;
- static private $request_object = null;
- static private $sent = null;
- static private $received = null;
- static private $log = array();
-
- /**
- * Start any logging, checks the system configuration if logging is needed.
- *
- * @param OAuthRequest $request_object
- */
- static function start ( $request_object = null )
- {
- if (defined('OAUTH_LOG_REQUEST'))
- {
- if (is_null(OAuthRequestLogger::$enable_logging))
- {
- OAuthRequestLogger::$enable_logging = true;
- }
- if (is_null(OAuthRequestLogger::$store_log))
- {
- OAuthRequestLogger::$store_log = true;
- }
- }
-
- if (OAuthRequestLogger::$enable_logging && !OAuthRequestLogger::$logging)
- {
- OAuthRequestLogger::$logging = true;
- OAuthRequestLogger::$request_object = $request_object;
- ob_start();
-
- // Make sure we flush our log entry when we stop the request (eg on an exception)
- register_shutdown_function(array('OAuthRequestLogger','flush'));
- }
- }
-
-
- /**
- * Force logging, needed for performing test connects independent from the debugging setting.
- *
- * @param boolean store_log (optional) true to store the log in the db
- */
- static function enableLogging ( $store_log = null )
- {
- OAuthRequestLogger::$enable_logging = true;
- if (!is_null($store_log))
- {
- OAuthRequestLogger::$store_log = $store_log;
- }
- }
-
-
- /**
- * Logs the request to the database, sends any cached output.
- * Also called on shutdown, to make sure we always log the request being handled.
- */
- static function flush ()
- {
- if (OAuthRequestLogger::$logging)
- {
- OAuthRequestLogger::$logging = false;
-
- if (is_null(OAuthRequestLogger::$sent))
- {
- // What has been sent to the user-agent?
- $data = ob_get_contents();
- if (strlen($data) > 0)
- {
- ob_end_flush();
- }
- elseif (ob_get_level())
- {
- ob_end_clean();
- }
- $hs = headers_list();
- $sent = implode("\n", $hs) . "\n\n" . $data;
- }
- else
- {
- // The request we sent
- $sent = OAuthRequestLogger::$sent;
- }
-
- if (is_null(OAuthRequestLogger::$received))
- {
- // Build the request we received
- $hs0 = getallheaders();
- $hs = array();
- foreach ($hs0 as $h => $v)
- {
- $hs[] = "$h: $v";
- }
-
- $data = '';
- $fh = @fopen('php://input', 'r');
- if ($fh)
- {
- while (!feof($fh))
- {
- $s = fread($fh, 1024);
- if (is_string($s))
- {
- $data .= $s;
- }
- }
- fclose($fh);
- }
- $received = implode("\n", $hs) . "\n\n" . $data;
- }
- else
- {
- // The answer we received
- $received = OAuthRequestLogger::$received;
- }
-
- // The request base string
- if (OAuthRequestLogger::$request_object)
- {
- $base_string = OAuthRequestLogger::$request_object->signatureBaseString();
- }
- else
- {
- $base_string = '';
- }
-
- // Figure out to what keys we want to log this request
- $keys = array();
- if (OAuthRequestLogger::$request_object)
- {
- $consumer_key = OAuthRequestLogger::$request_object->getParam('oauth_consumer_key', true);
- $token = OAuthRequestLogger::$request_object->getParam('oauth_token', true);
-
- switch (get_class(OAuthRequestLogger::$request_object))
- {
- // tokens are access/request tokens by a consumer
- case 'OAuthServer':
- case 'OAuthRequestVerifier':
- $keys['ocr_consumer_key'] = $consumer_key;
- $keys['oct_token'] = $token;
- break;
-
- // tokens are access/request tokens to a server
- case 'OAuthRequester':
- case 'OAuthRequestSigner':
- $keys['osr_consumer_key'] = $consumer_key;
- $keys['ost_token'] = $token;
- break;
- }
- }
-
- // Log the request
- if (OAuthRequestLogger::$store_log)
- {
- $store = OAuthStore::instance();
- $store->addLog($keys, $received, $sent, $base_string, OAuthRequestLogger::$note, OAuthRequestLogger::$user_id);
- }
-
- OAuthRequestLogger::$log[] = array(
- 'keys' => $keys,
- 'received' => $received,
- 'sent' => $sent,
- 'base_string' => $base_string,
- 'note' => OAuthRequestLogger::$note
- );
- }
- }
-
-
- /**
- * Add a note, used by the OAuthException to log all exceptions.
- *
- * @param string note
- */
- static function addNote ( $note )
- {
- OAuthRequestLogger::$note .= $note . "\n\n";
- }
-
- /**
- * Set the OAuth request object being used
- *
- * @param OAuthRequest request_object
- */
- static function setRequestObject ( $request_object )
- {
- OAuthRequestLogger::$request_object = $request_object;
- }
-
-
- /**
- * Set the relevant user (defaults to the current user)
- *
- * @param int user_id
- */
- static function setUser ( $user_id )
- {
- OAuthRequestLogger::$user_id = $user_id;
- }
-
-
- /**
- * Set the request we sent
- *
- * @param string request
- */
- static function setSent ( $request )
- {
- OAuthRequestLogger::$sent = $request;
- }
-
- /**
- * Set the reply we received
- *
- * @param string request
- */
- static function setReceived ( $reply )
- {
- OAuthRequestLogger::$received = $reply;
- }
-
-
- /**
- * Get the the log till now
- *
- * @return array
- */
- static function getLog ()
- {
- return OAuthRequestLogger::$log;
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php b/mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php
deleted file mode 100644
index 9f83f287f..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequestSigner.php
+++ /dev/null
@@ -1,209 +0,0 @@
-<?php
-
-/**
- * Sign requests before performing the request.
- *
- * @version $Id: OAuthRequestSigner.php 58 2009-02-23 01:47:23Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:02:49 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-require_once dirname(__FILE__) . '/OAuthStore.php';
-require_once dirname(__FILE__) . '/OAuthRequest.php';
-
-
-class OAuthRequestSigner extends OAuthRequest
-{
- protected $request;
- protected $store;
- protected $usr_id = 0;
- private $signed = false;
-
-
- /**
- * Construct the request to be signed. Parses or appends the parameters in the params url.
- * When you supply an params array, then the params should not be urlencoded.
- * When you supply a string, then it is assumed it is of the type application/x-www-form-urlencoded
- *
- * @param string request url
- * @param string method PUT, GET, POST etc.
- * @param mixed params string (for urlencoded data, or array with name/value pairs)
- * @param string body optional body for PUT and/or POST requests
- */
- function __construct ( $request, $method = 'GET', $params = null, $body = null )
- {
- $this->store = OAuthStore::instance();
-
- if (is_string($params))
- {
- parent::__construct($request, $method, $params);
- }
- else
- {
- parent::__construct($request, $method);
- if (is_array($params))
- {
- foreach ($params as $name => $value)
- {
- $this->setParam($name, $value);
- }
- }
- }
-
- // With put/ post we might have a body (not for application/x-www-form-urlencoded requests)
- if ($method == 'PUT' || $method == 'POST')
- {
- $this->setBody($body);
- }
- }
-
-
- /**
- * Reset the 'signed' flag, so that any changes in the parameters force a recalculation
- * of the signature.
- */
- function setUnsigned ()
- {
- $this->signed = false;
- }
-
-
- /**
- * Sign our message in the way the server understands.
- * Set the needed oauth_xxxx parameters.
- *
- * @param int usr_id (optional) user that wants to sign this request
- * @param array secrets secrets used for signing, when empty then secrets will be fetched from the token registry
- * @param string name name of the token to be used for signing
- * @exception OAuthException when there is no oauth relation with the server
- * @exception OAuthException when we don't support the signing methods of the server
- */
- function sign ( $usr_id = 0, $secrets = null, $name = '' )
- {
- $url = $this->getRequestUrl();
- if (empty($secrets))
- {
- // get the access tokens for the site (on an user by user basis)
- $secrets = $this->store->getSecretsForSignature($url, $usr_id, $name);
- }
- if (empty($secrets))
- {
- throw new OAuthException('No OAuth relation with the server for at "'.$url.'"');
- }
-
- $signature_method = $this->selectSignatureMethod($secrets['signature_methods']);
-
- $token = isset($secrets['token']) ? $secrets['token'] : '';
- $token_secret = isset($secrets['token_secret']) ? $secrets['token_secret'] : '';
-
- $this->setParam('oauth_signature_method',$signature_method);
- $this->setParam('oauth_signature', '');
- $this->setParam('oauth_nonce', !empty($secrets['nonce']) ? $secrets['nonce'] : uniqid(''));
- $this->setParam('oauth_timestamp', !empty($secrets['timestamp']) ? $secrets['timestamp'] : time());
- $this->setParam('oauth_token', $token);
- $this->setParam('oauth_consumer_key', $secrets['consumer_key']);
- $this->setParam('oauth_version', '1.0');
-
- $body = $this->getBody();
- if (!is_null($body))
- {
- // We also need to sign the body, use the default signature method
- $body_signature = $this->calculateDataSignature($body, $secrets['consumer_secret'], $token_secret, $signature_method);
- $this->setParam('xoauth_body_signature', $body_signature, true);
- }
-
- $signature = $this->calculateSignature($secrets['consumer_secret'], $token_secret);
- $this->setParam('oauth_signature', $signature, true);
-
- $this->signed = true;
- $this->usr_id = $usr_id;
- }
-
-
- /**
- * Builds the Authorization header for the request.
- * Adds all oauth_ and xoauth_ parameters to the Authorization header.
- *
- * @return string
- */
- function getAuthorizationHeader ()
- {
- if (!$this->signed)
- {
- $this->sign($this->usr_id);
- }
- $h = array();
- $h[] = 'Authorization: OAuth realm=""';
- foreach ($this->param as $name => $value)
- {
- if (strncmp($name, 'oauth_', 6) == 0 || strncmp($name, 'xoauth_', 7) == 0)
- {
- $h[] = $name.'="'.$value.'"';
- }
- }
- $hs = implode(', ', $h);
- return $hs;
- }
-
-
- /**
- * Builds the application/x-www-form-urlencoded parameter string. Can be appended as
- * the query part to a GET or inside the request body for a POST.
- *
- * @param boolean oauth_as_header (optional) set to false to include oauth parameters
- * @return string
- */
- function getQueryString ( $oauth_as_header = true )
- {
- $parms = array();
- foreach ($this->param as $name => $value)
- {
- if ( !$oauth_as_header
- || (strncmp($name, 'oauth_', 6) != 0 && strncmp($name, 'xoauth_', 7) != 0))
- {
- if (is_array($value))
- {
- foreach ($value as $v)
- {
- $parms[] = $name.'='.$v;
- }
- }
- else
- {
- $parms[] = $name.'='.$value;
- }
- }
- }
- return implode('&', $parms);
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php b/mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php
deleted file mode 100644
index 4b4db9685..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequestVerifier.php
+++ /dev/null
@@ -1,262 +0,0 @@
-<?php
-
-/**
- * Verify the current request. Checks if signed and if the signature is correct.
- * When correct then also figures out on behalf of which user this request is being made.
- *
- * @version $Id: OAuthRequestVerifier.php 51 2008-10-15 15:15:47Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:35:03 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once dirname(__FILE__) . '/OAuthStore.php';
-require_once dirname(__FILE__) . '/OAuthRequest.php';
-
-
-class OAuthRequestVerifier extends OAuthRequest
-{
- private $request;
- private $store;
-
- /**
- * Construct the request to be verified
- *
- * @param string request
- * @param string method
- */
- function __construct ( $uri = null, $method = 'GET' )
- {
- $this->store = OAuthStore::instance();
- parent::__construct($uri, $method);
-
- OAuthRequestLogger::start($this);
- }
-
-
- /**
- * See if the current request is signed with OAuth
- *
- * @return boolean
- */
- static public function requestIsSigned ()
- {
- if (isset($_REQUEST['oauth_signature']))
- {
- $signed = true;
- }
- else
- {
- $hs = getallheaders();
- if (isset($hs['Authorization']) && strpos($hs['Authorization'], 'oauth_signature') !== false)
- {
- $signed = true;
- }
- else
- {
- $signed = false;
- }
- }
- return $signed;
- }
-
-
- /**
- * Verify the request if it seemed to be signed.
- *
- * @param string token_type the kind of token needed, defaults to 'access'
- * @exception OAuthException thrown when the request did not verify
- * @return boolean true when signed, false when not signed
- */
- public function verifyIfSigned ( $token_type = 'access' )
- {
- if ($this->getParam('oauth_consumer_key'))
- {
- OAuthRequestLogger::start($this);
- $this->verify($token_type);
- $signed = true;
- OAuthRequestLogger::flush();
- }
- else
- {
- $signed = false;
- }
- return $signed;
- }
-
-
- /**
- * Verify the request
- *
- * @param string token_type the kind of token needed, defaults to 'access' (false, 'access', 'request')
- * @exception OAuthException thrown when the request did not verify
- * @return int user_id associated with token (false when no user associated)
- */
- public function verify ( $token_type = 'access' )
- {
- $consumer_key = $this->getParam('oauth_consumer_key');
- $token = $this->getParam('oauth_token');
- $user_id = false;
-
- if ($consumer_key && ($token_type === false || $token))
- {
- $secrets = $this->store->getSecretsForVerify( $this->urldecode($consumer_key),
- $this->urldecode($token),
- $token_type);
-
- $this->store->checkServerNonce( $this->urldecode($consumer_key),
- $this->urldecode($token),
- $this->getParam('oauth_timestamp', true),
- $this->getParam('oauth_nonce', true));
-
- $oauth_sig = $this->getParam('oauth_signature');
- if (empty($oauth_sig))
- {
- throw new OAuthException('Verification of signature failed (no oauth_signature in request).');
- }
-
- try
- {
- $this->verifySignature($secrets['consumer_secret'], $secrets['token_secret'], $token_type);
- }
- catch (OAuthException $e)
- {
- throw new OAuthException('Verification of signature failed (signature base string was "'.$this->signatureBaseString().'").');
- }
-
- // Check the optional body signature
- if ($this->getParam('xoauth_body_signature'))
- {
- $method = $this->getParam('xoauth_body_signature_method');
- if (empty($method))
- {
- $method = $this->getParam('oauth_signature_method');
- }
-
- try
- {
- $this->verifyDataSignature($this->getBody(), $secrets['consumer_secret'], $secrets['token_secret'], $method, $this->getParam('xoauth_body_signature'));
- }
- catch (OAuthException $e)
- {
- throw new OAuthException('Verification of body signature failed.');
- }
- }
-
- // All ok - fetch the user associated with this request
- if (isset($secrets['user_id']))
- {
- $user_id = $secrets['user_id'];
- }
-
- // Check if the consumer wants us to reset the ttl of this token
- $ttl = $this->getParam('xoauth_token_ttl', true);
- if (is_numeric($ttl))
- {
- $this->store->setConsumerAccessTokenTtl($this->urldecode($token), $ttl);
- }
- }
- else
- {
- throw new OAuthException('Can\'t verify request, missing oauth_consumer_key or oauth_token');
- }
- return $user_id;
- }
-
-
-
- /**
- * Verify the signature of the request, using the method in oauth_signature_method.
- * The signature is returned encoded in the form as used in the url. So the base64 and
- * urlencoding has been done.
- *
- * @param string consumer_secret
- * @param string token_secret
- * @exception OAuthException thrown when the signature method is unknown
- * @exception OAuthException when not all parts available
- * @exception OAuthException when signature does not match
- */
- public function verifySignature ( $consumer_secret, $token_secret, $token_type = 'access' )
- {
- $required = array(
- 'oauth_consumer_key',
- 'oauth_signature_method',
- 'oauth_timestamp',
- 'oauth_nonce',
- 'oauth_signature'
- );
-
- if ($token_type !== false)
- {
- $required[] = 'oauth_token';
- }
-
- foreach ($required as $req)
- {
- if (!isset($this->param[$req]))
- {
- throw new OAuthException('Can\'t verify request signature, missing parameter "'.$req.'"');
- }
- }
-
- $this->checks();
-
- $base = $this->signatureBaseString();
- $this->verifyDataSignature($base, $consumer_secret, $token_secret, $this->param['oauth_signature_method'], $this->param['oauth_signature']);
- }
-
-
-
- /**
- * Verify the signature of a string.
- *
- * @param string data
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature_method
- * @param string signature
- * @exception OAuthException thrown when the signature method is unknown
- * @exception OAuthException when signature does not match
- */
- public function verifyDataSignature ( $data, $consumer_secret, $token_secret, $signature_method, $signature )
- {
- if (is_null($data))
- {
- $data = '';
- }
-
- $sig = $this->getSignatureMethod($signature_method);
- if (!$sig->verify($this, $data, $consumer_secret, $token_secret, $signature))
- {
- throw new OAuthException('Signature verification failed ('.$signature_method.')');
- }
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthRequester.php b/mod/oauth_api/vendors/oauth/library/OAuthRequester.php
deleted file mode 100644
index 87f9586c0..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthRequester.php
+++ /dev/null
@@ -1,508 +0,0 @@
-<?php
-
-/**
- * Perform a signed OAuth request with a GET, POST, PUT or DELETE operation.
- *
- * @version $Id: OAuthRequester.php 63 2009-02-25 10:24:33Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 20, 2007 1:41:38 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once dirname(__FILE__) . '/OAuthRequestSigner.php';
-require_once dirname(__FILE__) . '/body/OAuthBodyContentDisposition.php';
-
-
-class OAuthRequester extends OAuthRequestSigner
-{
- protected $files;
-
- /**
- * Construct a new request signer. Perform the request with the doRequest() method below.
- *
- * A request can have either one file or a body, not both.
- *
- * The files array consists of arrays:
- * - file the filename/path containing the data for the POST/PUT
- * - data data for the file, omit when you have a file
- * - mime content-type of the file
- * - filename filename for content disposition header
- *
- * When OAuth (and PHP) can support multipart/form-data then we can handle more than one file.
- * For now max one file, with all the params encoded in the query string.
- *
- * @param string request
- * @param string method http method. GET, PUT, POST etc.
- * @param array params name=>value array with request parameters
- * @param string body optional body to send
- * @param array files optional files to send (max 1 till OAuth support multipart/form-data posts)
- */
- function __construct ( $request, $method = 'GET', $params = null, $body = null, $files = null )
- {
- parent::__construct($request, $method, $params, $body);
-
- // When there are files, then we can construct a POST with a single file
- if (!empty($files))
- {
- $empty = true;
- foreach ($files as $f)
- {
- $empty = $empty && empty($f['file']) && !isset($f['data']);
- }
-
- if (!$empty)
- {
- if (!is_null($body))
- {
- throw new OAuthException('When sending files, you can\'t send a body as well.');
- }
- $this->files = $files;
- }
- }
- }
-
-
- /**
- * Perform the request, returns the response code, headers and body.
- *
- * @param int usr_id optional user id for which we make the request
- * @param array curl_options optional extra options for curl request
- * @param array options options like name and token_ttl
- * @exception OAuthException when authentication not accepted
- * @exception OAuthException when signing was not possible
- * @return array (code=>int, headers=>array(), body=>string)
- */
- function doRequest ( $usr_id = 0, $curl_options = array(), $options = array() )
- {
- $name = isset($options['name']) ? $options['name'] : '';
- if (isset($options['token_ttl']))
- {
- $this->setParam('xoauth_token_ttl', intval($options['token_ttl']));
- }
-
- if (!empty($this->files))
- {
- // At the moment OAuth does not support multipart/form-data, so try to encode
- // the supplied file (or data) as the request body and add a content-disposition header.
- list($extra_headers, $body) = OAuthBodyContentDisposition::encodeBody($this->files);
- $this->setBody($body);
- $curl_options = $this->prepareCurlOptions($curl_options, $extra_headers);
- }
- $this->sign($usr_id, null, $name);
- $text = $this->curl_raw($curl_options);
- $result = $this->curl_parse($text);
- if ($result['code'] >= 400)
- {
- throw new OAuthException('Request failed with code ' . $result['code'] . ': ' . $result['body']);
- }
-
- // Record the token time to live for this server access token, immediate delete iff ttl <= 0
- // Only done on a succesful request.
- $token_ttl = $this->getParam('xoauth_token_ttl', false);
- if (is_numeric($token_ttl))
- {
- $this->store->setServerTokenTtl($this->getParam('oauth_consumer_key',true), $this->getParam('oauth_token',true), $token_ttl);
- }
-
- return $result;
- }
-
-
- /**
- * Request a request token from the site belonging to consumer_key
- *
- * @param string consumer_key
- * @param int usr_id
- * @param array params (optional) extra arguments for when requesting the request token
- * @param string method (optional) change the method of the request, defaults to POST (as it should be)
- * @param array options (optional) options like name and token_ttl
- * @exception OAuthException when no key could be fetched
- * @exception OAuthException when no server with consumer_key registered
- * @return array (authorize_uri, token)
- */
- static function requestRequestToken ( $consumer_key, $usr_id, $params = null, $method = 'POST', $options = array() )
- {
- OAuthRequestLogger::start();
-
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $params['xoauth_token_ttl'] = intval($options['token_ttl']);
- }
-
- $store = OAuthStore::instance();
- $r = $store->getServer($consumer_key, $usr_id);
- $uri = $r['request_token_uri'];
-
- $oauth = new OAuthRequester($uri, $method, $params);
- $oauth->sign($usr_id, $r);
- $text = $oauth->curl_raw();
-
- if (empty($text))
- {
- throw new OAuthException('No answer from the server "'.$uri.'" while requesting a request token');
- }
- $data = $oauth->curl_parse($text);
- if ($data['code'] != 200)
- {
- throw new OAuthException('Unexpected result from the server "'.$uri.'" ('.$data['code'].') while requesting a request token');
- }
- $token = array();
- $params = explode('&', $data['body']);
- foreach ($params as $p)
- {
- @list($name, $value) = explode('=', $p, 2);
- $token[$name] = $oauth->urldecode($value);
- }
-
- if (!empty($token['oauth_token']) && !empty($token['oauth_token_secret']))
- {
- $opts = array();
- if (isset($options['name']))
- {
- $opts['name'] = $options['name'];
- }
- if (isset($token['xoauth_token_ttl']))
- {
- $opts['token_ttl'] = $token['xoauth_token_ttl'];
- }
- $store->addServerToken($consumer_key, 'request', $token['oauth_token'], $token['oauth_token_secret'], $usr_id, $opts);
- }
- else
- {
- throw new OAuthException('The server "'.$uri.'" did not return the oauth_token or the oauth_token_secret');
- }
-
- OAuthRequestLogger::flush();
-
- // Now we can direct a browser to the authorize_uri
- return array(
- 'authorize_uri' => $r['authorize_uri'],
- 'token' => $token['oauth_token']
- );
- }
-
-
- /**
- * Request an access token from the site belonging to consumer_key.
- * Before this we got an request token, now we want to exchange it for
- * an access token.
- *
- * @param string consumer_key
- * @param string token
- * @param int usr_id user requesting the access token
- * @param string method (optional) change the method of the request, defaults to POST (as it should be)
- * @param array options (optional) extra options for request, eg token_ttl
- * @exception OAuthException when no key could be fetched
- * @exception OAuthException when no server with consumer_key registered
- */
- static function requestAccessToken ( $consumer_key, $token, $usr_id, $method = 'POST', $options = array() )
- {
- OAuthRequestLogger::start();
-
- $store = OAuthStore::instance();
- $r = $store->getServerTokenSecrets($consumer_key, $token, 'request', $usr_id);
- $uri = $r['access_token_uri'];
- $token_name = $r['token_name'];
-
- // Delete the server request token, this one was for one use only
- $store->deleteServerToken($consumer_key, $r['token'], 0, true);
-
- // Try to exchange our request token for an access token
- $oauth = new OAuthRequester($uri, $method);
-
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $oauth->setParam('xoauth_token_ttl', intval($options['token_ttl']));
- }
-
- OAuthRequestLogger::setRequestObject($oauth);
-
- $oauth->sign($usr_id, $r);
- $text = $oauth->curl_raw();
- if (empty($text))
- {
- throw new OAuthException('No answer from the server "'.$uri.'" while requesting a request token');
- }
- $data = $oauth->curl_parse($text);
-
- if ($data['code'] != 200)
- {
- throw new OAuthException('Unexpected result from the server "'.$uri.'" ('.$data['code'].') while requesting a request token');
- }
-
- $token = array();
- $params = explode('&', $data['body']);
- foreach ($params as $p)
- {
- @list($name, $value) = explode('=', $p, 2);
- $token[$oauth->urldecode($name)] = $oauth->urldecode($value);
- }
-
- if (!empty($token['oauth_token']) && !empty($token['oauth_token_secret']))
- {
- $opts = array();
- $opts['name'] = $token_name;
- if (isset($token['xoauth_token_ttl']))
- {
- $opts['token_ttl'] = $token['xoauth_token_ttl'];
- }
- $store->addServerToken($consumer_key, 'access', $token['oauth_token'], $token['oauth_token_secret'], $usr_id, $opts);
- }
- else
- {
- throw new OAuthException('The server "'.$uri.'" did not return the oauth_token or the oauth_token_secret');
- }
-
- OAuthRequestLogger::flush();
- }
-
-
-
- /**
- * Open and close a curl session passing all the options to the curl libs
- *
- * @param string url the http address to fetch
- * @exception OAuthException when temporary file for PUT operation could not be created
- * @return string the result of the curl action
- */
- protected function curl_raw ( $opts = array() )
- {
- if (isset($opts[CURLOPT_HTTPHEADER]))
- {
- $header = $opts[CURLOPT_HTTPHEADER];
- }
- else
- {
- $header = array();
- }
-
- $ch = curl_init();
- $method = $this->getMethod();
- $url = $this->getRequestUrl();
- $header[] = $this->getAuthorizationHeader();
- $query = $this->getQueryString();
- $body = $this->getBody();
-
- $has_content_type = false;
- foreach ($header as $h)
- {
- if (strncasecmp($h, 'Content-Type:', 13) == 0)
- {
- $has_content_type = true;
- }
- }
-
- if (!is_null($body))
- {
- if ($method == 'TRACE')
- {
- throw new OAuthException('A body can not be sent with a TRACE operation');
- }
-
- // PUT and POST allow a request body
- if (!empty($query))
- {
- $url .= '?'.$query;
- }
-
- // Make sure that the content type of the request is ok
- if (!$has_content_type)
- {
- $header[] = 'Content-Type: application/octet-stream';
- $has_content_type = true;
- }
-
- // When PUTting, we need to use an intermediate file (because of the curl implementation)
- if ($method == 'PUT')
- {
- /*
- if (version_compare(phpversion(), '5.2.0') >= 0)
- {
- // Use the data wrapper to create the file expected by the put method
- $put_file = fopen('data://application/octet-stream;base64,'.base64_encode($body));
- }
- */
-
- $put_file = @tmpfile();
- if (!$put_file)
- {
- throw new OAuthException('Could not create tmpfile for PUT operation');
- }
- fwrite($put_file, $body);
- fseek($put_file, 0);
-
- curl_setopt($ch, CURLOPT_PUT, true);
- curl_setopt($ch, CURLOPT_INFILE, $put_file);
- curl_setopt($ch, CURLOPT_INFILESIZE, strlen($body));
- }
- else
- {
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
- }
- }
- else
- {
- // a 'normal' request, no body to be send
- if ($method == 'POST')
- {
- if (!$has_content_type)
- {
- $header[] = 'Content-Type: application/x-www-form-urlencoded';
- $has_content_type = true;
- }
-
- curl_setopt($ch, CURLOPT_POST, true);
- curl_setopt($ch, CURLOPT_POSTFIELDS, $query);
- }
- else
- {
- if (!empty($query))
- {
- $url .= '?'.$query;
- }
- if ($method != 'GET')
- {
- curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $method);
- }
- }
- }
-
- curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
- curl_setopt($ch, CURLOPT_USERAGENT, 'anyMeta/OAuth 1.0 - ($LastChangedRevision: 63 $)');
- curl_setopt($ch, CURLOPT_URL, $url);
- curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
- curl_setopt($ch, CURLOPT_HEADER, true);
-
- foreach ($opts as $k => $v)
- {
- if ($k != CURLOPT_HTTPHEADER)
- {
- curl_setopt($ch, $k, $v);
- }
- }
-
- $txt = curl_exec($ch);
- curl_close($ch);
-
- if (!empty($put_file))
- {
- fclose($put_file);
- }
-
- // Tell the logger what we requested and what we received back
- $data = $method . " $url\n".implode("\n",$header);
- if (is_string($body))
- {
- $data .= "\n\n".$body;
- }
- else if ($method == 'POST')
- {
- $data .= "\n\n".$query;
- }
-
- OAuthRequestLogger::setSent($data, $body);
- OAuthRequestLogger::setReceived($txt);
-
- return $txt;
- }
-
-
- /**
- * Parse an http response
- *
- * @param string response the http text to parse
- * @return array (code=>http-code, headers=>http-headers, body=>body)
- */
- protected function curl_parse ( $response )
- {
- if (empty($response))
- {
- return array();
- }
-
- @list($headers,$body) = explode("\r\n\r\n",$response,2);
- $lines = explode("\r\n",$headers);
-
- if (preg_match('@^HTTP/[0-9]\.[0-9] +100@', $lines[0]))
- {
- /* HTTP/1.x 100 Continue
- * the real data is on the next line
- */
- @list($headers,$body) = explode("\r\n\r\n",$body,2);
- $lines = explode("\r\n",$headers);
- }
-
- // first line of headers is the HTTP response code
- $http_line = array_shift($lines);
- if (preg_match('@^HTTP/[0-9]\.[0-9] +([0-9]{3})@', $http_line, $matches))
- {
- $code = $matches[1];
- }
-
- // put the rest of the headers in an array
- $headers = array();
- foreach ($lines as $l)
- {
- list($k, $v) = explode(': ', $l, 2);
- $headers[strtolower($k)] = $v;
- }
-
- return array( 'code' => $code, 'headers' => $headers, 'body' => $body);
- }
-
-
- /**
- * Mix the given headers into the headers that were given to curl
- *
- * @param array curl_options
- * @param array extra_headers
- * @return array new curl options
- */
- protected function prepareCurlOptions ( $curl_options, $extra_headers )
- {
- $hs = array();
- if (!empty($curl_options[CURLOPT_HTTPHEADER]) && is_array($curl_options[CURLOPT_HTTPHEADER]))
- {
- foreach ($curl_options[CURLOPT_HTTPHEADER] as $h)
- {
- list($opt, $val) = explode(':', $h, 2);
- $opt = str_replace(' ', '-', ucwords(str_replace('-', ' ', $opt)));
- $hs[$opt] = $val;
- }
- }
-
- $curl_options[CURLOPT_HTTPHEADER] = array();
- $hs = array_merge($hs, $extra_headers);
- foreach ($hs as $h => $v)
- {
- $curl_options[CURLOPT_HTTPHEADER][] = "$h: $v";
- }
- return $curl_options;
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthServer.php b/mod/oauth_api/vendors/oauth/library/OAuthServer.php
deleted file mode 100644
index c7f9097b3..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthServer.php
+++ /dev/null
@@ -1,232 +0,0 @@
-<?php
-
-/**
- * Server layer over the OAuthRequest handler
- *
- * @version $Id: OAuthServer.php 51 2008-10-15 15:15:47Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 27, 2007 12:36:38 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once 'OAuthRequestVerifier.php';
-
-class OAuthServer extends OAuthRequestVerifier
-{
- /**
- * Handle the request_token request.
- * Returns the new request token and request token secret.
- *
- * TODO: add correct result code to exception
- *
- * @return string returned request token, false on an error
- */
- public function requestToken ()
- {
- OAuthRequestLogger::start($this);
- try
- {
- $this->verify(false);
-
- $options = array();
- $ttl = $this->getParam('xoauth_token_ttl', false);
- if ($ttl)
- {
- $options['token_ttl'] = $ttl;
- }
-
- // Create a request token
- $store = OAuthStore::instance();
- $token = $store->addConsumerRequestToken($this->getParam('oauth_consumer_key', true), $options);
- $result = 'oauth_token='.$this->urlencode($token['token'])
- .'&oauth_token_secret='.$this->urlencode($token['token_secret']);
-
- if (!empty($token['token_ttl']))
- {
- $result .= '&xoauth_token_ttl='.$this->urlencode($token['token_ttl']);
- }
-
- $request_token = $token['token'];
-
- header('HTTP/1.1 200 OK');
- header('Content-Length: '.strlen($result));
- header('Content-Type: application/x-www-form-urlencoded');
-
- echo $result;
- }
- catch (OAuthException $e)
- {
- $request_token = false;
-
- header('HTTP/1.1 401 Unauthorized');
- header('Content-Type: text/plain');
-
- echo "OAuth Verification Failed: " . $e->getMessage();
- }
-
- OAuthRequestLogger::flush();
- return $request_token;
- }
-
-
- /**
- * Verify the start of an authorization request. Verifies if the request token is valid.
- * Next step is the method authorizeFinish()
- *
- * Nota bene: this stores the current token, consumer key and callback in the _SESSION
- *
- * @exception OAuthException thrown when not a valid request
- * @return array token description
- */
- public function authorizeVerify ( )
- {
- OAuthRequestLogger::start($this);
-
- $store = OAuthStore::instance();
- $token = $this->getParam('oauth_token', true);
- $rs = $store->getConsumerRequestToken($token);
- if (empty($rs))
- {
- throw new OAuthException('Unknown request token "'.$token.'"');
- }
-
- // We need to remember the callback
- if ( empty($_SESSION['verify_oauth_token'])
- || strcmp($_SESSION['verify_oauth_token'], $rs['token']))
- {
- $_SESSION['verify_oauth_token'] = $rs['token'];
- $_SESSION['verify_oauth_consumer_key'] = $rs['consumer_key'];
- $_SESSION['verify_oauth_callback'] = $this->getParam('oauth_callback', true);
- }
- OAuthRequestLogger::flush();
- return $rs;
- }
-
-
- /**
- * Overrule this method when you want to display a nice page when
- * the authorization is finished. This function does not know if the authorization was
- * succesfull, you need to check the token in the database.
- *
- * @param boolean authorized if the current token (oauth_token param) is authorized or not
- * @param int user_id user for which the token was authorized (or denied)
- */
- public function authorizeFinish ( $authorized, $user_id )
- {
- OAuthRequestLogger::start($this);
-
- $token = $this->getParam('oauth_token', true);
- if ( isset($_SESSION['verify_oauth_token'])
- && $_SESSION['verify_oauth_token'] == $token)
- {
- // Flag the token as authorized, or remove the token when not authorized
- $store = OAuthStore::instance();
-
- // Fetch the referrer host from the oauth callback parameter
- $referrer_host = '';
- $oauth_callback = false;
- if (!empty($_SESSION['verify_oauth_callback']))
- {
- $oauth_callback = $_SESSION['verify_oauth_callback'];
- $ps = parse_url($oauth_callback);
- if (isset($ps['host']))
- {
- $referrer_host = $ps['host'];
- }
- }
-
- if ($authorized)
- {
- OAuthRequestLogger::addNote('Authorized token "'.$token.'" for user '.$user_id.' with referrer "'.$referrer_host.'"');
- $store->authorizeConsumerRequestToken($token, $user_id, $referrer_host);
- }
- else
- {
- OAuthRequestLogger::addNote('Authorization rejected for token "'.$token.'" for user '.$user_id."\nToken has been deleted");
- $store->deleteConsumerRequestToken($token);
- }
-
- if (!empty($oauth_callback))
- {
- $this->redirect($oauth_callback, array('oauth_token'=>rawurlencode($token)));
- }
- }
- OAuthRequestLogger::flush();
- }
-
-
- /**
- * Exchange a request token for an access token.
- * The exchange is only succesful iff the request token has been authorized.
- *
- * Never returns, calls exit() when token is exchanged or when error is returned.
- */
- public function accessToken ()
- {
- OAuthRequestLogger::start($this);
-
- try
- {
- $this->verify('request');
-
- $options = array();
- $ttl = $this->getParam('xoauth_token_ttl', false);
- if ($ttl)
- {
- $options['token_ttl'] = $ttl;
- }
-
- $store = OAuthStore::instance();
- $token = $store->exchangeConsumerRequestForAccessToken($this->getParam('oauth_token', true), $options);
- $result = 'oauth_token='.$this->urlencode($token['token'])
- .'&oauth_token_secret='.$this->urlencode($token['token_secret']);
-
- if (!empty($token['token_ttl']))
- {
- $result .= '&xoauth_token_ttl='.$this->urlencode($token['token_ttl']);
- }
-
- header('HTTP/1.1 200 OK');
- header('Content-Length: '.strlen($result));
- header('Content-Type: application/x-www-form-urlencoded');
-
- echo $result;
- }
- catch (OAuthException $e)
- {
- header('HTTP/1.1 401 Access Denied');
- header('Content-Type: text/plain');
-
- echo "OAuth Verification Failed: " . $e->getMessage();
- }
-
- OAuthRequestLogger::flush();
- exit();
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/OAuthStore.php b/mod/oauth_api/vendors/oauth/library/OAuthStore.php
deleted file mode 100644
index 1841ab5fa..000000000
--- a/mod/oauth_api/vendors/oauth/library/OAuthStore.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-/**
- * Storage container for the oauth credentials, both server and consumer side.
- * This is the factory to select the store you want to use
- *
- * @version $Id: OAuthStore.php 49 2008-10-01 09:43:19Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:03:30 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once dirname(__FILE__) . '/OAuthException.php';
-
-class OAuthStore
-{
- static private $instance = false;
-
- /**
- * Request an instance of the OAuthStore
- */
- public static function instance ( $store = 'MySQL', $options = array() )
- {
- if (!OAuthStore::$instance)
- {
- // Select the store you want to use
- if (strpos($store, '/') === false)
- {
- $class = 'OAuthStore'.$store;
- $file = dirname(__FILE__) . '/store/'.$class.'.php';
- }
- else
- {
- $file = $store;
- $store = basename($file, '.php');
- $class = $store;
- }
-
- if (is_file($file))
- {
- require_once $file;
-
- if (class_exists($class))
- {
- OAuthStore::$instance = new $class($options);
- }
- else
- {
- throw new OAuthException('Could not find class '.$class.' in file '.$file);
- }
- }
- else
- {
- throw new OAuthException('No OAuthStore for '.$store.' (file '.$file.')');
- }
- }
- return OAuthStore::$instance;
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php b/mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php
deleted file mode 100644
index 84123b6d0..000000000
--- a/mod/oauth_api/vendors/oauth/library/body/OAuthBodyContentDisposition.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-/**
- * Add the extra headers for a PUT or POST request with a file.
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-class OAuthBodyContentDisposition
-{
- /**
- * Builds the request string.
- *
- * The files array can be a combination of the following (either data or file):
- *
- * file => "path/to/file", filename=, mime=, data=
- *
- * @param array files (name => filedesc) (not urlencoded)
- * @return array (headers, body)
- */
- static function encodeBody ( $files )
- {
- $headers = array();
- $body = null;
-
- // 1. Add all the files to the post
- if (!empty($files))
- {
- foreach ($files as $name => $f)
- {
- $data = false;
- $filename = false;
-
- if (isset($f['filename']))
- {
- $filename = $f['filename'];
- }
-
- if (!empty($f['file']))
- {
- $data = @file_get_contents($f['file']);
- if ($data === false)
- {
- throw new OAuthException(sprintf('Could not read the file "%s" for request body', $f['file']));
- }
- if (empty($filename))
- {
- $filename = basename($f['file']);
- }
- }
- else if (isset($f['data']))
- {
- $data = $f['data'];
- }
-
- // When there is data, add it as a request body, otherwise silently skip the upload
- if ($data !== false)
- {
- if (isset($headers['Content-Disposition']))
- {
- throw new OAuthException('Only a single file (or data) allowed in a signed PUT/POST request body.');
- }
-
- if (empty($filename))
- {
- $filename = 'untitled';
- }
- $mime = !empty($f['mime']) ? $f['mime'] : 'application/octet-stream';
-
- $headers['Content-Disposition'] = 'attachment; filename="'.OAuthBodyContentDisposition::encodeParameterName($filename).'"';
- $headers['Content-Type'] = $mime;
-
- $body = $data;
- }
-
- }
-
- // When we have a body, add the content-length
- if (!is_null($body))
- {
- $headers['Content-Length'] = strlen($body);
- }
- }
- return array($headers, $body);
- }
-
-
- /**
- * Encode a parameter's name for use in a multipart header.
- * For now we do a simple filter that removes some unwanted characters.
- * We might want to implement RFC1522 here. See http://tools.ietf.org/html/rfc1522
- *
- * @param string name
- * @return string
- */
- static function encodeParameterName ( $name )
- {
- return preg_replace('/[^\x20-\x7f]|"/', '-', $name);
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php b/mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php
deleted file mode 100644
index 048fdeb63..000000000
--- a/mod/oauth_api/vendors/oauth/library/body/OAuthBodyMultipartFormdata.php
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-
-/**
- * Create the body for a multipart/form-data message.
- *
- * @version $Id: OAuthMultipartFormdata.php 6 2008-02-13 12:35:09Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Jan 31, 2008 12:50:05 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-class OAuthBodyMultipartFormdata
-{
- /**
- * Builds the request string.
- *
- * The files array can be a combination of the following (either data or file):
- *
- * file => "path/to/file", filename=, mime=, data=
- *
- * @param array params (name => value) (all names and values should be urlencoded)
- * @param array files (name => filedesc) (not urlencoded)
- * @return array (headers, body)
- */
- static function encodeBody ( $params, $files )
- {
- $headers = array();
- $body = '';
- $boundary = 'OAuthRequester_'.md5(uniqid('multipart') . microtime());
- $headers['Content-Type'] = 'multipart/form-data; boundary=' . $boundary;
-
-
- // 1. Add the parameters to the post
- if (!empty($params))
- {
- foreach ($params as $name => $value)
- {
- $body .= '--'.$boundary."\r\n";
- $body .= 'Content-Disposition: form-data; name="'.OAuthBodyMultipartFormdata::encodeParameterName(rawurldecode($name)).'"';
- $body .= "\r\n\r\n";
- $body .= urldecode($value);
- $body .= "\r\n";
- }
- }
-
- // 2. Add all the files to the post
- if (!empty($files))
- {
- $untitled = 1;
-
- foreach ($files as $name => $f)
- {
- $data = false;
- $filename = false;
-
- if (isset($f['filename']))
- {
- $filename = $f['filename'];
- }
-
- if (!empty($f['file']))
- {
- $data = @file_get_contents($f['file']);
- if ($data === false)
- {
- throw new OAuthException(sprintf('Could not read the file "%s" for form-data part', $f['file']));
- }
- if (empty($filename))
- {
- $filename = basename($f['file']);
- }
- }
- else if (isset($f['data']))
- {
- $data = $f['data'];
- }
-
- // When there is data, add it as a form-data part, otherwise silently skip the upload
- if ($data !== false)
- {
- if (empty($filename))
- {
- $filename = sprintf('untitled-%d', $untitled++);
- }
- $mime = !empty($f['mime']) ? $f['mime'] : 'application/octet-stream';
- $body .= '--'.$boundary."\r\n";
- $body .= 'Content-Disposition: form-data; name="'.OAuthBodyMultipartFormdata::encodeParameterName($name).'"; filename="'.OAuthBodyMultipartFormdata::encodeParameterName($filename).'"'."\r\n";
- $body .= 'Content-Type: '.$mime;
- $body .= "\r\n\r\n";
- $body .= $data;
- $body .= "\r\n";
- }
-
- }
- }
- $body .= '--'.$boundary."--\r\n";
-
- $headers['Content-Length'] = strlen($body);
- return array($headers, $body);
- }
-
-
- /**
- * Encode a parameter's name for use in a multipart header.
- * For now we do a simple filter that removes some unwanted characters.
- * We might want to implement RFC1522 here. See http://tools.ietf.org/html/rfc1522
- *
- * @param string name
- * @return string
- */
- static function encodeParameterName ( $name )
- {
- return preg_replace('/[^\x20-\x7f]|"/', '-', $name);
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php b/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php
deleted file mode 100644
index c9cf94997..000000000
--- a/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.php
+++ /dev/null
@@ -1,304 +0,0 @@
-<?php
-
-/**
- * Parse a XRDS discovery description to a simple array format.
- *
- * For now a simple parse of the document. Better error checking
- * in a later version.
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-/* example of use:
-
-header('content-type: text/plain');
-$file = file_get_contents('../../test/discovery/xrds-magnolia.xrds');
-$xrds = xrds_parse($file);
-print_r($xrds);
-
- */
-
-/**
- * Parse the xrds file in the argument. The xrds description must have been
- * fetched via curl or something else.
- *
- * TODO: more robust checking, support for more service documents
- * TODO: support for URIs to definition instead of local xml:id
- *
- * @param string data contents of xrds file
- * @exception Exception when the file is in an unknown format
- * @return array
- */
-function xrds_parse ( $data )
-{
- $oauth = array();
- $doc = @DOMDocument::loadXML($data);
- if ($doc === false)
- {
- throw new Exception('Error in XML, can\'t load XRDS document');
- }
-
- $xpath = new DOMXPath($doc);
- $xpath->registerNamespace('xrds', 'xri://$xrds');
- $xpath->registerNamespace('xrd', 'xri://$XRD*($v*2.0)');
- $xpath->registerNamespace('simple', 'http://xrds-simple.net/core/1.0');
-
- // Yahoo! uses this namespace, with lowercase xrd in it
- $xpath->registerNamespace('xrd2', 'xri://$xrd*($v*2.0)');
-
- $uris = xrds_oauth_service_uris($xpath);
-
- foreach ($uris as $uri)
- {
- // TODO: support uris referring to service documents outside this one
- if ($uri{0} == '#')
- {
- $id = substr($uri, 1);
- $oauth = xrds_xrd_oauth($xpath, $id);
- if (is_array($oauth) && !empty($oauth))
- {
- return $oauth;
- }
- }
- }
-
- return false;
-}
-
-
-/**
- * Parse a XRD definition for OAuth and return the uris etc.
- *
- * @param XPath xpath
- * @param string id
- * @return array
- */
-function xrds_xrd_oauth ( $xpath, $id )
-{
- $oauth = array();
- $xrd = $xpath->query('//xrds:XRDS/xrd:XRD[@xml:id="'.$id.'"]');
- if ($xrd->length == 0)
- {
- // Yahoo! uses another namespace
- $xrd = $xpath->query('//xrds:XRDS/xrd2:XRD[@xml:id="'.$id.'"]');
- }
-
- if ($xrd->length >= 1)
- {
- $x = $xrd->item(0);
- $services = array();
- foreach ($x->childNodes as $n)
- {
- switch ($n->nodeName)
- {
- case 'Type':
- if ($n->nodeValue != 'xri://$xrds*simple')
- {
- // Not a simple XRDS document
- return false;
- }
- break;
- case 'Expires':
- $oauth['expires'] = $n->nodeValue;
- break;
- case 'Service':
- list($type,$service) = xrds_xrd_oauth_service($n);
- if ($type)
- {
- $services[$type][xrds_priority($n)][] = $service;
- }
- break;
- }
- }
-
- // Flatten the services on priority
- foreach ($services as $type => $service)
- {
- $oauth[$type] = xrds_priority_flatten($service);
- }
- }
- else
- {
- $oauth = false;
- }
- return $oauth;
-}
-
-
-/**
- * Parse a service definition for OAuth in a simple xrd element
- *
- * @param DOMElement n
- * @return array (type, service desc)
- */
-function xrds_xrd_oauth_service ( $n )
-{
- $service = array(
- 'uri' => '',
- 'signature_method' => array(),
- 'parameters' => array()
- );
-
- $type = false;
- foreach ($n->childNodes as $c)
- {
- $name = $c->nodeName;
- $value = $c->nodeValue;
-
- if ($name == 'URI')
- {
- $service['uri'] = $value;
- }
- else if ($name == 'Type')
- {
- if (strncmp($value, 'http://oauth.net/core/1.0/endpoint/', 35) == 0)
- {
- $type = basename($value);
- }
- else if (strncmp($value, 'http://oauth.net/core/1.0/signature/', 36) == 0)
- {
- $service['signature_method'][] = basename($value);
- }
- else if (strncmp($value, 'http://oauth.net/core/1.0/parameters/', 37) == 0)
- {
- $service['parameters'][] = basename($value);
- }
- else if (strncmp($value, 'http://oauth.net/discovery/1.0/consumer-identity/', 49) == 0)
- {
- $type = 'consumer_identity';
- $service['method'] = basename($value);
- unset($service['signature_method']);
- unset($service['parameters']);
- }
- else
- {
- $service['unknown'][] = $value;
- }
- }
- else if ($name == 'LocalID')
- {
- $service['consumer_key'] = $value;
- }
- else if ($name{0} != '#')
- {
- $service[strtolower($name)] = $value;
- }
- }
- return array($type, $service);
-}
-
-
-/**
- * Return the OAuth service uris in order of the priority.
- *
- * @param XPath xpath
- * @return array
- */
-function xrds_oauth_service_uris ( $xpath )
-{
- $uris = array();
- $xrd_oauth = $xpath->query('//xrds:XRDS/xrd:XRD/xrd:Service/xrd:Type[.=\'http://oauth.net/discovery/1.0\']');
- if ($xrd_oauth->length > 0)
- {
- $service = array();
- foreach ($xrd_oauth as $xo)
- {
- // Find the URI of the service definition
- $cs = $xo->parentNode->childNodes;
- foreach ($cs as $c)
- {
- if ($c->nodeName == 'URI')
- {
- $prio = xrds_priority($xo);
- $service[$prio][] = $c->nodeValue;
- }
- }
- }
- $uris = xrds_priority_flatten($service);
- }
- return $uris;
-}
-
-
-
-/**
- * Flatten an array according to the priority
- *
- * @param array ps buckets per prio
- * @return array one dimensional array
- */
-function xrds_priority_flatten ( $ps )
-{
- $prio = array();
- $null = array();
- ksort($ps);
- foreach ($ps as $idx => $bucket)
- {
- if (!empty($bucket))
- {
- if ($idx == 'null')
- {
- $null = $bucket;
- }
- else
- {
- $prio = array_merge($prio, $bucket);
- }
- }
- }
- $prio = array_merge($prio, $bucket);
- return $prio;
-}
-
-
-/**
- * Fetch the priority of a element
- *
- * @param DOMElement elt
- * @return mixed 'null' or int
- */
-function xrds_priority ( $elt )
-{
- if ($elt->hasAttribute('priority'))
- {
- $prio = $elt->getAttribute('priority');
- if (is_numeric($prio))
- {
- $prio = intval($prio);
- }
- }
- else
- {
- $prio = 'null';
- }
- return $prio;
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt b/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt
deleted file mode 100644
index fd867ea9f..000000000
--- a/mod/oauth_api/vendors/oauth/library/discovery/xrds_parse.txt
+++ /dev/null
@@ -1,101 +0,0 @@
-The xrds_parse.php script contains the function:
-
- function xrds_parse ( $data. )
-
-$data Contains the contents of a XRDS XML file.
-When the data is invalid XML then this will throw an exception.
-
-After parsing a XRDS definition it will return a datastructure much like the one below.
-
-Array
-(
- [expires] => 2008-04-13T07:34:58Z
- [request] => Array
- (
- [0] => Array
- (
- [uri] => https://ma.gnolia.com/oauth/get_request_token
- [signature_method] => Array
- (
- [0] => HMAC-SHA1
- [1] => RSA-SHA1
- [2] => PLAINTEXT
- )
-
- [parameters] => Array
- (
- [0] => auth-header
- [1] => post-body
- [2] => uri-query
- )
- )
- )
-
- [authorize] => Array
- (
- [0] => Array
- (
- [uri] => http://ma.gnolia.com/oauth/authorize
- [signature_method] => Array
- (
- )
-
- [parameters] => Array
- (
- [0] => auth-header
- [1] => uri-query
- )
- )
- )
-
- [access] => Array
- (
- [0] => Array
- (
- [uri] => https://ma.gnolia.com/oauth/get_access_token
- [signature_method] => Array
- (
- [0] => HMAC-SHA1
- [1] => RSA-SHA1
- [2] => PLAINTEXT
- )
-
- [parameters] => Array
- (
- [0] => auth-header
- [1] => post-body
- [2] => uri-query
- )
- )
- )
-
- [resource] => Array
- (
- [0] => Array
- (
- [uri] =>
- [signature_method] => Array
- (
- [0] => HMAC-SHA1
- [1] => RSA-SHA1
- )
-
- [parameters] => Array
- (
- [0] => auth-header
- [1] => post-body
- [2] => uri-query
- )
- )
- )
-
- [consumer_identity] => Array
- (
- [0] => Array
- (
- [uri] => http://ma.gnolia.com/applications/new
- [method] => oob
- )
- )
-)
-
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php
deleted file mode 100644
index 34ccb428c..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod.class.php
+++ /dev/null
@@ -1,69 +0,0 @@
-<?php
-
-/**
- * Interface for OAuth signature methods
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:04:35 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-abstract class OAuthSignatureMethod
-{
- /**
- * Return the name of this signature
- *
- * @return string
- */
- abstract public function name();
-
- /**
- * Return the signature for the given request
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- abstract public function signature ( $request, $base_string, $consumer_secret, $token_secret );
-
- /**
- * Check if the request signature corresponds to the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string data to be signed, usually the base string, can be a request body
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature from the request, still urlencoded
- * @return string
- */
- abstract public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature );
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php
deleted file mode 100644
index 4bc949c10..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_HMAC_SHA1.php
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-/**
- * OAuth signature implementation using HMAC-SHA1
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:21:19 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php';
-
-
-class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod
-{
- public function name ()
- {
- return 'HMAC-SHA1';
- }
-
-
- /**
- * Calculate the signature using HMAC-SHA1
- * This function is copyright Andy Smith, 2007.
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- function signature ( $request, $base_string, $consumer_secret, $token_secret )
- {
- $key = $request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret);
- if (function_exists('hash_hmac'))
- {
- $signature = base64_encode(hash_hmac("sha1", $base_string, $key, true));
- }
- else
- {
- $blocksize = 64;
- $hashfunc = 'sha1';
- if (strlen($key) > $blocksize)
- {
- $key = pack('H*', $hashfunc($key));
- }
- $key = str_pad($key,$blocksize,chr(0x00));
- $ipad = str_repeat(chr(0x36),$blocksize);
- $opad = str_repeat(chr(0x5c),$blocksize);
- $hmac = pack(
- 'H*',$hashfunc(
- ($key^$opad).pack(
- 'H*',$hashfunc(
- ($key^$ipad).$base_string
- )
- )
- )
- );
- $signature = base64_encode($hmac);
- }
- return $request->urlencode($signature);
- }
-
-
- /**
- * Check if the request signature corresponds to the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string data to be signed, usually the base string, can be a request body
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature from the request, still urlencoded
- * @return string
- */
- public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
- {
- $a = $request->urldecode($signature);
- $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret));
-
- // We have to compare the decoded values
- $valA = base64_decode($a);
- $valB = base64_decode($b);
-
- // Crude binary comparison
- return rawurlencode($a) == rawurlencode($b);
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php
deleted file mode 100644
index 6f593a47f..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_MD5.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-/**
- * OAuth signature implementation using MD5
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:09:43 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php';
-
-
-class OAuthSignatureMethod_MD5 extends OAuthSignatureMethod
-{
- public function name ()
- {
- return 'MD5';
- }
-
-
- /**
- * Calculate the signature using MD5
- * Binary md5 digest, as distinct from PHP's built-in hexdigest.
- * This function is copyright Andy Smith, 2007.
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- function signature ( $request, $base_string, $consumer_secret, $token_secret )
- {
- $s .= '&'.$request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret);
- $md5 = md5($base_string);
- $bin = '';
-
- for ($i = 0; $i < strlen($md5); $i += 2)
- {
- $bin .= chr(hexdec($md5{$i+1}) + hexdec($md5{$i}) * 16);
- }
- return $request->urlencode(base64_encode($bin));
- }
-
-
- /**
- * Check if the request signature corresponds to the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string data to be signed, usually the base string, can be a request body
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature from the request, still urlencoded
- * @return string
- */
- public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
- {
- $a = $request->urldecode($signature);
- $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret));
-
- // We have to compare the decoded values
- $valA = base64_decode($a);
- $valB = base64_decode($b);
-
- // Crude binary comparison
- return rawurlencode($a) == rawurlencode($b);
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php
deleted file mode 100644
index 92ef30867..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_PLAINTEXT.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * OAuth signature implementation using PLAINTEXT
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:09:43 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once dirname(__FILE__).'/OAuthSignatureMethod.class.php';
-
-
-class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod
-{
- public function name ()
- {
- return 'PLAINTEXT';
- }
-
-
- /**
- * Calculate the signature using PLAINTEXT
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- function signature ( $request, $base_string, $consumer_secret, $token_secret )
- {
- return $request->urlencode($request->urlencode($consumer_secret).'&'.$request->urlencode($token_secret));
- }
-
-
- /**
- * Check if the request signature corresponds to the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string data to be signed, usually the base string, can be a request body
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature from the request, still urlencoded
- * @return string
- */
- public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
- {
- $a = $request->urldecode($signature);
- $b = $request->urldecode($this->signature($request, $base_string, $consumer_secret, $token_secret));
-
- return $request->urldecode($a) == $request->urldecode($b);
- }
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php b/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php
deleted file mode 100644
index 3bbde7d90..000000000
--- a/mod/oauth_api/vendors/oauth/library/signature_method/OAuthSignatureMethod_RSA_SHA1.php
+++ /dev/null
@@ -1,136 +0,0 @@
-<?php
-
-/**
- * OAuth signature implementation using PLAINTEXT
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Sep 8, 2008 12:00:14 PM
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod
-{
- public function name()
- {
- return 'RSA-SHA1';
- }
-
-
- /**
- * Fetch the public CERT key for the signature
- *
- * @param OAuthRequest request
- * @return string public key
- */
- protected function fetch_public_cert ( $request )
- {
- // not implemented yet, ideas are:
- // (1) do a lookup in a table of trusted certs keyed off of consumer
- // (2) fetch via http using a url provided by the requester
- // (3) some sort of specific discovery code based on request
- //
- // either way should return a string representation of the certificate
- throw OAuthException("OAuthSignatureMethod_RSA_SHA1::fetch_public_cert not implemented");
- }
-
-
- /**
- * Fetch the private CERT key for the signature
- *
- * @param OAuthRequest request
- * @return string private key
- */
- protected function fetch_private_cert ( $request )
- {
- // not implemented yet, ideas are:
- // (1) do a lookup in a table of trusted certs keyed off of consumer
- //
- // either way should return a string representation of the certificate
- throw OAuthException("OAuthSignatureMethod_RSA_SHA1::fetch_private_cert not implemented");
- }
-
-
- /**
- * Calculate the signature using RSA-SHA1
- * This function is copyright Andy Smith, 2008.
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @return string
- */
- public function signature ( $request, $base_string, $consumer_secret, $token_secret )
- {
- // Fetch the private key cert based on the request
- $cert = $this->fetch_private_cert($request);
-
- // Pull the private key ID from the certificate
- $privatekeyid = openssl_get_privatekey($cert);
-
- // Sign using the key
- $sig = false;
- $ok = openssl_sign($base_string, $sig, $privatekeyid);
-
- // Release the key resource
- openssl_free_key($privatekeyid);
-
- return $request->urlencode(base64_encode($sig));
- }
-
-
- /**
- * Check if the request signature is the same as the one calculated for the request.
- *
- * @param OAuthRequest request
- * @param string base_string
- * @param string consumer_secret
- * @param string token_secret
- * @param string signature
- * @return string
- */
- public function verify ( $request, $base_string, $consumer_secret, $token_secret, $signature )
- {
- $decoded_sig = base64_decode($request->urldecode($signature));
-
- // Fetch the public key cert based on the request
- $cert = $this->fetch_public_cert($request);
-
- // Pull the public key ID from the certificate
- $publickeyid = openssl_get_publickey($cert);
-
- // Check the computed signature against the one passed in the query
- $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
-
- // Release the key resource
- openssl_free_key($publickeyid);
- return $ok == 1;
- }
-
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php b/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php
deleted file mode 100644
index e7cca981a..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAbstract.class.php
+++ /dev/null
@@ -1,149 +0,0 @@
-<?php
-
-/**
- * Abstract base class for OAuthStore implementations
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-abstract class OAuthStoreAbstract
-{
- abstract public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' );
- abstract public function getSecretsForSignature ( $uri, $user_id );
- abstract public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' );
- abstract public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() );
-
- abstract public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false );
- abstract public function getServer( $consumer_key, $user_id, $user_is_admin = false );
- abstract public function getServerForUri ( $uri, $user_id );
- abstract public function listServerTokens ( $user_id );
- abstract public function countServerTokens ( $consumer_key );
- abstract public function getServerToken ( $consumer_key, $token, $user_id );
- abstract public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false );
- abstract public function listServers ( $q = '', $user_id );
- abstract public function updateServer ( $server, $user_id, $user_is_admin = false );
-
- abstract public function updateConsumer ( $consumer, $user_id, $user_is_admin = false );
- abstract public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false );
- abstract public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false );
- abstract public function getConsumerStatic ();
-
- abstract public function addConsumerRequestToken ( $consumer_key, $options = array() );
- abstract public function getConsumerRequestToken ( $token );
- abstract public function deleteConsumerRequestToken ( $token );
- abstract public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' );
- abstract public function countConsumerAccessTokens ( $consumer_key );
- abstract public function exchangeConsumerRequestForAccessToken ( $token, $options = array() );
- abstract public function getConsumerAccessToken ( $token, $user_id );
- abstract public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false );
- abstract public function setConsumerAccessTokenTtl ( $token, $ttl );
-
- abstract public function listConsumers ( $user_id );
- abstract public function listConsumerTokens ( $user_id );
-
- abstract public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce );
-
- abstract public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null );
- abstract public function listLog ( $options, $user_id );
-
- abstract public function install ();
-
- /**
- * Fetch the current static consumer key for this site, create it when it was not found.
- * The consumer secret for the consumer key is always empty.
- *
- * @return string consumer key
- */
-
-
- /* ** Some handy utility functions ** */
-
- /**
- * Generate a unique key
- *
- * @param boolean unique force the key to be unique
- * @return string
- */
- public function generateKey ( $unique = false )
- {
- $key = md5(uniqid(rand(), true));
- if ($unique)
- {
- list($usec,$sec) = explode(' ',microtime());
- $key .= dechex($usec).dechex($sec);
- }
- return $key;
- }
-
- /**
- * Check to see if a string is valid utf8
- *
- * @param string $s
- * @return boolean
- */
- protected function isUTF8 ( $s )
- {
- return preg_match('%(?:
- [\xC2-\xDF][\x80-\xBF] # non-overlong 2-byte
- |\xE0[\xA0-\xBF][\x80-\xBF] # excluding overlongs
- |[\xE1-\xEC\xEE\xEF][\x80-\xBF]{2} # straight 3-byte
- |\xED[\x80-\x9F][\x80-\xBF] # excluding surrogates
- |\xF0[\x90-\xBF][\x80-\xBF]{2} # planes 1-3
- |[\xF1-\xF3][\x80-\xBF]{3} # planes 4-15
- |\xF4[\x80-\x8F][\x80-\xBF]{2} # plane 16
- )+%xs', $s);
- }
-
-
- /**
- * Make a string utf8, replacing all non-utf8 chars with a '.'
- *
- * @param string
- * @return string
- */
- protected function makeUTF8 ( $s )
- {
- if (function_exists('iconv'))
- {
- do
- {
- $ok = true;
- $text = @iconv('UTF-8', 'UTF-8//TRANSLIT', $s);
- if (strlen($text) != strlen($s))
- {
- // Remove the offending character...
- $s = $text . '.' . substr($s, strlen($text) + 1);
- $ok = false;
- }
- }
- while (!$ok);
- }
- return $s;
- }
-
-}
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php b/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php
deleted file mode 100644
index 9c971733f..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreAnyMeta.php
+++ /dev/null
@@ -1,265 +0,0 @@
-<?php
-
-/**
- * Storage container for the oauth credentials, both server and consumer side.
- * This file can only be used in conjunction with anyMeta.
- *
- * @version $Id: OAuthStoreAnyMeta.php 49 2008-10-01 09:43:19Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:03:30 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once dirname(__FILE__) . '/../../../../core/inc/any_database.inc.php';
-require_once dirname(__FILE__) . '/OAuthStoreMySQL.php';
-
-
-class OAuthStoreAnymeta extends OAuthStoreMySQL
-{
- /**
- * Construct the OAuthStoreAnymeta
- *
- * @param array options
- */
- function __construct ( $options = array() )
- {
- parent::__construct(array('conn' => any_db_conn()));
- }
-
-
- /**
- * Add an entry to the log table
- *
- * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token)
- * @param string received
- * @param string sent
- * @param string base_string
- * @param string notes
- * @param int (optional) user_id
- */
- public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null )
- {
- if (is_null($user_id) && isset($GLOBALS['any_auth']))
- {
- $user_id = $GLOBALS['any_auth']->getUserId();
- }
- parent::addLog($keys, $received, $sent, $base_string, $notes, $user_id);
- }
-
-
- /**
- * Get a page of entries from the log. Returns the last 100 records
- * matching the options given.
- *
- * @param array options
- * @param int user_id current user
- * @return array log records
- */
- public function listLog ( $options, $user_id )
- {
- $where = array();
- $args = array();
- if (empty($options))
- {
- $where[] = 'olg_usa_id_ref = %d';
- $args[] = $user_id;
- }
- else
- {
- foreach ($options as $option => $value)
- {
- if (strlen($value) > 0)
- {
- switch ($option)
- {
- case 'osr_consumer_key':
- case 'ocr_consumer_key':
- case 'ost_token':
- case 'oct_token':
- $where[] = 'olg_'.$option.' = \'%s\'';
- $args[] = $value;
- break;
- }
- }
- }
-
- $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = %d)';
- $args[] = $user_id;
- }
-
- $rs = any_db_query_all_assoc('
- SELECT olg_id,
- olg_osr_consumer_key AS osr_consumer_key,
- olg_ost_token AS ost_token,
- olg_ocr_consumer_key AS ocr_consumer_key,
- olg_oct_token AS oct_token,
- olg_usa_id_ref AS user_id,
- olg_received AS received,
- olg_sent AS sent,
- olg_base_string AS base_string,
- olg_notes AS notes,
- olg_timestamp AS timestamp,
- INET_NTOA(olg_remote_ip) AS remote_ip
- FROM oauth_log
- WHERE '.implode(' AND ', $where).'
- ORDER BY olg_id DESC
- LIMIT 0,100', $args);
-
- return $rs;
- }
-
-
-
- /**
- * Initialise the database
- */
- public function install ()
- {
- parent::install();
-
- any_db_query("ALTER TABLE oauth_consumer_registry MODIFY ocr_usa_id_ref int(11) unsigned");
- any_db_query("ALTER TABLE oauth_consumer_token MODIFY oct_usa_id_ref int(11) unsigned not null");
- any_db_query("ALTER TABLE oauth_server_registry MODIFY osr_usa_id_ref int(11) unsigned");
- any_db_query("ALTER TABLE oauth_server_token MODIFY ost_usa_id_ref int(11) unsigned not null");
- any_db_query("ALTER TABLE oauth_log MODIFY olg_usa_id_ref int(11) unsigned");
-
- any_db_alter_add_fk('oauth_consumer_registry', 'ocr_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete set null');
- any_db_alter_add_fk('oauth_consumer_token', 'oct_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade');
- any_db_alter_add_fk('oauth_server_registry', 'osr_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete set null');
- any_db_alter_add_fk('oauth_server_token', 'ost_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade');
- any_db_alter_add_fk('oauth_log', 'olg_usa_id_ref', 'any_user_auth(usa_id_ref)', 'on update cascade on delete cascade');
- }
-
-
-
- /** Some simple helper functions for querying the mysql db **/
-
- /**
- * Perform a query, ignore the results
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- */
- protected function query ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- any_db_query($sql, $args);
- }
-
-
- /**
- * Perform a query, ignore the results
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_all_assoc ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- return any_db_query_all_assoc($sql, $args);
- }
-
-
- /**
- * Perform a query, return the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_row_assoc ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- return any_db_query_row_assoc($sql, $args);
- }
-
-
- /**
- * Perform a query, return the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_row ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- return any_db_query_row($sql, $args);
- }
-
-
- /**
- * Perform a query, return the first column of the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return mixed
- */
- protected function query_one ( $sql )
- {
- list($sql, $args) = $this->sql_args(func_get_args());
- return any_db_query_one($sql, $args);
- }
-
-
- /**
- * Return the number of rows affected in the last query
- *
- * @return int
- */
- protected function query_affected_rows ()
- {
- return any_db_affected_rows();
- }
-
-
- /**
- * Return the id of the last inserted row
- *
- * @return int
- */
- protected function query_insert_id ()
- {
- return any_db_insert_id();
- }
-
-
- private function sql_args ( $args )
- {
- $sql = array_shift($args);
- if (count($args) == 1 && is_array($args[0]))
- {
- $args = $args[0];
- }
- return array($sql, $args);
- }
-
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php b/mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php
deleted file mode 100644
index a1b04c5c8..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/OAuthStoreMySQL.php
+++ /dev/null
@@ -1,1879 +0,0 @@
-<?php
-
-/**
- * Storage container for the oauth credentials, both server and consumer side.
- * Based on MySQL
- *
- * @version $Id: OAuthStoreMySQL.php 64 2009-08-16 19:37:00Z marcw@pobox.com $
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 16, 2007 4:03:30 PM
- *
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-
-require_once dirname(__FILE__) . '/OAuthStoreAbstract.class.php';
-
-
-class OAuthStoreMySQL extends OAuthStoreAbstract
-{
- /**
- * The MySQL connection
- */
- protected $conn;
-
- /**
- * Maximum delta a timestamp may be off from a previous timestamp.
- * Allows multiple consumers with some clock skew to work with the same token.
- * Unit is seconds, default max skew is 10 minutes.
- */
- protected $max_timestamp_skew = 600;
-
- /**
- * Default ttl for request tokens
- */
- protected $max_request_token_ttl = 3600;
-
-
- /**
- * Construct the OAuthStoreMySQL.
- * In the options you have to supply either:
- * - server, username, password and database (for a mysql_connect)
- * - conn (for the connection to be used)
- *
- * @param array options
- */
- function __construct ( $options = array() )
- {
- if (isset($options['conn']))
- {
- $this->conn = $options['conn'];
- }
- else
- {
- if (isset($options['server']))
- {
- $server = $options['server'];
- $username = $options['username'];
-
- if (isset($options['password']))
- {
- $this->conn = mysql_connect($server, $username, $options['password']);
- }
- else
- {
- $this->conn = mysql_connect($server, $username);
- }
- }
- else
- {
- // Try the default mysql connect
- $this->conn = mysql_connect();
- }
-
- if ($this->conn === false)
- {
- throw new OAuthException('Could not connect to MySQL database: ' . mysql_error());
- }
-
- if (isset($options['database']))
- {
- if (!mysql_select_db($options['database'], $this->conn))
- {
- $this->sql_errcheck();
- }
- }
- $this->query('set character set utf8');
- }
- }
-
-
- /**
- * Find stored credentials for the consumer key and token. Used by an OAuth server
- * when verifying an OAuth request.
- *
- * @param string consumer_key
- * @param string token
- * @param string token_type false, 'request' or 'access'
- * @exception OAuthException when no secrets where found
- * @return array assoc (consumer_secret, token_secret, osr_id, ost_id, user_id)
- */
- public function getSecretsForVerify ( $consumer_key, $token, $token_type = 'access' )
- {
- if ($token_type === false)
- {
- $rs = $this->query_row_assoc('
- SELECT osr_id,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret
- FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- AND osr_enabled = 1
- ',
- $consumer_key);
-
- if ($rs)
- {
- $rs['token'] = false;
- $rs['token_secret'] = false;
- $rs['user_id'] = false;
- $rs['ost_id'] = false;
- }
- }
- else
- {
- $rs = $this->query_row_assoc('
- SELECT osr_id,
- ost_id,
- ost_usa_id_ref as user_id,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- ost_token as token,
- ost_token_secret as token_secret
- FROM oauth_server_registry
- JOIN oauth_server_token
- ON ost_osr_id_ref = osr_id
- WHERE ost_token_type = \'%s\'
- AND osr_consumer_key = \'%s\'
- AND ost_token = \'%s\'
- AND osr_enabled = 1
- AND ost_token_ttl >= NOW()
- ',
- $token_type, $consumer_key, $token);
- }
-
- if (empty($rs))
- {
- throw new OAuthException('The consumer_key "'.$consumer_key.'" token "'.$token.'" combination does not exist or is not enabled.');
- }
- return $rs;
- }
-
-
- /**
- * Find the server details for signing a request, always looks for an access token.
- * The returned credentials depend on which local user is making the request.
- *
- * The consumer_key must belong to the user or be public (user id is null)
- *
- * For signing we need all of the following:
- *
- * consumer_key consumer key associated with the server
- * consumer_secret consumer secret associated with this server
- * token access token associated with this server
- * token_secret secret for the access token
- * signature_methods signing methods supported by the server (array)
- *
- * @todo filter on token type (we should know how and with what to sign this request, and there might be old access tokens)
- * @param string uri uri of the server
- * @param int user_id id of the logged on user
- * @param string name (optional) name of the token (case sensitive)
- * @exception OAuthException when no credentials found
- * @return array
- */
- public function getSecretsForSignature ( $uri, $user_id, $name = '' )
- {
- // Find a consumer key and token for the given uri
- $ps = parse_url($uri);
- $host = isset($ps['host']) ? $ps['host'] : 'localhost';
- $path = isset($ps['path']) ? $ps['path'] : '';
-
- if (empty($path) || substr($path, -1) != '/')
- {
- $path .= '/';
- }
-
- // The owner of the consumer_key is either the user or nobody (public consumer key)
- $secrets = $this->query_row_assoc('
- SELECT ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- oct_token as token,
- oct_token_secret as token_secret,
- ocr_signature_methods as signature_methods
- FROM oauth_consumer_registry
- JOIN oauth_consumer_token ON oct_ocr_id_ref = ocr_id
- WHERE ocr_server_uri_host = \'%s\'
- AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path))
- AND (ocr_usa_id_ref = %s OR ocr_usa_id_ref IS NULL)
- AND oct_usa_id_ref = %d
- AND oct_token_type = \'access\'
- AND oct_name = \'%s\'
- AND oct_token_ttl >= NOW()
- ORDER BY ocr_usa_id_ref DESC, ocr_consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC
- LIMIT 0,1
- ', $host, $path, $user_id, $user_id, $name
- );
-
- if (empty($secrets))
- {
- throw new OAuthException('No server tokens available for '.$uri);
- }
- $secrets['signature_methods'] = explode(',', $secrets['signature_methods']);
- return $secrets;
- }
-
-
- /**
- * Get the token and token secret we obtained from a server.
- *
- * @param string consumer_key
- * @param string token
- * @param string token_type
- * @param int user_id the user owning the token
- * @param string name optional name for a named token
- * @exception OAuthException when no credentials found
- * @return array
- */
- public function getServerTokenSecrets ( $consumer_key, $token, $token_type, $user_id, $name = '' )
- {
- if ($token_type != 'request' && $token_type != 'access')
- {
- throw new OAuthException('Unkown token type "'.$token_type.'", must be either "request" or "access"');
- }
-
- // Take the most recent token of the given type
- $r = $this->query_row_assoc('
- SELECT ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- oct_token as token,
- oct_token_secret as token_secret,
- oct_name as token_name,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri,
- IF(oct_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(oct_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl
- FROM oauth_consumer_registry
- JOIN oauth_consumer_token
- ON oct_ocr_id_ref = ocr_id
- WHERE ocr_consumer_key = \'%s\'
- AND oct_token_type = \'%s\'
- AND oct_token = \'%s\'
- AND oct_usa_id_ref = %d
- AND oct_token_ttl >= NOW()
- ', $consumer_key, $token_type, $token, $user_id
- );
-
- if (empty($r))
- {
- throw new OAuthException('Could not find a "'.$token_type.'" token for consumer "'.$consumer_key.'" and user '.$user_id);
- }
- if (isset($r['signature_methods']) && !empty($r['signature_methods']))
- {
- $r['signature_methods'] = explode(',',$r['signature_methods']);
- }
- else
- {
- $r['signature_methods'] = array();
- }
- return $r;
- }
-
-
- /**
- * Add a request token we obtained from a server.
- *
- * @todo remove old tokens for this user and this ocr_id
- * @param string consumer_key key of the server in the consumer registry
- * @param string token_type one of 'request' or 'access'
- * @param string token
- * @param string token_secret
- * @param int user_id the user owning the token
- * @param array options extra options, name and token_ttl
- * @exception OAuthException when server is not known
- * @exception OAuthException when we received a duplicate token
- */
- public function addServerToken ( $consumer_key, $token_type, $token, $token_secret, $user_id, $options = array() )
- {
- if ($token_type != 'request' && $token_type != 'access')
- {
- throw new OAuthException('Unknown token type "'.$token_type.'", must be either "request" or "access"');
- }
-
- // Maximum time to live for this token
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $ttl = 'DATE_ADD(NOW(), INTERVAL '.intval($options['token_ttl']).' SECOND)';
- }
- else if ($token == 'request')
- {
- $ttl = 'DATE_ADD(NOW(), INTERVAL '.$this->max_request_token_ttl.' SECOND)';
- }
- else
- {
- $ttl = "'9999-12-31'";
- }
-
- $ocr_id = $this->query_one('
- SELECT ocr_id
- FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- ', $consumer_key);
-
- if (empty($ocr_id))
- {
- throw new OAuthException('No server associated with consumer_key "'.$consumer_key.'"');
- }
-
- // Named tokens, unique per user/consumer key
- if (isset($options['name']) && $options['name'] != '')
- {
- $name = $options['name'];
- }
- else
- {
- $name = '';
- }
-
- // Delete any old tokens with the same type and name for this user/server combination
- $this->query('
- DELETE FROM oauth_consumer_token
- WHERE oct_ocr_id_ref = %d
- AND oct_usa_id_ref = %d
- AND oct_token_type = LOWER(\'%s\')
- AND oct_name = \'%s\'
- ',
- $ocr_id,
- $user_id,
- $token_type,
- $name);
-
- // Insert the new token
- $this->query('
- INSERT IGNORE INTO oauth_consumer_token
- SET oct_ocr_id_ref = %d,
- oct_usa_id_ref = %d,
- oct_name = \'%s\',
- oct_token = \'%s\',
- oct_token_secret= \'%s\',
- oct_token_type = LOWER(\'%s\'),
- oct_timestamp = NOW(),
- oct_token_ttl = '.$ttl.'
- ',
- $ocr_id,
- $user_id,
- $name,
- $token,
- $token_secret,
- $token_type);
-
- if (!$this->query_affected_rows())
- {
- throw new OAuthException('Received duplicate token "'.$token.'" for the same consumer_key "'.$consumer_key.'"');
- }
- }
-
-
- /**
- * Delete a server key. This removes access to that site.
- *
- * @param string consumer_key
- * @param int user_id user registering this server
- * @param boolean user_is_admin
- */
- public function deleteServer ( $consumer_key, $user_id, $user_is_admin = false )
- {
- if ($user_is_admin)
- {
- $this->query('
- DELETE FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ', $consumer_key, $user_id);
- }
- else
- {
- $this->query('
- DELETE FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND ocr_usa_id_ref = %d
- ', $consumer_key, $user_id);
- }
- }
-
-
- /**
- * Get a server from the consumer registry using the consumer key
- *
- * @param string consumer_key
- * @param int user_id
- * @param boolean user_is_admin (optional)
- * @exception OAuthException when server is not found
- * @return array
- */
- public function getServer ( $consumer_key, $user_id, $user_is_admin = false )
- {
- $r = $this->query_row_assoc('
- SELECT ocr_id as id,
- ocr_usa_id_ref as user_id,
- ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri
- FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ', $consumer_key, $user_id);
-
- if (empty($r))
- {
- throw new OAuthException('No server with consumer_key "'.$consumer_key.'" has been registered (for this user)');
- }
-
- if (isset($r['signature_methods']) && !empty($r['signature_methods']))
- {
- $r['signature_methods'] = explode(',',$r['signature_methods']);
- }
- else
- {
- $r['signature_methods'] = array();
- }
- return $r;
- }
-
-
-
- /**
- * Find the server details that might be used for a request
- *
- * The consumer_key must belong to the user or be public (user id is null)
- *
- * @param string uri uri of the server
- * @param int user_id id of the logged on user
- * @exception OAuthException when no credentials found
- * @return array
- */
- public function getServerForUri ( $uri, $user_id )
- {
- // Find a consumer key and token for the given uri
- $ps = parse_url($uri);
- $host = isset($ps['host']) ? $ps['host'] : 'localhost';
- $path = isset($ps['path']) ? $ps['path'] : '';
-
- if (empty($path) || substr($path, -1) != '/')
- {
- $path .= '/';
- }
-
- // The owner of the consumer_key is either the user or nobody (public consumer key)
- $server = $this->query_row_assoc('
- SELECT ocr_id as id,
- ocr_usa_id_ref as user_id,
- ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri
- FROM oauth_consumer_registry
- WHERE ocr_server_uri_host = \'%s\'
- AND ocr_server_uri_path = LEFT(\'%s\', LENGTH(ocr_server_uri_path))
- AND (ocr_usa_id_ref = %s OR ocr_usa_id_ref IS NULL)
- ORDER BY ocr_usa_id_ref DESC, consumer_secret DESC, LENGTH(ocr_server_uri_path) DESC
- LIMIT 0,1
- ', $host, $path, $user_id
- );
-
- if (empty($server))
- {
- throw new OAuthException('No server available for '.$uri);
- }
- $server['signature_methods'] = explode(',', $server['signature_methods']);
- return $server;
- }
-
-
- /**
- * Get a list of all server token this user has access to.
- *
- * @param int usr_id
- * @return array
- */
- public function listServerTokens ( $user_id )
- {
- $ts = $this->query_all_assoc('
- SELECT ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- oct_id as token_id,
- oct_token as token,
- oct_token_secret as token_secret,
- oct_usa_id_ref as user_id,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_server_uri_host as server_uri_host,
- ocr_server_uri_path as server_uri_path,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri,
- oct_timestamp as timestamp
- FROM oauth_consumer_registry
- JOIN oauth_consumer_token
- ON oct_ocr_id_ref = ocr_id
- WHERE oct_usa_id_ref = %d
- AND oct_token_type = \'access\'
- AND oct_token_ttl >= NOW()
- ORDER BY ocr_server_uri_host, ocr_server_uri_path
- ', $user_id);
- return $ts;
- }
-
-
- /**
- * Count how many tokens we have for the given server
- *
- * @param string consumer_key
- * @return int
- */
- public function countServerTokens ( $consumer_key )
- {
- $count = $this->query_one('
- SELECT COUNT(oct_id)
- FROM oauth_consumer_token
- JOIN oauth_consumer_registry
- ON oct_ocr_id_ref = ocr_id
- WHERE oct_token_type = \'access\'
- AND ocr_consumer_key = \'%s\'
- AND oct_token_ttl >= NOW()
- ', $consumer_key);
-
- return $count;
- }
-
-
- /**
- * Get a specific server token for the given user
- *
- * @param string consumer_key
- * @param string token
- * @param int user_id
- * @exception OAuthException when no such token found
- * @return array
- */
- public function getServerToken ( $consumer_key, $token, $user_id )
- {
- $ts = $this->query_row_assoc('
- SELECT ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- oct_token as token,
- oct_token_secret as token_secret,
- oct_usa_id_ref as usr_id,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_server_uri_host as server_uri_host,
- ocr_server_uri_path as server_uri_path,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri,
- oct_timestamp as timestamp
- FROM oauth_consumer_registry
- JOIN oauth_consumer_token
- ON oct_ocr_id_ref = ocr_id
- WHERE ocr_consumer_key = \'%s\'
- AND oct_usa_id_ref = %d
- AND oct_token_type = \'access\'
- AND oct_token = \'%s\'
- AND oct_token_ttl >= NOW()
- ', $consumer_key, $user_id, $token);
-
- if (empty($ts))
- {
- throw new OAuthException('No such consumer key ('.$consumer_key.') and token ('.$token.') combination for user "'.$user_id.'"');
- }
- return $ts;
- }
-
-
- /**
- * Delete a token we obtained from a server.
- *
- * @param string consumer_key
- * @param string token
- * @param int user_id
- * @param boolean user_is_admin
- */
- public function deleteServerToken ( $consumer_key, $token, $user_id, $user_is_admin = false )
- {
- if ($user_is_admin)
- {
- $this->query('
- DELETE oauth_consumer_token
- FROM oauth_consumer_token
- JOIN oauth_consumer_registry
- ON oct_ocr_id_ref = ocr_id
- WHERE ocr_consumer_key = \'%s\'
- AND oct_token = \'%s\'
- ', $consumer_key, $token);
- }
- else
- {
- $this->query('
- DELETE oauth_consumer_token
- FROM oauth_consumer_token
- JOIN oauth_consumer_registry
- ON oct_ocr_id_ref = ocr_id
- WHERE ocr_consumer_key = \'%s\'
- AND oct_token = \'%s\'
- AND oct_usa_id_ref = %d
- ', $consumer_key, $token, $user_id);
- }
- }
-
-
- /**
- * Set the ttl of a server access token. This is done when the
- * server receives a valid request with a xoauth_token_ttl parameter in it.
- *
- * @param string consumer_key
- * @param string token
- * @param int token_ttl
- */
- public function setServerTokenTtl ( $consumer_key, $token, $token_ttl )
- {
- if ($token_ttl <= 0)
- {
- // Immediate delete when the token is past its ttl
- $this->deleteServerToken($consumer_key, $token, 0, true);
- }
- else
- {
- // Set maximum time to live for this token
- $this->query('
- UPDATE oauth_consumer_token, oauth_consumer_registry
- SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND)
- WHERE ocr_consumer_key = \'%s\'
- AND oct_ocr_id_ref = ocr_id
- AND oct_token = \'%s\'
- ', $token_ttl, $consumer_key, $token);
- }
- }
-
-
- /**
- * Get a list of all consumers from the consumer registry.
- * The consumer keys belong to the user or are public (user id is null)
- *
- * @param string q query term
- * @param int user_id
- * @return array
- */
- public function listServers ( $q = '', $user_id )
- {
- $q = trim(str_replace('%', '', $q));
- $args = array();
-
- if (!empty($q))
- {
- $where = ' WHERE ( ocr_consumer_key like \'%%%s%%\'
- OR ocr_server_uri like \'%%%s%%\'
- OR ocr_server_uri_host like \'%%%s%%\'
- OR ocr_server_uri_path like \'%%%s%%\')
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ';
-
- $args[] = $q;
- $args[] = $q;
- $args[] = $q;
- $args[] = $q;
- $args[] = $user_id;
- }
- else
- {
- $where = ' WHERE ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL';
- $args[] = $user_id;
- }
-
- $servers = $this->query_all_assoc('
- SELECT ocr_id as id,
- ocr_usa_id_ref as user_id,
- ocr_consumer_key as consumer_key,
- ocr_consumer_secret as consumer_secret,
- ocr_signature_methods as signature_methods,
- ocr_server_uri as server_uri,
- ocr_server_uri_host as server_uri_host,
- ocr_server_uri_path as server_uri_path,
- ocr_request_token_uri as request_token_uri,
- ocr_authorize_uri as authorize_uri,
- ocr_access_token_uri as access_token_uri
- FROM oauth_consumer_registry
- '.$where.'
- ORDER BY ocr_server_uri_host, ocr_server_uri_path
- ', $args);
- return $servers;
- }
-
-
- /**
- * Register or update a server for our site (we will be the consumer)
- *
- * (This is the registry at the consumers, registering servers ;-) )
- *
- * @param array server
- * @param int user_id user registering this server
- * @param boolean user_is_admin
- * @exception OAuthException when fields are missing or on duplicate consumer_key
- * @return consumer_key
- */
- public function updateServer ( $server, $user_id, $user_is_admin = false )
- {
- foreach (array('consumer_key', 'server_uri') as $f)
- {
- if (empty($server[$f]))
- {
- throw new OAuthException('The field "'.$f.'" must be set and non empty');
- }
- }
-
- if (!empty($server['id']))
- {
- $exists = $this->query_one('
- SELECT ocr_id
- FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND ocr_id <> %d
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ', $server['consumer_key'], $server['id'], $user_id);
- }
- else
- {
- $exists = $this->query_one('
- SELECT ocr_id
- FROM oauth_consumer_registry
- WHERE ocr_consumer_key = \'%s\'
- AND (ocr_usa_id_ref = %d OR ocr_usa_id_ref IS NULL)
- ', $server['consumer_key'], $user_id);
- }
-
- if ($exists)
- {
- throw new OAuthException('The server with key "'.$server['consumer_key'].'" has already been registered');
- }
-
- $parts = parse_url($server['server_uri']);
- $host = (isset($parts['host']) ? $parts['host'] : 'localhost');
- $path = (isset($parts['path']) ? $parts['path'] : '/');
-
- if (isset($server['signature_methods']))
- {
- if (is_array($server['signature_methods']))
- {
- $server['signature_methods'] = strtoupper(implode(',', $server['signature_methods']));
- }
- }
- else
- {
- $server['signature_methods'] = '';
- }
-
- // When the user is an admin, then the user can update the user_id of this record
- if ($user_is_admin && array_key_exists('user_id', $server))
- {
- if (is_null($server['user_id']))
- {
- $update_user = ', ocr_usa_id_ref = NULL';
- }
- else
- {
- $update_user = ', ocr_usa_id_ref = '.intval($server['user_id']);
- }
- }
- else
- {
- $update_user = '';
- }
-
- if (!empty($server['id']))
- {
- // Check if the current user can update this server definition
- if (!$user_is_admin)
- {
- $ocr_usa_id_ref = $this->query_one('
- SELECT ocr_usa_id_ref
- FROM oauth_consumer_registry
- WHERE ocr_id = %d
- ', $server['id']);
-
- if ($ocr_usa_id_ref != $user_id)
- {
- throw new OAuthException('The user "'.$user_id.'" is not allowed to update this server');
- }
- }
-
- // Update the consumer registration
- $this->query('
- UPDATE oauth_consumer_registry
- SET ocr_consumer_key = \'%s\',
- ocr_consumer_secret = \'%s\',
- ocr_server_uri = \'%s\',
- ocr_server_uri_host = \'%s\',
- ocr_server_uri_path = \'%s\',
- ocr_timestamp = NOW(),
- ocr_request_token_uri = \'%s\',
- ocr_authorize_uri = \'%s\',
- ocr_access_token_uri = \'%s\',
- ocr_signature_methods = \'%s\'
- '.$update_user.'
- WHERE ocr_id = %d
- ',
- $server['consumer_key'],
- $server['consumer_secret'],
- $server['server_uri'],
- strtolower($host),
- $path,
- isset($server['request_token_uri']) ? $server['request_token_uri'] : '',
- isset($server['authorize_uri']) ? $server['authorize_uri'] : '',
- isset($server['access_token_uri']) ? $server['access_token_uri'] : '',
- $server['signature_methods'],
- $server['id']
- );
- }
- else
- {
- if (empty($update_user))
- {
- // Per default the user owning the key is the user registering the key
- $update_user = ', ocr_usa_id_ref = '.intval($user_id);
- }
-
- $this->query('
- INSERT INTO oauth_consumer_registry
- SET ocr_consumer_key = \'%s\',
- ocr_consumer_secret = \'%s\',
- ocr_server_uri = \'%s\',
- ocr_server_uri_host = \'%s\',
- ocr_server_uri_path = \'%s\',
- ocr_timestamp = NOW(),
- ocr_request_token_uri = \'%s\',
- ocr_authorize_uri = \'%s\',
- ocr_access_token_uri = \'%s\',
- ocr_signature_methods = \'%s\'
- '.$update_user,
- $server['consumer_key'],
- $server['consumer_secret'],
- $server['server_uri'],
- strtolower($host),
- $path,
- isset($server['request_token_uri']) ? $server['request_token_uri'] : '',
- isset($server['authorize_uri']) ? $server['authorize_uri'] : '',
- isset($server['access_token_uri']) ? $server['access_token_uri'] : '',
- $server['signature_methods']
- );
-
- $ocr_id = $this->query_insert_id();
- }
- return $server['consumer_key'];
- }
-
-
- /**
- * Insert/update a new consumer with this server (we will be the server)
- * When this is a new consumer, then also generate the consumer key and secret.
- * Never updates the consumer key and secret.
- * When the id is set, then the key and secret must correspond to the entry
- * being updated.
- *
- * (This is the registry at the server, registering consumers ;-) )
- *
- * @param array consumer
- * @param int user_id user registering this consumer
- * @param boolean user_is_admin
- * @return string consumer key
- */
- public function updateConsumer ( $consumer, $user_id, $user_is_admin = false )
- {
- if (!$user_is_admin)
- {
- foreach (array('requester_name', 'requester_email') as $f)
- {
- if (empty($consumer[$f]))
- {
- throw new OAuthException('The field "'.$f.'" must be set and non empty');
- }
- }
- }
-
- if (!empty($consumer['id']))
- {
- if (empty($consumer['consumer_key']))
- {
- throw new OAuthException('The field "consumer_key" must be set and non empty');
- }
- if (!$user_is_admin && empty($consumer['consumer_secret']))
- {
- throw new OAuthException('The field "consumer_secret" must be set and non empty');
- }
-
- // Check if the current user can update this server definition
- if (!$user_is_admin)
- {
- $osr_usa_id_ref = $this->query_one('
- SELECT osr_usa_id_ref
- FROM oauth_server_registry
- WHERE osr_id = %d
- ', $consumer['id']);
-
- if ($osr_usa_id_ref != $user_id)
- {
- throw new OAuthException('The user "'.$user_id.'" is not allowed to update this consumer');
- }
- }
- else
- {
- // User is an admin, allow a key owner to be changed or key to be shared
- if (array_key_exists('user_id',$consumer))
- {
- if (is_null($consumer['user_id']))
- {
- $this->query('
- UPDATE oauth_server_registry
- SET osr_usa_id_ref = NULL
- WHERE osr_id = %d
- ', $consumer['id']);
- }
- else
- {
- $this->query('
- UPDATE oauth_server_registry
- SET osr_usa_id_ref = %d
- WHERE osr_id = %d
- ', $consumer['user_id'], $consumer['id']);
- }
- }
- }
-
- $this->query('
- UPDATE oauth_server_registry
- SET osr_requester_name = \'%s\',
- osr_requester_email = \'%s\',
- osr_callback_uri = \'%s\',
- osr_application_uri = \'%s\',
- osr_application_title = \'%s\',
- osr_application_descr = \'%s\',
- osr_application_notes = \'%s\',
- osr_application_type = \'%s\',
- osr_application_commercial = IF(%d,1,0),
- osr_timestamp = NOW()
- WHERE osr_id = %d
- AND osr_consumer_key = \'%s\'
- AND osr_consumer_secret = \'%s\'
- ',
- $consumer['requester_name'],
- $consumer['requester_email'],
- isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '',
- isset($consumer['application_uri']) ? $consumer['application_uri'] : '',
- isset($consumer['application_title']) ? $consumer['application_title'] : '',
- isset($consumer['application_descr']) ? $consumer['application_descr'] : '',
- isset($consumer['application_notes']) ? $consumer['application_notes'] : '',
- isset($consumer['application_type']) ? $consumer['application_type'] : '',
- isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0,
- $consumer['id'],
- $consumer['consumer_key'],
- $consumer['consumer_secret']
- );
-
-
- $consumer_key = $consumer['consumer_key'];
- }
- else
- {
- $consumer_key = $this->generateKey(true);
- $consumer_secret= $this->generateKey();
-
- // When the user is an admin, then the user can be forced to something else that the user
- if ($user_is_admin && array_key_exists('user_id',$consumer))
- {
- if (is_null($consumer['user_id']))
- {
- $owner_id = 'NULL';
- }
- else
- {
- $owner_id = intval($consumer['user_id']);
- }
- }
- else
- {
- // No admin, take the user id as the owner id.
- $owner_id = intval($user_id);
- }
-
- $this->query('
- INSERT INTO oauth_server_registry
- SET osr_enabled = 1,
- osr_status = \'active\',
- osr_usa_id_ref = %s,
- osr_consumer_key = \'%s\',
- osr_consumer_secret = \'%s\',
- osr_requester_name = \'%s\',
- osr_requester_email = \'%s\',
- osr_callback_uri = \'%s\',
- osr_application_uri = \'%s\',
- osr_application_title = \'%s\',
- osr_application_descr = \'%s\',
- osr_application_notes = \'%s\',
- osr_application_type = \'%s\',
- osr_application_commercial = IF(%d,1,0),
- osr_timestamp = NOW(),
- osr_issue_date = NOW()
- ',
- $owner_id,
- $consumer_key,
- $consumer_secret,
- $consumer['requester_name'],
- $consumer['requester_email'],
- isset($consumer['callback_uri']) ? $consumer['callback_uri'] : '',
- isset($consumer['application_uri']) ? $consumer['application_uri'] : '',
- isset($consumer['application_title']) ? $consumer['application_title'] : '',
- isset($consumer['application_descr']) ? $consumer['application_descr'] : '',
- isset($consumer['application_notes']) ? $consumer['application_notes'] : '',
- isset($consumer['application_type']) ? $consumer['application_type'] : '',
- isset($consumer['application_commercial']) ? $consumer['application_commercial'] : 0
- );
- }
- return $consumer_key;
-
- }
-
-
-
- /**
- * Delete a consumer key. This removes access to our site for all applications using this key.
- *
- * @param string consumer_key
- * @param int user_id user registering this server
- * @param boolean user_is_admin
- */
- public function deleteConsumer ( $consumer_key, $user_id, $user_is_admin = false )
- {
- if ($user_is_admin)
- {
- $this->query('
- DELETE FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- AND (osr_usa_id_ref = %d OR osr_usa_id_ref IS NULL)
- ', $consumer_key, $user_id);
- }
- else
- {
- $this->query('
- DELETE FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- AND osr_usa_id_ref = %d
- ', $consumer_key, $user_id);
- }
- }
-
-
-
- /**
- * Fetch a consumer of this server, by consumer_key.
- *
- * @param string consumer_key
- * @param int user_id
- * @param boolean user_is_admin (optional)
- * @exception OAuthException when consumer not found
- * @return array
- */
- public function getConsumer ( $consumer_key, $user_id, $user_is_admin = false )
- {
- $consumer = $this->query_row_assoc('
- SELECT *
- FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- ', $consumer_key);
-
- if (!is_array($consumer))
- {
- throw new OAuthException('No consumer with consumer_key "'.$consumer_key.'"');
- }
-
- $c = array();
- foreach ($consumer as $key => $value)
- {
- $c[substr($key, 4)] = $value;
- }
- $c['user_id'] = $c['usa_id_ref'];
-
- if (!$user_is_admin && !empty($c['user_id']) && $c['user_id'] != $user_id)
- {
- throw new OAuthException('No access to the consumer information for consumer_key "'.$consumer_key.'"');
- }
- return $c;
- }
-
-
- /**
- * Fetch the static consumer key for this provider. The user for the static consumer
- * key is NULL (no user, shared key). If the key did not exist then the key is created.
- *
- * @return string
- */
- public function getConsumerStatic ()
- {
- $consumer = $this->query_one('
- SELECT osr_consumer_key
- FROM oauth_server_registry
- WHERE osr_consumer_key LIKE \'sc-%%\'
- AND osr_usa_id_ref IS NULL
- ');
-
- if (empty($consumer))
- {
- $consumer_key = 'sc-'.$this->generateKey(true);
- $this->query('
- INSERT INTO oauth_server_registry
- SET osr_enabled = 1,
- osr_status = \'active\',
- osr_usa_id_ref = NULL,
- osr_consumer_key = \'%s\',
- osr_consumer_secret = \'\',
- osr_requester_name = \'\',
- osr_requester_email = \'\',
- osr_callback_uri = \'\',
- osr_application_uri = \'\',
- osr_application_title = \'Static shared consumer key\',
- osr_application_descr = \'\',
- osr_application_notes = \'Static shared consumer key\',
- osr_application_type = \'\',
- osr_application_commercial = 0,
- osr_timestamp = NOW(),
- osr_issue_date = NOW()
- ',
- $consumer_key
- );
-
- // Just make sure that if the consumer key is truncated that we get the truncated string
- $consumer = $this->getConsumerStatic();
- }
- return $consumer;
- }
-
-
- /**
- * Add an unautorized request token to our server.
- *
- * @param string consumer_key
- * @param array options (eg. token_ttl)
- * @return array (token, token_secret)
- */
- public function addConsumerRequestToken ( $consumer_key, $options = array() )
- {
- $token = $this->generateKey(true);
- $secret = $this->generateKey();
- $osr_id = $this->query_one('
- SELECT osr_id
- FROM oauth_server_registry
- WHERE osr_consumer_key = \'%s\'
- AND osr_enabled = 1
- ', $consumer_key);
-
- if (!$osr_id)
- {
- throw new OAuthException('No server with consumer_key "'.$consumer_key.'" or consumer_key is disabled');
- }
-
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $ttl = intval($options['token_ttl']);
- }
- else
- {
- $ttl = $this->max_request_token_ttl;
- }
-
- $this->query('
- INSERT INTO oauth_server_token
- SET ost_osr_id_ref = %d,
- ost_usa_id_ref = 1,
- ost_token = \'%s\',
- ost_token_secret = \'%s\',
- ost_token_type = \'request\',
- ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND)
- ON DUPLICATE KEY UPDATE
- ost_osr_id_ref = VALUES(ost_osr_id_ref),
- ost_usa_id_ref = VALUES(ost_usa_id_ref),
- ost_token = VALUES(ost_token),
- ost_token_secret = VALUES(ost_token_secret),
- ost_token_type = VALUES(ost_token_type),
- ost_token_ttl = VALUES(ost_token_ttl),
- ost_timestamp = NOW()
- ', $osr_id, $token, $secret, $ttl);
-
- return array('token'=>$token, 'token_secret'=>$secret, 'token_ttl'=>$ttl);
- }
-
-
- /**
- * Fetch the consumer request token, by request token.
- *
- * @param string token
- * @return array token and consumer details
- */
- public function getConsumerRequestToken ( $token )
- {
- $rs = $this->query_row_assoc('
- SELECT ost_token as token,
- ost_token_secret as token_secret,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- ost_token_type as token_type
- FROM oauth_server_token
- JOIN oauth_server_registry
- ON ost_osr_id_ref = osr_id
- WHERE ost_token_type = \'request\'
- AND ost_token = \'%s\'
- AND ost_token_ttl >= NOW()
- ', $token);
-
- return $rs;
- }
-
-
- /**
- * Delete a consumer token. The token must be a request or authorized token.
- *
- * @param string token
- */
- public function deleteConsumerRequestToken ( $token )
- {
- $this->query('
- DELETE FROM oauth_server_token
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'request\'
- ', $token);
- }
-
-
- /**
- * Upgrade a request token to be an authorized request token.
- *
- * @param string token
- * @param int user_id user authorizing the token
- * @param string referrer_host used to set the referrer host for this token, for user feedback
- */
- public function authorizeConsumerRequestToken ( $token, $user_id, $referrer_host = '' )
- {
- $this->query('
- UPDATE oauth_server_token
- SET ost_authorized = 1,
- ost_usa_id_ref = %d,
- ost_timestamp = NOW(),
- ost_referrer_host = \'%s\'
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'request\'
- ', $user_id, $referrer_host, $token);
- }
-
-
- /**
- * Count the consumer access tokens for the given consumer.
- *
- * @param string consumer_key
- * @return int
- */
- public function countConsumerAccessTokens ( $consumer_key )
- {
- $count = $this->query_one('
- SELECT COUNT(ost_id)
- FROM oauth_server_token
- JOIN oauth_server_registry
- ON ost_osr_id_ref = osr_id
- WHERE ost_token_type = \'access\'
- AND osr_consumer_key = \'%s\'
- AND ost_token_ttl >= NOW()
- ', $consumer_key);
-
- return $count;
- }
-
-
- /**
- * Exchange an authorized request token for new access token.
- *
- * @param string token
- * @param array options options for the token, token_ttl
- * @exception OAuthException when token could not be exchanged
- * @return array (token, token_secret)
- */
- public function exchangeConsumerRequestForAccessToken ( $token, $options = array() )
- {
- $new_token = $this->generateKey(true);
- $new_secret = $this->generateKey();
-
- // Maximum time to live for this token
- if (isset($options['token_ttl']) && is_numeric($options['token_ttl']))
- {
- $ttl_sql = 'DATE_ADD(NOW(), INTERVAL '.intval($options['token_ttl']).' SECOND)';
- }
- else
- {
- $ttl_sql = "'9999-12-31'";
- }
-
- $this->query('
- UPDATE oauth_server_token
- SET ost_token = \'%s\',
- ost_token_secret = \'%s\',
- ost_token_type = \'access\',
- ost_timestamp = NOW(),
- ost_token_ttl = '.$ttl_sql.'
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'request\'
- AND ost_authorized = 1
- AND ost_token_ttl >= NOW()
- ', $new_token, $new_secret, $token);
-
- if ($this->query_affected_rows() != 1)
- {
- throw new OAuthException('Can\'t exchange request token "'.$token.'" for access token. No such token or not authorized');
- }
-
- $ret = array('token' => $new_token, 'token_secret' => $new_secret);
- $ttl = $this->query_one('
- SELECT IF(ost_token_ttl >= \'9999-12-31\', NULL, UNIX_TIMESTAMP(ost_token_ttl) - UNIX_TIMESTAMP(NOW())) as token_ttl
- FROM oauth_server_token
- WHERE ost_token = \'%s\'', $new_token);
-
- if (is_numeric($ttl))
- {
- $ret['token_ttl'] = intval($ttl);
- }
- return $ret;
- }
-
-
- /**
- * Fetch the consumer access token, by access token.
- *
- * @param string token
- * @param int user_id
- * @exception OAuthException when token is not found
- * @return array token and consumer details
- */
- public function getConsumerAccessToken ( $token, $user_id )
- {
- $rs = $this->query_row_assoc('
- SELECT ost_token as token,
- ost_token_secret as token_secret,
- ost_referrer_host as token_referrer_host,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- osr_application_uri as application_uri,
- osr_application_title as application_title,
- osr_application_descr as application_descr
- FROM oauth_server_token
- JOIN oauth_server_registry
- ON ost_osr_id_ref = osr_id
- WHERE ost_token_type = \'access\'
- AND ost_token = \'%s\'
- AND ost_usa_id_ref = %d
- AND ost_token_ttl >= NOW()
- ', $token, $user_id);
-
- if (empty($rs))
- {
- throw new OAuthException('No server_token "'.$token.'" for user "'.$user_id.'"');
- }
- return $rs;
- }
-
-
- /**
- * Delete a consumer access token.
- *
- * @param string token
- * @param int user_id
- * @param boolean user_is_admin
- */
- public function deleteConsumerAccessToken ( $token, $user_id, $user_is_admin = false )
- {
- if ($user_is_admin)
- {
- $this->query('
- DELETE FROM oauth_server_token
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'access\'
- ', $token);
- }
- else
- {
- $this->query('
- DELETE FROM oauth_server_token
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'access\'
- AND ost_usa_id_ref = %d
- ', $token, $user_id);
- }
- }
-
-
- /**
- * Set the ttl of a consumer access token. This is done when the
- * server receives a valid request with a xoauth_token_ttl parameter in it.
- *
- * @param string token
- * @param int ttl
- */
- public function setConsumerAccessTokenTtl ( $token, $token_ttl )
- {
- if ($token_ttl <= 0)
- {
- // Immediate delete when the token is past its ttl
- $this->deleteConsumerAccessToken($token, 0, true);
- }
- else
- {
- // Set maximum time to live for this token
- $this->query('
- UPDATE oauth_server_token
- SET ost_token_ttl = DATE_ADD(NOW(), INTERVAL %d SECOND)
- WHERE ost_token = \'%s\'
- AND ost_token_type = \'access\'
- ', $token_ttl, $token);
- }
- }
-
-
- /**
- * Fetch a list of all consumer keys, secrets etc.
- * Returns the public (user_id is null) and the keys owned by the user
- *
- * @param int user_id
- * @return array
- */
- public function listConsumers ( $user_id )
- {
- $rs = $this->query_all_assoc('
- SELECT osr_id as id,
- osr_usa_id_ref as user_id,
- osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- osr_enabled as enabled,
- osr_status as status,
- osr_issue_date as issue_date,
- osr_application_uri as application_uri,
- osr_application_title as application_title,
- osr_application_descr as application_descr,
- osr_requester_name as requester_name,
- osr_requester_email as requester_email
- FROM oauth_server_registry
- WHERE (osr_usa_id_ref = %d OR osr_usa_id_ref IS NULL)
- ORDER BY osr_application_title
- ', $user_id);
- return $rs;
- }
-
-
- /**
- * Fetch a list of all consumer tokens accessing the account of the given user.
- *
- * @param int user_id
- * @return array
- */
- public function listConsumerTokens ( $user_id )
- {
- $rs = $this->query_all_assoc('
- SELECT osr_consumer_key as consumer_key,
- osr_consumer_secret as consumer_secret,
- osr_enabled as enabled,
- osr_status as status,
- osr_application_uri as application_uri,
- osr_application_title as application_title,
- osr_application_descr as application_descr,
- ost_timestamp as timestamp,
- ost_token as token,
- ost_token_secret as token_secret,
- ost_referrer_host as token_referrer_host
- FROM oauth_server_registry
- JOIN oauth_server_token
- ON ost_osr_id_ref = osr_id
- WHERE ost_usa_id_ref = %d
- AND ost_token_type = \'access\'
- AND ost_token_ttl >= NOW()
- ORDER BY osr_application_title
- ', $user_id);
- return $rs;
- }
-
-
- /**
- * Check an nonce/timestamp combination. Clears any nonce combinations
- * that are older than the one received.
- *
- * @param string consumer_key
- * @param string token
- * @param int timestamp
- * @param string nonce
- * @exception OAuthException thrown when the timestamp is not in sequence or nonce is not unique
- */
- public function checkServerNonce ( $consumer_key, $token, $timestamp, $nonce )
- {
- $r = $this->query_row('
- SELECT MAX(osn_timestamp), MAX(osn_timestamp) > %d + %d
- FROM oauth_server_nonce
- WHERE osn_consumer_key = \'%s\'
- AND osn_token = \'%s\'
- ', $timestamp, $this->max_timestamp_skew, $consumer_key, $token);
-
- if (!empty($r) && $r[1])
- {
- throw new OAuthException('Timestamp is out of sequence. Request rejected. Got '.$timestamp.' last max is '.$r[0].' allowed skew is '.$this->max_timestamp_skew);
- }
-
- // Insert the new combination
- $this->query('
- INSERT IGNORE INTO oauth_server_nonce
- SET osn_consumer_key = \'%s\',
- osn_token = \'%s\',
- osn_timestamp = %d,
- osn_nonce = \'%s\'
- ', $consumer_key, $token, $timestamp, $nonce);
-
- if ($this->query_affected_rows() == 0)
- {
- throw new OAuthException('Duplicate timestamp/nonce combination, possible replay attack. Request rejected.');
- }
-
- // Clean up all timestamps older than the one we just received
- $this->query('
- DELETE FROM oauth_server_nonce
- WHERE osn_consumer_key = \'%s\'
- AND osn_token = \'%s\'
- AND osn_timestamp < %d - %d
- ', $consumer_key, $token, $timestamp, $this->max_timestamp_skew);
- }
-
-
- /**
- * Add an entry to the log table
- *
- * @param array keys (osr_consumer_key, ost_token, ocr_consumer_key, oct_token)
- * @param string received
- * @param string sent
- * @param string base_string
- * @param string notes
- * @param int (optional) user_id
- */
- public function addLog ( $keys, $received, $sent, $base_string, $notes, $user_id = null )
- {
- $args = array();
- $ps = array();
- foreach ($keys as $key => $value)
- {
- $args[] = $value;
- $ps[] = "olg_$key = '%s'";
- }
-
- if (!empty($_SERVER['REMOTE_ADDR']))
- {
- $remote_ip = $_SERVER['REMOTE_ADDR'];
- }
- else if (!empty($_SERVER['REMOTE_IP']))
- {
- $remote_ip = $_SERVER['REMOTE_IP'];
- }
- else
- {
- $remote_ip = '0.0.0.0';
- }
-
- // Build the SQL
- $ps[] = "olg_received = '%s'"; $args[] = $this->makeUTF8($received);
- $ps[] = "olg_sent = '%s'"; $args[] = $this->makeUTF8($sent);
- $ps[] = "olg_base_string= '%s'"; $args[] = $base_string;
- $ps[] = "olg_notes = '%s'"; $args[] = $this->makeUTF8($notes);
- $ps[] = "olg_usa_id_ref = NULLIF(%d,0)"; $args[] = $user_id;
- $ps[] = "olg_remote_ip = IFNULL(INET_ATON('%s'),0)"; $args[] = $remote_ip;
-
- $this->query('INSERT INTO oauth_log SET '.implode(',', $ps), $args);
- }
-
-
- /**
- * Get a page of entries from the log. Returns the last 100 records
- * matching the options given.
- *
- * @param array options
- * @param int user_id current user
- * @return array log records
- */
- public function listLog ( $options, $user_id )
- {
- $where = array();
- $args = array();
- if (empty($options))
- {
- $where[] = 'olg_usa_id_ref = %d';
- $args[] = $user_id;
- }
- else
- {
- foreach ($options as $option => $value)
- {
- if (strlen($value) > 0)
- {
- switch ($option)
- {
- case 'osr_consumer_key':
- case 'ocr_consumer_key':
- case 'ost_token':
- case 'oct_token':
- $where[] = 'olg_'.$option.' = \'%s\'';
- $args[] = $value;
- break;
- }
- }
- }
-
- $where[] = '(olg_usa_id_ref IS NULL OR olg_usa_id_ref = %d)';
- $args[] = $user_id;
- }
-
- $rs = $this->query_all_assoc('
- SELECT olg_id,
- olg_osr_consumer_key AS osr_consumer_key,
- olg_ost_token AS ost_token,
- olg_ocr_consumer_key AS ocr_consumer_key,
- olg_oct_token AS oct_token,
- olg_usa_id_ref AS user_id,
- olg_received AS received,
- olg_sent AS sent,
- olg_base_string AS base_string,
- olg_notes AS notes,
- olg_timestamp AS timestamp,
- INET_NTOA(olg_remote_ip) AS remote_ip
- FROM oauth_log
- WHERE '.implode(' AND ', $where).'
- ORDER BY olg_id DESC
- LIMIT 0,100', $args);
-
- return $rs;
- }
-
-
-
- /**
- * Initialise the database
- */
- public function install ()
- {
- require_once dirname(__FILE__) . '/mysql/install.php';
- }
-
-
- /* ** Some simple helper functions for querying the mysql db ** */
-
- /**
- * Perform a query, ignore the results
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- */
- protected function query ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- if (is_resource($res))
- {
- mysql_free_result($res);
- }
- }
-
-
- /**
- * Perform a query, ignore the results
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_all_assoc ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- $rs = array();
- while ($row = mysql_fetch_assoc($res))
- {
- $rs[] = $row;
- }
- mysql_free_result($res);
- return $rs;
- }
-
-
- /**
- * Perform a query, return the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_row_assoc ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- if ($row = mysql_fetch_assoc($res))
- {
- $rs = $row;
- }
- else
- {
- $rs = false;
- }
- mysql_free_result($res);
- return $rs;
- }
-
-
- /**
- * Perform a query, return the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return array
- */
- protected function query_row ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- if ($row = mysql_fetch_array($res))
- {
- $rs = $row;
- }
- else
- {
- $rs = false;
- }
- mysql_free_result($res);
- return $rs;
- }
-
-
- /**
- * Perform a query, return the first column of the first row
- *
- * @param string sql
- * @param vararg arguments (for sprintf)
- * @return mixed
- */
- protected function query_one ( $sql )
- {
- $sql = $this->sql_printf(func_get_args());
- if (!($res = mysql_query($sql, $this->conn)))
- {
- $this->sql_errcheck($sql);
- }
- $val = @mysql_result($res, 0, 0);
- mysql_free_result($res);
- return $val;
- }
-
-
- /**
- * Return the number of rows affected in the last query
- */
- protected function query_affected_rows ()
- {
- return mysql_affected_rows($this->conn);
- }
-
-
- /**
- * Return the id of the last inserted row
- *
- * @return int
- */
- protected function query_insert_id ()
- {
- return mysql_insert_id($this->conn);
- }
-
-
- protected function sql_printf ( $args )
- {
- $sql = array_shift($args);
- if (count($args) == 1 && is_array($args[0]))
- {
- $args = $args[0];
- }
- $args = array_map(array($this, 'sql_escape_string'), $args);
- return vsprintf($sql, $args);
- }
-
-
- protected function sql_escape_string ( $s )
- {
- if (is_string($s))
- {
- return mysql_real_escape_string($s, $this->conn);
- }
- else if (is_null($s))
- {
- return NULL;
- }
- else if (is_bool($s))
- {
- return intval($s);
- }
- else if (is_int($s) || is_float($s))
- {
- return $s;
- }
- else
- {
- return mysql_real_escape_string(strval($s), $this->conn);
- }
- }
-
-
- protected function sql_errcheck ( $sql )
- {
- if (mysql_errno($this->conn))
- {
- $msg = "SQL Error in OAuthStoreMySQL: ".mysql_error($this->conn)."\n\n" . $sql;
- throw new OAuthException($msg);
- }
- }
-}
-
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/mysql/install.php b/mod/oauth_api/vendors/oauth/library/store/mysql/install.php
deleted file mode 100644
index 0015da5e3..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/mysql/install.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-/**
- * Installs all tables in the mysql.sql file, using the default mysql connection
- */
-
-/* Change and uncomment this when you need to: */
-
-/*
-mysql_connect('localhost', 'root');
-if (mysql_errno())
-{
- die(' Error '.mysql_errno().': '.mysql_error());
-}
-mysql_select_db('test');
-*/
-
-$sql = file_get_contents(dirname(__FILE__) . '/mysql.sql');
-$ps = explode('#--SPLIT--', $sql);
-
-foreach ($ps as $p)
-{
- $p = preg_replace('/^\s*#.*$/m', '', $p);
-
- mysql_query($p);
- if (mysql_errno())
- {
- die(' Error '.mysql_errno().': '.mysql_error());
- }
-}
-
-?> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql b/mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql
deleted file mode 100644
index d652a1c99..000000000
--- a/mod/oauth_api/vendors/oauth/library/store/mysql/mysql.sql
+++ /dev/null
@@ -1,219 +0,0 @@
-# Datamodel for OAuthStoreMySQL
-#
-# You need to add the foreign key constraints for the user ids your are using.
-# I have commented the constraints out, just look for 'usa_id_ref' to enable them.
-#
-# The --SPLIT-- markers are used by the install.php script
-#
-# @version $Id: mysql.sql 51 2008-10-15 15:15:47Z marcw@pobox.com $
-# @author Marc Worrell
-#
-
-# Changes:
-#
-# 2008-10-15 (on r48) Added ttl to consumer and server tokens, added named server tokens
-#
-# ALTER TABLE oauth_server_token
-# ADD ost_token_ttl datetime not null default '9999-12-31',
-# ADD KEY (ost_token_ttl);
-#
-# ALTER TABLE oauth_consumer_token
-# ADD oct_name varchar(64) binary not null default '',
-# ADD oct_token_ttl datetime not null default '9999-12-31',
-# DROP KEY oct_usa_id_ref,
-# ADD UNIQUE KEY (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name),
-# ADD KEY (oct_token_ttl);
-#
-# 2008-09-09 (on r5) Added referrer host to server access token
-#
-# ALTER TABLE oauth_server_token ADD ost_referrer_host VARCHAR(128) NOT NULL;
-#
-
-
-#
-# Log table to hold all OAuth request when you enabled logging
-#
-
-CREATE TABLE IF NOT EXISTS oauth_log (
- olg_id int(11) not null auto_increment,
- olg_osr_consumer_key varchar(64) binary,
- olg_ost_token varchar(64) binary,
- olg_ocr_consumer_key varchar(64) binary,
- olg_oct_token varchar(64) binary,
- olg_usa_id_ref int(11),
- olg_received text not null,
- olg_sent text not null,
- olg_base_string text not null,
- olg_notes text not null,
- olg_timestamp timestamp not null default current_timestamp,
- olg_remote_ip bigint not null,
-
- primary key (olg_id),
- key (olg_osr_consumer_key, olg_id),
- key (olg_ost_token, olg_id),
- key (olg_ocr_consumer_key, olg_id),
- key (olg_oct_token, olg_id),
- key (olg_usa_id_ref, olg_id)
-
-# , foreign key (olg_usa_id_ref) references any_user_auth (usa_id_ref)
-# on update cascade
-# on delete cascade
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-#
-# /////////////////// CONSUMER SIDE ///////////////////
-#
-
-# This is a registry of all consumer codes we got from other servers
-# The consumer_key/secret is obtained from the server
-# We also register the server uri, so that we can find the consumer key and secret
-# for a certain server. From that server we can check if we have a token for a
-# particular user.
-
-CREATE TABLE IF NOT EXISTS oauth_consumer_registry (
- ocr_id int(11) not null auto_increment,
- ocr_usa_id_ref int(11),
- ocr_consumer_key varchar(64) binary not null,
- ocr_consumer_secret varchar(64) binary not null,
- ocr_signature_methods varchar(255) not null default 'HMAC-SHA1,PLAINTEXT',
- ocr_server_uri varchar(255) not null,
- ocr_server_uri_host varchar(128) not null,
- ocr_server_uri_path varchar(128) binary not null,
-
- ocr_request_token_uri varchar(255) not null,
- ocr_authorize_uri varchar(255) not null,
- ocr_access_token_uri varchar(255) not null,
- ocr_timestamp timestamp not null default current_timestamp,
-
- primary key (ocr_id),
- unique key (ocr_consumer_key, ocr_usa_id_ref),
- key (ocr_server_uri),
- key (ocr_server_uri_host, ocr_server_uri_path),
- key (ocr_usa_id_ref)
-
-# , foreign key (ocr_usa_id_ref) references any_user_auth(usa_id_ref)
-# on update cascade
-# on delete set null
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-# Table used to sign requests for sending to a server by the consumer
-# The key is defined for a particular user. Only one single named
-# key is allowed per user/server combination
-
-CREATE TABLE IF NOT EXISTS oauth_consumer_token (
- oct_id int(11) not null auto_increment,
- oct_ocr_id_ref int(11) not null,
- oct_usa_id_ref int(11) not null,
- oct_name varchar(64) binary not null default '',
- oct_token varchar(64) binary not null,
- oct_token_secret varchar(64) binary not null,
- oct_token_type enum('request','authorized','access'),
- oct_token_ttl datetime not null default '9999-12-31',
- oct_timestamp timestamp not null default current_timestamp,
-
- primary key (oct_id),
- unique key (oct_ocr_id_ref, oct_token),
- unique key (oct_usa_id_ref, oct_ocr_id_ref, oct_token_type, oct_name),
- key (oct_token_ttl),
-
- foreign key (oct_ocr_id_ref) references oauth_consumer_registry (ocr_id)
- on update cascade
- on delete cascade
-
-# , foreign key (oct_usa_id_ref) references any_user_auth (usa_id_ref)
-# on update cascade
-# on delete cascade
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-
-#
-# ////////////////// SERVER SIDE /////////////////
-#
-
-# Table holding consumer key/secret combos an user issued to consumers.
-# Used for verification of incoming requests.
-
-CREATE TABLE IF NOT EXISTS oauth_server_registry (
- osr_id int(11) not null auto_increment,
- osr_usa_id_ref int(11),
- osr_consumer_key varchar(64) binary not null,
- osr_consumer_secret varchar(64) binary not null,
- osr_enabled tinyint(1) not null default '1',
- osr_status varchar(16) not null,
- osr_requester_name varchar(64) not null,
- osr_requester_email varchar(64) not null,
- osr_callback_uri varchar(255) not null,
- osr_application_uri varchar(255) not null,
- osr_application_title varchar(80) not null,
- osr_application_descr text not null,
- osr_application_notes text not null,
- osr_application_type varchar(20) not null,
- osr_application_commercial tinyint(1) not null default '0',
- osr_issue_date datetime not null,
- osr_timestamp timestamp not null default current_timestamp,
-
- primary key (osr_id),
- unique key (osr_consumer_key),
- key (osr_usa_id_ref)
-
-# , foreign key (osr_usa_id_ref) references any_user_auth(usa_id_ref)
-# on update cascade
-# on delete set null
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-# Nonce used by a certain consumer, every used nonce should be unique, this prevents
-# replaying attacks. We need to store all timestamp/nonce combinations for the
-# maximum timestamp received.
-
-CREATE TABLE IF NOT EXISTS oauth_server_nonce (
- osn_id int(11) not null auto_increment,
- osn_consumer_key varchar(64) binary not null,
- osn_token varchar(64) binary not null,
- osn_timestamp bigint not null,
- osn_nonce varchar(80) binary not null,
-
- primary key (osn_id),
- unique key (osn_consumer_key, osn_token, osn_timestamp, osn_nonce)
-) engine=InnoDB default charset=utf8;
-
-#--SPLIT--
-
-# Table used to verify signed requests sent to a server by the consumer
-# When the verification is succesful then the associated user id is returned.
-
-CREATE TABLE IF NOT EXISTS oauth_server_token (
- ost_id int(11) not null auto_increment,
- ost_osr_id_ref int(11) not null,
- ost_usa_id_ref int(11) not null,
- ost_token varchar(64) binary not null,
- ost_token_secret varchar(64) binary not null,
- ost_token_type enum('request','access'),
- ost_authorized tinyint(1) not null default '0',
- ost_referrer_host varchar(128) not null,
- ost_token_ttl datetime not null default '9999-12-31',
- ost_timestamp timestamp not null default current_timestamp,
-
- primary key (ost_id),
- unique key (ost_token),
- key (ost_osr_id_ref),
- key (ost_token_ttl),
-
- foreign key (ost_osr_id_ref) references oauth_server_registry (osr_id)
- on update cascade
- on delete cascade
-
-# , foreign key (ost_usa_id_ref) references any_user_auth (usa_id_ref)
-# on update cascade
-# on delete cascade
-) engine=InnoDB default charset=utf8;
-
-
-
diff --git a/mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds b/mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds
deleted file mode 100644
index 0f5eba222..000000000
--- a/mod/oauth_api/vendors/oauth/test/discovery/xrds-fireeagle.xrds
+++ /dev/null
@@ -1,78 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<XRDS xmlns="xri://$xrds">
-
- <!-- FireEagle User-Centric OAuth Configuration -->
- <XRD xml:id="oauth" xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" version="2.0">
-
- <Type>xri://$xrds*simple</Type>
- <Expires>2008-04-15T00:25:30-07:00</Expires>
-
- <!-- Request Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/request</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
-
- <URI>https://fireeagle.yahooapis.com/oauth/request_token</URI>
- </Service>
-
- <!-- User Authorization -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
-
- <URI>https://fireeagle.yahooapis.com/oauth/access_token</URI>
- </Service>
-
- <!-- Access Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/access</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
-
- <URI>http://fireeagle.yahoo.net/oauth/authorize</URI>
- </Service>
-
- <!-- Protected Resources -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/resource</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
- </Service>
-
- <!-- Consumer Identity -->
-
- <!-- Manual Consumer Identity Allocation -->
- <Service>
- <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type>
- <URI>https://fireeagle.yahoo.net/developer/create</URI>
- </Service>
- </XRD>
-
- <!-- Global Resource Definition -->
-
- <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
- <Type>xri://$xrds*simple</Type>
-
- <!-- OAuth Endpoints Definition -->
- <Service>
- <Type>http://oauth.net/discovery/1.0</Type>
- <URI>#oauth</URI>
- </Service>
- </XRD>
-
-</XRDS> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds b/mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds
deleted file mode 100644
index ab94b5bea..000000000
--- a/mod/oauth_api/vendors/oauth/test/discovery/xrds-getsatisfaction.xrds
+++ /dev/null
@@ -1,73 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<XRDS xmlns="xri://$xrds">
-
- <XRD xml:id="oauth" xmlns:simple="http://xrds-simple.net/core/1.0" xmlns="xri://$XRD*($v*2.0)" version="2.0">
- <Type>xri://$xrds*simple</Type>
- <Expires>2008-04-30T23:59:59Z</Expires>
-
- <!-- Request Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/request</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
-
- <URI>http://getsatisfaction.com/api/request_token</URI>
- </Service>
-
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
-
- <URI>http://getsatisfaction.com/api/authorize</URI>
- </Service>
-
- <!-- Access Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/access</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
-
- <URI>http://getsatisfaction.com/api/access_token</URI>
- </Service>
-
- <!-- Protected Resources -->
- <!--
-
- To test successful access token grant, make a request against
-
- http://api.getsatisfaction.com/me
-
- The API should respond with hCard of the user who authorized the token
- -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/resource</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
-
- </Service>
-
- <!-- Consumer Identity -->
-
- <Service>
- <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type>
- <URI>http://getsatisfaction.com/me/extensions/new</URI>
- </Service>
- </XRD>
-
- <!-- Global Resource Definition -->
-
- <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
- <Type>xri://$xrds*simple</Type>
-
- <!-- OAuth Endpoints Definition -->
- <Service priority="10">
- <Type>http://oauth.net/discovery/1.0</Type>
- <URI>#oauth</URI>
- </Service>
- </XRD>
-
-</XRDS> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds b/mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds
deleted file mode 100644
index 361b5c9a1..000000000
--- a/mod/oauth_api/vendors/oauth/test/discovery/xrds-magnolia.xrds
+++ /dev/null
@@ -1,81 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<XRDS xmlns="xri://$xrds">
-
- <!-- Ma.gnolia OAuth Configuration -->
- <XRD xml:id="oauth" xmlns="xri://$XRD*($v*2.0)" version="2.0">
-
- <Type>xri://$xrds*simple</Type>
- <Expires>2008-04-13T07:34:58Z</Expires>
-
- <!-- Request Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/request</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
-
- <URI>https://ma.gnolia.com/oauth/get_request_token</URI>
- </Service>
-
- <!-- User Authorization (HTTPS Prefered) -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/authorize</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
-
- <URI priority="10">https://ma.gnolia.com/oauth/authorize</URI>
- <URI priority="20">http://ma.gnolia.com/oauth/authorize</URI>
- </Service>
-
- <!-- Access Token -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/access</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/PLAINTEXT</Type>
-
- <URI>https://ma.gnolia.com/oauth/get_access_token</URI>
- </Service>
-
- <!-- Protected Resources -->
- <Service>
- <Type>http://oauth.net/core/1.0/endpoint/resource</Type>
-
- <Type>http://oauth.net/core/1.0/parameters/auth-header</Type>
- <Type>http://oauth.net/core/1.0/parameters/post-body</Type>
- <Type>http://oauth.net/core/1.0/parameters/uri-query</Type>
- <Type>http://oauth.net/core/1.0/signature/HMAC-SHA1</Type>
- <Type>http://oauth.net/core/1.0/signature/RSA-SHA1</Type>
- </Service>
-
- <!-- Consumer Identity -->
-
- <!-- Manual Consumer Identity Allocation -->
- <Service>
- <Type>http://oauth.net/discovery/1.0/consumer-identity/oob</Type>
- <URI>http://ma.gnolia.com/applications/new</URI>
- </Service>
- </XRD>
-
- <!-- Global Resource Definition -->
-
- <XRD xmlns="xri://$XRD*($v*2.0)" version="2.0">
- <Type>xri://$xrds*simple</Type>
-
- <!-- OAuth Endpoints Definition -->
- <Service priority="10">
- <Type>http://oauth.net/discovery/1.0</Type>
- <URI>#oauth</URI>
- </Service>
- </XRD>
-
-</XRDS> \ No newline at end of file
diff --git a/mod/oauth_api/vendors/oauth/test/oauth_test.php b/mod/oauth_api/vendors/oauth/test/oauth_test.php
deleted file mode 100644
index 0c0504c70..000000000
--- a/mod/oauth_api/vendors/oauth/test/oauth_test.php
+++ /dev/null
@@ -1,188 +0,0 @@
-<?php
-
-/**
- * Tests of OAuth implementation.
- *
- * @version $Id$
- * @author Marc Worrell <marcw@pobox.com>
- * @date Nov 29, 2007 3:46:56 PM
- * @see http://wiki.oauth.net/TestCases
- *
- * The MIT License
- *
- * Copyright (c) 2007-2008 Mediamatic Lab
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in
- * all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- * THE SOFTWARE.
- */
-
-require_once dirname(__FILE__) . '/../library/OAuthRequest.php';
-require_once dirname(__FILE__) . '/../library/OAuthRequester.php';
-require_once dirname(__FILE__) . '/../library/OAuthRequestSigner.php';
-require_once dirname(__FILE__) . '/../library/OAuthRequestVerifier.php';
-
-if (!function_exists('getallheaders'))
-{
- function getallheaders()
- {
- return array();
- }
-}
-
-
-oauth_test();
-
-function oauth_test ()
-{
- error_reporting(E_ALL);
-
- header('Content-Type: text/plain; charset=utf-8');
-
- echo "Performing OAuth module tests.\n\n";
- echo "See also: http://wiki.oauth.net/TestCases\n\n";
-
- assert_options(ASSERT_CALLBACK, 'oauth_assert_handler');
- assert_options(ASSERT_WARNING, 0);
-
- $req = new OAuthRequest('http://www.example.com', 'GET');
-
- echo "***** Parameter Encoding *****\n\n";
-
- assert('$req->urlencode(\'abcABC123\') == \'abcABC123\'');
- assert('$req->urlencode(\'-._~\') == \'-._~\'');
- assert('$req->urlencode(\'%\') == \'%25\'');
- assert('$req->urlencode(\'&=*\') == \'%26%3D%2A\'');
- assert('$req->urlencode(\'&=*\') == \'%26%3D%2A\'');
- assert('$req->urlencode("\n") == \'%0A\'');
- assert('$req->urlencode(" ") == \'%20\'');
- assert('$req->urlencode("\x7f") == \'%7F\'');
-
-
- echo "***** Normalize Request Parameters *****\n\n";
-
- $req = new OAuthRequest('http://example.com/?name', 'GET');
- assert('$req->getNormalizedParams() == \'name=\'');
-
- $req = new OAuthRequest('http://example.com/?a=b', 'GET');
- assert('$req->getNormalizedParams() == \'a=b\'');
-
- $req = new OAuthRequest('http://example.com/?a=b&c=d', 'GET');
- assert('$req->getNormalizedParams() == \'a=b&c=d\'');
-
- // At this moment we don't support two parameters with the same name
- // so I changed this test case to "a=" and "b=" and not "a=" and "a="
- $req = new OAuthRequest('http://example.com/?b=x!y&a=x+y', 'GET');
- assert('$req->getNormalizedParams() == \'a=x%20y&b=x%21y\'');
-
- $req = new OAuthRequest('http://example.com/?x!y=a&x=a', 'GET');
- assert('$req->getNormalizedParams() == \'x=a&x%21y=a\'');
-
-
- echo "***** Base String *****\n\n";
-
- $req = new OAuthRequest('http://example.com/?n=v', 'GET');
- assert('$req->signatureBaseString() == \'GET&http%3A%2F%2Fexample.com%2F&n%3Dv\'');
-
- $req = new OAuthRequest(
- 'https://photos.example.net/request_token',
- 'POST',
- 'oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_timestamp=1191242090&oauth_nonce=hsu94j3884jdopsl&oauth_signature_method=PLAINTEXT&oauth_signature=ignored',
- array('X-OAuth-Test' => true));
- assert('$req->signatureBaseString() == \'POST&https%3A%2F%2Fphotos.example.net%2Frequest_token&oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dhsu94j3884jdopsl%26oauth_signature_method%3DPLAINTEXT%26oauth_timestamp%3D1191242090%26oauth_version%3D1.0\'');
-
- $req = new OAuthRequest(
- 'http://photos.example.net/photos?file=vacation.jpg&size=original&oauth_version=1.0&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk&oauth_timestamp=1191242096&oauth_nonce=kllo9940pd9333jh&oauth_signature=ignored&oauth_signature_method=HMAC-SHA1',
- 'GET');
- assert('$req->signatureBaseString() == \'GET&http%3A%2F%2Fphotos.example.net%2Fphotos&file%3Dvacation.jpg%26oauth_consumer_key%3Ddpf43f3p2l4k3l03%26oauth_nonce%3Dkllo9940pd9333jh%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1191242096%26oauth_token%3Dnnch734d00sl2jdk%26oauth_version%3D1.0%26size%3Doriginal\'');
-
-
- echo "***** HMAC-SHA1 *****\nRequest signing\n";
-
- OAuthStore::instance('MySQL', array('conn'=>false));
- $req = new OAuthRequestSigner('http://photos.example.net/photos?file=vacation.jpg&size=original', 'GET');
-
- assert('$req->urldecode($req->calculateDataSignature(\'bs\', \'cs\', \'\', \'HMAC-SHA1\')) == \'egQqG5AJep5sJ7anhXju1unge2I=\'');
- assert('$req->urldecode($req->calculateDataSignature(\'bs\', \'cs\', \'ts\', \'HMAC-SHA1\')) == \'VZVjXceV7JgPq/dOTnNmEfO0Fv8=\'');
-
- $secrets = array(
- 'consumer_key' => 'dpf43f3p2l4k3l03',
- 'consumer_secret' => 'kd94hf93k423kf44',
- 'token' => 'nnch734d00sl2jdk',
- 'token_secret' => 'pfkkdhi9sl3r4s00',
- 'signature_methods' => array('HMAC-SHA1'),
- 'nonce' => 'kllo9940pd9333jh',
- 'timestamp' => '1191242096'
- );
- $req->sign(0, $secrets);
- assert('$req->getParam(\'oauth_signature\', true) == \'tR3+Ty81lMeYAr/Fid0kMTYa/WM=\'');
-
- echo "***** HMAC-SHA1 *****\nRequest verification\n";
-
- $req = new OAuthRequestVerifier(
- 'http://photos.example.net/photos?file=vacation.jpg&size=original'
- .'&oauth_consumer_key=dpf43f3p2l4k3l03&oauth_token=nnch734d00sl2jdk'
- .'&oauth_signature_method=HMAC-SHA1&oauth_nonce=kllo9940pd9333jh'
- .'&oauth_timestamp=1191242096&oauth_version=1.0'
- .'&oauth_signature='.rawurlencode('tR3+Ty81lMeYAr/Fid0kMTYa/WM=')
- , 'GET');
-
- $req->verifySignature('kd94hf93k423kf44', 'pfkkdhi9sl3r4s00');
-
- echo "\n";
- echo "***** Yahoo! test case ******\n\n";
-
- OAuthStore::instance('MySQL', array('conn'=>false));
- $req = new OAuthRequestSigner('http://example.com:80/photo', 'GET');
-
- $req->setParam('title', 'taken with a 30% orange filter');
- $req->setParam('file', 'mountain & water view');
- $req->setParam('format', 'jpeg');
- $req->setParam('include', array('date','aperture'));
-
- $secrets = array(
- 'consumer_key' => '1234=asdf=4567',
- 'consumer_secret' => 'erks823*43=asd&123ls%23',
- 'token' => 'asdf-4354=asew-5698',
- 'token_secret' => 'dis9$#$Js009%==',
- 'signature_methods' => array('HMAC-SHA1'),
- 'nonce' => '3jd834jd9',
- 'timestamp' => '12303202302'
- );
- $req->sign(0, $secrets);
-
- // echo "Basestring:\n",$req->signatureBaseString(), "\n\n";
-
- //echo "queryString:\n",$req->getQueryString(), "\n\n";
- assert('$req->getQueryString() == \'title=taken%20with%20a%2030%25%20orange%20filter&file=mountain%20%26%20water%20view&format=jpeg&include=date&include=aperture\'');
-
- //echo "oauth_signature:\n",$req->getParam('oauth_signature', true),"\n\n";
- assert('$req->getParam(\'oauth_signature\', true) == \'jMdUSR1vOr3SzNv3gZ5DDDuGirA=\'');
-
- echo "\n\nFinished.\n";
-}
-
-
-function oauth_assert_handler ( $file, $line, $code )
-{
- echo "\nAssertion failed in $file:$line
- $code\n\n";
-}
-
-/* vi:set ts=4 sts=4 sw=4 binary noeol: */
-
-?> \ No newline at end of file
diff --git a/mod/pages/actions/annotations/page/delete.php b/mod/pages/actions/annotations/page/delete.php
new file mode 100644
index 000000000..156b516d2
--- /dev/null
+++ b/mod/pages/actions/annotations/page/delete.php
@@ -0,0 +1,20 @@
+<?php
+/**
+ * Remove a page (revision) annotation
+ *
+ * @package ElggPages
+ */
+
+// Make sure we can get the annotations and entity in question
+$annotation_id = (int) get_input('annotation_id');
+$annotation = elgg_get_annotation_from_id($annotation_id);
+$entity = get_entity($annotation->entity_guid);
+
+if ($annotation && $entity->canEdit() && $annotation->canEdit()) {
+ $annotation->delete();
+ system_message(elgg_echo("pages:revision:delete:success"));
+} else {
+ register_error(elgg_echo("pages:revision:delete:failure"));
+}
+
+forward("pages/history/{$annotation->entity_guid}"); \ No newline at end of file
diff --git a/mod/pages/actions/pages/delete.php b/mod/pages/actions/pages/delete.php
index 7a314a280..fd5791e4d 100644
--- a/mod/pages/actions/pages/delete.php
+++ b/mod/pages/actions/pages/delete.php
@@ -21,11 +21,33 @@ if (elgg_instanceof($page, 'object', 'page') || elgg_instanceof($page, 'object',
'metadata_value' => $page->getGUID()
));
if ($children) {
+ $db_prefix = elgg_get_config('dbprefix');
+ $subtype_id = (int)get_subtype_id('object', 'page_top');
+ $newentity_cache = is_memcache_available() ? new ElggMemcache('new_entity_cache') : null;
+
foreach ($children as $child) {
- $child->parent_guid = $parent;
+ if ($parent) {
+ $child->parent_guid = $parent;
+ } else {
+ // If no parent, we need to transform $child to a page_top
+ $child_guid = (int)$child->guid;
+
+ update_data("UPDATE {$db_prefix}entities
+ SET subtype = $subtype_id WHERE guid = $child_guid");
+
+ elgg_delete_metadata(array(
+ 'guid' => $child_guid,
+ 'metadata_name' => 'parent_guid',
+ ));
+
+ _elgg_invalidate_cache_for_entity($child_guid);
+ if ($newentity_cache) {
+ $newentity_cache->delete($child_guid);
+ }
+ }
}
}
-
+
if ($page->delete()) {
system_message(elgg_echo('pages:delete:success'));
if ($parent) {
diff --git a/mod/pages/languages/en.php b/mod/pages/languages/en.php
index 930676b3e..c204c1901 100644
--- a/mod/pages/languages/en.php
+++ b/mod/pages/languages/en.php
@@ -15,7 +15,7 @@ $english = array(
'pages:owner' => "%s's pages",
'pages:friends' => "Friends' pages",
'pages:all' => "All site pages",
- 'pages:add' => "Add page",
+ 'pages:add' => "Add a page",
'pages:group' => "Group pages",
'groups:enablepages' => 'Enable group pages',
@@ -25,6 +25,8 @@ $english = array(
'pages:history' => "History",
'pages:view' => "View page",
'pages:revision' => "Revision",
+ 'pages:current_revision' => "Current Revision",
+ 'pages:revert' => "Revert",
'pages:navigation' => "Navigation",
'pages:new' => "A new page",
@@ -75,6 +77,9 @@ View and comment on the new page:
'pages:error:no_title' => 'You must specify a title for this page.',
'pages:delete:success' => 'The page was successfully deleted.',
'pages:delete:failure' => 'The page could not be deleted.',
+ 'pages:revision:delete:success' => 'The page revision was successfully deleted.',
+ 'pages:revision:delete:failure' => 'The page revision could not be deleted.',
+ 'pages:revision:not_found' => 'Cannot find this revision.',
/**
* Page
diff --git a/mod/pages/lib/pages.php b/mod/pages/lib/pages.php
index afe42b68f..7f90d53d8 100644
--- a/mod/pages/lib/pages.php
+++ b/mod/pages/lib/pages.php
@@ -9,7 +9,7 @@
* @param ElggObject $page
* @return array
*/
-function pages_prepare_form_vars($page = null, $parent_guid = 0) {
+function pages_prepare_form_vars($page = null, $parent_guid = 0, $revision = null) {
// input names => defaults
$values = array(
@@ -41,6 +41,11 @@ function pages_prepare_form_vars($page = null, $parent_guid = 0) {
elgg_clear_sticky_form('page');
+ // load the revision annotation if requested
+ if ($revision instanceof ElggAnnotation && $revision->entity_guid == $page->getGUID()) {
+ $values['description'] = $revision->value;
+ }
+
return $values;
}
diff --git a/mod/pages/pages/pages/edit.php b/mod/pages/pages/pages/edit.php
index 1f411b94d..a925cdc55 100644
--- a/mod/pages/pages/pages/edit.php
+++ b/mod/pages/pages/pages/edit.php
@@ -8,6 +8,7 @@
gatekeeper();
$page_guid = (int)get_input('guid');
+$revision = (int)get_input('annotation_id');
$page = get_entity($page_guid);
if (!$page) {
register_error(elgg_echo('noaccess'));
@@ -28,7 +29,17 @@ elgg_push_breadcrumb(elgg_echo('edit'));
$title = elgg_echo("pages:edit");
if ($page->canEdit()) {
- $vars = pages_prepare_form_vars($page);
+
+ if ($revision) {
+ $revision = elgg_get_annotation_from_id($revision);
+ if (!$revision || !($revision->entity_guid == $page_guid)) {
+ register_error(elgg_echo('pages:revision:not_found'));
+ forward(REFERER);
+ }
+ }
+
+ $vars = pages_prepare_form_vars($page, $page->parent_guid, $revision);
+
$content = elgg_view_form('pages/edit', array(), $vars);
} else {
$content = elgg_echo("pages:noaccess");
diff --git a/mod/pages/pages/pages/friends.php b/mod/pages/pages/pages/friends.php
index 87ac631c2..cecc4053b 100644
--- a/mod/pages/pages/pages/friends.php
+++ b/mod/pages/pages/pages/friends.php
@@ -7,7 +7,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('pages/all');
+ forward('', '404');
}
elgg_push_breadcrumb($owner->name, "pages/owner/$owner->username");
diff --git a/mod/pages/pages/pages/history.php b/mod/pages/pages/pages/history.php
index 872596179..7f5fa4f4f 100644
--- a/mod/pages/pages/pages/history.php
+++ b/mod/pages/pages/pages/history.php
@@ -9,12 +9,12 @@ $page_guid = get_input('guid');
$page = get_entity($page_guid);
if (!$page) {
-
+ forward('', '404');
}
$container = $page->getContainerEntity();
if (!$container) {
-
+ forward('', '404');
}
elgg_set_page_owner_guid($container->getGUID());
diff --git a/mod/pages/pages/pages/owner.php b/mod/pages/pages/pages/owner.php
index b29332ee1..7de74a3b4 100644
--- a/mod/pages/pages/pages/owner.php
+++ b/mod/pages/pages/pages/owner.php
@@ -7,7 +7,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('pages/all');
+ forward('', '404');
}
// access check for closed groups
@@ -20,8 +20,8 @@ elgg_push_breadcrumb($owner->name);
elgg_register_title_button();
$content = elgg_list_entities(array(
- 'types' => 'object',
- 'subtypes' => 'page_top',
+ 'type' => 'object',
+ 'subtype' => 'page_top',
'container_guid' => elgg_get_page_owner_guid(),
'full_view' => false,
));
diff --git a/mod/pages/pages/pages/world.php b/mod/pages/pages/pages/world.php
index e6a705b6b..c130a6bd6 100644
--- a/mod/pages/pages/pages/world.php
+++ b/mod/pages/pages/pages/world.php
@@ -13,8 +13,8 @@ elgg_push_breadcrumb(elgg_echo('pages'));
elgg_register_title_button();
$content = elgg_list_entities(array(
- 'types' => 'object',
- 'subtypes' => 'page_top',
+ 'type' => 'object',
+ 'subtype' => 'page_top',
'full_view' => false,
));
if (!$content) {
diff --git a/mod/pages/start.php b/mod/pages/start.php
index 6d974f122..f9c34cd85 100644
--- a/mod/pages/start.php
+++ b/mod/pages/start.php
@@ -28,9 +28,10 @@ function pages_init() {
elgg_register_annotation_url_handler('page', 'pages_revision_url');
// Register some actions
- $action_base = elgg_get_plugins_path() . 'pages/actions/pages';
- elgg_register_action("pages/edit", "$action_base/edit.php");
- elgg_register_action("pages/delete", "$action_base/delete.php");
+ $action_base = elgg_get_plugins_path() . 'pages/actions';
+ elgg_register_action("pages/edit", "$action_base/pages/edit.php");
+ elgg_register_action("pages/delete", "$action_base/pages/delete.php");
+ elgg_register_action("annotations/page/delete", "$action_base/annotations/page/delete.php");
// Extend the main css view
elgg_extend_view('css/elgg', 'pages/css');
@@ -80,8 +81,13 @@ function pages_init() {
// entity menu
elgg_register_plugin_hook_handler('register', 'menu:entity', 'pages_entity_menu_setup');
+ // hook into annotation menu
+ elgg_register_plugin_hook_handler('register', 'menu:annotation', 'pages_annotation_menu_setup');
+
// register ecml views to parse
elgg_register_plugin_hook_handler('get_views', 'ecml', 'pages_ecml_views_hook');
+
+ elgg_register_event_handler('upgrade', 'system', 'pages_run_upgrades');
}
/**
@@ -281,25 +287,37 @@ function page_notify_message($hook, $entity_type, $returnvalue, $params) {
/**
* Extend permissions checking to extend can-edit for write users.
*
- * @param unknown_type $hook
- * @param unknown_type $entity_type
- * @param unknown_type $returnvalue
- * @param unknown_type $params
+ * @param string $hook
+ * @param string $entity_type
+ * @param bool $returnvalue
+ * @param array $params
*/
-function pages_write_permission_check($hook, $entity_type, $returnvalue, $params)
-{
+function pages_write_permission_check($hook, $entity_type, $returnvalue, $params) {
if ($params['entity']->getSubtype() == 'page'
|| $params['entity']->getSubtype() == 'page_top') {
$write_permission = $params['entity']->write_access_id;
$user = $params['user'];
- if (($write_permission) && ($user)) {
- // $list = get_write_access_array($user->guid);
- $list = get_access_array($user->guid); // get_access_list($user->guid);
-
- if (($write_permission!=0) && (in_array($write_permission,$list))) {
- return true;
+ if ($write_permission && $user) {
+ switch ($write_permission) {
+ case ACCESS_PRIVATE:
+ // Elgg's default decision is what we want
+ return;
+ break;
+ case ACCESS_FRIENDS:
+ $owner = $params['entity']->getOwnerEntity();
+ if ($owner && $owner->isFriendsWith($user->guid)) {
+ return true;
+ }
+ break;
+ default:
+ $list = get_access_array($user->guid);
+ if (in_array($write_permission, $list)) {
+ // user in the access collection
+ return true;
+ }
+ break;
}
}
}
diff --git a/mod/pages/upgrades/2012061800.php b/mod/pages/upgrades/2012061800.php
new file mode 100644
index 000000000..c21ccae3b
--- /dev/null
+++ b/mod/pages/upgrades/2012061800.php
@@ -0,0 +1,49 @@
+<?php
+/**
+ * Restore disappeared subpages. This is caused by its parent page being deleted
+ * when the parent page is a top level page. We take advantage of the fact that
+ * the parent_guid was deleted for the subpages.
+ *
+ * This upgrade script will no longer work once we have converted all pages to
+ * have the same entity subtype.
+ */
+
+
+/**
+ * Update subtype
+ *
+ * @param ElggObject $page
+ */
+function pages_2012061800($page) {
+ $dbprefix = elgg_get_config('dbprefix');
+ $subtype_id = (int)get_subtype_id('object', 'page_top');
+ $page_guid = (int)$page->guid;
+ update_data("UPDATE {$dbprefix}entities
+ SET subtype = $subtype_id WHERE guid = $page_guid");
+ error_log("called");
+ return true;
+}
+
+$previous_access = elgg_set_ignore_access(true);
+
+$dbprefix = elgg_get_config('dbprefix');
+$name_metastring_id = get_metastring_id('parent_guid');
+if (!$name_metastring_id) {
+ return;
+}
+
+// Looking for pages without metadata
+$options = array(
+ 'type' => 'object',
+ 'subtype' => 'page',
+ 'wheres' => "NOT EXISTS (
+ SELECT 1 FROM {$dbprefix}metadata md
+ WHERE md.entity_guid = e.guid
+ AND md.name_id = $name_metastring_id)"
+);
+$batch = new ElggBatch('elgg_get_entities_from_metadata', $options, 'pages_2012061800', 50, false);
+elgg_set_ignore_access($previous_access);
+
+if ($batch->callbackResult) {
+ error_log("Elgg Pages upgrade (2012061800) succeeded");
+}
diff --git a/mod/pages/views/default/annotation/page.php b/mod/pages/views/default/annotation/page.php
index a621b9281..ecb289092 100644
--- a/mod/pages/views/default/annotation/page.php
+++ b/mod/pages/views/default/annotation/page.php
@@ -39,4 +39,22 @@ $body = <<< HTML
<p class="elgg-subtext">$subtitle</p>
HTML;
+if (!elgg_in_context('widgets')) {
+ $menu = elgg_view_menu('annotation', array(
+ 'annotation' => $annotation,
+ 'sort_by' => 'priority',
+ 'class' => 'elgg-menu-hz float-alt',
+ ));
+}
+
+$body = <<<HTML
+<div class="mbn">
+ $menu
+ <h3>$title_link</h3>
+ <span class="elgg-subtext">
+ $subtitle
+ </span>
+</div>
+HTML;
+
echo elgg_view_image_block($icon, $body); \ No newline at end of file
diff --git a/mod/pages/views/default/object/page_top.php b/mod/pages/views/default/object/page_top.php
index 945a22eed..f35202993 100644
--- a/mod/pages/views/default/object/page_top.php
+++ b/mod/pages/views/default/object/page_top.php
@@ -60,18 +60,26 @@ if ($comments_count != 0 && !$revision) {
$comments_link = '';
}
-$metadata = elgg_view_menu('entity', array(
- 'entity' => $vars['entity'],
- 'handler' => 'pages',
- 'sort_by' => 'priority',
- 'class' => 'elgg-menu-hz',
-));
-
$subtitle = "$editor_text $comments_link $categories";
// do not show the metadata and controls in widget view
-if (elgg_in_context('widgets') || $revision) {
- $metadata = '';
+if (!elgg_in_context('widgets')) {
+ // If we're looking at a revision, display annotation menu
+ if ($revision) {
+ $metadata = elgg_view_menu('annotation', array(
+ 'annotation' => $annotation,
+ 'sort_by' => 'priority',
+ 'class' => 'elgg-menu-hz float-alt',
+ ));
+ } else {
+ // Regular entity menu
+ $metadata = elgg_view_menu('entity', array(
+ 'entity' => $vars['entity'],
+ 'handler' => 'pages',
+ 'sort_by' => 'priority',
+ 'class' => 'elgg-menu-hz',
+ ));
+ }
}
if ($full) {
diff --git a/mod/pages/views/default/pages/sidebar/history.php b/mod/pages/views/default/pages/sidebar/history.php
index 7077edb9a..e0e8ed11a 100644
--- a/mod/pages/views/default/pages/sidebar/history.php
+++ b/mod/pages/views/default/pages/sidebar/history.php
@@ -14,6 +14,7 @@ if ($vars['page']) {
'limit' => 20,
'reverse_order_by' => true
);
+ elgg_push_context('widgets');
$content = elgg_list_annotations($options);
}
diff --git a/mod/profile/icondirect.php b/mod/profile/icondirect.php
index dbab5d31f..5f1599e0d 100644
--- a/mod/profile/icondirect.php
+++ b/mod/profile/icondirect.php
@@ -55,13 +55,13 @@ if ($mysql_dblink) {
$user_path = date('Y/m/d/', $join_date) . $guid;
$filename = "$data_root$user_path/profile/{$guid}{$size}.jpg";
- $size = @filesize($filename);
- if ($size) {
+ $filesize = @filesize($filename);
+ if ($filesize) {
header("Content-type: image/jpeg");
header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+6 months")), true);
header("Pragma: public");
header("Cache-Control: public");
- header("Content-Length: $size");
+ header("Content-Length: $filesize");
header("ETag: \"$etag\"");
readfile($filename);
exit;
diff --git a/mod/profile/views/default/profile/details.php b/mod/profile/views/default/profile/details.php
index 7b05b0e15..da4e95690 100644
--- a/mod/profile/views/default/profile/details.php
+++ b/mod/profile/views/default/profile/details.php
@@ -21,8 +21,22 @@ if (is_array($profile_fields) && sizeof($profile_fields) > 0) {
continue;
}
$value = $user->$shortname;
+
if (!empty($value)) {
- //This function controls the alternating class
+
+ // fix profile URLs populated by https://github.com/Elgg/Elgg/issues/5232
+ // @todo Replace with upgrade script, only need to alter users with last_update after 1.8.13
+ if ($valtype == 'url' && $value == 'http://') {
+ $user->$shortname = '';
+ continue;
+ }
+
+ // validate urls
+ if ($valtype == 'url' && !preg_match('~^https?\://~i', $value)) {
+ $value = "http://$value";
+ }
+
+ // this controls the alternating class
$even_odd = ( 'odd' != $even_odd ) ? 'odd' : 'even';
?>
<div class="<?php echo $even_odd; ?>">
diff --git a/mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php b/mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php
index 32f108312..ed52a536d 100644
--- a/mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php
+++ b/mod/reportedcontent/views/default/admin/administer_utilities/reportedcontent.php
@@ -5,7 +5,7 @@
* @package ElggReportedContent
*/
-$list = elgg_list_entities(array('types' => 'object', 'subtypes' => 'reported_content'));
+$list = elgg_list_entities(array('type' => 'object', 'subtype' => 'reported_content'));
if (!$list) {
$list = '<p class="mtm">' . elgg_echo('reportedcontent:none') . '</p>';
}
diff --git a/mod/reportedcontent/views/default/object/reported_content.php b/mod/reportedcontent/views/default/object/reported_content.php
index 0e733e154..cc33f54fb 100644
--- a/mod/reportedcontent/views/default/object/reported_content.php
+++ b/mod/reportedcontent/views/default/object/reported_content.php
@@ -57,16 +57,6 @@ if ($report->state == 'archived') {
<p>
<b><?php echo elgg_echo('reportedcontent:objecttitle'); ?>:</b>
<?php echo $report->title; ?>
- <br />
- <?php echo elgg_view('output/url', array(
- 'href' => "#report-$report->guid",
- 'text' => elgg_echo('reportedcontent:moreinfo'),
- 'rel' => "toggle",
- ));
- ?>
- </p>
- </div>
- <div class="report-details hidden" id="report-<?php echo $report->getGUID();?>">
<p>
<b><?php echo elgg_echo('reportedcontent:objecturl'); ?>:</b>
<?php echo elgg_view('output/url', array(
@@ -77,6 +67,16 @@ if ($report->state == 'archived') {
?>
</p>
<p>
+ <?php echo elgg_view('output/url', array(
+ 'href' => "#report-$report->guid",
+ 'text' => elgg_echo('reportedcontent:moreinfo'),
+ 'rel' => "toggle",
+ ));
+ ?>
+ </p>
+ </div>
+ <div class="report-details hidden" id="report-<?php echo $report->getGUID();?>">
+ <p>
<b><?php echo elgg_echo('reportedcontent:reason'); ?>:</b>
<?php echo $report->description; ?>
</p>
diff --git a/mod/reportedcontent/views/default/widgets/reportedcontent/content.php b/mod/reportedcontent/views/default/widgets/reportedcontent/content.php
index 4c6595653..0095decca 100644
--- a/mod/reportedcontent/views/default/widgets/reportedcontent/content.php
+++ b/mod/reportedcontent/views/default/widgets/reportedcontent/content.php
@@ -4,8 +4,8 @@
*/
$list = elgg_list_entities(array(
- 'types' => 'object',
- 'subtypes' => 'reported_content',
+ 'type' => 'object',
+ 'subtype' => 'reported_content',
'limit' => $vars['entity']->num_display,
'pagination' => false,
));
diff --git a/mod/search/README.txt b/mod/search/README.txt
index 98a002dd5..ac5930e5f 100644
--- a/mod/search/README.txt
+++ b/mod/search/README.txt
@@ -273,4 +273,4 @@ MySQL's fulltext engine returns *ZERO* rows if more than 50% of
the rows searched match.
The default search hooks for users and groups ignore subtypes.
-See [trac ticket 1499](http://trac.elgg.org/elgg/ticket/1499)
+See [GitHub issue 1499](https://github.com/elgg/elgg/issues/1499)
diff --git a/mod/search/pages/search/index.php b/mod/search/pages/search/index.php
index fcd95c43e..9542e0751 100644
--- a/mod/search/pages/search/index.php
+++ b/mod/search/pages/search/index.php
@@ -17,15 +17,7 @@ $search_type = get_input('search_type', 'all');
// XSS protection is more important that searching for HTML.
$query = stripslashes(get_input('q', get_input('tag', '')));
-// @todo - create function for sanitization of strings for display in 1.8
-// encode <,>,&, quotes and characters above 127
-if (function_exists('mb_convert_encoding')) {
- $display_query = mb_convert_encoding($query, 'HTML-ENTITIES', 'UTF-8');
-} else {
- // if no mbstring extension, we just strip characters
- $display_query = preg_replace("/[^\x01-\x7F]/", "", $query);
-}
-$display_query = htmlspecialchars($display_query, ENT_QUOTES, 'UTF-8', false);
+$display_query = _elgg_get_display_query($query);
// check that we have an actual query
if (!$query) {
@@ -63,7 +55,7 @@ switch ($sort) {
break;
}
-$order = get_input('sort', 'desc');
+$order = get_input('order', 'desc');
if ($order != 'asc' && $order != 'desc') {
$order = 'desc';
}
diff --git a/mod/search/search_hooks.php b/mod/search/search_hooks.php
index 62058abf1..923cf0aa8 100644
--- a/mod/search/search_hooks.php
+++ b/mod/search/search_hooks.php
@@ -3,17 +3,17 @@
* Elgg core search.
*
* @package Elgg
- * @subpackage Core
+ * @subpackage Search
*/
/**
- * Return default results for searches on objects.
+ * Get objects that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_objects_hook($hook, $type, $value, $params) {
@@ -23,7 +23,7 @@ function search_objects_hook($hook, $type, $value, $params) {
$params['joins'] = array($join);
$fields = array('title', 'description');
- $where = search_get_where_sql('oe', $fields, $params, FALSE);
+ $where = search_get_where_sql('oe', $fields, $params);
$params['wheres'] = array($where);
$params['count'] = TRUE;
@@ -35,6 +35,7 @@ function search_objects_hook($hook, $type, $value, $params) {
}
$params['count'] = FALSE;
+ $params['order_by'] = search_get_order_by_sql('e', 'oe', $params['sort'], $params['order']);
$entities = elgg_get_entities($params);
// add the volatile data for why these entities have been returned.
@@ -53,13 +54,13 @@ function search_objects_hook($hook, $type, $value, $params) {
}
/**
- * Return default results for searches on groups.
+ * Get groups that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_groups_hook($hook, $type, $value, $params) {
$db_prefix = elgg_get_config('dbprefix');
@@ -68,12 +69,9 @@ function search_groups_hook($hook, $type, $value, $params) {
$join = "JOIN {$db_prefix}groups_entity ge ON e.guid = ge.guid";
$params['joins'] = array($join);
-
$fields = array('name', 'description');
- // force into boolean mode because we've having problems with the
- // "if > 50% match 0 sets are returns" problem.
- $where = search_get_where_sql('ge', $fields, $params, FALSE);
+ $where = search_get_where_sql('ge', $fields, $params);
$params['wheres'] = array($where);
@@ -89,6 +87,7 @@ function search_groups_hook($hook, $type, $value, $params) {
}
$params['count'] = FALSE;
+ $params['order_by'] = search_get_order_by_sql('e', 'ge', $params['sort'], $params['order']);
$entities = elgg_get_entities($params);
// add the volatile data for why these entities have been returned.
@@ -107,15 +106,15 @@ function search_groups_hook($hook, $type, $value, $params) {
}
/**
- * Return default results for searches on users.
- *
- * @todo add profile field MD searching
+ * Get users that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * Searches on username, display name, and profile fields
+ *
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_users_hook($hook, $type, $value, $params) {
$db_prefix = elgg_get_config('dbprefix');
@@ -159,6 +158,7 @@ function search_users_hook($hook, $type, $value, $params) {
}
$params['count'] = FALSE;
+ $params['order_by'] = search_get_order_by_sql('e', 'ue', $params['sort'], $params['order']);
$entities = elgg_get_entities($params);
// add the volatile data for why these entities have been returned.
@@ -175,11 +175,20 @@ function search_users_hook($hook, $type, $value, $params) {
$entity->setVolatileData('search_matched_title', $title);
$matched = '';
- foreach ($profile_fields as $md) {
- $text = $entity->$md;
- if (stristr($text, $query)) {
- $matched .= elgg_echo("profile:{$md}") . ': '
- . search_get_highlighted_relevant_substrings($text, $query);
+ foreach ($profile_fields as $md_name) {
+ $metadata = $entity->$md_name;
+ if (is_array($metadata)) {
+ foreach ($metadata as $text) {
+ if (stristr($text, $query)) {
+ $matched .= elgg_echo("profile:{$md_name}") . ': '
+ . search_get_highlighted_relevant_substrings($text, $query);
+ }
+ }
+ } else {
+ if (stristr($metadata, $query)) {
+ $matched .= elgg_echo("profile:{$md_name}") . ': '
+ . search_get_highlighted_relevant_substrings($metadata, $query);
+ }
}
}
@@ -193,13 +202,13 @@ function search_users_hook($hook, $type, $value, $params) {
}
/**
- * Return default results for searches on tags.
+ * Get entities with tags that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_tags_hook($hook, $type, $value, $params) {
$db_prefix = elgg_get_config('dbprefix');
@@ -261,6 +270,7 @@ function search_tags_hook($hook, $type, $value, $params) {
}
$params['count'] = FALSE;
+ $params['order_by'] = search_get_order_by_sql('e', null, $params['sort'], $params['order']);
$entities = elgg_get_entities($params);
// add the volatile data for why these entities have been returned.
@@ -327,11 +337,11 @@ function search_tags_hook($hook, $type, $value, $params) {
/**
* Register tags as a custom search type.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Array of custom search types
+ * @param array $params Search parameters
+ * @return array
*/
function search_custom_types_tags_hook($hook, $type, $value, $params) {
$value[] = 'tags';
@@ -340,13 +350,13 @@ function search_custom_types_tags_hook($hook, $type, $value, $params) {
/**
- * Return default results for searches on comments.
+ * Get comments that match the search parameters.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Empty array
+ * @param array $params Search parameters
+ * @return array
*/
function search_comments_hook($hook, $type, $value, $params) {
$db_prefix = elgg_get_config('dbprefix');
@@ -395,9 +405,19 @@ function search_comments_hook($hook, $type, $value, $params) {
// don't continue if nothing there...
if (!$count) {
- return array ('entities' => array(), 'count' => 0);
+ return array('entities' => array(), 'count' => 0);
}
-
+
+ // no full text index on metastrings table
+ if ($params['sort'] == 'relevance') {
+ $params['sort'] = 'created';
+ }
+
+ $order_by = search_get_order_by_sql('a', null, $params['sort'], $params['order']);
+ if ($order_by) {
+ $order_by = "ORDER BY $order_by";
+ }
+
$q = "SELECT DISTINCT a.*, msv.string as comment FROM {$db_prefix}annotations a
JOIN {$db_prefix}metastrings msn ON a.name_id = msn.id
JOIN {$db_prefix}metastrings msv ON a.value_id = msv.id
@@ -407,7 +427,8 @@ function search_comments_hook($hook, $type, $value, $params) {
AND $e_access
AND $a_access
$container_and
-
+
+ $order_by
LIMIT $offset, $limit
";
@@ -434,10 +455,17 @@ function search_comments_hook($hook, $type, $value, $params) {
}
$comment_str = search_get_highlighted_relevant_substrings($comment->comment, $query);
- $entity->setVolatileData('search_match_annotation_id', $comment->id);
- $entity->setVolatileData('search_matched_comment', $comment_str);
- $entity->setVolatileData('search_matched_comment_owner_guid', $comment->owner_guid);
- $entity->setVolatileData('search_matched_comment_time_created', $comment->time_created);
+ $comments_data = $entity->getVolatileData('search_comments_data');
+ if (!$comments_data) {
+ $comments_data = array();
+ }
+ $comments_data[] = array(
+ 'annotation_id' => $comment->id,
+ 'text' => $comment_str,
+ 'owner_guid' => $comment->owner_guid,
+ 'time_created' => $comment->time_created,
+ );
+ $entity->setVolatileData('search_comments_data', $comments_data);
$entities[] = $entity;
}
@@ -450,11 +478,11 @@ function search_comments_hook($hook, $type, $value, $params) {
/**
* Register comments as a custom search type.
*
- * @param unknown_type $hook
- * @param unknown_type $type
- * @param unknown_type $value
- * @param unknown_type $params
- * @return unknown_type
+ * @param string $hook Hook name
+ * @param string $type Hook type
+ * @param array $value Array of custom search types
+ * @param array $params Search parameters
+ * @return array
*/
function search_custom_types_comments_hook($hook, $type, $value, $params) {
$value[] = 'comments';
diff --git a/mod/search/views/default/search/comments/entity.php b/mod/search/views/default/search/comments/entity.php
index 005bb270c..77e950843 100644
--- a/mod/search/views/default/search/comments/entity.php
+++ b/mod/search/views/default/search/comments/entity.php
@@ -6,8 +6,11 @@
*/
$entity = $vars['entity'];
+$comments_data = $entity->getVolatileData('search_comments_data');
+$comment_data = array_shift($comments_data);
+$entity->setVolatileData('search_comments_data', $comments_data);
-$owner = get_entity($entity->getVolatileData('search_matched_comment_owner_guid'));
+$owner = get_entity($comment_data['owner_guid']);
if ($owner instanceof ElggUser) {
$icon = elgg_view_entity_icon($owner, 'tiny');
@@ -38,12 +41,12 @@ if ($entity->getVolatileData('search_unavailable_entity')) {
$title = elgg_echo('search:comment_on', array($title));
// @todo this should use something like $comment->getURL()
- $url = $entity->getURL() . '#comment_' . $entity->getVolatileData('search_match_annotation_id');
+ $url = $entity->getURL() . '#comment_' . $comment_data['annotation_id'];
$title = "<a href=\"$url\">$title</a>";
}
-$description = $entity->getVolatileData('search_matched_comment');
-$tc = $entity->getVolatileData('search_matched_comment_time_created');;
+$description = $comment_data['text'];
+$tc = $comment_data['time_created'];
$time = elgg_view_friendly_time($tc);
$body = "<p class=\"mbn\">$title</p>$description";
diff --git a/mod/search/views/default/search/list.php b/mod/search/views/default/search/list.php
index 1ed40be1b..90aa28989 100644
--- a/mod/search/views/default/search/list.php
+++ b/mod/search/views/default/search/list.php
@@ -36,16 +36,21 @@ $query = http_build_query(
$url = elgg_get_site_url() . "search?$query";
+$more_items = $vars['results']['count'] - ($vars['params']['offset'] + $vars['params']['limit']);
+
// get pagination
if (array_key_exists('pagination', $vars['params']) && $vars['params']['pagination']) {
- $nav = elgg_view('navigation/pagination',array(
+ $nav = elgg_view('navigation/pagination', array(
'base_url' => $url,
'offset' => $vars['params']['offset'],
'count' => $vars['results']['count'],
'limit' => $vars['params']['limit'],
));
+ $show_more = false;
} else {
+ // faceted search page so no pagination
$nav = '';
+ $show_more = $more_items > 0;
}
// figure out what we're dealing with.
@@ -75,12 +80,7 @@ if (array_key_exists('search_type', $vars['params'])
$type_str = $search_type_str;
}
-// get any more links.
-$more_check = $vars['results']['count'] - ($vars['params']['offset'] + $vars['params']['limit']);
-$more = ($more_check > 0) ? $more_check : 0;
-
-if ($more) {
- $title_key = ($more == 1) ? 'comment' : 'comments';
+if ($show_more) {
$more_str = elgg_echo('search:more', array($count, $type_str));
$more_url = elgg_http_remove_url_query_element($url, 'limit');
$more_link = "<li class='elgg-item'><a href=\"$more_url\">$more_str</a></li>";
diff --git a/mod/search/views/rss/search/comments/entity.php b/mod/search/views/rss/search/comments/entity.php
index 869779f35..e47afec4a 100644
--- a/mod/search/views/rss/search/comments/entity.php
+++ b/mod/search/views/rss/search/comments/entity.php
@@ -6,9 +6,12 @@
*/
$entity = $vars['entity'];
+$comments_data = $entity->getVolatileData('search_comments_data');
+$comment_data = array_shift($comments_data);
+$entity->setVolatileData('search_comments_data', $comments_data);
$author_name = '';
-$comment_author_guid = $entity->getVolatileData('search_matched_comment_owner_guid');
+$comment_author_guid = $comment_data['owner_guid'];
$author = get_user($comment_author_guid);
if ($author) {
$author_name = $author->name;
@@ -34,11 +37,11 @@ if ($entity->getVolatileData('search_unavailable_entity')) {
$title = elgg_echo('search:comment_on', array($title));
$title .= ' ' . elgg_echo('search:comment_by') . ' ' . $author_name;
- $url = $entity->getURL() . '#annotation-' . $entity->getVolatileData('search_match_annotation_id');
+ $url = $entity->getURL() . '#annotation-' . $comment_data['annotation_id'];
}
-$description = $entity->getVolatileData('search_matched_comment');
-$tc = $entity->getVolatileData('search_matched_comment_time_created');;
+$description = $comment_data['text'];
+$tc = $comment_data['time_created'];
?>
diff --git a/mod/thewire/pages/thewire/everyone.php b/mod/thewire/pages/thewire/everyone.php
index 909f0caf2..c7438747e 100644
--- a/mod/thewire/pages/thewire/everyone.php
+++ b/mod/thewire/pages/thewire/everyone.php
@@ -18,7 +18,7 @@ if (elgg_is_logged_in()) {
$content .= elgg_list_entities(array(
'type' => 'object',
'subtype' => 'thewire',
- 'limit' => 15,
+ 'limit' => get_input('limit', 15),
));
$body = elgg_view_layout('content', array(
diff --git a/mod/thewire/pages/thewire/friends.php b/mod/thewire/pages/thewire/friends.php
index e7f5eed59..efa7e7a56 100644
--- a/mod/thewire/pages/thewire/friends.php
+++ b/mod/thewire/pages/thewire/friends.php
@@ -5,7 +5,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('thewire/all');
+ forward('', '404');
}
$title = elgg_echo('thewire:friends');
diff --git a/mod/thewire/pages/thewire/owner.php b/mod/thewire/pages/thewire/owner.php
index 6246c1770..dc25940e1 100644
--- a/mod/thewire/pages/thewire/owner.php
+++ b/mod/thewire/pages/thewire/owner.php
@@ -6,7 +6,7 @@
$owner = elgg_get_page_owner_entity();
if (!$owner) {
- forward('thewire/all');
+ forward('', '404');
}
$title = elgg_echo('thewire:user', array($owner->name));
@@ -26,7 +26,7 @@ $content .= elgg_list_entities(array(
'type' => 'object',
'subtype' => 'thewire',
'owner_guid' => $owner->guid,
- 'limit' => 15,
+ 'limit' => get_input('limit', 15),
));
$body = elgg_view_layout('content', array(
diff --git a/mod/thewire/views/default/thewire/profile_status.php b/mod/thewire/views/default/thewire/profile_status.php
index b5d9dbd80..26e1403fe 100644
--- a/mod/thewire/views/default/thewire/profile_status.php
+++ b/mod/thewire/views/default/thewire/profile_status.php
@@ -9,8 +9,8 @@ $owner = $vars['entity']->guid;
//grab the user's latest from the wire
$params = array(
- 'types' => 'object',
- 'subtypes' => 'thewire',
+ 'type' => 'object',
+ 'subtype' => 'thewire',
'owner_guid' => $owner,
'limit' => 1,
);
diff --git a/mod/tinymce/vendor/tinymce/changelog.txt b/mod/tinymce/vendor/tinymce/changelog.txt
index 12727f119..69b7ae50c 100644
--- a/mod/tinymce/vendor/tinymce/changelog.txt
+++ b/mod/tinymce/vendor/tinymce/changelog.txt
@@ -1,3 +1,219 @@
+Version 3.5.8 (2012-11-20)
+ Fixed bug where html5 data attributes where stripped from contents.
+ Fixed bug where toolbar was annouced multiple times with JAWS on Firefox.
+ Fixed bug where the editor view whouldn't scroll to BR elements when using shift+enter or br enter mode.
+ Fixed bug where a JS error would be thrown when trying to paste table rows then the rows clipboard was empty.
+ Fixed bug with auto detection logic for youtube urls in the media plugin.
+ Fixed bug where the formatter would throw errors if you used the jQuery version of TinyMCE and the latest jQuery.
+ Fixed bug where the latest WebKit versions would produce span elements when deleting text between blocks.
+ Fixed bug where the autolink plugin would produce DOM exceptions when pressing shift+enter inside a block element.
+ Fixed bug where toggling of blockquotes when using br enter mode would produce an exception.
+ Fixed bug where focusing out of the body of the editor wouldn't properly add an undo level.
+ Fixed issue with warning message being displayed on IE 9+ about the meta header fix for IE 8.
+Version 3.5.7 (2012-09-20)
+ Changed table row properties dialog to not update multiple rows when row type is header or footer.
+ Fixed bug in hyperlink dialog for IE9 where links with no target attr set had target value of --
+ Changing toolbars to have a toolbar role for FF keyboard navigation works correctly.
+ Fixed bug where applying formatting to an empty block element would produce redundant spans.
+ Fixed bug where caret formatting on IE wouldn't properly apply if you pressed enter/return.
+ Fixed bug where loading TinyMCE using an async script wouldn't properly initialize editors.
+ Fixed bug where some white space would be removed after inline elements before block elements.
+ Fixed bug where it wouldn't properly parse attributes with a single backslash as it's contents.
+ Fixed bug where noscript elements would loose it's contents on older IE versions.
+ Fixed bug where backspace inside empty blockquote wouldn't delete it properly.
+ Fixed bug where custom elements with . in their names wouldn't work properly.
+ Fixed bug where the custom_elements option didn't properly setup the block elements schema structure.
+ Fixed bug where the custom_elements option didn't auto populate the extended_valid_elements.
+ Fixed bug where the whole TD element would get blcok formatted when there where BR elements in it.
+ Fixed bug where IE 9 might crash if the editor was hidden and specific styles where applied to surrounding contents.
+ Fixed bug where shift+enter inside a table cell on Gecko would produce an zero width non breaking space between tr:s.
+ Fixed bug where the advlink dialog wouldn't properly populate the anchors dropdown if the HTML5 schema was used.
+ Fixed issue with missing autofocus attribute on input element when using the HTML5 schema.
+ Fixed issue where enter inside a block contained within an LI element wouldn't produce a new LI.
+Version 3.5.6 (2012-07-26)
+ Added "text" as a valid option to the editor.getContent format option. Makes it easier to get a text representation of the editor contents.
+ Fixed bug where resizing an image to less that 0x0 pixels would display the ghost image at an incorrect position.
+ Fixed bug where the remove format button would produce extra paragraphs on WebKit if all of the contents was selected.
+ Fixed issue where edge resize handles on images of wouldn't scale it with the same aspect ratio.
+ Fixed so force_p_newlines option works again since some users want mixed mode paragraphs.
+ Fixed so directionality plugin modifies the dir attribute of all selected blocks in the editor.
+ Fixed bug where backspace/delete of a custom element would move it's attributes to the parent block on Gecko.
+Version 3.5.5 (2012-07-19)
+ Added full resize support for images and tables on WebKit/Opera. It now behaves just like Gecko.
+ Added automatic embed support for Vimeo, Stream.cz and Google Maps in media plugin. Patch contributed by Jakub Matas.
+ Fixed bug where the lists plugin wouldn't properly remove all li elements when toggling selected items of. Patched by Taku AMANO.
+ Fixed bug where the lists plugin would remove the entire list if you pressed deleted at the beginning of the first element. Patched by Taku AMANO.
+ Fixed bug where the ordered/unordered list buttons could both be enabled if you nested lists. Patch contributed by Craig Petchell.
+ Fixed bug where shift+enter wouldn't produce a BR in a LI when having forced_root_blocks set to false.
+ Fixed bug where scrollbars aren't visible in fullscreen when window is resized.
+ Fixed bug with updating the border size using the advimage dialog on IE 9.
+ Fixed bug where the selection of inner elements on IE 8 in contentEditable mode would select the whole parent element.
+ Fixed bug where the enter key would produce an empty anchor if you pressed it at the space after a link on IE.
+ Fixed bug where autolink plugin would produce an exception for specific html see bug #5365
+ Fixed so the formatChanged function takes an optional "similar" parameter to use while matching the format.
+Version 3.5.4.1 (2012-06-24)
+ Fixed issue with Shift+A selecting all contents on Chrome.
+Version 3.5.4 (2012-06-21)
+ Added missing mouse events to HTML5 schema. Some events needs to be manually defined though since the spec is huge.
+ Added image resizing for WebKit browsers by faking the whole resize behavior.
+ Fixed bug in context menu plugin where listener to hide menu wasn't removed correctly.
+ Fixed bug where media plugin wouldn't use placeholder size for the object/video elements.
+ Fixed bug where jQuery plugin would break attr function in jQuery 1.7.2.
+ Fixed bug where jQuery plugin would throw an error if you used the tinymce pseudo selector when TinyMCE wasn't loaded.
+ Fixed so encoding option gets applied when using jQuery val() or attr() to extract the contents.
+ Fixed so any non valid width/height passed to media plugin would get parsed to proper integer or percent values.
+Version 3.5.3 (2012-06-19)
+ Added missing wbr element to HTML5 schema.
+ Added new mceToggleFormat command. Enabled you to toggle a specific format on/off.
+ Fixed bug where undo/redo state didn't update correctly after executing an execCommand call.
+ Fixed bug where the editor would get auto focused on IE running in quirks mode.
+ Fixed bug where pressing enter before an IMG or INPUT element wouldn't properly split the block.
+ Fixed bug where backspace would navigate back when selecting control types on IE.
+ Fixed bug where the editor remove method would unbind events for controls outside the editor instance UI.
+ Fixed bug where the autosave plugin would try to store a draft copy of editors that where removed.
+ Fixed bug where floated elements wouldn't expand the block created when pressing enter on non IE browsers.
+ Fixed bug where the caret would be placed in the wrong location when pressing enter at the beginning of a block.
+ Fixed bug where it wasn't possible to block events using the handle_event_callback option.
+ Fixed bug where keyboard navigation of the ColorSplitButton.js didn't work correctly.
+ Fixed bug where keyboard navigation didn't work correctly on split buttons.
+ Fixed bug where the legacy Event.add function didn't properly handle multiple id:s passed in.
+ Fixed bug where the caret would disappear on IE when selecting all contents and pressing backspace/delete.
+ Fixed bug where the getStart/getEnd methods would sometimes return elements from the wrong document on IE.
+ Fixed so paragraphs gets created if you press enter inside a form element.
+Version 3.5.2 (2012-05-31)
+ Added new formatChanged method to tinymce.Formatter class. Enables easier state change handling of formats.
+ Added new selectorChanged method to tinymce.dom.Selection class. Enables easier state change handling of matching CSS selectors.
+ Changed the default theme to be advanced instead of simple since most users uses the advanced theme.
+ Changed so the theme_advanced_buttons doesn't have a default set if one button row is specified.
+ Changed the theme_advanced_toolbar_align default value to "left".
+ Changed the theme_advanced_toolbar_location default value to "top".
+ Changed the theme_advanced_statusbar_location default value to "bottom".
+ Fixed bug where the simple link dialog would remove class and target attributes from links when updating them if the drop downs wasn't visible.
+ Fixed bug where the link/unlink buttons wouldn't get disabled once a link was created by the autolink plugin logic.
+ Fixed bug where the border attribute was missing in the HTML5 schema.
+ Fixed bug where the legacyoutput plugin would use inline styles for font color.
+ Fixed bug where editing of anchor names wouldn't produce an undo level.
+ Fixed bug where the table plugin would delete the last empty block element in the editor.
+ Fixed bug where pasting table rows when they where selected would make it impossible to editor that table row.
+ Fixed bug with pressing enter in IE while having a select list focused would produce a JS error.
+ Fixed bug where it wasn't possible to merge table cells by selecting them and using merge from context menu.
+ Removed summary from HTML5 table attributes and fixed so this and other deprecated table fields gets hidden in the table dialog.
+Version 3.5.1.1 (2012-05-25)
+ Fixed bug with control creation where plugin specific controls didn't work as expected.
+Version 3.5.1 (2012-05-25)
+ Added new onBeforeAdd event to UndoManager patch contributed by Dan Rumney.
+ Added support for overriding the theme rendering logic by using a custom function.
+ Fixed bug where links wasn't automatically created by the autolink plugin on old IE versions when pressing enter in BR mode.
+ Fixed bug where enter on older IE versions wouldn't produce a new paragraph if the previous sibling paragraph was empty.
+ Fixed bug where toString on a faked DOM range on older IE versions wouldn't return a proper string.
+ Fixed bug where named anchors wouldn't work properly when schema was set to HTML5.
+ Fixed bug where HTML5 datalist options wasn't correctly parsed or indented.
+ Fixed bug where linking would add anchors around block elements when the HTML5 schema was used.
+ Fixed issue where the autolink plugin wouldn't properly handle mailto:user@domain.com.
+ Optimized initialization and reduced rendering flicker by hiding the target element while initializing.
+Version 3.5.0.1 (2012-05-10)
+ Fixed bug where selection normalization logic would break the selections of parent elements using the element path.
+ Fixed bug where the autolink plugin would include trailing dots in domain names in the link creation.
+ Fixed bug where the autolink plugin would produce an error on older IE versions when pressing enter.
+ Fixed bug where old IE versions would throw an error during initialization when the editor was placed in an size restricted div.
+Version 3.5 (2012-05-03)
+ Fixed menu rendering issue if the document was in rtl mode.
+ Fixed bug where the hide function would throw an error about a missing variable.
+ Fixed bug where autolink wouldn't convert URLs when hitting enter on IE due to the new enter key logic.
+ Fixed bug where formatting using shortcuts like ctrl+b wouldn't work properly the first time.
+ Fixed bug where selection.setContent after a formatter call wouldn't generate formatted contents.
+ Fixed bug where whitespace would be removed before/after invalid_elements when they where removed.
+ Fixed bug where updating styles using the theme image dialog in non inline mode on IE9 would produce errors.
+ Fixed bug where IE 8 would produce an error when using the contextmenu plugin.
+ Fixed bug where delete/backspace could remove contents of noneditable elements.
+ Fixed so background color in style preview gets computed from body element if the current style element is transparent.
+Version 3.5b3 (2012-03-29)
+ Added cancel button to colour picker dialog.
+ Added figure and figcaption to the html5 visualblocks plugin.
+ Added default alignment options for the figure element.
+ Fixed bug where empty inline elements within block elements would sometimes produce a br child element.
+ Fixed bug where urls pointing to the same domain as the current one would cause undefined errors. Patch contributed by Paul Giberson.
+ Fixed bug where enter inside an editable element inside an non editable element would split the element.
+ Fixed bug where cut/copy/paste of noneditable elements didn't work.
+ Fixed bug where backspace would sometimes produce font elements on WebKit.
+ Fixed bug where WebKit would produce spans out of various inline elements when using backspace.
+ Fixed bug where IE9 wouldn't properly update image styles when images where resized.
+ Fixed bug where drag/drop of noneditable elements didn't work correctly.
+ Fixed bug where applying formatting to all contents wouldn't work correctly when an end point was inside an empty bock. Patch contributed by Jose Luiz.
+ Fixed bug where IE10 removed the scopeName from the DOM element interface and there for it produced an undefined string in element path.
+ Fixed bug where the caret would be placed at an incorrect location if you applied block formatting while having the caret at the end of the block.
+ Fixed bug where applying column changes using the cell dialog would only update the first column. Patch contributed by krzyko.
+ Fixed bug where the visualblocks plugin would force editor focus if it was turned on by default.
+ Fixed bug where the tabfocus plugin would tab to iframes these are now ignored.
+ Fixed bug where format drop down list wouldn't show the currently active format for a parent element.
+ Fixed bug where paste of plain text in IE 9 would remove the new line characters from text.
+ Fixed bug where the menu buttons/split button menus wouldn't be opened at the right location on older IE versions.
+ Fixed bug where Gecko browsers wouldn't properly display the right format when having the selection as specific places.
+ Fixed bug where shift+enter inside the body when having forced_root_blocks set to false would throw an error.
+ Fixed bug where the jQuery plugin would break the attr method of jQuery 1.7.2. Patch contributed by Markus Kemmerling.
+ Fixed so options like content_css accepts and array as well as a comma separated string as input.
+ Restructured the internal logic to make it more separate from Editor.js.
+ Updated the Sizzle engine to the latest version.
+Version 3.5b2 (2012-03-15)
+ Rewrote the enter key logic to normalize browser behavior.
+ Fixed so enter within PRE elements produces a BR and shift+enter breaks/end the PRE. Can be disabled using the br_in_pre option.
+ Fixed bug where the selection wouldn't be correct after applying formatting and having the caret at the end of the new format node.
+ Fixed bug where the noneditable plugin would process contents on raw input calls for example on undo/redo calls.
+ Fixed bug where WebKit could produce an exception when a bookmark was requested when there wasn't a proper selection.
+ Fixed bug where WebKit would fail to open the image dialog since it would be returning false for a class name instead of a string.
+ Fixed so alignment and indentation works properly when forced_root_blocks is set to false. It will produce a DIV by default.
+Version 3.5b1 (2012-03-08)
+ Added new event class that is faster and enables support for faking events.
+ Added new self_closing_elements, short_ended_elements, boolean_attributes, non_empty_elements and block_elements options to control the HTML Schema.
+ Added new schema option and support for the HTML5 schema.
+ Added new visualblocks plugin that shows html5 blocks with visual borders.
+ Added new types and selector options to make it easier to create editor instances with different configs.
+ Added new preview of formatting options in various listboxes.
+ Added new preview_styles option that enables control over what gets previewed.
+ Fixed bug where content css would be loaded twice into iframe.
+ Fixed bug where start elements with only whitespace in the attribute part wouldn't be correctly parsed.
+ Fixed bug where the advlink dialog would produce an error about the addSelectAccessibility function not being defined.
+ Fixed bug where the caret would be placed at an incorrect position if span was removed by the invalid_elements setting.
+ Fixed bug where elements inside a white space preserve element like pre didn't inherit the behavior while parsing.
+Version 3.4.9 (2012-02-23)
+ Added settings to wordcount plugin to configure update rate and checking wordcount on backspace and delete using wordcount_update_rate and wordcount_update_on_delete.
+ Fixed bug in Webkit and IE where deleting empty paragraphs would remove entire editor contents.
+ Fixed bug where pressing enter on end of list item with a heading would create a new item with heading.
+ Fixed edit css style dialog text-decoration none checkbox so it disables other text-decoration options when enabled.
+ Fixed bug in Gecko where undo wasn't added when focus was lost.
+ Fixed bug in Gecko where shift-enter in table cell ending with BR doesn't move caret to new line.
+ Fixed bug where right-click on formatted text in IE selected the entire line.
+ Fixed bug where text ending with space could not be unformatted in IE.
+ Fixed bug where caret formatting would be removed when moving the caret when a selector expression was used.
+ Fixed bug where formatting would be applied to the body element when all contents where selected and format had both inline and selector parts.
+ Fixed bug where the media plugin would throw errors if you had iframe set as an invalid element in config.
+ Fixed bug where the caret would be placed at the top of the document if you inserted a table and undo:ed that operation. Patch contributed by Wesley Walser.
+ Fixed bug where content css files where loaded twice into the iframe.
+ Fixed so elements with comments would be trated as non empty elements. Patch contributed by Arjan Scherpenisse.
+Version 3.4.8 (2012-02-02)
+ Fixed bug in IE where selected text ending with space cannot be formatted then formatted again to get original text.
+ Fixed bug in IE where images larger than editor area were being deselected when toolbar buttons are clicked.
+ Fixed bug where wrong text align buttons are active when multiple block elements are selected.
+ Fixed bug where selected link not showing in target field of link dialog in some selection cases.
+ Use settings for remove_trailing_br so this can be turned off instead of hard coding the value.
+ Fixed bug in IE where the media plugin displayed null text when some values aren't filled in.
+ Added API method 'onSetAttrib' that fires when the attribute value on a node changes.
+ Fix font size dropdown value not being updated when text already has a font size in the advanced template.
+ Fixed bug in IE where IE doesn't use ARIA attributes properly on options - causing labels to be read out 2 times.
+ Fixed bug where caret cannot be placed after table if table is at end of document in IE.
+ Fixed bug where adding range isn't always successful so we need to check range count otherwise an exception can occur.
+ Added spacebar onclick handler to toolbar buttons to ensure that the accessibility behaviour works correctly.
+ Fixed bug where a stranded bullet point would get created in WebKit.
+ Fixed bug where selecting text in a blockquote and pressing backspace toggles the style.
+ Fixed bug where pressing enter from a heading in IE, the resulting P tag below it shares the style property.
+ Fix white space in between spans from being deleted.
+ Fixed bug where scrollbars where visible in the character map dialog on Gecko.
+ Fixed issue with missing translation for one of the emoticons.
+ Fixed bug where dots in id:s where causing problems. Patch provided by Abhishek Dev.
+ Fixed bug where urls with an at sign in the path wouldn't be parsed correctly. Patch contributed by Jason Grout.
+ Fixed bug where Opera would remove the first character of a inline formatted word if you pressed backspace.
+ Fixed bugs with the autoresize plugin on various browsers and removed the need for the throbber.
+ Fixed performance issue where the contextmenu plugin would try to remove the menu even if it was removed. Patch contributed by mhu.
Version 3.4.7 (2011-11-03)
Modified the caret formatting behavior to word similar to common desktop wordprocessors like Word or Libre Office.
Fixed bug in Webkit - Cursor positioning does not work vertically within a table cell with multiple lines of text.
@@ -259,1201 +475,3 @@ Version 3.4 (2011-03-10)
Fixed so the values entered in the color picker are forced to hex values.
Removed dialog workaround for IE 9 beta since the RC is now out and people should upgrade.
Removed obsolete calls in various plugins to the mceBeginUndoLevel command.
-Version 3.4b3 (2011-02-10)
- Added WAI-ARIA support for the main UI and dialogs this feature was contributed by Ephox.
- Added iframe support to media plugin in order to handle the new YouTube HTML5 video formats.
- Fixed bug where anchors would wrap the text contents after it due to a bug in the DomParser logic.
- Fixed bug where the selected state wouldn't be removed on ListBox controls when a menu item was selected.
- Fixed bug where IE could throw an unspecified error exception when the getBookmark logic was executed.
- Fixed bug where IE would throw an invalid argument error when focus was applied to an empty editor instance.
- Fixed bug where applying inline format wouldn't work if the start cell in the selection was empty.
- Fixed bug where auto detection logic for YouTube and Google Video wouldn't work in the new media plugin.
- Fixed bug where td elements would get a colspan/rowspan of 1 when created by the table plugin.
- Fixed bug where removal/padding of empty elements wasn't handled correctly.
- Fixed bug where internal elements would show up in element path.
- Fixed bug where internal elements would get serialized as valid output.
- Fixed bug where color wasn't correctly applied to anchor elements.
- Fixed bug where float option in the style plugin dialog wouldn't be handled correctly on WebKit.
- Fixed bug where the tinymce.dom.TreeWalker prev function wouldn't walk the DOM correctly.
- Fixed bug where mceInsertContent command could produce empty block elements after the inserted content.
- Fixed bug where mceInsertContent command wouldn't apply visual aids on tables and similar elements.
- Fixed bug where empty block elements would get double br bogus elements in them.
- Fixed bug where the color menu wouldn't apply the color correctly on IE when the viewport was to small.
- Fixed bug where right clicking out side the body element of the editor iframe would prevent paste from working on IE.
- Fixed bug where the onContextMenu event wouldn't fire correctly on IE if you clicked out side the body element.
- Fixed bug where the onContextMenu event wouldn't fire correctly on modern Opera versions that now support it by default.
- Fixed bug where legacy content wasn't converted correctly when inserted using mceInsertContent or through the source dialog.
- Fixed bug where resizing images or tables wouldn't update the style attribute correctly or leave data-mce prefixed attributes.
- Fixed bug where adding links wouldn't work correctly when using TinyMCE jQuery version with jQuery 1.5.
- Fixed bug where single quotes inside param elements wasn't treated correctly by the media plugin.
- Fixed bug where pasting plain text in WebKit wouldn't work correctly. It will now auto detect the WebKit bug and use plain text mode.
- Fixed bug where the DomParser would fail to move out invalid elements within invalid elements on complex contents.
- Fixed bug where paste as plain text would not decode html entities properly.
- Fixed bug where large paragraphs would cause incorrect scrolling behavior if you would split them using enter.
- Fixed bug where the SaxParser wouldn't properly parse some specific short ended elements.
- Fixed so mceReplaceContent supports caret position and makes sure that the contents inserted gets validated.
- Fixed so unnecessary traling br elements in blocks gets removed on Gecko/WebKit when using mceInsertContent command.
- Moved some plugin css contents into the skin content css files to reduce the number of http requests.
- Moved some plugin specific images into the theme img directory since they can then be shared.
-Version 3.4b2 (2011-01-13)
- Added new custom flash player, this player supports mp4 and flv and has skin support.
- Fixed so mceInsertContent handles context correctly to enforce valid nesting of elements.
- Fixed bug where scrolling would become jerky on IE on some contents.
- Fixed bug where paste as plain text would throw exception of missing entities setting.
- Fixed bug where anchor nodes where removed by the new serializer engine.
- Fixed bug where IE would crash if when backspace where used on some specific contents.
- Fixed bug where pasting of plain text in WebKit would result in merging of text lines.
- Fixed bug where it wasn't possible to delete images or tables using backspace on IE9.
- Fixed bug where urls in styles would generate a JS error due to incorrect scope.
- Fixed bug where copy paste from Java applications would produce extra contents in FF on Mac.
- Fixed bug where the verify_html option wouldn't allow all elements and attributes.
-Version 3.4b1 (2010-12-20)
- Added new serialization engine that increases performance and enforces valid output according to the specified schema settings.
- Added new HTML parser logic used by the serialization engine and can handle malformed html contents.
- Added new valid_children config option, enables more fine grain control of elements can be inside other elements.
- Added new entities encoding logic boost performance and will only encode entities based on context i.e. attributes/text nodes.
- Added new protect setting that enables users to protect template items from being removed by the serializer logic.
- Added new {$caret} marker for the mceInsertContent command. Makes it possible to move the caret to a specific position when inserting contents.
- Added new validation of anchor names. Only valid W3C names will be accepted.
- Replaced the internal _mce_ prefixed attributes to the more standard HTML5 data-mce- prefix. This will also resolve future browser santiaztion issues.
- Fixed bug where the paste plugin wouldn't convert Word lists with more than 9 items to real ol lists. Patch contributed by Mike (yogaboy).
- Fixed bug where clicking on a format title would produce errors if the current selection didn't have any formats.
- Fixed bug where paste of simple texts wouldn't work correctly in Gecko using the paste plugin since it keeps block formatting.
- Fixed bug where confirm dialogs didn't display correctly due to resent IE9 fixes.
- Fixed bug where spaces in URLs wouldn't be properly encoded to %20 if the user entered them in the link dialogs. Patch contributed by Ephox.
- Fixed bug where the image alignment buttons wouldn't reposition the resize handles on FF due to a browser issue. Patch contributed by Ephox.
- Fixed bug where the compareBoundaryPoints method of the IE Range class didn't work correctly. Patch contributed by Ephox.
- Fixed bug where selection of elements using double click wouldn't select the clicked element but rather the parent node on FF. Patch contributed by Ephox.
- Fixed bug where IE would scroll the user to the current selection causing parent document to scroll as well. Patch contributed by Ephox.
- Fixed bug where style compression would incorrectly compress items with different values. It now only compresses if the values are the same. Patch contributed by Ephox.
- Fixed bug where FF would add non breaking spaces outside TD elements if formatting was applied to table cells. Patch contributed by Ephox.
- Fixed bug where the caret position would be lost on WebKit browsers if you pasted images multiple times. Patch contributed by Ephox.
- Fixed bug where non word contents like * would be counted as words in the wordcount pluging. Patch contributed by David Balatero.
- Fixed bug where the toggle absolute button in the layer plugin wouldn't remove the existing internal style attribute first.
- Fixed bug where the autosave plugin would generate an exception on IE if the user had disabled userdata persistence.
- Fixed bug where the paste plugin would remove dashed classes on IE since the regexps didn't include that character.
- Fixed bug where applying text color would not add spans inside link elements. This is needed due to CSS style inheritance.
- Fixed bug where applying block formats to empty elements wouldn't render correctly on IE.
- Fixed bug where the searchreplace plugin would add a f or r character when shortcuts where used on IE while using default dialogs.
- Fixed bug where Opera wouldn't load scripts correctly since the onreadystate would fire even though the script wasn't loaded.
- Fixed issue where &nbsp; wouldn't be handled correctly in the bbcode plugin if entity_encoding was set to raw.
- Fixed issue where contents would flicker since the content css files where asynchronously loaded.
- Fixed bug where WebKit wouldn't create links on images with a float style.
-Version 3.3.9.3 (2010-12-20)
- Fixed issue where WebKit wouldn't correctly apply ins/del in xhtmlxtras plugin.
- Fixed bug where paste as plaintext on WebKit wouldn't produce br and p elements correctly.
- Fixed bug where the confirm dialog texts would be incorrectly placed due to recent IE 9 workarounds in the window.css.
- Fixed bug where applying text color would not add spans inside link elements. This is needed due to CSS style inheritance.
-Version 3.3.9.2 (2010-09-29)
- Fixed bug where placing the caret in IE 9 beta 1 would not work correctly if you clicked out side the document body element.
- Fixed bug where IE 9 beta 1 wouldn't resize the editor correctly since the events didn't fire as previous versions did.
- Fixed bug where FF would produce an error message when being rendered inside a hidden div element.
- Fixed bug where resize logic could produce a cookie with a width/height less than the size of the container.
- Fixed bug where content_css wouldn't populate the styles dropdown correctly.
-Version 3.3.9.1 (2010-09-23)
- Fixed bug where WebKit browsers wouldn't activate the image button when images where selected.
- Fixed bug where Opera Presto 10.60 deletes elements when restoring bookmarks.
- Fixed bug where IE9 beta1 doesn't handle regexp replacement values correctly.
- Fixed bug where IE9 beta1 didn't render the inline dialogs correctly due to a bug with CSS clip.
- Fixed bug where IE9 beta1 would produce error messages on load since they removed the document.recalc method.
- Fixed bug where IE9 beta1 would produce <html xmlns=""> since they haven't implemented document.implementation.createDocument correctly.
- Fixed bug where IE9 beta1 would searchreplace doesn't work since their native DOM Range doesn't have a find method.
- Fixed bug where IE9 beta1 would render the source view incorrectly due to incorrect viewport size measurements.
- Fixed bug where IE9 beta1 would crash when running the basic functionality unit tests.
- Fixed bug where IE9 beta1 would wrap elements in blocks correctly due to changes to the selection object.
- Fixed bug where IE9 beta1 would fail to insert contents since they havn't implemented the createContextualFragment method in their DOM Range.
- Fixed bug where IE9 beta1 would fail to handle image selection since they currently doesn't support control selections in their DOM Range.
- Fixed bug where IE9 beta1 would fail to load scripts since they fire the onload event before the scripts are parsed and executed.
-Version 3.3.9 (2010-09-08)
- Fixed bug where inserting table rows into a table with subtable would produce an incorrect column count.
- Fixed bug where the selection of cells in a table with subtables could produce invalid selections.
- Fixed bug where the table plugin would produce a script error if you tried to move the caret before a first child table.
- Fixed bug where the keep_styles feature on IE would move the caret to an incorrect location at the end of list blocks.
- Fixed so attributes from legacy elements such as font gets retained when they get converted to spans.
- Fixed minor issue where the select boxes wouldn't be set the not set by default in the table dialog.
-Version 3.3.8 (2010-06-30)
- On IE8+ and FireFox 3.5+, dragging an image now correctly adds an undo
- event.
- Fixed bug where WebKit would not move the caret to a correct position after a paste operation.
- Fixed bug where WebKit would produce a div wrapper element when pasting some contents.
- Fixed bug where the visual chars and nonbreaking plugin wouldn't show nbsp elements correctly.
- Fixed bug where the format states would be enabled even after the format was removed.
- Fixed bug where the delete key would move the caret to an incorrect position.
- Fixed bug where it wasn't possible to toggle of the current font size/family/style by clicking the title item.
- Fixed bug where the abbr element wouldn't get serialized correctly on IE6.
- Fixed so that the examples checks if they are executed from the local file system since that might not work properly.
-Version 3.3.7 (2010-06-10)
- Fixed bug where context menu would produce an error on IE if you right clicked twice and left clicked once.
- Fixed bug where resizing of the window on WebKit browsers in fullscreen mode wouldn't position the statusbar correctly.
- Fixed bug where IE would produce an error if the editor was empty and you where undoing to that initial level.
- Fixed bug where setting the table background on gecko would produce \" entities inside the url style property.
- Fixed bug where the button states wouldn't be updated correctly on IE if you placed the caret inside the new element.
- Fixed bug where undo levels wasn't properly added after applying styles or font sizes.
- Fixed bug where IE would throw an error if you used "select all" on empty elements and applied formatting to that.
- Fixed bug where IE could select one extra character when you did a bookmark call on a caret location.
- Fixed bug where IE could produce a script error on delete since it would sometimes produce an invalid DOM.
- Fixed bug where IE would return the wrong start element if the whole element was selected.
- Fixed bug where formatting states wasn't updated on IE if you pressed enter at the end of a block with formatting.
- Fixed bug where submenus for the context menu wasn't removed correctly when the editor was destroyed.
- Fixed bug where Gecko could select the wrong element after applying format to multiple elements.
- Fixed bug where Gecko would delete parts of the previous element if the selection range was a element selection.
- Fixed bug where Gecko would not merge paragraph elements correctly if they contained br elements.
- Fixed bug where the cleanup button could produce span artifacts if you pressed it twice in a row.
- Fixed bug where the fullpage plugin header/footer would be have it's header reseted to it's initial state on undo.
- Fixed bug where an empty paragraph would be collapsed if you performed a cleanup while having the caret inside it.
- Fixed a few memory leaks on IE especially with drop menus in listboxes and the spellchecker.
- Fixed so formats applied to the current caret gets merged to reduce the number of output elements.
- Added the latest version of Sizzle for the CSS selector logic to fix a compatibility issue with prototype.
-Version 3.3.6 (2010-05-20)
- Fixed bug where a editor.focus call could produce errors on IE in very specific scenarios.
- Fixed bug where Gecko would produce an error if you unformatted text inside an empty element.
- Fixed bug where IE would produce an error if the caret was placed before a table and you used the align buttons.
- Fixed bug where the font size drop down didn't display the a preview correctly.
- Fixed bug where the paste plugin wouldn't include all contents some times on WebKit browsers.
- Fixed bug where the plain text mode toggle wouldn't work properly on WebKit.
- Fixed bug where the editors statusbar would become invisible when you resized the window in fullscreen mode.
-Version 3.3.5.1 (2010-05-07)
- Fixed a critical bug with the fullscreen plugin. Produced error messages when the state was toggled on/off.
-Version 3.3.5 (2010-05-06)
- Added new merge_with_parents option to formats, enables the control of removal of elements with similar parents.
- Fixed so the default behavior for applying classes isn't a toggle state but the old behavior from before the 3.3 release.
- Fixed bug where selecting contents using double click on Gecko would produce errors when using removing format.
- Fixed bug where the IE DOM could get messed up when non valid contents was pasted into the editor.
- Fixed bug where merging selected table cells using the context menu didn't work as expected.
- Fixed bug where some nestled formatting would be applied incorrectly.
- Fixed bug with enter in list items when using the force_br_newlines mode on WebKit patch contributed by Ryan Koopmans.
- Fixed bug where undo/redo could produce js errors on some specific operations.
- Fixed bug where the theme_advanced_font_sizes didn't work as before 3.3 when complex settings where used.
- Fixed bug where the table plugin would copy cell/row id attributes when making new rows/cells.
-Version 3.3.4 (2010-04-27)
- Fixed bug where fullscreen plugin would add two editor instances to EditorManager collection.
- Fixed bug where it was difficult to enter text on non western languages such as Japanese on IE.
- Fixed bug where removing contents from nodes could result in an exception when using undo/redo.
- Fixed bug with selection of images inside layers or other resizable containers on IE.
- Fixed so editors isn't initialized on iPhone/iPad devices since they don't have caret support.
-Version 3.3.3 (2010-04-19)
- Added new script_loaded callback function setting for the jQuery plugin.
- Added various fixes and new rpc methods for the spellchecker plugin. Patch contributed by Michael Peters.
- Removed some unnecessary inline style information from some of the dialogs.
- Fixed some issues with the chaining for the TinyMCE jQuery plugin.
- Fixed so any extra arguments passed to patched jQuery functions gets passed through. Patch contributed by Lee Henson.
- Fixed so spellchecking/contextmenu can be toggled on/off if the browser has native spellchecker support.
- Fixed bug where some texts in the new paste plugin wasn't placed in language pack.
- Fixed bug where IE would produce an incorrect information message when cutting.
- Fixed bug where removing items using the xhtmlxtras plugin wouldn't work correctly.
- Fixed bug where setting table background images would add extra quotes on Gecko.
- Fixed bug where shortcut for bold/italic/underline wouldn't work properly on WebKit.
- Fixed bug where IE would produce an error message if only contents was an image tag and bold was used.
- Fixed bug where the caret would move if alignment was applied to empty block elements.
- Fixed bug where some shortcut key commands wouldn't apply formatting correctly.
-Version 3.3.2 (2010-03-25)
- Fixed bug where it was possible to scale the editor iframe smaller than the editor UI.
- Fixed bug where some of the resizing option didn't work with the new live resize.
- Fixed bug where the format listbox didn't show nestled formats correctly.
- Fixed bug where the native listboxes didn't work correctly.
- Fixed bug where font size selection in using the legacyoutput plugin would produce errors.
- Fixed so block and blockquote formats remove their matching element regardless of it's attributes.
-Version 3.3.1 (2010-03-18)
- Added new live resize feature, the editor contents is now visible while resizing.
- Fixed bug where some valid_element patterns would produce an unknown property error.
- Fixed bug where it wasn't possible to toggle off blockquotes.
- Fixed bug where an undo level wasn't produced when applying formatting using the styles dropdown.
- Fixed bug where IE 6/7 wouldn't perform caret formatting due to a focus/event bug in IE.
- Fixed bug where undo/redo wasn't restoring the previous selection correctly.
- Fixed bug where the caret would become invisible if you resized the editor in latest Gecko.
- Fixed bug where the class attribute wasn't completely removed in IE 6/7 when the removeClass function was used.
- Fixed so the matchNode method of the Formatter class returns the matched format rule.
- Fixed so it's possible to apply formatting to both blocks and as inline elements.
-Version 3.3 (2010-03-10)
- Fixed bug where backspace on a table on IE would produce an empty tbody and some JS exceptions.
- Fixed bug where some redundant children wasn't removed properly when applying inline styles to them.
- Fixed bug where Chrome would produce incorect dialog sizes if the inlinepopups plugin wasn't used.
- Fixed bug where spans with different classes would get merged if they where siblings to each other.
- Fixed bug where IE 8 would crash if you used the spellchecker.
- Fixed bug where Input Method for non western languages didn't work correctly.
- Fixed bug where the UI would render incorrectly in FF 3.6 on Mac due to a bug n their rendering engine.
- Fixed bug where WebKit wouldn't scroll down correctly if Shift+Enter was used. Patch contributed by Thomas Andersen.
-Version 3.3rc1 (2010-02-23)
- Fixed bug with new legacyoutput plugin not working correctly on it's own.
- Fixed bug some performance issues with removing text formats.
- Fixed bug where TinyMCE specific attributes wasn't removed properly by remove format.
- Fixed bug where it wasn't possible to align images within inline elements.
- Fixed bug where Ctrl+Delete/Backspace would produce an invalid argument exception on IE.
- Fixed bug where the search/replace logic could produce an infinite loop on IE for reverse searches.
- Fixed bug where cloning formats in cells didn't work properly on IE.
- Fixed bug where IE6 would produce a horizontal scroll bar.
- Fixed so remove jQuery method removes the TinyMCE instance as well as the specified textarea.
- Fixed so selected rows and cells gets updated using the row/cell properties dialogs.
-Version 3.3b2 (2010-02-04)
- Fixed bug where sometimes img elements would be removed by split method in DOMUtils.
- Fixed bug where merging of span elements could occur on bookmark nodes.
- Fixed bug where classes wasn't properly removed when removeformat was used on IE 6.
- Fixed bug where multiple calls to an tinyMCE.init with mode set to exact could produce the same unique ID.
- Fixed bug with the IE selection implementation when it was feeded an document range.
- Fixed bug where block elements formatting wasn't properly removed by removeformat on all browsers.
- Fixed bug where selection location was lost if you performed a manual cleanup.
- Fixed bug where removeformat wouldn't remove span elements within styled block elements.
- Fixed bug where an error would be thrown if you clicked on the separator lines in menus.
- Fixed bug with the jQuery plugin adding always adding a querystring value to other resources.
- Fixed bug where IE would produce an error message if you had an empty editor instance.
- Fixed bug where Shift+Enter didn't produce br elements on WebKit browsers.
- Fixed bug where a temporary marker element wasn't removed by the paste plugin.
- Fixed bug where inserting a table would produce two undo levels instead of one.
-Version 3.3b1 (2010-01-25)
- Added new text formatting engine. Fixes a lot of browser quirks and adds new possibilities.
- Added new advlist plugin that enables you to set the formats of list elements.
- Added new paste plugin logic that enables you to retain style information from Office.
- Added new autosave plugin logic that automatically saves contents in local storage.
- Added new valid_styles option. Adds the possibility to restrict styles and their order.
- Added new theme_advanced_runtime_fontsize option to display the runtime font size in font size select box.
- Added new jquery plugin version that handles the gzip compressor amongst other things. Contributed by Speednet.
- Added new $ function to tinymce namespace and editor instances for the jQuery build.
- Added the possibility to get editors by index as well as name in the tinyMCE.editors collection.
- Fixed so the contents inside the editor renders in standards mode by default.
- Fixed bug where it wasn't possible to move the caret on short documents running in standards mode on IE.
- Fixed bug where the decode method of the DOMUtils class could end up in an endless loop.
- Fixed bug where it was possible to bypass the paste cleanup on non IE browsers if you clicked while pasting.
- Fixed bug where some attributes wasn't serialized correctly on IE if wildcard attribute patters where used.
- Fixed bug where entity decoding was performed on strings that didn't have any valid entities in them.
- Fixed bugs with the insertNode method of the IE DOMRange implementation. Patch contributed by Scott McNaught.
- Rewrote the getBookmark/moveToBookmark selection logic to boost performance on larger documents.
- Rewrote the table plugin to include new cell selection logic and fixed various bugs and issues.
- Merged the tinyMCE, tinymce and tinymce.EditorManager into the same instance makes more sense.
- Removed browser setting since the browser support for TinyMCE is not far better than it was when that setting was introduced.
- Changed the mce_ attribute prefix to the more standard _mce_ prefix. This is similar to browser vendors prefixes.
- Optimized performance with named entities on Gecko. Regexp replace was executing very slowly probably due to a Gecko bug.
- Optimized performance of the IE specific selection/range implementation.
- Removed the safari plugin since we now replaced all text formatting logic to custom code.
-Version 3.2.7 (2009-09-22)
- Fixed bug where uppercase paragraphs could still produce an invalid DOM tree on IE.
- Fixed bug where split command didn't work on WebKit since the node serializer needs a real document to work with.
- Fixed bug where it was impossible in Gecko to place the caret before a table if it was the first one.
- Fixed bug where linking to urls like ../../ would produce an extra traling slash ../..//.
- Fixed bug where the template cdate functionality was using an old 2.x API call. Patch contributed by vectorjohn.
- Fixed bug where urls to the same site but different protocol would be converted when relative_urls where set to false. Patch contributed by Ted Rust.
- Fixed bug where the paste plugin would remove mceItem prefixed classes.
- Fixed bug where the paste plugin would sometimes add items in a reverse order on WebKit.
- Fixed bug where the paste buttons would present an error message on Gecko even if you changed user.js. Patch contributed by Todd (teeaykay).
- Fixed bug where Opera would crash if you had tables incorrectly placed inside paragraphs.
- Fixed bug where styles elements wasn't properly processed if you had bad input HTML.
- Fixed bug where style attributes wasn't properly forced into a specific format.
- Fixed bug and issues with boolean attributes like checked, nowrap etc.
- Fixed bug where input elements could override attributes on form elements.
- Fixed bug where script or style elements could get modified by the DOMUtils processHTML method.
- Fixed bug where the selected attribute could get lost when force root blocks logic got executed on IE. Patch contributed by Attila Mezei-Horvati.
- Fixed bug where getAttribs method didn't handle boolean attributes correctly on IE.
- Fixed so the paste from word dialog is presented if you paste content on an IE with to restrictive security settings.
- Fixed so the paste_strip_class_attributes option is set to none by default in the paste plugin.
- Removed default border=0 on tables for the default value of valid_elements.
-Version 3.2.6 (2009-08-19)
- Added new wordcount plugin, this will display the number of typed words as you write. Contributed by Andrew Ozz.
- Added new getNext and getPrev methods to DOM utils. These will return the first matching sibling.
- Fixed bug where it was impossible to place the caret after a table on Gecko. It will now add a paragraph after tables.
- Fixed bug where inline dialogs would fail if used in a window opened using a showModalDialog. Patch contributed by Derek Britt.
- Fixed bug where IE could sometimes render a unknown runtime error on invalid input HTML.
- Fixed bug where some incorrectly placed tables wouldn't be moved outside the paragraphs on IE.
- Fixed bug where uppercase script/style element wouldn't be handled correctly and converted to valid lowercase.
- Fixed bug where some WebKit versions on Mac OS X would produce issues with hidden select fields.
- Fixed bug where the media plugin would fail on WebKit since the node wasn't properly imported to the right document.
- Fixed bug where absolute URLs for the TinyMCE script using a base href element would cause loading problems in IE 6/7.
- Fixed bug where pasting using the paste plugin wasn't possible on IE with to restrictive security settings.
- Fixed bug where pasting of whitespace was impossible using the new custom paste method.
- Fixed bug where pasting on some WebKit browsers would not work if you pasted specific contents due to a WebKit bug.
- Fixed bug where doctypes with multiple lines would not be parsed correctly by the fullpage plugin. Patch contributed by Colin.
- Fixed bug where the autoresize plugin would break the fullscreen functionality.
- Fixed bug where tables would be chopped up running on IE using invalid contents and pasting paragraphs into a cell.
- Fixed bug where the each method of jQuery build didn't iterate styleSheets. We now use the TinyMCE API one instead.
- Fixed bug where auto switching to paragraphs after headers some times failed in Gecko.
- Fixed so all editor options gets passed to the Serializer class. Patch contributed by Jasper Mattsson.
- Fixed so script/style blocks isn't wrapped in paragraphs as other inline elements.
- Fixed so the XHR requests sends the X-Requested-With HTTP header.
- Fixed so the data url scheme is handled in the tinymce.util.URI class.
- Changed inline documentation to use moxiedoc style comments.
- Removed the compat2x plugin people should have upgraded to the 3.x API by now. 3.0 was released more then a year ago.
- Re-added Gecko specific message for users who doesn't understand the security concept regarding paste.
-Version 3.2.5 (2009-06-29)
- Added new jQuery plugin for the jQuery specific package. This enables you to more easily load and use TinyMCE.
- Added new autoresize plugin contributed by Peter Dekkers. This plugin will auto resize the editor to the size of the contents.
- Fixed so all packages have the same directory structure. Previous releases had a different structure for the production package.
- Fixed so the paste from word dialog forces the contents to be processed as word contents even if it's not.
- Fixed so the jQuery build adapter build works. It's currently only excluding Sizzle.
- Fixed so noscript element contents is retained during the editing process.
- Fixed bug where the getBookmark method would need a "simple" string input when the documented way is a boolean.
- Fixed bug where invalid contents could break the fix_table_elements logic.
- Fixed bug where Sizzle specific attributes would be serialized if the valid_elements was set to *[*].
- Fixed bug where IE would produce an error if you specified a relative content_css and opened the paste dialog.
- Fixed bug where pasting images on IE would produce broken images if they came from an external site.
- Fixed bug where memory was leaked if you add/remove controls dynamically. Some event handlers wasn't removed properly.
- Fixed bug where domain relaxing wasn't treated correctly if you added it after the TinyMCE script element.
- Fixed bug where the activeEditor wasn't set to null if the last editor instance was removed.
- Fixed bug where IE was leaking memory on the onbeforeunload event due to some recently introduced logic. Patch contributed by Options.
- Fixed bug where inserting tables in Safari 4 didn't work due to a new WebKit bug where some element names are reserved.
- Fixed bug where URLs having a :// value in the query string would make it absolute regardless of URL settings.
- Fixed the WebKit specific bug where DOM Ranges would fail if the node wasn't attached to something in a different way.
- Removed the auto_resize option and the resizeToContent method from the tinymce.Editor class. Use the new autoresize plugin instead.
-Version 3.2.4.1 (2009-05-25)
- Fixed bug where Gecko browsers would produce an extra space after for example strong when loaded from sub domains.
- Fixed bug where script elements would be removed if they where placed inside a paragraph element.
- Fixed bug where IE 8 would produce 1 item remaining when loading CSS files dynamically with an empty cache.
- Fixed bug where bound events would be removed from other editor instances if a specific one was removed.
- Fixed various bugs and issues with script and style elements inside the editor.
- Fixed so all script contents gets wrapped in CDATA sections so that they can be parsed using a XML parser.
- Fixed so it's impossible for elements marked as closed to have child nodes rendered in output.
-Version 3.2.4 (2009-05-21)
- Added new paste_remove_styles/paste_remove_styles_if_webkit option to paste plugin concept contributed by Hadrien Gardeur.
- Added new functionality to paste plugin contributed by Scott Eade aka monkeybrain.
- Added new paste_block_drop option to the paste plugin this is disabled by default and will block any drag/drop event.
- Added new bind/unbind methods to DOMUtils these works like Event.add/Event.remove but is easier to access.
- Added new paste_dialog_width/paste_dialog_height options to paste pluign. Enables you to change the dialog sizes.
- Fixed bug on IE 8 where it would sometimes produce a "1 item remaining" status message that would never finish.
- Fixed bug on Safari 4 beta that would produce DOM Range exceptions on the DOMUtils split method since the browser has a bug.
- Fixed bug where the paste plugin could accidentally think that some word sentences was supposed to be list elements.
- Fixed bug where paste plugin would produce one extra empty undo level on some browsers.
- Fixed bug where spans wasn't produced correctly on new line when the keep_styles option was enabled.
- Fixed bug where the caret would be placed at the beginning of contents in IE 8 if you selected colors from the color pickers.
- Fixed so the Event class is a normal class instead of a static one. The tinymce.dom.Event is now a global instance of that class.
- Fixed so internal events for instances gets removed when the DOMUtils instance is removed.
- Fixed so preventDefault and stopPropagation methods can be used on the event object in all browsers.
-Version 3.2.3.1 (2009-05-05)
- Fixed bug where paragraphs containing form elements such as input or textarea would be removed.
- Fixed bug where some IE versions would produce a wrapper function for events attributes.
- Fixed bug where table cell contents could be removed if you pressed return/enter at the end of the cell contents.
- Fixed bug where the paste plugin would remove a extra character if the selection range was collapsed.
- Fixed bug where creating tables with % width wouldn't be handled correctly on WebKit browsers.
-Version 3.2.3 (2009-04-23)
- Added new paste plugin logic. This new version will autodetect Word contents and clean it up.
- Added a optional root element argument to getPos so you can tell it where to stop the calculation.
- Added new DOM ready logic to remove the usage of document.write. We now use basically the same method as jQuery.
- Fixed bug where WebKit browsers would fail when selecting all contents in the area using Ctrl+A.
- Fixed bug where IE would produce paragraphs with empty inline style elements.
- Fixed bug where WebKit browsers would fail when inserting tables with a non pixel width.
- Fixed bug where block elements could get a redundant br element at the end of the element.
- Fixed bug where the tabfocus plugin only worked with a single editor instance on page.
- Fixed bug where IE 8 was loosing caret position if the selection was collapsed and a menu was clicked.
- Fixed bug with application/xhtml+xml mode where menus wasn't working properly.
- Fixed bug where the onstop workaround fix for IE would produce errors in an ASP update panel.
- Fixed bug where the submit function override could produce errors if executed in the wrong scope.
- Fixed bug where the area element wasn't closed by a short ending.
- Fixed various number issues in the style plugins properties dialog. Contributed by datpaulchen.
- Fixed issues with size suffix values in the style plugin dialog.
- Fixed issue where hasDuplicate variable would leak out to the global space due to a bug in the Sizzle engine.
- Fixed issue where the paste event would fire a dialog warning on IE since we extracted the text contents.
- Updated Sizzle engine to the latest version, this version fixes a few bugs that was reported.
-Version 3.2.2.3 (2009-03-26)
- Fixed regression bug with the getPos method, it would return invalid if the view port was to small.
-Version 3.2.2.2 (2009-03-25)
- Fixed so the DOMUtils getPos method can be used cross documents if needed.
- Fixed bug where undo/redo wasn't working correctly in Gecko browsers.
-Version 3.2.2.1 (2009-03-19)
- Added support for tel: URL prefixes. Even though this doesn't match any official RFC.
- Fixed so the select method of the Selection class selects the first best suitable contents.
- Fixed bug where the regexps for www. prefixes for link and advlink dialogs would match wwwX.
- Fixed bug where the preview dialog would fail to open if the content_css wasn't defined. Patch contributed by David Bildström (ChronoZ).
- Fixed bug where editors wasn't converted in application/xhtml+xml mode due to an issue with Sizzle.
- Fixed bug where alignment would fail if multiple lines where selected.
- Updated Sizzle engine to the latest version, this version fixes a few bugs that was reported.
-Version 3.2.2 (2009-03-05)
- Added new CSS selector engine. Sizzle the same one that jQuery and other libraries are using.
- Added new is and getParents methods to the DOMUtils class. These use the new Sizzle engine to select elements.
- Added new removeformat_selector option, enables you to specify a CSS selector pattern of elements to remove when using removeformat.
- Fixed so the getParent method can take CSS expressions when selecting it's parents.
- Added new ant based build process, includes a new javabased preprocessor and a yuicompressor ant task.
- Moved the tab_focus logic into a plugin called tabfocus, so the old tab_focus option has been removed from the core.
- Replaced the TinyMCE custom unit testing framework with Qunit and rewrote all tests to match the new logic.
- Moved the examples/testcases to a root directory called tests since it now includes slickspeed.
- Fixed bug where nbsp wasn't replaced correctly in ForceBlocks.js. Patch contributed by thorn.
- Fixed bug where an dom exception would be thrown in Gecko when the theme_advanced_path path was set to false under xml application mode.
- Fixed bug where it was impossible to get out of a link at the end of a block element in Gecko.
- Fixed bug where the latest WebKit nightly would fail when changing font size and font family.
- Fixed bug where the latest WebKit nightly would fail when opening dialogs due to changes to the arguments object.
- Fixed bug where paragraphs wasn't added to elements positioned absolute using classes.
- Fixed bug where font size values with dot's like 1.4em would produce a class instead of the style value.
- Fixed bug where IE 8 would return an incorrect position for elements.
- Fixed bug where IE 8 would render colorpicker/filepicker icons incorrectly.
- Fixed bug where trailing slashes for directories in URLs would be removed.
- Fixed bug where autostart and other boolean values in the media dialog wouldn't be stored/parsed correctly.
- Fixed bug where the repaint call for the media plugin wouldn't be executed due to a typo in the source.
- Fixed bug where id attribute of object elements wasn't kept intact by the media plugin.
- Fixed bug where preview of embeded elements when the media_use_script option was used would fail.
- Fixed bug where inlinepopups could be rendered at an incorrect location on IE 6 while dragging.
- Fixed bug where the blocker shim could be placed at an incorrect location on IE 6.
- Fixed bug where the multiple and size attributes of select elements would produce incorrect values while running in IE.
- Fixed bug where IE would loose the caret position is you selected a color from the color drop down.
- Fixed bug where remove format wouldn't work on IE since it couldn't remove span elements that had style information.
- Fixed bug where Opera was removing links when removing formatting from selected contents.
- Fixed bug where paragraphs could be produced inside non positional elements styled with the CSS position value of static.
- Fixed bug where removeformat wouldn't work if you selected part of a span in IE.
- Fixed bug where media plugin didn't retain the style attribute on embed/object elements.
- Fixed bug where auto focus on empty editor instances could produce strange results if you inserted an image into it.
- Fixed bug where &nbsp; characters would be removed in FF when inserted with the mceInsertContent or selection.setContent methods.
- Fixed bug where warning message of missing paste support wasn't displayed on WebKit browsers.
- Fixed bug where anchor links could include other links. The selected range is now unlinked before adding news links to it.
- Fixed memory leak when TinyMCE was used with prototype. Patch contributed by James Ots.
- Fixed so the non documented fullpage_hide_in_source_view option for the fullpage plugin works again in the 3.x branch.
- Fixed so tables doesn't get inserted into paragraphs by default since it's not W3C valid. Can be disabled by using the fix_table_elements option.
- Fixed so the source view dialog sets a source_view state to the event object. Enables plugins to intercept the source view mode.
- Fixed various validation issues with the html dialogs and pages.
- Removed ask mode option since there is way better ways of doing this now. Use the add/remove control methods instead.
- Removed logic for compatibility with Safari 2.x, this browser is no longer supported since no one is using it.
- Removed the auto domain relaxing feature. If loading scripts cross sub domains it's better to specify the document.domain by hand.
-Version 3.2.1.1 (2008-11-27)
- Added new theme_advanced_default_background_color/theme_advanced_default_foreground_color options. Patch contributed by David Bildström (ChronoZ).
- Fixed font style formatting compatibility issue with Adobe Air.
- Fixed so legacy font elements get converted into spans even if cleanup_on_startup isn't enabled.
- Fixed bug where pre elements could be incorrectly modified by an IE bug workaround. Patch contributed by hu vime.
- Fixed bug where input elements inside inlinepopups wasn't editable in Firefox 2.
- Fixed bug where the xhtmlxtras plugin wasn't replacing attribute values correctly.
- Fixed bug where menu buttons in skin variants would look strange due to IE 8 fixes.
- Fixed bug where WebKit browsers would on backspace take you back to the previous page if the editor was empty.
- Fixed bug where DOMUtils decode method wouldn't handle strings larger than 4096kb due to node chunking.
- Fixed bug where meta key wasn't handled as ctrl key on Mac OS X for custom keyboard short cuts.
- Fixed bug where init event would get fired twice on WebKit on Mac OS X.
-Version 3.2.1 (2008-11-04)
- Added support for custom icon image for drop menus. Use icon_src to set a custom image directly.
- Added new media_strict option to media plugin. Enables you to control if the flash embed is strict or not. Enabled by default.
- Fixed so the editors script files gets dynamically loaded without using XHR or eval.
- Fixed so the media plugin outputs valid XHTML object elements for Flash movies. Can be disabled with the media_strict option.
- Fixed so dynamic loading doesn't require eval calls on non IE browsers for better Air support.
- Fixed bug where the editor wasn't treated as empty if the remaining paragraph had attributes.
- Fixed bug where id's of elements was removed ones they got wrapped in paragraphs. Patch contributed by ChronoZ.
- Fixed bug where WebKit browsers where placing list elements inside paragraph elements.
- Fixed bug where inserting images or links would produce absolute urls on WebKit browsers.
- Fixed bug where values for checked, readonly, disabled and selected attributes was incorrect on IE.
- Fixed bug where positive values for checked, readonly, disabled and selected attributes wasn't forced to valid values.
- Fixed bug where selecting the first option in a native select box would produce an undefined error.
- Fixed bug where tabindex 32768 could be outputted on IE if element attributes where cloned.
- Fixed bug where the media dialogs preview window would display incorrect contents due to duplicate clsid prefixes.
- Fixed bug where non pixel or percent heights for textarea elements would produce errors on IE.
- Fixed bug where cdata sections in script elements wasn't handled correctly.
- Fixed bug where nowrap of table cells would produce a 65535 value output.
- Fixed bug where media plugin would produce an error if you selected the first item in the items list.
- Fixed bug where media plugin would modify links with the item _value in them.
- Fixed so table width/height is better forced if inline_styles is enabled. Patch contributed by daKmoR.
- Fixed css for IE 8 such as opacity and other rendering quirks.
-Version 3.2.0.2 (2008-10-02)
- Fixed bug where the SelectBox and NativeSelectBox wasn't updated correctly if undefined was passed to them.
- Fixed bug where the style dropdown wasn't correctly changed back to it's original state when element had no class.
- Fixed bug where multiple pending font styles wasn't handled correctly.
- Fixed so you can disable all auto css loading for dialogs by setting the popups_css option to false.
-Version 3.2.0.1 (2008-09-17)
- Fixed bug where font sizes and faces wouldn't be changed correctly when there was a parent with a different style.
- Fixed bug where adding fonts to the same selection would produce redundant spans.
-Version 3.2 (2008-09-11)
- Added new text style support, it will now use span elements internally instead of font elements.
- Added new improved support for the theme_advanced_font_sizes option, check the Wiki for details.
- Added new keep_style setting that maintains the text style on return/enter on non IE browsers, enabled by default.
- Added new onBeforeSetContent/onBeforeGetContent/onSetContent/onGetContent events to the Selection class.
- Added new selectByIndex method to ListBox class. This enables you to select list items by an index instead of a value.
- Added new possibility to the select method of the ListBox class. This can now have a selector function as it's value argument.
- Added new possibility to skip the loading of popups css by setting the feature popup_css to the value false.
- Added new possibility to skip translation of popups by setting the translate_i18n feature to false.
- Added new element_format option enables you to produce HTML element endings instead of XHTML. But we are still in the XHTML is better camp.
- Added missing allowfullscreen and quality options for flash elements, this will now get correctly stored.
- Fixed bug where table cell dialog didn't close properly unless the accessibility_warnings option was set to false.
- Fixed bug where the modal dialog blocker element for inlinepopups wasn't placed at a correct location if the page had scroll.
- Fixed bug where non inline dialogs didn't close correctly if the inlinepopups plugin was used.
- Fixed bug where non inline dialogs could make the modal dialog blocker to work incorrectly.
- Fixed bug where style select wasn't populated correctly if you pressed the arrow. Patch by Hari Karam Singh.
- Fixed bug where toggling the fullscreen mode didn't restore scrollbars on IE when the editor was inside a frame. Patch by Jacob Barrett.
- Fixed bug where inserting flash contents using the template plugin didn't work correctly.
- Fixed bug where inserting flash contents using the selection.setContent or mceInsertContent command didn't work correctly.
- Fixed bug where IE would produce an exception if a comment started with -.
- Fixed bug where the blockquote button would wrap lists incorrectly on non IE browsers.
- Fixed bug where Opera would display BR elements in the element path.
- Fixed bug where xhtmlxtras didn't insert elements correctly on IE.
- Fixed bug where the buttons wasn't activated correctly in the xhtmlxtras plugin.
- Fixed bug where adding an object as the style attribute for the dom setAttribs method wouldn't work.
- Fixed bug where the background color would bleed out to parent container element in Gecko.
- Fixed bug where the insert column actions for tables would fail if you did it in a thead or tfoot. Patch contributed by T Andersen (tan73).
- Fixed bug where event blocker element wasn't positioned correctly for the inlinepopups plugin.
- Fixed bug where pasting from Office 2007 would produce an odd comment in the contents.
- Fixed bug where the paste as plain text could remove an extra character. Patch contributed by Speednet.
- Fixed bug where some characters where missing for the paste_replace_list option. Patch contributed by Speednet.
- Fixed bug where removing non existing editor instances by the mceRemoveControl command would produce an error.
- Fixed bug where meta elements with the name description would produce errors in IE.
- Fixed bug where color and background colors wouldn't be updated properly.
- Fixed bug where the createMenuButton of tinymce.ControlManager didn't implement the last class argument.
- Fixed bug where the editor_css option was relative from the TinyMCE installation directory not the current page.
- Fixed bug where elements wouldn't be padded if the element contained bogus br elements. For example TD elements.
- Fixed bug where parsing of <body > in fullpage plugin would produce an error.
- Fixed bug where relative urls with just ./ would become an empty string.
- Fixed bug where outdent button would be disabled if inline_styles where set to false.
- Fixed bug where replace with an empty search string would produce an error on IE.
- Fixed bug where restoring the overflow state of the body in fullscreen plugin running on IE would produce vertical scrollbars.
- Fixed bug where pressing return/enter in list items would sometimes move the caret the to top of the content area in FF.
- Fixed bug where the style listbox wouldn't be updated correctly if you used the use_native_selects option.
- Fixed bug where WebKit browsers would produce a div element when ending list elements using return.
- Fixed so translation of popup contents only occurs if it's needed.
- Optimized the URI object in regards or converting absolute URIs to relative URIs.
-Version 3.1.1 (2008-08-18)
- Added new getSize method to DOMUtils it will return the dimensions only of an element.
- Added new alert/confirm methods to the tinyMCEPopup class to prevent focus problems and also to shorten method calls.
- Added new plugin_preview_inline option to preview plugin to enable/disable native/inline dialogs.
- Added new readonly option. If this is set the editor will only display the contents for the user.
- Added missing tabindex and accesskey to input elements in the default valid_elements setup.
- Updated firebug lite to 1.2, to enable it use the tiny_mce_dev.js?debug=1 on the development package.
- Fixed so the preview dialog in the preview plugin uses inline dialogs/popups.
- Fixed so CDATA sections remains intact through the serialization process of the DOM tree.
- Fixed various issues with the getAttrib command. It will now return more correct values.
- Fixed bug where the embed element wasn't properly parsed in the media plugin it now supports 3 formats.
- Fixed bug where the noshade attribute was serialized incorrectly on IE.
- Fixed bug where editing an existing link element didn't force it relative.
- Fixed bug where image link creation fails on Safari if the image is aligned.
- Fixed bug where it was possible to scroll the fullscreen mode in Opera 9.50.
- Fixed bug where removal of center image alignment would fail. Patch contributed by Andrew Ozz.
- Fixed bug where inlinedialogs didn't work properly if the doctype was incorrect in IE.
- Fixed bug where cross domain loading didn't work correctly in Opera 9.50.
- Fixed bug where breaking huge text blocks with return/enter key would scroll to end of block.
- Fixed bug where replace button kept inserting the replacement text even if there is no more matches.
- Fixed bug with fullpage plugin where value wasn't set correctly. Patch contributed by Pascal Chantelois.
- Fixed bug where the dom utils setAttrib method call could produce an exception if the input was null/false.
- Fixed bug where pressing backspace would sometimes remove one extra character in Gecko browsers.
- Fixed bug where the native confirm/alert boxes would move focus to parent document if fired in dialogs.
- Fixed bug where Opera 9.50 was telling you that the selection is collapsed even when it isn't.
- Fixed bug where mceInsertContent would break up existing elements in Opera and Gecko.
- Fixed bug where TinyMCE fails to detect some keyboard combos on Mac, contributed by MattyRob.
- Fixed bug where replace all didn't move the caret to beginning of text before searching.
- Fixed bug where the oninit callback wasn't executed correctly when the strict_loading_mode option was used, thanks goes to Nicholas Oxhoej.
- Fixed bug where a access denied exception was thrown if some other script specified document.domain before loading TinyMCE.
- Fixed so setting language to empty string will skip language loading if translations are made by some backend.
- Fixed so dialog_type is automatically modal if you use the inlinepopups plugin use dialog_type : "window" to re-enable the old behavior.
-Version 3.1.0.1 (2008-06-18)
- Fixed bug where the Opera line break fix didn't work correctly on Mac OS X and Unix.
- Fixed bug where IE was producing the default value the maxlength attribute of input elements.
-Version 3.1.0 (2008-06-17)
- Fixed bug where the paste as text didn't work correctly it encoded produced paragraphs and br elements.
- Fixed bug where embed element in XHTML style didn't work correctly in the media plugin.
- Fixed bug where style elements was forced empty in IE. The will now be wrapped in a comment just like script elements.
- Fixed bug where some script elements wrapped in CDATA could fail to be serialized correctly.
- Fixed bug where FF 3 produced -moz- internal styles in some style attributes.
- Fixed bug where query strings and external URLs didn't work correctly in style attributes.
- Fixed bug where shape attribute of area elements got serialized as rect regardless of it's initial value in IE 6.
- Fixed bug where selection of elements inside layers would fail in IE since focus was moved to the document body.
- Fixed bug where pressing enter/return in an editable select box would produce an __mce_add_custom__ class value.
- Fixed bug where changing font size of text placed inside a colored text chunk would remove the parent node.
- Fixed bug where Opera 9.5 final produced a strange line break behavior due to a workaround for previous Opera versions.
- Fixed bug where text/background color would produce a strange focus problem when you tried to click on the body in IE.
- Fixed issue where selecting the title of an listbox equals the old 2.x behavior of changing the value to an empty string.
- Fixed issue where it was common for the media plugin to break if the _value attribute wasn't added for the param element.
- Fixed issue where the wrong parent editor instance might be updated if you use fullscreen mode in an incorrect way.
- Fixed issue where Safari was producing a warning about the base element not being closed correctly.
- Removed redundant form element name matching from regexp in the DOMUtils class.
-Version 3.0.9 (2008-06-02)
- Added new contextmenu_offset_x/contextmenu_offset_y options for the contextmenu plugin.
- Added cite attribute to the default rule for the blockquote element.
- Added support for using arrow keys for selection of items in listboxes.
- Added support for using arrow keys for selection of items in dropmenus.
- Fixed bug where blockformat change on elements with BR inside them didn't change correctly on Firefox.
- Fixed bug where removing table rows inside thead or tfoot would remove the whole table if it was the last one.
- Fixed bug where XHR synchronous mode didn't execute the callback handlers synchronously.
- Fixed bug where setting border to 0 didn't add border: 0 to the style attribute when using the advimage dialog.
- Fixed bug where the selection of images and table cells didn't work correctly when the editor is placed in a frame and running on IE.
- Fixed bug where the store/restore of a selection didn't work correctly in non IE browsers.
- Fixed bug where only the first element would be invalid for the invalid_elements option.
- Fixed bug where paste as plain text didn't encode the characters correctly when they where inserted.
- Fixed bug where HTML source window couldn't be maximized on Gecko when the maximizable feature was enabled.
- Fixed bug where color selection using the color picker could produce exception in IE.
- Fixed bug where font size changes could produce produce extra redundant elements.
- Fixed bug where IE could produce unknown runtime error if you replaced a image with another image from a separate frame.
- Fixed bug where the domLoaded state for the Event class wasn't set correctly if the editor was loaded dynamically using the gzip compressor.
- Fixed bug where handling of the base element for a page would produce incorrect urls. Based on a patch contributed by John LeSueur.
- Fixed bug where table constraint alert boxes was presented with an empty value and wasn't the skinned inline ones.
- Fixed bug where the onChange event wasn't fired when the form was submitted. It's now also triggered when the save method is called.
- Fixed bug where encoding set to xml didn't work as expected. It now encodes the contents into XML entities.
- Fixed bug where numrows didn't work correctly for the merge cells dialog of the table plugin.
- Fixed bug where the onGetContent event was fired even when the no_events flag was set.
- Fixed bug where the preview panels for the advimage and the media plugin could overflow on Safari and FF 3.
- Fixed bug where the editing and removal of abbr elements using the xhtmlxtras plugin working correctly on IE.
- Fixed bug where save button in the save plugin didn't work correctly on IE.
- Fixed bug where dragging layers didn't work as expected since it would snap back to it's original location if you saved.
- Fixed bug where the description of the template plugin dialog wasn't updated correctly.
- Fixed bug where the values for frame and rules in the table dialogs where swapped.
- Fixed bug where the elements like ins, del, cite, acronym and abbr didn't have the default editing style as the old 2.x branch.
- Fixed bug where ask mode would lock the focused textarea if you pressed cancel in the confirm dialog on FF 3.
- Fixed bug where ask mode would produce contents for empty textareas if you reloaded the page.
- Fixed so the onGetContent event gets the full pass through object just like the other events.
- Fixed so attributes for block elements remains the same when you change format of a element.
-Version 3.0.8 (2008-04-30)
- Fixed bug where IE would produce an error if textareas without names where converted.
- Fixed bug where editor wasn't forced empty when there was only a single br or empty paragraph left.
- Fixed bug where IE would produce an warning message if object elements where produced in the media plugins preview running on https.
- Fixed bug where new addVer function didn't handle hash items correctly. Patch contributed by Mirek Burkon.
- Fixed bug where font_size_style_values option wasn't applied correctly to fonts inside the editor.
- Fixed bug where image selection could be lost if a image was edited using context menu on IE.
- Fixed bug where style values wasn't updated properly due to an invalid regexp.
- Fixed bug where IE 6 where displaying warning message about insecure items when inserting an image while using https. Patch contributed by Norifumi Sunaoka.
- Fixed bug where IE was producing an auto save message if you selected a color from the color split button.
- Fixed bug where backspace sometimes would move the caret to the end of the previous block in Gecko.
- Fixed bug where the rowlayout manager didn't work as described in the documentation.
- Fixed bug where the default options for the fullpage plugin wasn't applied correctly.
- Fixed bug where selection would jump one character if you applied a styles to a words in non IE browsers.
- Fixed bug where undo levels wasn't added correctly if you went back in undo history and added a new event.
- Fixed bug where font size dropdown didn't mark the selected size in IE.
- Fixed bug where the size of the editor was determined using clientWidth instead of offsetWidth.
- Fixed so the onchange event doesn't fire on the initial undo level, it will also fire when the editor is blurred.
- Fixed so the advhr plugin produces XHTML valid output instead of non standard attributes.
- Fixed so blockquote gets converted into [quote] in when the bbcode plugin is enabled.
- Fixed so theme_advanced_font_sizes can be named for example Font 1=1, Font 2=2 etc.
- Fixed so editor_selector/editor_deselector can be regexps. By default only strings are allowed not part regexps like before.
- Fixed so that the version suffix is optional. It still requires the build process so you need to export it manually.
- Fixed so it's possible to tab to table cells in non Gecko browsers and also produce new rows if you tab at the end of a table. Contributed by Josh Peek.
-Version 3.0.7 (2008-04-14)
- Added new version suffix to all internal GET requests to make sure that the users cache gets cleared correctly.
- Fixed issue with isDirty returning true event if it wasn't dirty on IE due to changes in tables during initialization.
- Fixed memory leak in IE where if a page was unloaded before all images on the page was loaded it would leak.
- Fixed bug in IE where underline and strikethrough could produce an exception error message.
- Fixed bug where inserting paragraphs in totally empty table cells would produce odd effects.
- Fixed bug where layer style data wasn't updated correctly due to some performance enhancements with the DOM serializer.
- Fixed bug where it would convert the wrong element if there was two elements with the same name and id on the page.
- Fixed bug where it was possible to add style information to the body element using the style plugin.
- Fixed bug where Gecko would add an extra undo level some times due to the blur event.
- Fixed bug where the underline icon would get active if the caret was inside a link element.
- Fixed bug where merging th cells not working correctly. Patch contributed by André R.
- Fixed bug where forecolorpicker and backcolorpicker buttons where rendered incorrectly when the o2k7 skin was used.
- Fixed bug where comment couldn't contain -- since it's invalid markup. It will now at least not break on those invalid comments.
- Fixed bug where apos wasn't handled correctly in IE. It will now convert apos to &#39; on IE since that browser doesn't support that entity.
- Fixed bug where entities wasn't encoded correctly inside pre elements since they where protected from whitespace removal.
- Fixed bug where color split buttons where rendered incorrectly on IE6 when using the non default theme.
- Fixed so caret is placed after links ones they are created, to improve usability of the editor.
- Fixed so you can select tables by clicking on it's borders in non IE browsers to normalize the behavior.
- Fixed so the menus can be toggled by clicking once more on the icon in listboxes, menubuttons and splitbuttons based on code contributed by Josh Peek.
- Fixed so buttons can be labeled, currently only works with the default skin, so it's kind of experimental. Patch contributed by Daniel Insley.
- Fixed so forecolorpicker and backcolorpicker remembers the last selected color. Patch contributed by Shane Tomlinson.
- Fixed so that you can only execute the mceAddEditor command once for the same instance name.
- Fixed so command functions added with addCommand can pass though the call to default handles if it returns true.
-Version 3.0.6.2 (2008-04-07)
- Fixed bug where empty tables couldn't be edited correctly on non IE browsers if they where loaded into the editor.
- Fixed bug where it was impossible to resize layers correctly in IE since it thought it was an image.
- Fixed bug where an editor instance was stealing focus in IE resulting in a scroll to the editor on page reloads.
- Fixed bug where Safari was crashing on Mac OS X if you closed dialogs using the Esc key.
-Version 3.0.6.1 (2008-04-04)
- Added support for the missing mceAddFrameControl command. The input for this command has changed so consult the Wiki.
- Fixed bug where sub menus for the drop menus would leave an empty element behind.
- Fixed memory leak in IE if the editor was placed in a frame or iframe.
-Version 3.0.6 (2008-04-03)
- Added elements to the default value of valid_elements option. It now contains all XHTML strict elements and a few transitional.
- Added more accessibility fixes, it's now possible to navigate and close list boxes and split button menus with the keyboard.
- Added missing getInfo method to the contextmenu and safari plugin, this caused problems for the Drupal module.
- Added new inlinepopups_zindex option to the inlinepopups plugin so that you can configure the default start z-index.
- Added new setControlType method to the tinymce.ControlManager class. This method enables you to override the default classes.
- Added ability to specific an optional control class to use instead of the default one for the ControlManager methods. Based on concept by Josh Peek.
- Fixed bug where attribute rules for the DOM Serializer couldn't contain - or _ characters in their names.
- Fixed bug where inlinepopups event blocker and modal dialog blocker elements produced vertical scrollbars.
- Fixed bug where there was a rendering issue with quirks mode in Safari moving the resize handle to an incorrect position.
- Fixed bug with forecolor/backcolor controls on IE. Sometimes elements positioned relative will generate display errors.
- Fixed bug where a p2 was leaking out in the global name space when you selected a color from the forecolor/backcolor controls.
- Fixed bug where empty paragraphs didn't work as expected in browsers other than IE.
- Fixed bug where the load method of the tinymce.dom.ScriptLoader didn't check if the file was already loaded.
- Fixed bug where the load method for the PluginManager and ThemeManager didn't check if a plugin/theme by a specific name was all ready loaded.
- Fixed bug where the theme_advanced_link_targets option didn't work correctly with the advanced themes link dialog. Patch contributed by Arnold B.
- Fixed bug where the style command would merge classes into empty span elements.
- Fixed bug where the style command would remove empty span elements outside the current selection.
- Fixed bug where the fix for the Safari backspace bug removed all editor contents if it was filled with empty paragraphs.
- Fixed bug where alert and confirm boxes opened by the inlinepopups plugin would produce an exception if domain relaxing was used.
- Fixed bug where Safari was adding style attributes to all elements when you paste them into the editor.
- Fixed bug where the spellchecker menus was visually incorrect since the space for the non existing icon was still there.
- Fixed bug where remove_linebreaks option didn't remove line breaks inside the text contents of a element.
- Fixed bug where Safari 3.1 was introducing _mc_tmp into paragraphs due to the new querySelectorAll and a TinyMCE specific workaround.
- Fixed bug where getParam method in the Editor class was returning incorrect objects and would mess up the font drop down. Patch contributed by speednet.
- Fixed bug where the table dialog would produce an exception in IE when you edited tables since it tried to place focus in a disabled field.
- Fixed bug where class attribute on some span elements was removed on cleanup.
- Fixed bug where resizing the editor in IE could produce an exception if the editor width/height got to be a negative value.
- Fixed bug where wmv files wouldn't play since the src param was used instead of the url param.
- Fixed bug where br elements would be added here and there in Gecko. Geckos internal _moz_dirty br elements where serialized as well.
- Fixed bug where editing named anchors would produce two anchors instead of one updated one.
- Fixed bug where arrow and function keys didn't work when an noneditable element was focused within the editor.
- Fixed bug where the dispatcher could produce an exception if the listener list was altered inside an event callback.
- Fixed bug where it was impossible to totally empty the editor contents on Safari due to an mistreatment of nbsp as whitespace. Patch contributed by Andrew Ozz.
- Fixed bug where TinyMCE would not convert textareas with the same name attribute value. It will now generate an unique id for those textareas.
- Fixed bug where backspace/delete key was deleting td elements inside tables while running on Gecko.
- Fixed bug where Firefox 3.0b4 and Opera 9.26 where scrolling to the top of document when pressing return/enter.
- Fixed bug where the template plugin wasn't just inserting the mceTmpl tagged element.
- Fixed bug where the alert method of the default WindowManager implementation didn't translate input language strings like the inlinepopups dialog does.
- Fixed bugs with the backspace behavior in Gecko. The caret was placed on incorrect locations in the DOM sometimes.
- Fixed so advimage dialog and table dialogs has support for editable select boxes for the class value.
- Fixed so the media, pagebreak and spellchecker doesn't load it's default content.css file if the content_css option is set to false.
- Fixed so the paste_use_dialog option works again it's enabled by default but can be disabled on IE. Patch contributed by Speednet.
- Fixed so that the fullscreen editor is focused when switching fullscreen editing on.
- Fixed so it's possible to edit images and links inside tables using the context menu.
- Fixed so table dialogs and the advanced image dialog doesn't loose selection in IE if the dialogs where navigated/submitted with the keyboard.
- Fixed so the theme_advanced_blockformats options can have named items for example title 1=h1;title 2=h2.
- Fixed so it's possible to add a custom editor_css for the simple theme.
- Fixed quirks with directionality rtl, patch contributed by Andrew Ozz.
- Fixed so the inlinepopups default start zIndex is 300000.
- Fixed typo in media plugin Shockware is now replaced with Shockwave.
- Fixed psuedo memory leak in IE with the replaceChild method inside the DOMUtils.replace method.
- Fixed so memory is released when an editor instance is removed from page.
- Optimized the color split button menus so that they use less event handlers.
- Removed the util/mclayer.js file since it's no longer used by any of the TinyMCE dialogs and is considered deprecated.
-Version 3.0.5 (2008-03-12)
- Added new black skin variant to the o2k7 skin contributed by Stefan Moonen.
- Added new explode method to the tinymce core class. This does a split but removed whitespace it also defaults to a , delimiter.
- Added new detection logic for IE 8 standards mode into the DOMUtils class strMode can now be checked to see if that mode is on/off.
- Added new noscale option value for the scale select box for Flash in the media plugin.
- Fixed bug where the menu for the ColorSplitButton wasn't removed when the editor was removed.
- Fixed bug where font colors couldn't be edited correctly since the style of the element didn't get updated correctly.
- Fixed bug where class of elements would get lost when TinyMCE was fixing incorrect HTML markup.
- Fixed bug where table editing would produce double height values.
- Fixed bug where width style value wouldn't be removed if you switched width unit from cm/em to pixels or percent.
- Fixed bug where the search/replace input box wasn't auto focused like the other dialogs.
- Fixed bug where the old mceAddControl command would use the fullscreen settings next time it created an instance.
- Fixed bug where multiple lines where added to the target cell if you merged multiple empty cells.
- Fixed bug where drop down menus would be incorrectly positioned inside scrollable divs.
- Fixed bug where the separators of the silver skin variant didn't display correctly in IE 6.
- Fixed bug where createStyleSheet seems to load scripts at opposite order in some IE versions.
- Fixed bug where directionality could produce odd results for the UI and the dialogs.
- Fixed bug where the DOM serializer wouldn't serialize custom namespaced attributes in IE 6 using the *[*] valid elements rule.
- Fixed bug where table caption would be inserted after the thead element if you swapped a tr to be inside the thead.
- Fixed bug where the youtube detection logic for the media plugin was to generic.
- Fixed so the deprecated and undocumented theme_advanced_path_location set to none won't hide the whole statusbar.
- Fixed so most input lists can have whitespace in them they are now split using the new tinymce.explode method.
- Fixed so the popup_css and popup_css_add URLs are relative to where the current document is located.
- Fixed various bugs and quirks with the store/restore selection logic.
- Fixed so the editor starts in IE 8 standards mode but still that browser is very very buggy.
- Fixed so dialog_type set to modal will block the background and other inline windows and only give access to the front most window.
-Version 3.0.4.1 (2008-03-08)
- Fixed critical bug where it was impossible to edit images when inlinepopups where used due to lost selection in IE.
-Version 3.0.4 (2008-03-07)
- Added new option constrain_menus, this enables you to force view port constraints on all menus. Contributed by Shane Tomlinson.
- Fixed bug where table background wasn't visible inside the editor due to a default CSS rule overriding the style attribute.
- Fixed bug where links would get a null class added if no styles was used in IE.
- Fixed bug where spellchecker was auto focusing the editor in IE.
- Fixed bug where document.domain would produce invalid argument if the editor was loaded in IE6 over a network UNC path.
- Fixed bug where table height attribute was used, this is deprecated in XHTML so it now adds it as an style.
- Fixed bug where textareas with style values would produce error in IE.
- Fixed so the first element in each dialog is focused by default to enhance keyboard usage.
- Fixed so you can add a mceFocus class to elements to make it auto focused.
- Fixed so you can close dialogs using the esc key.
- Fixed so you can press return/enter to submit the action of each dialog.
- Fixed so tabbing inside an inline popups wont focus the resize anchor elements.
- Fixed so you can press ok in inline alert messages using the return/enter key.
- Fixed so textareas can be set to non px or % sizes for example em, cm, pt etc.
- Fixed so non pixel values can be used in width/height properties for tables.
- Fixed so the custom context menu can be disabled by holding down ctrl key while clicking.
- Fixed so the layout for the o2k7 skin looks better if you don't have separators before and after list boxes.
- Fixed so the sub classes get a copy of the super class constructor function to ease up type checking.
- Fixed so font sizes for the format block previews are normalized according to http://www.w3.org/TR/CSS21/sample.html (it can be overridden).
- Fixed so font sizes for h1-h6 in the default content.css is normalized according to http://www.w3.org/TR/CSS21/sample.html (it can be overridden).
-Version 3.0.3 (2008-03-03)
- Fixed bug where an error about document.domain would be thrown if TinyMCE was loaded using a different port.
- Fixed bug where mode exact would convert textareas without id or name if the elements option was omitted.
- Fixed bug where the caret could be placed at an incorrect location when backspace was used in Gecko.
- Fixed bug where local file:// URLs where converted into absolute domain URLs.
- Fixed bug where an error was produced if a editor was removed inside an editor command.
- Fixed bug where force_p_newlines didn't effect the paste plugin correctly.
- Fixed bug where the paste plugin was producing an exception on IE if you pasted contents with middots.
- Fixed bug where delete key could produce exceptions in Gecko sometimes due to the fix for the table cell bug.
- Fixed bug where the layer plugin would produce an visual add class called mceVisualAid this one is now renamed to mceItemVisualAid to mark it internal.
- Fixed bug where TinyMCE wouldn't initialize properly if ActiveX controls was disabled in IE.
- Fixed bug where tables and other elements that had visual aids on them would produce an extra space after any custom class names.
- Fixed bug where search with an empty string would produce some odd "invalid pointer" error in IE.
- Fixed bug where elements like menus where placed at incorrect positions in Opera 9.26.
- Fixed bug where IE was loosing focus of the editor when you clicked some dropmenu and if it was placed in a frame or iframe.
- Fixed bug where focus of images could be lost in IE if you focused the accessibility confirm dialog in the advimage plugin.
- Fixed bug where nestled font elements would produce odd output like missing font elements.
- Fixed bug where text colors and styles got removed if invalid_elements included the font element.
- Fixed bug where text-decoration set to underline or line-through would remove other styles from span elements.
- Fixed bug where editor contents like \n\n would be incorrectly handled and processed as real line feeds.
- Fixed bug where incorrectly encoded urls with ampersands in them would be decoded incorrectly.
- Optimized the DOMUtils decode method to be a lot faster if the string doesn't have any entities to decode.
-Version 3.0.2.1 (2008-02-26)
- Fixed alert/confirm dialogs so they display correctly.
-Version 3.0.2 (2008-02-26)
- Added new body_id option that enables you to specify the id of the body inside the editor iframe based on ideas by David Bildström (ChronoZ).
- Added new body_class option that enables you to set the class for the body of the editor iframe based on ideas by David Bildström (ChronoZ).
- Added new CSS class to the default content.css files mceForceColors that forces white background and black text can be used with the body_class option.
- Added new type parameter to the Editor.getParam function to reduce redundant logic for parsing hash tables.
- Added new isDone method to the ScriptLoaded class, this enables you to check if a script has been loaded or not.
- Added new resizeTo and resizeBy methods for the advanced theme. Can be called using tinyMCE.activeEditor.theme.resizeTo(w, h);
- Added new skin_variant option this can be used to extend existing skins with slight modifications like color.
- Added new variant of the o2k7 skin called "silver" based on a contribution made by Stefan Moonen.
- Fixed bug where the template plugin might produce errors if the template_mdate_classes wasn't configured.
- Fixed bug where the media plugin didn't convert the URLs for movies once they where inserted.
- Fixed bug where the style field for the advlink dialog didn't work correctly if you edited an existing link.
- Fixed bug where alignment of toolbars would fail in editor was uses in a quirks mode on IE, fix contributed by Peter Wood & Art Lawry.
- Fixed bug where initialization of multiple editors at the same time using the mceAddControl method would produce errors.
- Fixed bug where initialization of editors using mceAddControl command or new tinymce.Editor calls would fail during page load.
- Fixed bug where the check for domain relaxing could fail if the document.domain property was changed by another script.
- Fixed bug where textareas couldn't be named description or any other name that matches the meta elements in IE and Opera.
- Fixed bug where the element path would fail sometimes in IE due to "unknown runtime error" on innerHTML.
- Fixed bug where Safari would crash if you was hiding the editor before serializing the contents.
- Fixed bug where the editor wasn't scaled propertly in fullscreen mode using the old fullscreen_new_window option.
- Fixed bug where render method didn't load language packs in IE and Opera if you rendered an editor during page load.
- Fixed bug where resizing the browser window in fullscreen didn't resize the editor.
- Fixed bug where the blockquote command didn't move the caret inside the new empty blockquote if you used it on an empty document.
- Fixed bug where auto in a style width/height for the textarea would produce an editor with the size value of 100. Fix contributed by Shane Tomlinson.
- Fixed bug where restoration of selection at the beginning of an element could fail in Gecko.
- Fixed bug where caret restoration after a cleanup could place the it at an incorrect location.
- Fixed bug where delete key inside td elements would delete the cell in Gecko.
- Fixed so the blockquote button toggles individual lines. This behavior is a bit more like the old indentation behavior in the 2.x branch.
- Fixed so the dialog language packs only gets loaded the first time you open a dialog.
- Fixed so all classes in the whole UI is prefixed with "mce" to avoid collisions, use the skin converter to update your existing skins.
- Fixed so all classes in the inlinepopups logic is prefixed with "mce" to avoid collisions, use the skin converter to update your existing skins.
- Fixed so that the window in fullscreen mode can be resized when fullscreen_new_window option is enabled.
- Fixed so blockquote elements are formatted in the source output with an linefeed before and after it.
- Optimized the editor initialization by reducing the number of calls to getBookmark/moveToBookmark.
-Version 3.0.1 (2008-02-21)
- Added spellchecker plugin into the main package, but without any backend can be specified with the spellchecker_rpc_url option.
- Added src attribute for script elements to the default valid_elements option value.
- Added extra parameter to the class_filter callback it can now also filter out classes based on the whole CSS rule.
- Added support for domain relaxing, TinyMCE can now be loaded from an remote domain as long as they are on the same root domain.
- Added support for custom elements the new custom_elements option enables you to add non HTML elements to the editor.
- Added support for the W3C Selectors API that was added to latest nightly build of WebKit.
- Fixed bug where some object param element wasn't stored correctly using the media plugin.
- Fixed bug where Opera was scrolling to top of page is drop menus on list boxes where displayed.
- Fixed bug where IE6 was crashing if a format block was used on a container with anchor elements.
- Fixed bug where spans with font sizes wasn't handled correctly when editor was loading contents.
- Fixed bug where mode exact couldn't convert editors with name only. Id is no longer required but recommended.
- Fixed bug where the mceInsertRawHTML command produced an extra undo level.
- Fixed bug where the specific_textareas mode didn't work correctly this is the same thing as textareas now.
- Fixed bug where the values of input elements in the HTML page of dialogs pages where changed in IE.
- Fixed bug where fullscreen and fullpage plugins didn't work well together.
- Fixed bug where embed elements wasn't handled properly in the media plugin.
- Fixed bug where style information on span elements gets munged when fonts are converted to spans.
- Fixed bug where some entities in element attributes where encoded incorrectly in the latest WebKit build.
- Fixed bug where initialization would fail in IE if there where two input elements with the name submit in the form.
- Fixed bug where fullscreen mode didn't work correctly in IE when the fullscreen_new_window option was used.
- Fixed bug where invalid contents like an ul inside a p element would produce odd results in IE.
- Fixed bug where Opera 9.2x was placing the drop menus at incorrect locations if the editor was placed in a table.
- Fixed bug where Opera was producing odd results if enter/return was pressed while having forced_root_blocks disabled.
- Fixed bug where layer plugin was stealing focus in IE on initialization.
- Fixed bug where body attributes wasn't set properly in the fullpage plugin, fix contributed by Hiroaki Kawai.
- Fixed bug where insert image and insert link dialogs where producing an extra level in the undo history.
- Fixed bug where Gecko would produce an error if empty elements like <div></div> where inserted using mceInsertContent.
- Fixed bug where center alignment of images produced odd results inside table cells.
- Fixed bug where center alignment of images couldn't be toggled correctly.
- Fixed bug where alignment of images inside tables would produce double float style items in IE if the fix_table_elements option was enabled.
- Fixed bug where a variable called 'v' was polluting the global namespace. Objects tinymce and tinyMCE are the only ones allowed to be global.
- Fixed bug where insert table from context menu couldn't insert new tables inside existing tables.
- Fixed bug where Safari wouldn't produce br elements on enter when the force_br_newlines option was enabled.
- Fixed bug where switching cell type in table cell dialog would produce odd attributes in IE.
- Fixed bug where Gecko was outputting internal attributes if valid_elements where set to "*[*]".
- Fixed bug where the style plugin would produce non hex colors inside the dialog when running on Gecko.
- Fixed bug where an empty src value for insert image would remove the currently selected image if it wasn't and image element.
- Fixed bug where hidden input elements would break the logic for the tab_focus option.
- Fixed bug where save button wasn't working correctly in fullscreen mode.
- Fixed bug where the editor was forced to be placed in a form element if the save_onsavecallback option was used.
- Fixed bug where upper case param attributes wasn't parsed correctly in the media plugin.
- Fixed bug where render method of tinymce.Editor class would produce an exception if the strict_loading_mode option was omitted.
- Fixed bug where nodeChanged event could be fired while the editor was loading and there for produce an exception in FF.
- Fixed bug where no undo levels where added if the user created new table rows using the tab key on Gecko.
- Fixed bug where tables would be broken if you selected a different block format for contents withing an table cell.
- Fixed bug where the render method of the tinymce.Editor class didn't setup the tinymce.EditorManager.settings object correctly.
- Fixed bug where the advanced image dialog would go to the first tab if the alternative image was changed using the file browser link.
- Fixed bug where the forced_root_block option would produce BR elements inside empty blocks if the block wasn't a paragraph.
- Fixed bug where the forced_root_block doesn't work correctly on IE if the specified element was something else than paragraphs.
- Fixed bug where selection of images would get lost if user selected something from the context menu in IE.
- Fixed bug where the context menu plugin would pollute the global namespace with two variables p1 and p2.
- Fixed compatibility issue with Mootools, it is destroying document.getElementById on unload in IE. (Mantra: You don't own the internal objects).
- Fixed bugs where dialogs/tabs and other UI elements where rendered incorrectly in Firefox 3.
- Fixed so the auto CSS class importer is compatible with 2.x.
- Fixed so the editor UI and inlinedialogs works correctly with the YUI CSS reset package.
- Fixed so header and footer elements are forced to lower case when the fullpage plugin is used.
- Fixed so load prefixes "-" for plugins and themes isn't required if the plugin/theme was loaded by the ThemeManager/PluginManager.
- Fixed so the JSONRequest uses application/json content type to make Ruby on rails happy.
- Fixed so the CSS rule is more exact for the body in the default content.css files. Body is now defined as "body.mceContentBody" instead of just "body".
- Fixed so the tiny_mce_dev.js uses XHR instead of document.write to load scripts to resolve an issue with Opera 9.50.
- Fixed so language pack loading can be disabled by setting the language option to false. Can be useful for systems with their own language pack management.
-Version 3.0 (2008-01-30)
- Added map and area elements to the default valid_elements list and also some indentation rules.
- Fixed bug where empty paragraphs wasn't padded when loading contents.
- Fixed bug where the RowLayout manager didn't work at all.
- Fixed bug where style attribute data would get messed up in advimage dialog.
- Fixed bug where the table dialogs class select wasn't updated correctly.
- Fixed bug where elements would get extra whitespace around on insert when body was present in valid_elements.
- Fixed bug where coords attribute of the area element wasn't handled properly in IE.
- Fixed bug where Safari didn't produce BR elements on shift+return.
- Fixed bug where force blocks would cast odd invalid attribute exception in IE.
- Fixed bug where media plugin would produce extra whitespace before and after objects.
- Fixed bug where cleanup_callback could break the contents of the editor. But use the new event system instead of this option.
- Fixed bug where the tab_focus option didn't work between editor instanced. You can now tab between editors.
- Fixed bug where the load function of the ScriptLoader class didn't load single files without the load que as it was supposed to.
- Fixed bug where the execcommand_callback parameter order was incorrect. Recommendation use the new addCommand method.
- Fixed bug where range.select calls sometimes failed on some IE versions.
- Fixed bug where Safari was scrolling to top of document when enter/returned was pressed.
- Fixed bug where fullscreen_new_window option didn't work correctly.
- Fixed bug where the nonbreaking plugin inserted an space instead of an non breaking space the first time.
- Fixed bug where the visualization of non breaking spaces where visual in element path.
- Fixed so the focus is restored to the editor after inserting an custom character.
- Fixed so the isNotDirty state is set to false if a new undo level is added.
- Fixed so pointless style information for borders gets removed in IE.
- Fixed so the resize button has a se-resize cursor css value.
-Version 3.0rc2 (2008-01-18)
- Added new fix_nesting option to fix bug #1867292, this is disabled by default.
- Added new indentation option enables you to specify how much each indent/outdent call will add/remove.
- Added easier support for enabling/disabling icon columns on drop menues.
- Added new menu button control class. This control is very similar to the splitbutton but without any onclick action.
- Added support for previous tab focus (shift+tab). The tab_focus setting now takes two items next and previous element.
- Fixed bug where iframes inside the editor got removed in Firefox on initial load.
- Fixed bug where the CSS for abbr elements wasn't applied correctly in IE.
- Fixed bug where mceAddControl on element inside a hidden container produced errors.
- Fixed bug where closed anchors like <a /> produced strange results.
- Fixed bug where caret would jump to the top of the editor if enter was pressed a the end of a list.
- Fixed bug where remove editor failed if the editor wasn't properly initialized.
- Fixed bug where render call on for a non existing element produced exception.
- Fixed bug where parent window was hidden when the color picker was used in a non inlinepopups setup.
- Fixed bug where onchange event wasn't fired correctly on IE when color picker was used in dialogs.
- Fixed bug where save plugin could not save contents if the converted element wasn't an textarea.
- Fixed bug where events might be fired even after an editor instance was removed such as blur events.
- Fixed bug where an exception about undefined undo levels could be throwed sometimes.
- Fixed bug where the plugin_preview_pageurl option didn't work.
- Fixed bug where adding/removing an editor instance very fast could produce problems.
- Fixed bug where the link button was highlighted when an anchor element was selected.
- Fixed bug where the selected contents where removed if a new anchor element was added.
- Fixed bug where splitbuttons where rendered one pixel down in the default theme.
- Fixed bug where some buttons where placed at incorrect positions in the o2k7 theme.
- Fixed bug that made it impossible to visually disable a custom button that used an image instead of CSS sprites.
- Fixed bug where it wasn't possible to press delete/backspace if the editor was added+removed and re-added due to a FF bug.
- Fixed bug where an entities option with only 38,amp,60,lt,62,gt would fail in IE.
- Fixed bug where innerHTML sometimes generated unknown runtime error on IE.
- Fixed bug where content_css files wasn't loaded in the template preview iframe.
- Fixed bug where scroll position was incorrect when toggling fullscreen mode.
- Fixed bug where restoration of overflow didn't work correctly when disabling fullscreen mode in Opera.
- Fixed bug where drop menus where places at incorrect locations if the editor was placed in a scrollable container element.
- Fixed bug where hideMenu didn't hide sub menus correctly. It will now hide all menus recursively.
- Fixed so theme_advanced_path_location can be used in init options for compatibility reasons.
- Fixed so the drop menu colors matches the rest of o2k7 theme.
- Fixed so the preview example.html file is updated to the new 3.x API.
- Fixed so the margins are the same by default inside the editable area between IE and other browsers.
- Fixed so editor contents gets stored before it the onSubmit event is fired.
-Version 3.0rc1 (2008-01-08)
- Added new classes for toolbar rows in advanced theme mceToolbarRow1..n enabled you to change appearance of individual rows.
- Added auto detection for the strict_loading_mode option when running in application/xhtml+xml mode on Gecko.
- Optimized the HTML serializer by bundling some post process methods together.
- Fixed so that the toolbars have unique IDs, enables you to alter the toolbars using the ControlManager and the DOM.
- Fixed bug where delta values for dialog sizes in language packs didn't work correctly due to missing string to number casting.
- Fixed bug where paragraph generation logic didn't handle hr or table elements correctly if they where the only element.
- Fixed bug where some elements got extra linebreaks added after or before it in HTML output.
- Fixed bug where it was hard to modify existing style data on table rows and table cells.
- Fixed bug where the dom.getRect method didn't handle non pixel values correctly.
- Fixed bug where strikethrough and underline couldn't be toggled on existing span elements.
- Fixed bug where the postprocessor searched for nsbp instead of nbsp entities.
- Fixed bug where it was impossible to edit links that had child elements within them.
- Fixed bug where it was possible to click on the parent item of a submenu.
- Fixed bug where mouseover/mouseout images couldn't be removed in advimage dialog.
- Fixed bug where drop menus didn't work when running in application/xhtml+xml mode.
- Fixed bug where Opera added doctype to output in application/xhtml+xml mode.
- Fixed bug where some DOM methods didn't work correctly in the application/xhtml+xml mode.
- Fixed bug where the inlinepopups didn't work correctly in the application/xhtml+xml mode.
- Fixed bug where the ColorSplitButton didn't display correctly in the application/xhtml+xml mode.
- Fixed bug where the UI layout was incorrect on Gecko browsers when running in application/xhtml+xml mode.
- Fixed bug where the word paste plugin produced exception while running in application/xhtml+xml mode.
- Fixed bug where there wasn't any hidden input element generated for divs while running in application/xhtml+xml mode.
- Fixed bug where indentation of script/style/pre elements where incorrect.
- Fixed bug where script element contents was removed in IE.
- Fixed bug where script element contents got entity encoded.
- Fixed bug where you couldn't edit existing element styles using the styles plugin.
- Fixed bug where styles wasn't updated properly sometimes due to an performance enhancement.
- Fixed bug where font sizes couldn't be changed using the style plugin.
- Fixed bug where an error was produced in Gecko browsers when switching back from fullscreen mode.
- Fixed bug where Opera was producing br elements after elements like h3.
- Fixed bug where TinyMCE couldn't be loaded on a page using - characters in it's URL.
- Fixed bug where the editor container element was forced to have a specific name.
- Fixed bug with force_br_newlines option on Firefox, even though it should never be used (Read FAQ).
- Fixed bug where onclick event had an return true; prefix added when creating an popup.
- Fixed bug where the theme_advanced_statusbar_location option couldn't handle the value "none".
- Fixed issue with URLs with multiple at characters for example an Zope URI.
- Fixed so simple and advanced themes doesn't collide.
- Fixed so a elements gets removed when the href field is left empty, the href attribute is required in a link after all.
- Fixed so img elements gets removed when the src field is left empty, the src attribute is required for all images after all.
- Removed the indent and encode methods from the tinymce.dom.Serializer class due to performance enhancement and reduction of the API size.
-Version 3.0b3 (2007-12-14)
- Added new getElement method to Editor class, returns the element that was replaced with the editor instance.
- Added new unavailable prefix for disabled controls for accessibility reasons.
- Fixed bug where regexp patterns couldn't be used for the editor_selector/editor_deselector options.
- Fixed bug where the DOM wasn't properly initialized before the onInit event was executed in popups.
- Fixed bug where font sizes where reduced by font size actions on previous spans in Safari.
- Fixed bug where HR elements got places at the wrong location in IE.
- Fixed bug where align/justify didn't work correctly on multiple paragraphs.
- Fixed bug with missing translation for cell scope settings.
- Fixed bug where selection/caret position was lost on some table actions.
- Fixed bug where editor instances couldn't be added to hidden div elements.
- Fixed bug where list elements in Safari would get an odd ID attribute.
- Fixed bug where IE would return <html/> when the editor was completely empty.
- Fixed bug where accessibility title attribute for access keys wasn't setup properly.
- Fixed bug where forecolorpicker and backcolorpicker control names wasn't working.
- Fixed bug where inserting template content didn't work in Safari due to selection exception.
- Fixed bug where absolute URLs to remote hosts couldn't be used for background images.
- Fixed bug where mysterious span elements where produced in Safari when injecting HTML contents.
- Fixed bug where the media plugin didn't work correctly on the latest Opera 9.24.
- Fixed bug where indentation of HTML output wasn't applied to all block elements.
- Fixed bug where Safari was production DOM exception if you pressed enter in an empty editor.
- Fixed bug where media plugin didn't parse script tags correctly patch contributed by Mathieu Campagna.
- Fixed bug where the drop menus of list boxes like blockformat could produce scrolling of the page.
- Fixed bug where the drop menus where placed at an incorrect location if TinyMCE was placed in a scrollable div.
- Fixed bug where submit buttons couldn't be named submit, it's not recommended to name submit buttons submit anyway.
- Fixed bug where the stylelistbox produced an exception if there was only one class in the list box.
- Fixed bug where the stylelistbox wasn't updated correctly when the current class was removed.
- Fixed bug where the formatblock command sometimes removed the body element.
- Fixed bug where fullscreen switching in IE sometimes produced an exception when the spellchecker plugin was enabled.
- Fixed issue where FF produced an empty paragraph when the editor was completely empty.
- Fixed issue with size of image dialog in the advanced theme.
- Fixed issues with the bbcode plugin it now also handles spans and the [font] rule.
- Fixed so the style compression feature is a bit smarter to resolve issues with Opera.
- Reintroduced the remove_linebreaks option, this is enabled by default.
-Version 3.0b2 (2007-11-29)
- Added type and compact attributes to the default valid_elements list for the ul and ol elements.
- Added missing accessibility support to native list boxes in both the toolbar and dialogs.
- Added missing access key for the element path for accessibility reasons.
- Fixed support for loading themes from external URLs.
- Fixed bug where setOuterHTML didn't work correctly when multiple elements where passed to it.
- Fixed bug with visualchars plugin was moving elements around in the DOM.
- Fixed bug with DIV elements that got converted into editors on IE.
- Fixed bug with paste plugin using the old event API.
- Fixed bug where the spellchecker was removing the word when it was ignored.
- Fixed bug where fullscreen wasn't working properly.
- Fixed bug where the base href element and attribute was ignored.
- Fixed bug where redo function didn't work in IE.
- Fixed bug where content_css didn't work as previous 2.x branch.
- Fixed bug where preview dialog was throwing errors if the content_css wasn't defined.
- Fixed bug where the theme_advanced_path option didn't work like the 2.x branch.
- Fixed bug where the theme_advanced_statusbar_location was called theme_advanced_status_location.
- Fixed bug where the strict_loading_mode option didn't work if you created editors dynamically without using the EditorManager.
- Fixed bug where some language values wasn't translated such as insert and update in dialogs.
- Fixed bug where some image attributes wasn't stored correctly when inserting an image.
- Fixed bug where fullscreen mode didn't restore scrollbars when disabled.
- Fixed bug where there was no visual representation for tab focus in toolbars on IE.
- Fixed bug where HR elements wasn't treated as block elements so forced_root_block would fail on these.
- Fixed bug where autosave presented warning message even when the form was submitted normally.
- Fixed typo of openBrower it's now openBrowser in form_utils.js.
- Fixed various HTML problems like missing TD elements and duplicated doctypes.
- Fixed default values for theme_advanced_resize_horizontal, theme_advanced_resizing_use_cookie to be 2.x compatible.
- Moved spellchecker JS files into the development package.
- Removed support for theme_advanced_path_location since the theme_advanced_statusbar_location is the correct option name.
-Version 3.0b1 (2007-11-21)
- Added new tab_focus option, that enables you to specify a element id or that the next element to be focused on tab key down.
- Added new addQueryValueHandler method to the tinymce.Editor class.
- Added new class_filter option, this enables you to specify a function that can filter out CSS classes for the styles list box.
- Added support form [url=url]title[/url] to the bbcode plugin.
- Renamed the addCommandQueryState method in the tinymce.Editor class to addQueryStateHandler.
- Renamed loadQue to loadQueue, to correct spelling.
- Removed the createDOM method from the window manager and replace it with a createInstance method.
- Removed the add to beginning of class attribute parameter of the DOMUtils.addClass method.
- Fixed bug with the forced_root_block option, didn't work correctly with multiple inline elements.
- Fixed bug where image dialogs replaced the current image element with a new one even when it was updated.
- Fixed bug where the submit trigger wasn't executed when divs where converted into editor instances.
- Fixed bug where div elements that got converted into editors didn't get a hidden input element generated for them.
- Fixed bug where the the media_use_script option for the media plugin wasn't working correctly.
- Fixed bug where the font size and font family listboxes wasn't updated correctly on Safari.
- Fixed bug where the height of the fieldset in default image dialog for the advanced theme was to small.
- Fixed bug where the font sizes behaved incorrectly after a cleanup on Safari.
- Fixed bug where formatblock didn't work correctly in Safari on some elements.
- Fixed bug where template plugin didn't insert content correctly unless some options where specified.
- Fixed bug where charmap on Safari produced scrollbars.
- Fixed bug where there was white artifacts in some dialogs due to missing background color.
- Fixed bug where port was added to all external URLs if the editor was loaded from a custom port.
- Fixed bug where the context menus got duplicated on Safari 3.0.4 on Mac OS X.
- Fixed bug where dialogs like paste from word was huge on Firefox.
- Fixed bug with media plugin not working with windows media objects.
- Fixed bug where a forever loop was created if multiple instances where submitted using form.submit.
- Fixed bug with editing a table produce error in IE when inlinepopups where used.
- Fixed bug where the style plugin generated ugly looking style information in IE.
- Fixed bug where the inline dialogs that got opened while in fullscreen mode wasn't visible.
- Fixed bug where it was difficult to place the caret inside the word paste dialog.
- Fixed bug where Opera produced strange border in the word paste dialog.
- Fixed bug where viewport constraints could move a inlinepopup to a negative x, y position if the viewport was to small.
- Fixed bug where template plugin was producing an error due to a deprecated API call.
- Fixed bug where drag drop of images failed in Gecko if a document_base_url was specified.
- Fixed bug where Firefox 3 failed to apply block formats like H1-H6 it still breaks on DIVs this has been reported to bugzilla.
- Fixed bug where IE was producing a warning dialog about non secure items when running TinyMCE over HTTPS.
- Fixed bug where the onbeforeunload event was triggered when menus or dialogs where opened.
- Fixed bug where the fullscreen mode of the HTML view source box threw an error.
- Fixed bug where the mceFocus command didn't work correctly.
- Fixed bug where the selection could get lost in IE using inlinepopups.
- Fixed so the body of the editor area has the mceContentBody class just like the 2.x branch.
- Fixed so the media icon gets active when a media element is selected.
-Version 3.0a3 (2007-11-13)
- Added new experimental jQuery and Prototype framework adapters to the development package.
- Added new translation.html file for the development package. Helps with the internationalization of TinyMCE.
- Added new setup callback option, use this callback to add events to TinyMCE. This method is recommended over the old callbacks.
- Added new API documetation to all classes, functions, events, properties to the Wiki with examples etc.
- Added new init method to all plugins and themes, since it's shorter to write and it mimics interface capable languages better.
- Fixed various CSS issues in the default skin such as alignment of split buttons and separators.
- Fixed issues with mod_security. It didn't like that a content type of text/javascript was forced in a XHR.
- Fixed all events so that they now pass the sender object as it's first argument.
- Fixed some DOM methods so they now can take an array as input.
- Fixed so addButton and the methods of the ControlManager uses less arguments and it now uses a settings object instead.
- Fixed various issues with the tinymce.util.URI class.
- Fixed bug in IE and Safari and the on demand gzip loading feature.
- Fixed bug with moving inline windows sometimes failed in IE6.
- Fixed bug where save_callback function wasn't executed at all.
- Fixed bug where inlinepopups produces scrollbars if windows where moved to the corners of the browser.
- Fixed bug where view HTML source failed when inserting a embedded media object.
- Fixed bug where the listbox menus didn't display correctly on IE6.
- Fixed bug where undo level wasn't added when editor was blurred.
- Fixed bug where spellchecker wasn't disabled when fullscreen mode was enabled.
- Fixed bug where Firefox could crash some times when the user switched to fullscreen mode.
- Fixed bug where tinymce.ui.DropMenu didn't remove all item data when an item was removed from the menu.
- Fixed bug where anchor list in advlink dialog wasn't populated correctly in Safari.
- Fixed bug where it wasn't possible to edit tables in IE when inlinepopups was enabled.
- Fixed bug where it wasn't possible to change the table width of an existing table.
- Fixed bug where xhtmlxtras like abbr didn't work correctly on IE.
- Fixed bug where IE6 had some graphics rendering issues with the inlinepopups.
- Fixed bug where inlinepopup windows where moved incorrectly when they were boundary checked for min width.
- Fixed bug where textareas without id or name couldn't be converted into editor instances.
- Fixed bug where TinyMCE was stealing element focus on IE.
- Fixed bug where the getParam method didn't handle false values correctly.
- Fixed bug where inlinepopups was clipped by other TinyMCE instances or relative elements in IE.
- Fixed bug where the contextmenu was clipped by other TinyMCE instances or relative elements in IE.
- Fixed bug where listbox menus was clipped by other TinyMCE instances or relative elements in IE.
- Fixed bug where listboxes wasn't updated correctly when the a value wasn't found by select.
- Fixed various CSS issues that produced odd rendering bugs in IE.
- Fixed issues with tinymce.ui.DropMenu class, it required some optional settings to be specified.
- Fixed so multiple blockquotes can be removed with a easier method than before.
- Optimized some of the core API to boost performance.
- Removed some functions from the core API that wasn't needed.
-Version 3.0a2 (2007-11-02)
- Fixed critical bug where IE generaded an error on a hasAttribute call in the serialization engine.
- Fixed critical bug where some dialogs didn't open in the non dev package.
- Fixed bug when using the theme_advanced_styles option. Error was thrown in some dialogs.
- Fixed bug where the close buttons produced an error when native windows where used.
- Fixed bug in default skin so that split buttons gets activated correctly.
- Fixed so plugins can be loaded from external urls outsite the plugins directory.
-Version 3.0a1 (2007-11-01)
- Rewrote the core and most of the plugins and themes from scratch.
- Added new and improved serialization engine, faster and more powerful.
- Added new internal event system, things like editor.onClick.add(func).
- Added new inlinepopups plugin, the dialogs are now skinnable and uses clearlooks2 as default.
- Added new contextmenu plugin, context menus can now have submenus and plugins can add items on the fly.
- Added new skin support for the simple and advanced themes you can alter the whole UI using CSS.
- Added new o2k7 skin for the simple and advanced themes.
- Added new custom list boxes for font size/format/style etc with preview support.
- Added new UI management, enabled plugins to create controls like splitbuttons or menus easier.
- Added new JSON parser/serializer and JSON-RPC class to the core API.
- Added new cookie utility class to the core API.
- Added new Unit testing class to the core API only available in dev mode.
- Added new firebug lite integration when loading the dev version of TinyMCE.
- Added new Safari plugin, fixes lots compatibility of issues with Safari 3.x.
- Added new URI/URL parsing it now handles the hole RFC and even some exceptions.
- Added new pagebreak plugin, enables you to insert pagebreak comments like <!-- pagebreak -->
- Added new on demand loading of plugins and themes. Enables you to load and init TinyMCE at any time.
- Added new throbber/progress visualization a plugin can show/hide this when it's needed.
- Added new blockquote button. Enables you to wrap paragraphs in blockquotes.
- Added new compat2x plugin. Will provide a TinyMCE 2.x API for older plugins.
- Added new theme_advanced_resizing_min_width, theme_advanced_resizing_min_height options.
- Added new theme_advanced_resizing_max_height, theme_advanced_resizing_max_height options.
- Added new use_native_selects option. Enables you to toggle native listboxes on and off.
- Added new docs_url option enables you to specify where the TinyMCE user documentation is located.
- Added new frame and rules options for the table dialog.
- Added new global rule for valid_elements/extended_valid_elements enables you to specify global attributes for all elements.
- Added new deny attribute rule characher so it's possible to deny global attribute rules on specific elements.
- Added new unit tests in the dev package of TinyMCE. Runs tests on the core API, commands and settings of the editor.
- Readded the inline_styles option and enabled it by default so deprecated attributes are no longer used.
- Removed all button images and replaced them with CSS sprite images. Reduces the number of requests needed.
- Removed lots of language files and merged them into the base language files. Reduces the number of requests needed.
- Removed lots of unnecessary files and merged many of them together to reduce requests and improve loading speed.
- Reduced the over all script size by 33% and the number of files/requests by 75% so it loads a lot faster.
- Fixed so convert_fonts_to_spans are enabled by default. So no more font tags.
- Fixed so underline and strikethrough uses spans instread of deprecated U and STRIKE elements.
- Fixed so indent/outdent adds/removed margin-left instead of blockquotes.
- Fixed so alignment of paragraphs results in a text-align style value instead of the deprecated align attribute.
- Fixed so alignment of images uses float or vertical-align style values instead of the deprecated align attribute.
- Fixed so all classes from @import stylesheets gets imported into the editor.
- Fixed so the directionality can toggle the dir attribute on and off.
- Fixed so the fullscreen_settings can be used for all types of fullscreen modes.
- Fixed so the advanced HR dialog gets displayed when inserting a HR not only on edit.
- Fixed bug where word wrap didn't work in the source editor on Safari.
- Fixed so non HTML elements can be used within the editor such as <myns:tag>
- Fixed various memory leaks in IE and reduced the unload cleanups needed.
- Fixed so the preformatted option adds an invisible container pre tag inside the editor.
- Renamed the _template plugin to example and updated it to use the new 3.x API.
diff --git a/mod/tinymce/vendor/tinymce/examples/full.html b/mod/tinymce/vendor/tinymce/examples/full.html
index 39c2060b2..84b76ca7a 100644
--- a/mod/tinymce/vendor/tinymce/examples/full.html
+++ b/mod/tinymce/vendor/tinymce/examples/full.html
@@ -10,13 +10,13 @@
// General options
mode : "textareas",
theme : "advanced",
- plugins : "autolink,lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,wordcount,advlist,autosave",
+ plugins : "autolink,lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,fullscreen,noneditable,visualchars,nonbreaking,xhtmlxtras,template,wordcount,advlist,autosave,visualblocks",
// Theme options
theme_advanced_buttons1 : "save,newdocument,|,bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,styleselect,formatselect,fontselect,fontsizeselect",
theme_advanced_buttons2 : "cut,copy,paste,pastetext,pasteword,|,search,replace,|,bullist,numlist,|,outdent,indent,blockquote,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code,|,insertdate,inserttime,preview,|,forecolor,backcolor",
theme_advanced_buttons3 : "tablecontrols,|,hr,removeformat,visualaid,|,sub,sup,|,charmap,emotions,iespell,media,advhr,|,print,|,ltr,rtl,|,fullscreen",
- theme_advanced_buttons4 : "insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak,restoredraft",
+ theme_advanced_buttons4 : "insertlayer,moveforward,movebackward,absolute,|,styleprops,|,cite,abbr,acronym,del,ins,attribs,|,visualchars,nonbreaking,template,pagebreak,restoredraft,visualblocks",
theme_advanced_toolbar_location : "top",
theme_advanced_toolbar_align : "left",
theme_advanced_statusbar_location : "bottom",
diff --git a/mod/tinymce/vendor/tinymce/examples/lists/media_list.js b/mod/tinymce/vendor/tinymce/examples/lists/media_list.js
index 79b3f1bfe..2e049587c 100644
--- a/mod/tinymce/vendor/tinymce/examples/lists/media_list.js
+++ b/mod/tinymce/vendor/tinymce/examples/lists/media_list.js
@@ -10,5 +10,5 @@ var tinyMCEMediaList = [
["Some RealMedia", "media/sample.rm"],
["Some Shockwave", "media/sample.dcr"],
["Some Video", "media/sample.mp4"],
- ["Some FLV", "media/sample.flv"],
+ ["Some FLV", "media/sample.flv"]
]; \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/langs/en.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/langs/en.js
index 16d7a93e0..19324f74c 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/langs/en.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/langs/en.js
@@ -1 +1 @@
-tinyMCE.addI18n({en:{common:{"more_colors":"More Colors...","invalid_data":"Error: Invalid values entered, these are marked in red.","popup_blocked":"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.","clipboard_no_support":"Currently not supported by your browser, use keyboard shortcuts instead.","clipboard_msg":"Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?","not_set":"-- Not Set --","class_name":"Class",browse:"Browse",close:"Close",cancel:"Cancel",update:"Update",insert:"Insert",apply:"Apply","edit_confirm":"Do you want to use the WYSIWYG mode for this textarea?","invalid_data_number":"{#field} must be a number","invalid_data_min":"{#field} must be a number greater than {#min}","invalid_data_size":"{#field} must be a number or percentage",value:"(value)"},contextmenu:{full:"Full",right:"Right",center:"Center",left:"Left",align:"Alignment"},insertdatetime:{"day_short":"Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun","day_long":"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday","months_short":"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec","months_long":"January,February,March,April,May,June,July,August,September,October,November,December","inserttime_desc":"Insert Time","insertdate_desc":"Insert Date","time_fmt":"%H:%M:%S","date_fmt":"%Y-%m-%d"},print:{"print_desc":"Print"},preview:{"preview_desc":"Preview"},directionality:{"rtl_desc":"Direction Right to Left","ltr_desc":"Direction Left to Right"},layer:{content:"New layer...","absolute_desc":"Toggle Absolute Positioning","backward_desc":"Move Backward","forward_desc":"Move Forward","insertlayer_desc":"Insert New Layer"},save:{"save_desc":"Save","cancel_desc":"Cancel All Changes"},nonbreaking:{"nonbreaking_desc":"Insert Non-Breaking Space Character"},iespell:{download:"ieSpell not detected. Do you want to install it now?","iespell_desc":"Check Spelling"},advhr:{"delta_height":"","delta_width":"","advhr_desc":"Insert Horizontal Line"},emotions:{"delta_height":"","delta_width":"","emotions_desc":"Emotions"},searchreplace:{"replace_desc":"Find/Replace","delta_width":"","delta_height":"","search_desc":"Find"},advimage:{"delta_width":"","image_desc":"Insert/Edit Image","delta_height":""},advlink:{"delta_height":"","delta_width":"","link_desc":"Insert/Edit Link"},xhtmlxtras:{"attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":"","attribs_desc":"Insert/Edit Attributes","ins_desc":"Insertion","del_desc":"Deletion","acronym_desc":"Acronym","abbr_desc":"Abbreviation","cite_desc":"Citation"},style:{"delta_height":"","delta_width":"",desc:"Edit CSS Style"},paste:{"plaintext_mode":"Paste is now in plain text mode. Click again to toggle back to regular paste mode.","plaintext_mode_sticky":"Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.","selectall_desc":"Select All","paste_word_desc":"Paste from Word","paste_text_desc":"Paste as Plain Text"},"paste_dlg":{"word_title":"Use Ctrl+V on your keyboard to paste the text into the window.","text_linebreaks":"Keep Linebreaks","text_title":"Use Ctrl+V on your keyboard to paste the text into the window."},table:{"merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":"",cell:"Cell",col:"Column",row:"Row",del:"Delete Table","copy_row_desc":"Copy Table Row","cut_row_desc":"Cut Table Row","paste_row_after_desc":"Paste Table Row After","paste_row_before_desc":"Paste Table Row Before","props_desc":"Table Properties","cell_desc":"Table Cell Properties","row_desc":"Table Row Properties","merge_cells_desc":"Merge Table Cells","split_cells_desc":"Split Merged Table Cells","delete_col_desc":"Delete Column","col_after_desc":"Insert Column After","col_before_desc":"Insert Column Before","delete_row_desc":"Delete Row","row_after_desc":"Insert Row After","row_before_desc":"Insert Row Before",desc:"Insert/Edit Table"},autosave:{"warning_message":"If you restore the saved content, you will lose all the content that is currently in the editor.\n\nAre you sure you want to restore the saved content?","restore_content":"Restore auto-saved content.","unload_msg":"The changes you made will be lost if you navigate away from this page."},fullscreen:{desc:"Toggle Full Screen Mode"},media:{"delta_height":"","delta_width":"",edit:"Edit Embedded Media",desc:"Insert/Edit Embedded Media"},fullpage:{desc:"Document Properties","delta_width":"","delta_height":""},template:{desc:"Insert Predefined Template Content"},visualchars:{desc:"Show/Hide Visual Control Characters"},spellchecker:{desc:"Toggle Spell Checker",menu:"Spell Checker Settings","ignore_word":"Ignore Word","ignore_words":"Ignore All",langs:"Languages",wait:"Please wait...",sug:"Suggestions","no_sug":"No Suggestions","no_mpell":"No misspellings found.","learn_word":"Learn word"},pagebreak:{desc:"Insert Page Break for Printing"},advlist:{types:"Types",def:"Default","lower_alpha":"Lower Alpha","lower_greek":"Lower Greek","lower_roman":"Lower Roman","upper_alpha":"Upper Alpha","upper_roman":"Upper Roman",circle:"Circle",disc:"Disc",square:"Square"},colors:{"333300":"Dark olive","993300":"Burnt orange","000000":"Black","003300":"Dark green","003366":"Dark azure","000080":"Navy Blue","333399":"Indigo","333333":"Very dark gray","800000":"Maroon",FF6600:"Orange","808000":"Olive","008000":"Green","008080":"Teal","0000FF":"Blue","666699":"Grayish blue","808080":"Gray",FF0000:"Red",FF9900:"Amber","99CC00":"Yellow green","339966":"Sea green","33CCCC":"Turquoise","3366FF":"Royal blue","800080":"Purple","999999":"Medium gray",FF00FF:"Magenta",FFCC00:"Gold",FFFF00:"Yellow","00FF00":"Lime","00FFFF":"Aqua","00CCFF":"Sky blue","993366":"Brown",C0C0C0:"Silver",FF99CC:"Pink",FFCC99:"Peach",FFFF99:"Light yellow",CCFFCC:"Pale green",CCFFFF:"Pale cyan","99CCFF":"Light sky blue",CC99FF:"Plum",FFFFFF:"White"},aria:{"rich_text_area":"Rich Text Area"},wordcount:{words:"Words:"}}}); \ No newline at end of file
+tinyMCE.addI18n({en:{common:{"more_colors":"More Colors...","invalid_data":"Error: Invalid values entered, these are marked in red.","popup_blocked":"Sorry, but we have noticed that your popup-blocker has disabled a window that provides application functionality. You will need to disable popup blocking on this site in order to fully utilize this tool.","clipboard_no_support":"Currently not supported by your browser, use keyboard shortcuts instead.","clipboard_msg":"Copy/Cut/Paste is not available in Mozilla and Firefox.\nDo you want more information about this issue?","not_set":"-- Not Set --","class_name":"Class",browse:"Browse",close:"Close",cancel:"Cancel",update:"Update",insert:"Insert",apply:"Apply","edit_confirm":"Do you want to use the WYSIWYG mode for this textarea?","invalid_data_number":"{#field} must be a number","invalid_data_min":"{#field} must be a number greater than {#min}","invalid_data_size":"{#field} must be a number or percentage",value:"(value)"},contextmenu:{full:"Full",right:"Right",center:"Center",left:"Left",align:"Alignment"},insertdatetime:{"day_short":"Sun,Mon,Tue,Wed,Thu,Fri,Sat,Sun","day_long":"Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday,Sunday","months_short":"Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec","months_long":"January,February,March,April,May,June,July,August,September,October,November,December","inserttime_desc":"Insert Time","insertdate_desc":"Insert Date","time_fmt":"%H:%M:%S","date_fmt":"%Y-%m-%d"},print:{"print_desc":"Print"},preview:{"preview_desc":"Preview"},directionality:{"rtl_desc":"Direction Right to Left","ltr_desc":"Direction Left to Right"},layer:{content:"New layer...","absolute_desc":"Toggle Absolute Positioning","backward_desc":"Move Backward","forward_desc":"Move Forward","insertlayer_desc":"Insert New Layer"},save:{"save_desc":"Save","cancel_desc":"Cancel All Changes"},nonbreaking:{"nonbreaking_desc":"Insert Non-Breaking Space Character"},iespell:{download:"ieSpell not detected. Do you want to install it now?","iespell_desc":"Check Spelling"},advhr:{"delta_height":"","delta_width":"","advhr_desc":"Insert Horizontal Line"},emotions:{"delta_height":"","delta_width":"","emotions_desc":"Emotions"},searchreplace:{"replace_desc":"Find/Replace","delta_width":"","delta_height":"","search_desc":"Find"},advimage:{"delta_width":"","image_desc":"Insert/Edit Image","delta_height":""},advlink:{"delta_height":"","delta_width":"","link_desc":"Insert/Edit Link"},xhtmlxtras:{"attribs_delta_height":"","attribs_delta_width":"","ins_delta_height":"","ins_delta_width":"","del_delta_height":"","del_delta_width":"","acronym_delta_height":"","acronym_delta_width":"","abbr_delta_height":"","abbr_delta_width":"","cite_delta_height":"","cite_delta_width":"","attribs_desc":"Insert/Edit Attributes","ins_desc":"Insertion","del_desc":"Deletion","acronym_desc":"Acronym","abbr_desc":"Abbreviation","cite_desc":"Citation"},style:{"delta_height":"","delta_width":"",desc:"Edit CSS Style"},paste:{"plaintext_mode_stick":"Paste is now in plain text mode. Click again to toggle back to regular paste mode.","plaintext_mode":"Paste is now in plain text mode. Click again to toggle back to regular paste mode. After you paste something you will be returned to regular paste mode.","selectall_desc":"Select All","paste_word_desc":"Paste from Word","paste_text_desc":"Paste as Plain Text"},"paste_dlg":{"word_title":"Use Ctrl+V on your keyboard to paste the text into the window.","text_linebreaks":"Keep Linebreaks","text_title":"Use Ctrl+V on your keyboard to paste the text into the window."},table:{"merge_cells_delta_height":"","merge_cells_delta_width":"","table_delta_height":"","table_delta_width":"","cellprops_delta_height":"","cellprops_delta_width":"","rowprops_delta_height":"","rowprops_delta_width":"",cell:"Cell",col:"Column",row:"Row",del:"Delete Table","copy_row_desc":"Copy Table Row","cut_row_desc":"Cut Table Row","paste_row_after_desc":"Paste Table Row After","paste_row_before_desc":"Paste Table Row Before","props_desc":"Table Properties","cell_desc":"Table Cell Properties","row_desc":"Table Row Properties","merge_cells_desc":"Merge Table Cells","split_cells_desc":"Split Merged Table Cells","delete_col_desc":"Delete Column","col_after_desc":"Insert Column After","col_before_desc":"Insert Column Before","delete_row_desc":"Delete Row","row_after_desc":"Insert Row After","row_before_desc":"Insert Row Before",desc:"Insert/Edit Table"},autosave:{"warning_message":"If you restore the saved content, you will lose all the content that is currently in the editor.\n\nAre you sure you want to restore the saved content?","restore_content":"Restore auto-saved content.","unload_msg":"The changes you made will be lost if you navigate away from this page."},fullscreen:{desc:"Toggle Full Screen Mode"},media:{"delta_height":"","delta_width":"",edit:"Edit Embedded Media",desc:"Insert/Edit Embedded Media"},fullpage:{desc:"Document Properties","delta_width":"","delta_height":""},template:{desc:"Insert Predefined Template Content"},visualchars:{desc:"Show/Hide Visual Control Characters"},spellchecker:{desc:"Toggle Spell Checker",menu:"Spell Checker Settings","ignore_word":"Ignore Word","ignore_words":"Ignore All",langs:"Languages",wait:"Please wait...",sug:"Suggestions","no_sug":"No Suggestions","no_mpell":"No misspellings found.","learn_word":"Learn word"},pagebreak:{desc:"Insert Page Break for Printing"},advlist:{types:"Types",def:"Default","lower_alpha":"Lower Alpha","lower_greek":"Lower Greek","lower_roman":"Lower Roman","upper_alpha":"Upper Alpha","upper_roman":"Upper Roman",circle:"Circle",disc:"Disc",square:"Square"},colors:{"333300":"Dark olive","993300":"Burnt orange","000000":"Black","003300":"Dark green","003366":"Dark azure","000080":"Navy Blue","333399":"Indigo","333333":"Very dark gray","800000":"Maroon",FF6600:"Orange","808000":"Olive","008000":"Green","008080":"Teal","0000FF":"Blue","666699":"Grayish blue","808080":"Gray",FF0000:"Red",FF9900:"Amber","99CC00":"Yellow green","339966":"Sea green","33CCCC":"Turquoise","3366FF":"Royal blue","800080":"Purple","999999":"Medium gray",FF00FF:"Magenta",FFCC00:"Gold",FFFF00:"Yellow","00FF00":"Lime","00FFFF":"Aqua","00CCFF":"Sky blue","993366":"Brown",C0C0C0:"Silver",FF99CC:"Pink",FFCC99:"Peach",FFFF99:"Light yellow",CCFFCC:"Pale green",CCFFFF:"Pale cyan","99CCFF":"Light sky blue",CC99FF:"Plum",FFFFFF:"White"},aria:{"rich_text_area":"Rich Text Area"},wordcount:{words:"Words:"},visualblocks:{desc:'Show/hide block elements'}}}); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advimage/js/image.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advimage/js/image.js
index 546b69c0d..f0b7c6eef 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advimage/js/image.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advimage/js/image.js
@@ -395,12 +395,14 @@ var ImageDialog = {
if (v == '0')
img.style.border = isIE ? '0' : '0 none none';
else {
- if (b.length == 3 && b[isIE ? 2 : 1])
- bStyle = b[isIE ? 2 : 1];
+ var isOldIE = tinymce.isIE && (!document.documentMode || document.documentMode < 9);
+
+ if (b.length == 3 && b[isOldIE ? 2 : 1])
+ bStyle = b[isOldIE ? 2 : 1];
else if (!bStyle || bStyle == 'none')
bStyle = 'solid';
if (b.length == 3 && b[isIE ? 0 : 2])
- bColor = b[isIE ? 0 : 2];
+ bColor = b[isOldIE ? 0 : 2];
else if (!bColor || bColor == 'none')
bColor = 'black';
img.style.border = v + 'px ' + bStyle + ' ' + bColor;
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advlink/js/advlink.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advlink/js/advlink.js
index 837c937c6..f013aac1e 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advlink/js/advlink.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/advlink/js/advlink.js
@@ -54,16 +54,24 @@ function init() {
document.getElementById('popupurl').style.width = '180px';
elm = inst.dom.getParent(elm, "A");
+ if (elm == null) {
+ var prospect = inst.dom.create("p", null, inst.selection.getContent());
+ if (prospect.childNodes.length === 1) {
+ elm = prospect.firstChild;
+ }
+ }
+
if (elm != null && elm.nodeName == "A")
action = "update";
- formObj.insert.value = tinyMCEPopup.getLang(action, 'Insert', true);
+ formObj.insert.value = tinyMCEPopup.getLang(action, 'Insert', true);
setPopupControlsDisabled(true);
if (action == "update") {
var href = inst.dom.getAttrib(elm, 'href');
var onclick = inst.dom.getAttrib(elm, 'onclick');
+ var linkTarget = inst.dom.getAttrib(elm, 'target') ? inst.dom.getAttrib(elm, 'target') : "_self";
// Setup form data
setFormValue('href', href);
@@ -91,7 +99,7 @@ function init() {
setFormValue('onkeypress', inst.dom.getAttrib(elm, 'onkeypress'));
setFormValue('onkeydown', inst.dom.getAttrib(elm, 'onkeydown'));
setFormValue('onkeyup', inst.dom.getAttrib(elm, 'onkeyup'));
- setFormValue('target', inst.dom.getAttrib(elm, 'target'));
+ setFormValue('target', linkTarget);
setFormValue('classes', inst.dom.getAttrib(elm, 'class'));
// Parse onclick data
@@ -112,7 +120,7 @@ function init() {
addClassesToList('classlist', 'advlink_styles');
selectByValue(formObj, 'classlist', inst.dom.getAttrib(elm, 'class'), true);
- selectByValue(formObj, 'targetlist', inst.dom.getAttrib(elm, 'target'), true);
+ selectByValue(formObj, 'targetlist', linkTarget, true);
} else
addClassesToList('classlist', 'advlink_styles');
}
@@ -370,6 +378,9 @@ function getAnchorListHTML(id, target) {
for (i=0, len=nodes.length; i<len; i++) {
if ((name = ed.dom.getAttrib(nodes[i], "name")) != "")
html += '<option value="#' + name + '">' + name + '</option>';
+
+ if ((name = nodes[i].id) != "" && !nodes[i].href)
+ html += '<option value="#' + name + '">' + name + '</option>';
}
if (html == "")
@@ -481,7 +492,7 @@ function getLinkListHTML(elm_id, target_form_element, onchange_func) {
var html = "";
html += '<select id="' + elm_id + '" name="' + elm_id + '"';
- html += ' class="mceLinkList" onfoc2us="tinyMCE.addSelectAccessibility(event, this, window);" onchange="this.form.' + target_form_element + '.value=';
+ html += ' class="mceLinkList" onchange="this.form.' + target_form_element + '.value=';
html += 'this.options[this.selectedIndex].value;';
if (typeof(onchange_func) != "undefined")
@@ -503,7 +514,7 @@ function getTargetListHTML(elm_id, target_form_element) {
var targets = tinyMCEPopup.getParam('theme_advanced_link_targets', '').split(';');
var html = '';
- html += '<select id="' + elm_id + '" name="' + elm_id + '" onf2ocus="tinyMCE.addSelectAccessibility(event, this, window);" onchange="this.form.' + target_form_element + '.value=';
+ html += '<select id="' + elm_id + '" name="' + elm_id + '" onchange="this.form.' + target_form_element + '.value=';
html += 'this.options[this.selectedIndex].value;">';
html += '<option value="_self">' + tinyMCEPopup.getLang('advlink_dlg.target_same') + '</option>';
html += '<option value="_blank">' + tinyMCEPopup.getLang('advlink_dlg.target_blank') + ' (_blank)</option>';
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin.js
index fd293dca4..71d86bbec 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin.js
@@ -1 +1 @@
-(function(){tinymce.create("tinymce.plugins.AutolinkPlugin",{init:function(a,b){var c=this;if(tinyMCE.isIE){return}a.onKeyDown.add(function(d,f){if(f.keyCode==13){return c.handleEnter(d)}});a.onKeyPress.add(function(d,f){if(f.which==41){return c.handleEclipse(d)}});a.onKeyUp.add(function(d,f){if(f.keyCode==32){return c.handleSpacebar(d)}})},handleEclipse:function(a){this.parseCurrentLine(a,-1,"(",true)},handleSpacebar:function(a){this.parseCurrentLine(a,0,"",true)},handleEnter:function(a){this.parseCurrentLine(a,-1,"",false)},parseCurrentLine:function(i,d,b,g){var a,f,c,n,k,m,h,e,j;a=i.selection.getRng().cloneRange();if(a.startOffset<5){e=a.endContainer.previousSibling;if(e==null){if(a.endContainer.firstChild==null||a.endContainer.firstChild.nextSibling==null){return}e=a.endContainer.firstChild.nextSibling}j=e.length;a.setStart(e,j);a.setEnd(e,j);if(a.endOffset<5){return}f=a.endOffset;n=e}else{n=a.endContainer;if(n.nodeType!=3&&n.firstChild){while(n.nodeType!=3&&n.firstChild){n=n.firstChild}a.setStart(n,0);a.setEnd(n,n.nodeValue.length)}if(a.endOffset==1){f=2}else{f=a.endOffset-1-d}}c=f;do{a.setStart(n,f-2);a.setEnd(n,f-1);f-=1}while(a.toString()!=" "&&a.toString()!=""&&a.toString().charCodeAt(0)!=160&&(f-2)>=0&&a.toString()!=b);if(a.toString()==b||a.toString().charCodeAt(0)==160){a.setStart(n,f);a.setEnd(n,c);f+=1}else{if(a.startOffset==0){a.setStart(n,0);a.setEnd(n,c)}else{a.setStart(n,f);a.setEnd(n,c)}}m=a.toString();h=m.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)(.+)$/i);if(h){if(h[1]=="www."){h[1]="http://www."}k=i.selection.getBookmark();i.selection.setRng(a);tinyMCE.execCommand("createlink",false,h[1]+h[2]);i.selection.moveToBookmark(k);if(tinyMCE.isWebKit){i.selection.collapse(false);var l=Math.min(n.length,c+1);a.setStart(n,l);a.setEnd(n,l);i.selection.setRng(a)}}},getInfo:function(){return{longname:"Autolink",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autolink",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autolink",tinymce.plugins.AutolinkPlugin)})(); \ No newline at end of file
+(function(){tinymce.create("tinymce.plugins.AutolinkPlugin",{init:function(a,b){var c=this;a.onKeyDown.addToTop(function(d,f){if(f.keyCode==13){return c.handleEnter(d)}});if(tinyMCE.isIE){return}a.onKeyPress.add(function(d,f){if(f.which==41){return c.handleEclipse(d)}});a.onKeyUp.add(function(d,f){if(f.keyCode==32){return c.handleSpacebar(d)}})},handleEclipse:function(a){this.parseCurrentLine(a,-1,"(",true)},handleSpacebar:function(a){this.parseCurrentLine(a,0,"",true)},handleEnter:function(a){this.parseCurrentLine(a,-1,"",false)},parseCurrentLine:function(i,d,b,g){var a,f,c,n,k,m,h,e,j;a=i.selection.getRng(true).cloneRange();if(a.startOffset<5){e=a.endContainer.previousSibling;if(e==null){if(a.endContainer.firstChild==null||a.endContainer.firstChild.nextSibling==null){return}e=a.endContainer.firstChild.nextSibling}j=e.length;a.setStart(e,j);a.setEnd(e,j);if(a.endOffset<5){return}f=a.endOffset;n=e}else{n=a.endContainer;if(n.nodeType!=3&&n.firstChild){while(n.nodeType!=3&&n.firstChild){n=n.firstChild}if(n.nodeType==3){a.setStart(n,0);a.setEnd(n,n.nodeValue.length)}}if(a.endOffset==1){f=2}else{f=a.endOffset-1-d}}c=f;do{a.setStart(n,f>=2?f-2:0);a.setEnd(n,f>=1?f-1:0);f-=1}while(a.toString()!=" "&&a.toString()!=""&&a.toString().charCodeAt(0)!=160&&(f-2)>=0&&a.toString()!=b);if(a.toString()==b||a.toString().charCodeAt(0)==160){a.setStart(n,f);a.setEnd(n,c);f+=1}else{if(a.startOffset==0){a.setStart(n,0);a.setEnd(n,c)}else{a.setStart(n,f);a.setEnd(n,c)}}var m=a.toString();if(m.charAt(m.length-1)=="."){a.setEnd(n,c-1)}m=a.toString();h=m.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+-]+@)(.+)$/i);if(h){if(h[1]=="www."){h[1]="http://www."}else{if(/@$/.test(h[1])&&!/^mailto:/.test(h[1])){h[1]="mailto:"+h[1]}}k=i.selection.getBookmark();i.selection.setRng(a);tinyMCE.execCommand("createlink",false,h[1]+h[2]);i.selection.moveToBookmark(k);i.nodeChanged();if(tinyMCE.isWebKit){i.selection.collapse(false);var l=Math.min(n.length,c+1);a.setStart(n,l);a.setEnd(n,l);i.selection.setRng(a)}}},getInfo:function(){return{longname:"Autolink",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autolink",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autolink",tinymce.plugins.AutolinkPlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin_src.js
index 604da8b42..5b61f7a20 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autolink/editor_plugin_src.js
@@ -22,15 +22,15 @@
init : function(ed, url) {
var t = this;
- // Internet Explorer has built-in automatic linking
- if (tinyMCE.isIE)
- return;
-
// Add a key down handler
- ed.onKeyDown.add(function(ed, e) {
+ ed.onKeyDown.addToTop(function(ed, e) {
if (e.keyCode == 13)
return t.handleEnter(ed);
- });
+ });
+
+ // Internet Explorer has built-in automatic linking for most cases
+ if (tinyMCE.isIE)
+ return;
ed.onKeyPress.add(function(ed, e) {
if (e.which == 41)
@@ -61,7 +61,7 @@
// We need at least five characters to form a URL,
// hence, at minimum, five characters from the beginning of the line.
- r = ed.selection.getRng().cloneRange();
+ r = ed.selection.getRng(true).cloneRange();
if (r.startOffset < 5) {
// During testing, the caret is placed inbetween two text nodes.
// The previous text node contains the URL.
@@ -89,8 +89,11 @@
while (endContainer.nodeType != 3 && endContainer.firstChild)
endContainer = endContainer.firstChild;
- r.setStart(endContainer, 0);
- r.setEnd(endContainer, endContainer.nodeValue.length);
+ // Move range to text node
+ if (endContainer.nodeType == 3) {
+ r.setStart(endContainer, 0);
+ r.setEnd(endContainer, endContainer.nodeValue.length);
+ }
}
if (r.endOffset == 1)
@@ -104,8 +107,8 @@
do
{
// Move the selection one character backwards.
- r.setStart(endContainer, end - 2);
- r.setEnd(endContainer, end - 1);
+ r.setStart(endContainer, end >= 2 ? end - 2 : 0);
+ r.setEnd(endContainer, end >= 1 ? end - 1 : 0);
end -= 1;
// Loop until one of the following is found: a blank space, &nbsp;, delimeter, (end-2) >= 0
@@ -124,12 +127,20 @@
r.setEnd(endContainer, start);
}
+ // Exclude last . from word like "www.site.com."
+ var text = r.toString();
+ if (text.charAt(text.length - 1) == '.') {
+ r.setEnd(endContainer, start - 1);
+ }
+
text = r.toString();
- matches = text.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.)(.+)$/i);
+ matches = text.match(/^(https?:\/\/|ssh:\/\/|ftp:\/\/|file:\/|www\.|(?:mailto:)?[A-Z0-9._%+-]+@)(.+)$/i);
if (matches) {
if (matches[1] == 'www.') {
matches[1] = 'http://www.';
+ } else if (/@$/.test(matches[1]) && !/^mailto:/.test(matches[1])) {
+ matches[1] = 'mailto:' + matches[1];
}
bookmark = ed.selection.getBookmark();
@@ -137,6 +148,7 @@
ed.selection.setRng(r);
tinyMCE.execCommand('createlink',false, matches[1] + matches[2]);
ed.selection.moveToBookmark(bookmark);
+ ed.nodeChanged();
// TODO: Determine if this is still needed.
if (tinyMCE.isWebKit) {
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin.js
index 6c4ff0d5d..46d9dc3dd 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin.js
@@ -1 +1 @@
-(function(){tinymce.create("tinymce.plugins.AutoResizePlugin",{init:function(a,c){var d=this,e=0;if(a.getParam("fullscreen_is_enabled")){return}function b(){var i=a.getDoc(),f=i.body,k=i.documentElement,h=tinymce.DOM,j=d.autoresize_min_height,g;g=tinymce.isIE?f.scrollHeight:i.body.offsetHeight;if(g>d.autoresize_min_height){j=g}if(d.autoresize_max_height&&g>d.autoresize_max_height){j=d.autoresize_max_height;a.getBody().style.overflowY="auto"}else{a.getBody().style.overflowY="hidden"}if(j!==e){h.setStyle(h.get(a.id+"_ifr"),"height",j+"px");e=j}if(d.throbbing){a.setProgressState(false);a.setProgressState(true)}}d.editor=a;d.autoresize_min_height=parseInt(a.getParam("autoresize_min_height",a.getElement().offsetHeight));d.autoresize_max_height=parseInt(a.getParam("autoresize_max_height",0));a.onInit.add(function(f){f.dom.setStyle(f.getBody(),"paddingBottom",f.getParam("autoresize_bottom_margin",50)+"px")});a.onChange.add(b);a.onSetContent.add(b);a.onPaste.add(b);a.onKeyUp.add(b);a.onPostRender.add(b);if(a.getParam("autoresize_on_init",true)){a.onInit.add(function(g,f){g.setProgressState(true);d.throbbing=true;g.getBody().style.overflowY="hidden"});a.onLoadContent.add(function(g,f){b();setTimeout(function(){b();g.setProgressState(false);d.throbbing=false},1250)})}a.addCommand("mceAutoResize",b)},getInfo:function(){return{longname:"Auto Resize",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autoresize",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autoresize",tinymce.plugins.AutoResizePlugin)})(); \ No newline at end of file
+(function(){tinymce.create("tinymce.plugins.AutoResizePlugin",{init:function(a,c){var d=this,e=0;if(a.getParam("fullscreen_is_enabled")){return}function b(){var j,i=a.getDoc(),f=i.body,l=i.documentElement,h=tinymce.DOM,k=d.autoresize_min_height,g;g=tinymce.isIE?f.scrollHeight:(tinymce.isWebKit&&f.clientHeight==0?0:f.offsetHeight);if(g>d.autoresize_min_height){k=g}if(d.autoresize_max_height&&g>d.autoresize_max_height){k=d.autoresize_max_height;f.style.overflowY="auto";l.style.overflowY="auto"}else{f.style.overflowY="hidden";l.style.overflowY="hidden";f.scrollTop=0}if(k!==e){j=k-e;h.setStyle(h.get(a.id+"_ifr"),"height",k+"px");e=k;if(tinymce.isWebKit&&j<0){b()}}}d.editor=a;d.autoresize_min_height=parseInt(a.getParam("autoresize_min_height",a.getElement().offsetHeight));d.autoresize_max_height=parseInt(a.getParam("autoresize_max_height",0));a.onInit.add(function(f){f.dom.setStyle(f.getBody(),"paddingBottom",f.getParam("autoresize_bottom_margin",50)+"px")});a.onChange.add(b);a.onSetContent.add(b);a.onPaste.add(b);a.onKeyUp.add(b);a.onPostRender.add(b);if(a.getParam("autoresize_on_init",true)){a.onLoad.add(b);a.onLoadContent.add(b)}a.addCommand("mceAutoResize",b)},getInfo:function(){return{longname:"Auto Resize",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autoresize",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("autoresize",tinymce.plugins.AutoResizePlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin_src.js
index 7d113419d..7673bcff8 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autoresize/editor_plugin_src.js
@@ -11,7 +11,7 @@
(function() {
/**
* Auto Resize
- *
+ *
* This plugin automatically resizes the content area to fit its content height.
* It will retain a minimum height, which is the height of the content area when
* it's initialized.
@@ -35,10 +35,10 @@
* This method gets executed each time the editor needs to resize.
*/
function resize() {
- var d = ed.getDoc(), b = d.body, de = d.documentElement, DOM = tinymce.DOM, resizeHeight = t.autoresize_min_height, myHeight;
+ var deltaSize, d = ed.getDoc(), body = d.body, de = d.documentElement, DOM = tinymce.DOM, resizeHeight = t.autoresize_min_height, myHeight;
// Get height differently depending on the browser used
- myHeight = tinymce.isIE ? b.scrollHeight : d.body.offsetHeight;
+ myHeight = tinymce.isIE ? body.scrollHeight : (tinymce.isWebKit && body.clientHeight == 0 ? 0 : body.offsetHeight);
// Don't make it smaller than the minimum height
if (myHeight > t.autoresize_min_height)
@@ -47,30 +47,34 @@
// If a maximum height has been defined don't exceed this height
if (t.autoresize_max_height && myHeight > t.autoresize_max_height) {
resizeHeight = t.autoresize_max_height;
- ed.getBody().style.overflowY = "auto";
- } else
- ed.getBody().style.overflowY = "hidden";
+ body.style.overflowY = "auto";
+ de.style.overflowY = "auto"; // Old IE
+ } else {
+ body.style.overflowY = "hidden";
+ de.style.overflowY = "hidden"; // Old IE
+ body.scrollTop = 0;
+ }
// Resize content element
if (resizeHeight !== oldSize) {
+ deltaSize = resizeHeight - oldSize;
DOM.setStyle(DOM.get(ed.id + '_ifr'), 'height', resizeHeight + 'px');
oldSize = resizeHeight;
- }
- // if we're throbbing, we'll re-throb to match the new size
- if (t.throbbing) {
- ed.setProgressState(false);
- ed.setProgressState(true);
+ // WebKit doesn't decrease the size of the body element until the iframe gets resized
+ // So we need to continue to resize the iframe down until the size gets fixed
+ if (tinymce.isWebKit && deltaSize < 0)
+ resize();
}
};
t.editor = ed;
// Define minimum height
- t.autoresize_min_height = parseInt( ed.getParam('autoresize_min_height', ed.getElement().offsetHeight) );
+ t.autoresize_min_height = parseInt(ed.getParam('autoresize_min_height', ed.getElement().offsetHeight));
- // Define maximum height
- t.autoresize_max_height = parseInt( ed.getParam('autoresize_max_height', 0) );
+ // Define maximum height
+ t.autoresize_max_height = parseInt(ed.getParam('autoresize_max_height', 0));
// Add padding at the bottom for better UX
ed.onInit.add(function(ed){
@@ -85,30 +89,8 @@
ed.onPostRender.add(resize);
if (ed.getParam('autoresize_on_init', true)) {
- // Things to do when the editor is ready
- ed.onInit.add(function(ed, l) {
- // Show throbber until content area is resized properly
- ed.setProgressState(true);
- t.throbbing = true;
-
- // Hide scrollbars
- ed.getBody().style.overflowY = "hidden";
- });
-
- ed.onLoadContent.add(function(ed, l) {
- resize();
-
- // Because the content area resizes when its content CSS loads,
- // and we can't easily add a listener to its onload event,
- // we'll just trigger a resize after a short loading period
- setTimeout(function() {
- resize();
-
- // Disable throbber
- ed.setProgressState(false);
- t.throbbing = false;
- }, 1250);
- });
+ ed.onLoad.add(resize);
+ ed.onLoadContent.add(resize);
}
// Register the command so that it can be invoked by using tinyMCE.activeEditor.execCommand('mceExample');
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin.js
index f7d057600..6da98ff33 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin.js
@@ -1 +1 @@
-(function(e){var c="autosave",g="restoredraft",b=true,f,d,a=e.util.Dispatcher;e.create("tinymce.plugins.AutoSave",{init:function(i,j){var h=this,l=i.settings;h.editor=i;function k(n){var m={s:1000,m:60000};n=/^(\d+)([ms]?)$/.exec(""+n);return(n[2]?m[n[2]]:1)*parseInt(n)}e.each({ask_before_unload:b,interval:"30s",retention:"20m",minlength:50},function(n,m){m=c+"_"+m;if(l[m]===f){l[m]=n}});l.autosave_interval=k(l.autosave_interval);l.autosave_retention=k(l.autosave_retention);i.addButton(g,{title:c+".restore_content",onclick:function(){if(i.getContent({draft:true}).replace(/\s|&nbsp;|<\/?p[^>]*>|<br[^>]*>/gi,"").length>0){i.windowManager.confirm(c+".warning_message",function(m){if(m){h.restoreDraft()}})}else{h.restoreDraft()}}});i.onNodeChange.add(function(){var m=i.controlManager;if(m.get(g)){m.setDisabled(g,!h.hasDraft())}});i.onInit.add(function(){if(i.controlManager.get(g)){h.setupStorage(i);setInterval(function(){h.storeDraft();i.nodeChanged()},l.autosave_interval)}});h.onStoreDraft=new a(h);h.onRestoreDraft=new a(h);h.onRemoveDraft=new a(h);if(!d){window.onbeforeunload=e.plugins.AutoSave._beforeUnloadHandler;d=b}},getInfo:function(){return{longname:"Auto save",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autosave",version:e.majorVersion+"."+e.minorVersion}},getExpDate:function(){return new Date(new Date().getTime()+this.editor.settings.autosave_retention).toUTCString()},setupStorage:function(i){var h=this,k=c+"_test",j="OK";h.key=c+i.id;e.each([function(){if(localStorage){localStorage.setItem(k,j);if(localStorage.getItem(k)===j){localStorage.removeItem(k);return localStorage}}},function(){if(sessionStorage){sessionStorage.setItem(k,j);if(sessionStorage.getItem(k)===j){sessionStorage.removeItem(k);return sessionStorage}}},function(){if(e.isIE){i.getElement().style.behavior="url('#default#userData')";return{autoExpires:b,setItem:function(l,n){var m=i.getElement();m.setAttribute(l,n);m.expires=h.getExpDate();try{m.save("TinyMCE")}catch(o){}},getItem:function(l){var m=i.getElement();try{m.load("TinyMCE");return m.getAttribute(l)}catch(n){return null}},removeItem:function(l){i.getElement().removeAttribute(l)}}}},],function(l){try{h.storage=l();if(h.storage){return false}}catch(m){}})},storeDraft:function(){var i=this,l=i.storage,j=i.editor,h,k;if(l){if(!l.getItem(i.key)&&!j.isDirty()){return}k=j.getContent({draft:true});if(k.length>j.settings.autosave_minlength){h=i.getExpDate();if(!i.storage.autoExpires){i.storage.setItem(i.key+"_expires",h)}i.storage.setItem(i.key,k);i.onStoreDraft.dispatch(i,{expires:h,content:k})}}},restoreDraft:function(){var h=this,j=h.storage,i;if(j){i=j.getItem(h.key);if(i){h.editor.setContent(i);h.onRestoreDraft.dispatch(h,{content:i})}}},hasDraft:function(){var h=this,k=h.storage,i,j;if(k){j=!!k.getItem(h.key);if(j){if(!h.storage.autoExpires){i=new Date(k.getItem(h.key+"_expires"));if(new Date().getTime()<i.getTime()){return b}h.removeDraft()}else{return b}}}return false},removeDraft:function(){var h=this,k=h.storage,i=h.key,j;if(k){j=k.getItem(i);k.removeItem(i);k.removeItem(i+"_expires");if(j){h.onRemoveDraft.dispatch(h,{content:j})}}},"static":{_beforeUnloadHandler:function(h){var i;e.each(tinyMCE.editors,function(j){if(j.plugins.autosave){j.plugins.autosave.storeDraft()}if(j.getParam("fullscreen_is_enabled")){return}if(!i&&j.isDirty()&&j.getParam("autosave_ask_before_unload")){i=j.getLang("autosave.unload_msg")}});return i}}});e.PluginManager.add("autosave",e.plugins.AutoSave)})(tinymce); \ No newline at end of file
+(function(e){var c="autosave",g="restoredraft",b=true,f,d,a=e.util.Dispatcher;e.create("tinymce.plugins.AutoSave",{init:function(i,j){var h=this,l=i.settings;h.editor=i;function k(n){var m={s:1000,m:60000};n=/^(\d+)([ms]?)$/.exec(""+n);return(n[2]?m[n[2]]:1)*parseInt(n)}e.each({ask_before_unload:b,interval:"30s",retention:"20m",minlength:50},function(n,m){m=c+"_"+m;if(l[m]===f){l[m]=n}});l.autosave_interval=k(l.autosave_interval);l.autosave_retention=k(l.autosave_retention);i.addButton(g,{title:c+".restore_content",onclick:function(){if(i.getContent({draft:true}).replace(/\s|&nbsp;|<\/?p[^>]*>|<br[^>]*>/gi,"").length>0){i.windowManager.confirm(c+".warning_message",function(m){if(m){h.restoreDraft()}})}else{h.restoreDraft()}}});i.onNodeChange.add(function(){var m=i.controlManager;if(m.get(g)){m.setDisabled(g,!h.hasDraft())}});i.onInit.add(function(){if(i.controlManager.get(g)){h.setupStorage(i);setInterval(function(){if(!i.removed){h.storeDraft();i.nodeChanged()}},l.autosave_interval)}});h.onStoreDraft=new a(h);h.onRestoreDraft=new a(h);h.onRemoveDraft=new a(h);if(!d){window.onbeforeunload=e.plugins.AutoSave._beforeUnloadHandler;d=b}},getInfo:function(){return{longname:"Auto save",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/autosave",version:e.majorVersion+"."+e.minorVersion}},getExpDate:function(){return new Date(new Date().getTime()+this.editor.settings.autosave_retention).toUTCString()},setupStorage:function(i){var h=this,k=c+"_test",j="OK";h.key=c+i.id;e.each([function(){if(localStorage){localStorage.setItem(k,j);if(localStorage.getItem(k)===j){localStorage.removeItem(k);return localStorage}}},function(){if(sessionStorage){sessionStorage.setItem(k,j);if(sessionStorage.getItem(k)===j){sessionStorage.removeItem(k);return sessionStorage}}},function(){if(e.isIE){i.getElement().style.behavior="url('#default#userData')";return{autoExpires:b,setItem:function(l,n){var m=i.getElement();m.setAttribute(l,n);m.expires=h.getExpDate();try{m.save("TinyMCE")}catch(o){}},getItem:function(l){var m=i.getElement();try{m.load("TinyMCE");return m.getAttribute(l)}catch(n){return null}},removeItem:function(l){i.getElement().removeAttribute(l)}}}},],function(l){try{h.storage=l();if(h.storage){return false}}catch(m){}})},storeDraft:function(){var i=this,l=i.storage,j=i.editor,h,k;if(l){if(!l.getItem(i.key)&&!j.isDirty()){return}k=j.getContent({draft:true});if(k.length>j.settings.autosave_minlength){h=i.getExpDate();if(!i.storage.autoExpires){i.storage.setItem(i.key+"_expires",h)}i.storage.setItem(i.key,k);i.onStoreDraft.dispatch(i,{expires:h,content:k})}}},restoreDraft:function(){var h=this,j=h.storage,i;if(j){i=j.getItem(h.key);if(i){h.editor.setContent(i);h.onRestoreDraft.dispatch(h,{content:i})}}},hasDraft:function(){var h=this,k=h.storage,i,j;if(k){j=!!k.getItem(h.key);if(j){if(!h.storage.autoExpires){i=new Date(k.getItem(h.key+"_expires"));if(new Date().getTime()<i.getTime()){return b}h.removeDraft()}else{return b}}}return false},removeDraft:function(){var h=this,k=h.storage,i=h.key,j;if(k){j=k.getItem(i);k.removeItem(i);k.removeItem(i+"_expires");if(j){h.onRemoveDraft.dispatch(h,{content:j})}}},"static":{_beforeUnloadHandler:function(h){var i;e.each(tinyMCE.editors,function(j){if(j.plugins.autosave){j.plugins.autosave.storeDraft()}if(j.getParam("fullscreen_is_enabled")){return}if(!i&&j.isDirty()&&j.getParam("autosave_ask_before_unload")){i=j.getLang("autosave.unload_msg")}});return i}}});e.PluginManager.add("autosave",e.plugins.AutoSave)})(tinymce); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin_src.js
index 8311483f9..8b308f5aa 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/editor_plugin_src.js
@@ -136,8 +136,10 @@
// Auto save contents each interval time
setInterval(function() {
- self.storeDraft();
- ed.nodeChanged();
+ if (!ed.removed) {
+ self.storeDraft();
+ ed.nodeChanged();
+ }
}, settings.autosave_interval);
}
});
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/langs/en.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/langs/en.js
deleted file mode 100644
index fce6bd3e1..000000000
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/autosave/langs/en.js
+++ /dev/null
@@ -1,4 +0,0 @@
-tinyMCE.addI18n('en.autosave',{
-restore_content: "Restore auto-saved content",
-warning_message: "If you restore the saved content, you will lose all the content that is currently in the editor.\n\nAre you sure you want to restore the saved content?"
-}); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin.js
index af7ae5445..2ed042c3a 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin.js
@@ -1 +1 @@
-(function(){var a=tinymce.dom.Event,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.ContextMenu",{init:function(e){var h=this,f,d,i;h.editor=e;d=e.settings.contextmenu_never_use_native;h.onContextMenu=new tinymce.util.Dispatcher(this);f=e.onContextMenu.add(function(j,k){if((i!==0?i:k.ctrlKey)&&!d){return}a.cancel(k);if(k.target.nodeName=="IMG"){j.selection.select(k.target)}h._getMenu(j).showMenu(k.clientX||k.pageX,k.clientY||k.pageY);a.add(j.getDoc(),"click",function(l){g(j,l)});j.nodeChanged()});e.onRemove.add(function(){if(h._menu){h._menu.removeAll()}});function g(j,k){i=0;if(k&&k.button==2){i=k.ctrlKey;return}if(h._menu){h._menu.removeAll();h._menu.destroy();a.remove(j.getDoc(),"click",g)}}e.onMouseDown.add(g);e.onKeyDown.add(g);e.onKeyDown.add(function(j,k){if(k.shiftKey&&!k.ctrlKey&&!k.altKey&&k.keyCode===121){a.cancel(k);f(j,k)}})},getInfo:function(){return{longname:"Contextmenu",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/contextmenu",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_getMenu:function(e){var g=this,d=g._menu,j=e.selection,f=j.isCollapsed(),h=j.getNode()||e.getBody(),i,k;if(d){d.removeAll();d.destroy()}k=b.getPos(e.getContentAreaContainer());d=e.controlManager.createDropMenu("contextmenu",{offset_x:k.x+e.getParam("contextmenu_offset_x",0),offset_y:k.y+e.getParam("contextmenu_offset_y",0),constrain:1,keyboard_focus:true});g._menu=d;d.add({title:"advanced.cut_desc",icon:"cut",cmd:"Cut"}).setDisabled(f);d.add({title:"advanced.copy_desc",icon:"copy",cmd:"Copy"}).setDisabled(f);d.add({title:"advanced.paste_desc",icon:"paste",cmd:"Paste"});if((h.nodeName=="A"&&!e.dom.getAttrib(h,"name"))||!f){d.addSeparator();d.add({title:"advanced.link_desc",icon:"link",cmd:e.plugins.advlink?"mceAdvLink":"mceLink",ui:true});d.add({title:"advanced.unlink_desc",icon:"unlink",cmd:"UnLink"})}d.addSeparator();d.add({title:"advanced.image_desc",icon:"image",cmd:e.plugins.advimage?"mceAdvImage":"mceImage",ui:true});d.addSeparator();i=d.addMenu({title:"contextmenu.align"});i.add({title:"contextmenu.left",icon:"justifyleft",cmd:"JustifyLeft"});i.add({title:"contextmenu.center",icon:"justifycenter",cmd:"JustifyCenter"});i.add({title:"contextmenu.right",icon:"justifyright",cmd:"JustifyRight"});i.add({title:"contextmenu.full",icon:"justifyfull",cmd:"JustifyFull"});g.onContextMenu.dispatch(g,d,h,f);return d}});tinymce.PluginManager.add("contextmenu",tinymce.plugins.ContextMenu)})(); \ No newline at end of file
+(function(){var a=tinymce.dom.Event,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.ContextMenu",{init:function(f){var i=this,g,d,j,e;i.editor=f;d=f.settings.contextmenu_never_use_native;i.onContextMenu=new tinymce.util.Dispatcher(this);e=function(k){h(f,k)};g=f.onContextMenu.add(function(k,l){if((j!==0?j:l.ctrlKey)&&!d){return}a.cancel(l);if(l.target.nodeName=="IMG"){k.selection.select(l.target)}i._getMenu(k).showMenu(l.clientX||l.pageX,l.clientY||l.pageY);a.add(k.getDoc(),"click",e);k.nodeChanged()});f.onRemove.add(function(){if(i._menu){i._menu.removeAll()}});function h(k,l){j=0;if(l&&l.button==2){j=l.ctrlKey;return}if(i._menu){i._menu.removeAll();i._menu.destroy();a.remove(k.getDoc(),"click",e);i._menu=null}}f.onMouseDown.add(h);f.onKeyDown.add(h);f.onKeyDown.add(function(k,l){if(l.shiftKey&&!l.ctrlKey&&!l.altKey&&l.keyCode===121){a.cancel(l);g(k,l)}})},getInfo:function(){return{longname:"Contextmenu",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/contextmenu",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_getMenu:function(e){var g=this,d=g._menu,j=e.selection,f=j.isCollapsed(),h=j.getNode()||e.getBody(),i,k;if(d){d.removeAll();d.destroy()}k=b.getPos(e.getContentAreaContainer());d=e.controlManager.createDropMenu("contextmenu",{offset_x:k.x+e.getParam("contextmenu_offset_x",0),offset_y:k.y+e.getParam("contextmenu_offset_y",0),constrain:1,keyboard_focus:true});g._menu=d;d.add({title:"advanced.cut_desc",icon:"cut",cmd:"Cut"}).setDisabled(f);d.add({title:"advanced.copy_desc",icon:"copy",cmd:"Copy"}).setDisabled(f);d.add({title:"advanced.paste_desc",icon:"paste",cmd:"Paste"});if((h.nodeName=="A"&&!e.dom.getAttrib(h,"name"))||!f){d.addSeparator();d.add({title:"advanced.link_desc",icon:"link",cmd:e.plugins.advlink?"mceAdvLink":"mceLink",ui:true});d.add({title:"advanced.unlink_desc",icon:"unlink",cmd:"UnLink"})}d.addSeparator();d.add({title:"advanced.image_desc",icon:"image",cmd:e.plugins.advimage?"mceAdvImage":"mceImage",ui:true});d.addSeparator();i=d.addMenu({title:"contextmenu.align"});i.add({title:"contextmenu.left",icon:"justifyleft",cmd:"JustifyLeft"});i.add({title:"contextmenu.center",icon:"justifycenter",cmd:"JustifyCenter"});i.add({title:"contextmenu.right",icon:"justifyright",cmd:"JustifyRight"});i.add({title:"contextmenu.full",icon:"justifyfull",cmd:"JustifyFull"});g.onContextMenu.dispatch(g,d,h,f);return d}});tinymce.PluginManager.add("contextmenu",tinymce.plugins.ContextMenu)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin_src.js
index 956fbea99..48b0fff99 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/contextmenu/editor_plugin_src.js
@@ -27,7 +27,7 @@
* @param {string} url Absolute URL to where the plugin is located.
*/
init : function(ed) {
- var t = this, showMenu, contextmenuNeverUseNative, realCtrlKey;
+ var t = this, showMenu, contextmenuNeverUseNative, realCtrlKey, hideMenu;
t.editor = ed;
@@ -42,6 +42,10 @@
*/
t.onContextMenu = new tinymce.util.Dispatcher(this);
+ hideMenu = function(e) {
+ hide(ed, e);
+ };
+
showMenu = ed.onContextMenu.add(function(ed, e) {
// Block TinyMCE menu on ctrlKey and work around Safari issue
if ((realCtrlKey !== 0 ? realCtrlKey : e.ctrlKey) && !contextmenuNeverUseNative)
@@ -54,13 +58,11 @@
ed.selection.select(e.target);
t._getMenu(ed).showMenu(e.clientX || e.pageX, e.clientY || e.pageY);
- Event.add(ed.getDoc(), 'click', function(e) {
- hide(ed, e);
- });
+ Event.add(ed.getDoc(), 'click', hideMenu);
ed.nodeChanged();
});
-
+
ed.onRemove.add(function() {
if (t._menu)
t._menu.removeAll();
@@ -78,8 +80,9 @@
if (t._menu) {
t._menu.removeAll();
- t._menu.destroy();
- Event.remove(ed.getDoc(), 'click', hide);
+ t._menu.destroy();
+ Event.remove(ed.getDoc(), 'click', hideMenu);
+ t._menu = null;
}
};
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin.js
index bce8e7399..90847e78e 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin.js
@@ -1 +1 @@
-(function(){tinymce.create("tinymce.plugins.Directionality",{init:function(a,b){var c=this;c.editor=a;a.addCommand("mceDirectionLTR",function(){var d=a.dom.getParent(a.selection.getNode(),a.dom.isBlock);if(d){if(a.dom.getAttrib(d,"dir")!="ltr"){a.dom.setAttrib(d,"dir","ltr")}else{a.dom.setAttrib(d,"dir","")}}a.nodeChanged()});a.addCommand("mceDirectionRTL",function(){var d=a.dom.getParent(a.selection.getNode(),a.dom.isBlock);if(d){if(a.dom.getAttrib(d,"dir")!="rtl"){a.dom.setAttrib(d,"dir","rtl")}else{a.dom.setAttrib(d,"dir","")}}a.nodeChanged()});a.addButton("ltr",{title:"directionality.ltr_desc",cmd:"mceDirectionLTR"});a.addButton("rtl",{title:"directionality.rtl_desc",cmd:"mceDirectionRTL"});a.onNodeChange.add(c._nodeChange,c)},getInfo:function(){return{longname:"Directionality",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/directionality",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(b,a,e){var d=b.dom,c;e=d.getParent(e,d.isBlock);if(!e){a.setDisabled("ltr",1);a.setDisabled("rtl",1);return}c=d.getAttrib(e,"dir");a.setActive("ltr",c=="ltr");a.setDisabled("ltr",0);a.setActive("rtl",c=="rtl");a.setDisabled("rtl",0)}});tinymce.PluginManager.add("directionality",tinymce.plugins.Directionality)})(); \ No newline at end of file
+(function(){tinymce.create("tinymce.plugins.Directionality",{init:function(b,c){var d=this;d.editor=b;function a(e){var h=b.dom,g,f=b.selection.getSelectedBlocks();if(f.length){g=h.getAttrib(f[0],"dir");tinymce.each(f,function(i){if(!h.getParent(i.parentNode,"*[dir='"+e+"']",h.getRoot())){if(g!=e){h.setAttrib(i,"dir",e)}else{h.setAttrib(i,"dir",null)}}});b.nodeChanged()}}b.addCommand("mceDirectionLTR",function(){a("ltr")});b.addCommand("mceDirectionRTL",function(){a("rtl")});b.addButton("ltr",{title:"directionality.ltr_desc",cmd:"mceDirectionLTR"});b.addButton("rtl",{title:"directionality.rtl_desc",cmd:"mceDirectionRTL"});b.onNodeChange.add(d._nodeChange,d)},getInfo:function(){return{longname:"Directionality",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/directionality",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_nodeChange:function(b,a,e){var d=b.dom,c;e=d.getParent(e,d.isBlock);if(!e){a.setDisabled("ltr",1);a.setDisabled("rtl",1);return}c=d.getAttrib(e,"dir");a.setActive("ltr",c=="ltr");a.setDisabled("ltr",0);a.setActive("rtl",c=="rtl");a.setDisabled("rtl",0)}});tinymce.PluginManager.add("directionality",tinymce.plugins.Directionality)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin_src.js
index 4444959bf..b13401412 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/directionality/editor_plugin_src.js
@@ -15,30 +15,33 @@
t.editor = ed;
- ed.addCommand('mceDirectionLTR', function() {
- var e = ed.dom.getParent(ed.selection.getNode(), ed.dom.isBlock);
-
- if (e) {
- if (ed.dom.getAttrib(e, "dir") != "ltr")
- ed.dom.setAttrib(e, "dir", "ltr");
- else
- ed.dom.setAttrib(e, "dir", "");
+ function setDir(dir) {
+ var dom = ed.dom, curDir, blocks = ed.selection.getSelectedBlocks();
+
+ if (blocks.length) {
+ curDir = dom.getAttrib(blocks[0], "dir");
+
+ tinymce.each(blocks, function(block) {
+ // Add dir to block if the parent block doesn't already have that dir
+ if (!dom.getParent(block.parentNode, "*[dir='" + dir + "']", dom.getRoot())) {
+ if (curDir != dir) {
+ dom.setAttrib(block, "dir", dir);
+ } else {
+ dom.setAttrib(block, "dir", null);
+ }
+ }
+ });
+
+ ed.nodeChanged();
}
+ }
- ed.nodeChanged();
+ ed.addCommand('mceDirectionLTR', function() {
+ setDir("ltr");
});
ed.addCommand('mceDirectionRTL', function() {
- var e = ed.dom.getParent(ed.selection.getNode(), ed.dom.isBlock);
-
- if (e) {
- if (ed.dom.getAttrib(e, "dir") != "rtl")
- ed.dom.setAttrib(e, "dir", "rtl");
- else
- ed.dom.setAttrib(e, "dir", "");
- }
-
- ed.nodeChanged();
+ setDir("rtl");
});
ed.addButton('ltr', {title : 'directionality.ltr_desc', cmd : 'mceDirectionLTR'});
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/emotions/emotions.htm b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/emotions/emotions.htm
index 7d9a68cfe..101355654 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/emotions/emotions.htm
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/emotions/emotions.htm
@@ -30,7 +30,7 @@
<td><a class="emoticon_link" role="button" title="{#emotions_dlg.surprised}. {#emotions_dlg.usage}" href="javascript:EmotionsDialog.insert('smiley-surprised.gif','emotions_dlg.surprised');"><img src="img/smiley-surprised.gif" width="18" height="18" border="0" alt="{#emotions_dlg.surprised}. {#emotions_dlg.usage}" /></a></td>
</tr>
<tr>
- <td><a class="emoticon_link" role="button" title="{#emotions_dlg.tongue-out}. {#emotions_dlg.usage}" href="javascript:EmotionsDialog.insert('smiley-tongue-out.gif','emotions_dlg.tongue_out');"><img src="img/smiley-tongue-out.gif" width="18" height="18" border="0" alt="{#emotions_dlg.tongue-out}. {#emotions_dlg.usage}" /></a></td>
+ <td><a class="emoticon_link" role="button" title="{#emotions_dlg.tongue_out}. {#emotions_dlg.usage}" href="javascript:EmotionsDialog.insert('smiley-tongue-out.gif','emotions_dlg.tongue_out');"><img src="img/smiley-tongue-out.gif" width="18" height="18" border="0" alt="{#emotions_dlg.tongue-out}. {#emotions_dlg.usage}" /></a></td>
<td><a class="emoticon_link" role="button" title="{#emotions_dlg.undecided}. {#emotions_dlg.usage}" href="javascript:EmotionsDialog.insert('smiley-undecided.gif','emotions_dlg.undecided');"><img src="img/smiley-undecided.gif" width="18" height="18" border="0" alt="{#emotions_dlg.undecided}. {#emotions_dlg.usage}" /></a></td>
<td><a class="emoticon_link" role="button" title="{#emotions_dlg.wink}. {#emotions_dlg.usage}" href="javascript:EmotionsDialog.insert('smiley-wink.gif','emotions_dlg.wink');"><img src="img/smiley-wink.gif" width="18" height="18" border="0" alt="{#emotions_dlg.wink}. {#emotions_dlg.usage}" /></a></td>
<td><a class="emoticon_link" role="button" title="{#emotions_dlg.yell}. {#emotions_dlg.usage}" href="javascript:EmotionsDialog.insert('smiley-yell.gif','emotions_dlg.yell');"><img src="img/smiley-yell.gif" width="18" height="18" border="0" alt="{#emotions_dlg.yell}. {#emotions_dlg.usage}" /></a></td>
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin.js
index a6456f89d..a2eb03483 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin.js
@@ -1 +1 @@
-(function(){var a=tinymce.DOM;tinymce.create("tinymce.plugins.FullScreenPlugin",{init:function(d,e){var f=this,g={},c,b;f.editor=d;d.addCommand("mceFullScreen",function(){var i,j=a.doc.documentElement;if(d.getParam("fullscreen_is_enabled")){if(d.getParam("fullscreen_new_window")){closeFullscreen()}else{a.win.setTimeout(function(){tinymce.dom.Event.remove(a.win,"resize",f.resizeFunc);tinyMCE.get(d.getParam("fullscreen_editor_id")).setContent(d.getContent());tinyMCE.remove(d);a.remove("mce_fullscreen_container");j.style.overflow=d.getParam("fullscreen_html_overflow");a.setStyle(a.doc.body,"overflow",d.getParam("fullscreen_overflow"));a.win.scrollTo(d.getParam("fullscreen_scrollx"),d.getParam("fullscreen_scrolly"));tinyMCE.settings=tinyMCE.oldSettings},10)}return}if(d.getParam("fullscreen_new_window")){i=a.win.open(e+"/fullscreen.htm","mceFullScreenPopup","fullscreen=yes,menubar=no,toolbar=no,scrollbars=no,resizable=yes,left=0,top=0,width="+screen.availWidth+",height="+screen.availHeight);try{i.resizeTo(screen.availWidth,screen.availHeight)}catch(h){}}else{tinyMCE.oldSettings=tinyMCE.settings;g.fullscreen_overflow=a.getStyle(a.doc.body,"overflow",1)||"auto";g.fullscreen_html_overflow=a.getStyle(j,"overflow",1);c=a.getViewPort();g.fullscreen_scrollx=c.x;g.fullscreen_scrolly=c.y;if(tinymce.isOpera&&g.fullscreen_overflow=="visible"){g.fullscreen_overflow="auto"}if(tinymce.isIE&&g.fullscreen_overflow=="scroll"){g.fullscreen_overflow="auto"}if(tinymce.isIE&&(g.fullscreen_html_overflow=="visible"||g.fullscreen_html_overflow=="scroll")){g.fullscreen_html_overflow="auto"}if(g.fullscreen_overflow=="0px"){g.fullscreen_overflow=""}a.setStyle(a.doc.body,"overflow","hidden");j.style.overflow="hidden";c=a.getViewPort();a.win.scrollTo(0,0);if(tinymce.isIE){c.h-=1}if(tinymce.isIE6){b="absolute;top:"+c.y}else{b="fixed;top:0"}n=a.add(a.doc.body,"div",{id:"mce_fullscreen_container",style:"position:"+b+";left:0;width:"+c.w+"px;height:"+c.h+"px;z-index:200000;"});a.add(n,"div",{id:"mce_fullscreen"});tinymce.each(d.settings,function(k,l){g[l]=k});g.id="mce_fullscreen";g.width=n.clientWidth;g.height=n.clientHeight-15;g.fullscreen_is_enabled=true;g.fullscreen_editor_id=d.id;g.theme_advanced_resizing=false;g.save_onsavecallback=function(){d.setContent(tinyMCE.get(g.id).getContent());d.execCommand("mceSave")};tinymce.each(d.getParam("fullscreen_settings"),function(m,l){g[l]=m});if(g.theme_advanced_toolbar_location==="external"){g.theme_advanced_toolbar_location="top"}f.fullscreenEditor=new tinymce.Editor("mce_fullscreen",g);f.fullscreenEditor.onInit.add(function(){f.fullscreenEditor.setContent(d.getContent());f.fullscreenEditor.focus()});f.fullscreenEditor.render();f.fullscreenElement=new tinymce.dom.Element("mce_fullscreen_container");f.fullscreenElement.update();f.resizeFunc=tinymce.dom.Event.add(a.win,"resize",function(){var o=tinymce.DOM.getViewPort(),l=f.fullscreenEditor,k,m;k=l.dom.getSize(l.getContainer().firstChild);m=l.dom.getSize(l.getContainer().getElementsByTagName("iframe")[0]);l.theme.resizeTo(o.w-k.w+m.w,o.h-k.h+m.h)})}});d.addButton("fullscreen",{title:"fullscreen.desc",cmd:"mceFullScreen"});d.onNodeChange.add(function(i,h){h.setActive("fullscreen",i.getParam("fullscreen_is_enabled"))})},getInfo:function(){return{longname:"Fullscreen",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullscreen",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("fullscreen",tinymce.plugins.FullScreenPlugin)})(); \ No newline at end of file
+(function(){var a=tinymce.DOM;tinymce.create("tinymce.plugins.FullScreenPlugin",{init:function(d,e){var f=this,g={},c,b;f.editor=d;d.addCommand("mceFullScreen",function(){var i,j=a.doc.documentElement;if(d.getParam("fullscreen_is_enabled")){if(d.getParam("fullscreen_new_window")){closeFullscreen()}else{a.win.setTimeout(function(){tinymce.dom.Event.remove(a.win,"resize",f.resizeFunc);tinyMCE.get(d.getParam("fullscreen_editor_id")).setContent(d.getContent());tinyMCE.remove(d);a.remove("mce_fullscreen_container");j.style.overflow=d.getParam("fullscreen_html_overflow");a.setStyle(a.doc.body,"overflow",d.getParam("fullscreen_overflow"));a.win.scrollTo(d.getParam("fullscreen_scrollx"),d.getParam("fullscreen_scrolly"));tinyMCE.settings=tinyMCE.oldSettings},10)}return}if(d.getParam("fullscreen_new_window")){i=a.win.open(e+"/fullscreen.htm","mceFullScreenPopup","fullscreen=yes,menubar=no,toolbar=no,scrollbars=no,resizable=yes,left=0,top=0,width="+screen.availWidth+",height="+screen.availHeight);try{i.resizeTo(screen.availWidth,screen.availHeight)}catch(h){}}else{tinyMCE.oldSettings=tinyMCE.settings;g.fullscreen_overflow=a.getStyle(a.doc.body,"overflow",1)||"auto";g.fullscreen_html_overflow=a.getStyle(j,"overflow",1);c=a.getViewPort();g.fullscreen_scrollx=c.x;g.fullscreen_scrolly=c.y;if(tinymce.isOpera&&g.fullscreen_overflow=="visible"){g.fullscreen_overflow="auto"}if(tinymce.isIE&&g.fullscreen_overflow=="scroll"){g.fullscreen_overflow="auto"}if(tinymce.isIE&&(g.fullscreen_html_overflow=="visible"||g.fullscreen_html_overflow=="scroll")){g.fullscreen_html_overflow="auto"}if(g.fullscreen_overflow=="0px"){g.fullscreen_overflow=""}a.setStyle(a.doc.body,"overflow","hidden");j.style.overflow="hidden";c=a.getViewPort();a.win.scrollTo(0,0);if(tinymce.isIE){c.h-=1}if(tinymce.isIE6||document.compatMode=="BackCompat"){b="absolute;top:"+c.y}else{b="fixed;top:0"}n=a.add(a.doc.body,"div",{id:"mce_fullscreen_container",style:"position:"+b+";left:0;width:"+c.w+"px;height:"+c.h+"px;z-index:200000;"});a.add(n,"div",{id:"mce_fullscreen"});tinymce.each(d.settings,function(k,l){g[l]=k});g.id="mce_fullscreen";g.width=n.clientWidth;g.height=n.clientHeight-15;g.fullscreen_is_enabled=true;g.fullscreen_editor_id=d.id;g.theme_advanced_resizing=false;g.save_onsavecallback=function(){d.setContent(tinyMCE.get(g.id).getContent());d.execCommand("mceSave")};tinymce.each(d.getParam("fullscreen_settings"),function(m,l){g[l]=m});if(g.theme_advanced_toolbar_location==="external"){g.theme_advanced_toolbar_location="top"}f.fullscreenEditor=new tinymce.Editor("mce_fullscreen",g);f.fullscreenEditor.onInit.add(function(){f.fullscreenEditor.setContent(d.getContent());f.fullscreenEditor.focus()});f.fullscreenEditor.render();f.fullscreenElement=new tinymce.dom.Element("mce_fullscreen_container");f.fullscreenElement.update();f.resizeFunc=tinymce.dom.Event.add(a.win,"resize",function(){var o=tinymce.DOM.getViewPort(),l=f.fullscreenEditor,k,m;k=l.dom.getSize(l.getContainer().getElementsByTagName("table")[0]);m=l.dom.getSize(l.getContainer().getElementsByTagName("iframe")[0]);l.theme.resizeTo(o.w-k.w+m.w,o.h-k.h+m.h)})}});d.addButton("fullscreen",{title:"fullscreen.desc",cmd:"mceFullScreen"});d.onNodeChange.add(function(i,h){h.setActive("fullscreen",i.getParam("fullscreen_is_enabled"))})},getInfo:function(){return{longname:"Fullscreen",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/fullscreen",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("fullscreen",tinymce.plugins.FullScreenPlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin_src.js
index afa4f9b46..524b487aa 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/fullscreen/editor_plugin_src.js
@@ -65,7 +65,7 @@
// Fixes an IE bug where the scrollbars doesn't reappear
if (tinymce.isIE && (s.fullscreen_html_overflow == 'visible' || s.fullscreen_html_overflow == 'scroll'))
- s.fullscreen_html_overflow = 'auto';
+ s.fullscreen_html_overflow = 'auto';
if (s.fullscreen_overflow == '0px')
s.fullscreen_overflow = '';
@@ -79,13 +79,13 @@
vp.h -= 1;
// Use fixed position if it exists
- if (tinymce.isIE6)
+ if (tinymce.isIE6 || document.compatMode == 'BackCompat')
posCss = 'absolute;top:' + vp.y;
else
posCss = 'fixed;top:0';
n = DOM.add(DOM.doc.body, 'div', {
- id : 'mce_fullscreen_container',
+ id : 'mce_fullscreen_container',
style : 'position:' + posCss + ';left:0;width:' + vp.w + 'px;height:' + vp.h + 'px;z-index:200000;'});
DOM.add(n, 'div', {id : 'mce_fullscreen'});
@@ -127,7 +127,7 @@
var vp = tinymce.DOM.getViewPort(), fed = t.fullscreenEditor, outerSize, innerSize;
// Get outer/inner size to get a delta size that can be used to calc the new iframe size
- outerSize = fed.dom.getSize(fed.getContainer().firstChild);
+ outerSize = fed.dom.getSize(fed.getContainer().getElementsByTagName('table')[0]);
innerSize = fed.dom.getSize(fed.getContainer().getElementsByTagName('iframe')[0]);
fed.theme.resizeTo(vp.w - outerSize.w + innerSize.w, vp.h - outerSize.h + innerSize.h);
@@ -156,4 +156,4 @@
// Register plugin
tinymce.PluginManager.add('fullscreen', tinymce.plugins.FullScreenPlugin);
-})(); \ No newline at end of file
+})();
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin.js
index b3a4ce31c..2ed5f41ae 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin.js
@@ -1 +1 @@
-(function(a){a.onAddEditor.addToTop(function(c,b){b.settings.inline_styles=false});a.create("tinymce.plugins.LegacyOutput",{init:function(b){b.onInit.add(function(){var c="p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",e=a.explode(b.settings.font_size_style_values),d=b.schema;b.formatter.register({alignleft:{selector:c,attributes:{align:"left"}},aligncenter:{selector:c,attributes:{align:"center"}},alignright:{selector:c,attributes:{align:"right"}},alignfull:{selector:c,attributes:{align:"justify"}},bold:[{inline:"b",remove:"all"},{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}}],italic:[{inline:"i",remove:"all"},{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}}],underline:[{inline:"u",remove:"all"},{inline:"span",styles:{textDecoration:"underline"},exact:true}],strikethrough:[{inline:"strike",remove:"all"},{inline:"span",styles:{textDecoration:"line-through"},exact:true}],fontname:{inline:"font",attributes:{face:"%value"}},fontsize:{inline:"font",attributes:{size:function(f){return a.inArray(e,f.value)+1}}},forecolor:{inline:"font",styles:{color:"%value"}},hilitecolor:{inline:"font",styles:{backgroundColor:"%value"}}});a.each("b,i,u,strike".split(","),function(f){d.addValidElements(f+"[*]")});if(!d.getElementRule("font")){d.addValidElements("font[face|size|color|style]")}a.each(c.split(","),function(f){var h=d.getElementRule(f),g;if(h){if(!h.attributes.align){h.attributes.align={};h.attributesOrder.push("align")}}});b.onNodeChange.add(function(g,k){var j,f,h,i;f=g.dom.getParent(g.selection.getNode(),"font");if(f){h=f.face;i=f.size}if(j=k.get("fontselect")){j.select(function(l){return l==h})}if(j=k.get("fontsizeselect")){j.select(function(m){var l=a.inArray(e,m.fontSize);return l+1==i})}})})},getInfo:function(){return{longname:"LegacyOutput",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/legacyoutput",version:a.majorVersion+"."+a.minorVersion}}});a.PluginManager.add("legacyoutput",a.plugins.LegacyOutput)})(tinymce); \ No newline at end of file
+(function(a){a.onAddEditor.addToTop(function(c,b){b.settings.inline_styles=false});a.create("tinymce.plugins.LegacyOutput",{init:function(b){b.onInit.add(function(){var c="p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li,table,img",e=a.explode(b.settings.font_size_style_values),d=b.schema;b.formatter.register({alignleft:{selector:c,attributes:{align:"left"}},aligncenter:{selector:c,attributes:{align:"center"}},alignright:{selector:c,attributes:{align:"right"}},alignfull:{selector:c,attributes:{align:"justify"}},bold:[{inline:"b",remove:"all"},{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}}],italic:[{inline:"i",remove:"all"},{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}}],underline:[{inline:"u",remove:"all"},{inline:"span",styles:{textDecoration:"underline"},exact:true}],strikethrough:[{inline:"strike",remove:"all"},{inline:"span",styles:{textDecoration:"line-through"},exact:true}],fontname:{inline:"font",attributes:{face:"%value"}},fontsize:{inline:"font",attributes:{size:function(f){return a.inArray(e,f.value)+1}}},forecolor:{inline:"font",attributes:{color:"%value"}},hilitecolor:{inline:"font",styles:{backgroundColor:"%value"}}});a.each("b,i,u,strike".split(","),function(f){d.addValidElements(f+"[*]")});if(!d.getElementRule("font")){d.addValidElements("font[face|size|color|style]")}a.each(c.split(","),function(f){var h=d.getElementRule(f),g;if(h){if(!h.attributes.align){h.attributes.align={};h.attributesOrder.push("align")}}});b.onNodeChange.add(function(g,k){var j,f,h,i;f=g.dom.getParent(g.selection.getNode(),"font");if(f){h=f.face;i=f.size}if(j=k.get("fontselect")){j.select(function(l){return l==h})}if(j=k.get("fontsizeselect")){j.select(function(m){var l=a.inArray(e,m.fontSize);return l+1==i})}})})},getInfo:function(){return{longname:"LegacyOutput",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/legacyoutput",version:a.majorVersion+"."+a.minorVersion}}});a.PluginManager.add("legacyoutput",a.plugins.LegacyOutput)})(tinymce); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin_src.js
index e627ec76e..3cdcde579 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/legacyoutput/editor_plugin_src.js
@@ -68,7 +68,7 @@
},
// Setup font elements for colors as well
- forecolor : {inline : 'font', styles : {color : '%value'}},
+ forecolor : {inline : 'font', attributes : {color : '%value'}},
hilitecolor : {inline : 'font', styles : {backgroundColor : '%value'}}
});
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin.js
index 86b8b1a40..ec21b256e 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin.js
@@ -1 +1 @@
-(function(){var e=tinymce.each,r=tinymce.dom.Event,g;function p(t,s){while(t&&(t.nodeType===8||(t.nodeType===3&&/^[ \t\n\r]*$/.test(t.nodeValue)))){t=s(t)}return t}function b(s){return p(s,function(t){return t.previousSibling})}function i(s){return p(s,function(t){return t.nextSibling})}function d(s,u,t){return s.dom.getParent(u,function(v){return tinymce.inArray(t,v)!==-1})}function n(s){return s&&(s.tagName==="OL"||s.tagName==="UL")}function c(u,v){var t,w,s;t=b(u.lastChild);while(n(t)){w=t;t=b(w.previousSibling)}if(w){s=v.create("li",{style:"list-style-type: none;"});v.split(u,w);v.insertAfter(s,w);s.appendChild(w);s.appendChild(w);u=s.previousSibling}return u}function m(t,s,u){t=a(t,s,u);return o(t,s,u)}function a(u,s,v){var t=b(u.previousSibling);if(t){return h(t,u,s?t:false,v)}else{return u}}function o(u,t,v){var s=i(u.nextSibling);if(s){return h(u,s,t?s:false,v)}else{return u}}function h(u,s,t,v){if(l(u,s,!!t,v)){return f(u,s,t)}else{if(u&&u.tagName==="LI"&&n(s)){u.appendChild(s)}}return s}function l(u,t,s,v){if(!u||!t){return false}else{if(u.tagName==="LI"&&t.tagName==="LI"){return t.style.listStyleType==="none"||j(t)}else{if(n(u)){return(u.tagName===t.tagName&&(s||u.style.listStyleType===t.style.listStyleType))||q(t)}else{return v&&u.tagName==="P"&&t.tagName==="P"}}}}function q(t){var s=i(t.firstChild),u=b(t.lastChild);return s&&u&&n(t)&&s===u&&(n(s)||s.style.listStyleType==="none"||j(s))}function j(u){var t=i(u.firstChild),s=b(u.lastChild);return t&&s&&t===s&&n(t)}function f(w,v,s){var u=b(w.lastChild),t=i(v.firstChild);if(w.tagName==="P"){w.appendChild(w.ownerDocument.createElement("br"))}while(v.firstChild){w.appendChild(v.firstChild)}if(s){w.style.listStyleType=s.style.listStyleType}v.parentNode.removeChild(v);h(u,t,false);return w}function k(t,u){var s;if(!u.is(t,"li,ol,ul")){s=u.getParent(t,"li");if(s){t=s}}return t}tinymce.create("tinymce.plugins.Lists",{init:function(y){var v="TABBING";var s="EMPTY";var I="ESCAPE";var z="PARAGRAPH";var M="UNKNOWN";var x=M;function E(T){return T.keyCode===tinymce.VK.TAB&&!(T.altKey||T.ctrlKey)&&(y.queryCommandState("InsertUnorderedList")||y.queryCommandState("InsertOrderedList"))}function D(){var T=y.selection.getRng();var U=T.startContainer;if(U.nodeType==3){return(T.endOffset==U.nodeValue.length)}else{if(U.nodeType==1){return T.endOffset==U.childNodes.length}}return false}function N(){var U=y.selection.getNode();var T=U.tagName==="P"&&U.parentNode.tagName==="LI"&&U.parentNode.lastChild===U;return y.selection.isCollapsed()&&T&&D()}function w(){var T=B();var V=T.parentNode.parentNode;var U=T.parentNode.lastChild===T;return U&&!t(V)&&O(T)}function t(T){if(n(T)){return T.parentNode&&T.parentNode.tagName==="LI"}else{return T.tagName==="LI"}}function F(){return y.selection.isCollapsed()&&O(B())}function B(){var T=y.selection.getStart();return((T.tagName=="BR"||T.tagName=="")&&T.parentNode.tagName=="LI")?T.parentNode:T}function O(T){var U=T.childNodes.length;if(T.tagName==="LI"){return U==0?true:U==1&&(T.firstChild.tagName==""||T.firstChild.tagName=="BR"||H(T))}return false}function H(T){var U=tinymce.grep(T.parentNode.childNodes,function(X){return X.tagName=="LI"});var V=T==U[U.length-1];var W=T.firstChild;return tinymce.isIE9&&V&&(W.nodeValue==String.fromCharCode(160)||W.nodeValue==String.fromCharCode(32))}function S(T){return T.keyCode===tinymce.VK.ENTER}function A(T){return S(T)&&!T.shiftKey}function L(T){if(E(T)){return v}else{if(A(T)&&w()){return I}else{if(A(T)&&F()){return s}else{if(A(T)&&N()){return z}else{return M}}}}}function C(T,U){if(x==v||x==s||tinymce.isGecko&&x==I){r.cancel(U)}}function J(V,X){if(x==z){var W=V.selection.getNode();var U=V.dom.create("li");var T=V.dom.getParent(W,"li");V.dom.insertAfter(U,T);if(tinyMCE.isIE8){U.appendChild(V.dom.create("&nbsp;"));V.selection.setCursorLocation(U,1)}else{if(tinyMCE.isGecko){setTimeout(function(){var Y=V.getDoc().createTextNode("\uFEFF");U.appendChild(Y);V.selection.setCursorLocation(U,0)},0)}else{V.selection.setCursorLocation(U,0)}}r.cancel(X)}}function u(W,Y){var ab;if(!tinymce.isGecko){return}var U=W.selection.getStart();if(Y.keyCode!=tinymce.VK.BACKSPACE||U.tagName!=="IMG"){return}function V(af){var ag=af.firstChild;var ae=null;do{if(!ag){break}if(ag.tagName==="LI"){ae=ag}}while(ag=ag.nextSibling);return ae}function ad(af,ae){while(af.childNodes.length>0){ae.appendChild(af.childNodes[0])}}ab=U.parentNode.previousSibling;if(!ab){return}var Z;if(ab.tagName==="UL"||ab.tagName==="OL"){Z=ab}else{if(ab.previousSibling&&(ab.previousSibling.tagName==="UL"||ab.previousSibling.tagName==="OL")){Z=ab.previousSibling}else{return}}var ac=V(Z);var T=W.dom.createRng();T.setStart(ac,1);T.setEnd(ac,1);W.selection.setRng(T);W.selection.collapse(true);var X=W.selection.getBookmark();var aa=U.parentNode.cloneNode(true);if(aa.tagName==="P"||aa.tagName==="DIV"){ad(aa,ac)}else{ac.appendChild(aa)}U.parentNode.parentNode.removeChild(U.parentNode);W.selection.moveToBookmark(X)}function G(T){var U=y.dom.getParent(T,"ol,ul");if(U!=null){var V=U.lastChild;V.appendChild(y.getDoc().createElement(""));y.selection.setCursorLocation(V,0)}}this.ed=y;y.addCommand("Indent",this.indent,this);y.addCommand("Outdent",this.outdent,this);y.addCommand("InsertUnorderedList",function(){this.applyList("UL","OL")},this);y.addCommand("InsertOrderedList",function(){this.applyList("OL","UL")},this);y.onInit.add(function(){y.editorCommands.addCommands({outdent:function(){var U=y.selection,V=y.dom;function T(W){W=V.getParent(W,V.isBlock);return W&&(parseInt(y.dom.getStyle(W,"margin-left")||0,10)+parseInt(y.dom.getStyle(W,"padding-left")||0,10))>0}return T(U.getStart())||T(U.getEnd())||y.queryCommandState("InsertOrderedList")||y.queryCommandState("InsertUnorderedList")}},"state")});y.onKeyUp.add(function(U,V){if(x==v){U.execCommand(V.shiftKey?"Outdent":"Indent",true,null);x=M;return r.cancel(V)}else{if(x==s){var T=B();var X=U.settings.list_outdent_on_enter===true||V.shiftKey;U.execCommand(X?"Outdent":"Indent",true,null);if(tinymce.isIE){G(T)}return r.cancel(V)}else{if(x==I){if(tinymce.isIE8){var W=U.getDoc().createTextNode("\uFEFF");U.selection.getNode().appendChild(W)}else{if(tinymce.isIE9||tinymce.isGecko){U.execCommand("Outdent");return r.cancel(V)}}}}}});function K(U,T){var V=y.getDoc().createTextNode("\uFEFF");U.insertBefore(V,T);y.selection.setCursorLocation(V,0);y.execCommand("mceRepaint")}function Q(U,W){if(S(W)){var T=B();if(T){var V=T.parentNode;var X=V&&V.parentNode;if(X&&X.nodeName=="LI"&&X.firstChild==V&&T==V.firstChild){K(X,V)}}}}function R(U,W){if(S(W)){var T=B();if(U.dom.select("ul li",T).length===1){var V=T.firstChild;K(T,V)}}}function P(U,Y){function V(ac,Z){var ab=[];var ad=new tinymce.dom.TreeWalker(Z,ac);for(var aa=ad.current();aa;aa=ad.next()){if(U.dom.is(aa,"ol,ul,li")){ab.push(aa)}}return ab}if(Y.keyCode==tinymce.VK.BACKSPACE){var T=B();if(T){var X=U.dom.getParent(T,"ol,ul");if(X&&X.firstChild===T){var W=V(X,T);U.execCommand("Outdent",false,W);U.undoManager.add();return r.cancel(Y)}}}}y.onKeyDown.add(function(T,U){x=L(U)});y.onKeyDown.add(C);y.onKeyDown.add(u);y.onKeyDown.add(J);if(tinymce.isGecko){y.onKeyUp.add(Q)}if(tinymce.isIE8){y.onKeyUp.add(R)}if(tinymce.isGecko||tinymce.isWebKit){y.onKeyDown.add(P)}},applyList:function(y,v){var C=this,z=C.ed,I=z.dom,s=[],H=false,u=false,w=false,B,G=z.selection.getSelectedBlocks();function E(t){if(t&&t.tagName==="BR"){I.remove(t)}}function F(M){var N=I.create(y),t;function L(O){if(O.style.marginLeft||O.style.paddingLeft){C.adjustPaddingFunction(false)(O)}}if(M.tagName==="LI"){}else{if(M.tagName==="P"||M.tagName==="DIV"||M.tagName==="BODY"){K(M,function(P,O){J(P,O,M.tagName==="BODY"?null:P.parentNode);t=P.parentNode;L(t);E(O)});if(t){if(t.tagName==="LI"&&(M.tagName==="P"||G.length>1)){I.split(t.parentNode.parentNode,t.parentNode)}m(t.parentNode,true)}return}else{t=I.create("li");I.insertAfter(t,M);t.appendChild(M);L(M);M=t}}I.insertAfter(N,M);N.appendChild(M);m(N,true);s.push(M)}function J(P,L,N){var t,O=P,M;while(!I.isBlock(P.parentNode)&&P.parentNode!==I.getRoot()){P=I.split(P.parentNode,P.previousSibling);P=P.nextSibling;O=P}if(N){t=N.cloneNode(true);P.parentNode.insertBefore(t,P);while(t.firstChild){I.remove(t.firstChild)}t=I.rename(t,"li")}else{t=I.create("li");P.parentNode.insertBefore(t,P)}while(O&&O!=L){M=O.nextSibling;t.appendChild(O);O=M}if(t.childNodes.length===0){t.innerHTML='<br _mce_bogus="1" />'}F(t)}function K(Q,T){var N,R,O=3,L=1,t="br,ul,ol,p,div,h1,h2,h3,h4,h5,h6,table,blockquote,address,pre,form,center,dl";function P(X,U){var V=I.createRng(),W;g.keep=true;z.selection.moveToBookmark(g);g.keep=false;W=z.selection.getRng(true);if(!U){U=X.parentNode.lastChild}V.setStartBefore(X);V.setEndAfter(U);return !(V.compareBoundaryPoints(O,W)>0||V.compareBoundaryPoints(L,W)<=0)}function S(U){if(U.nextSibling){return U.nextSibling}if(!I.isBlock(U.parentNode)&&U.parentNode!==I.getRoot()){return S(U.parentNode)}}N=Q.firstChild;var M=false;e(I.select(t,Q),function(U){if(U.hasAttribute&&U.hasAttribute("_mce_bogus")){return true}if(P(N,U)){I.addClass(U,"_mce_tagged_br");N=S(U)}});M=(N&&P(N,undefined));N=Q.firstChild;e(I.select(t,Q),function(V){var U=S(V);if(V.hasAttribute&&V.hasAttribute("_mce_bogus")){return true}if(I.hasClass(V,"_mce_tagged_br")){T(N,V,R);R=null}else{R=V}N=U});if(M){T(N,undefined,R)}}function D(t){K(t,function(M,L,N){J(M,L);E(L);E(N)})}function A(t){if(tinymce.inArray(s,t)!==-1){return}if(t.parentNode.tagName===v){I.split(t.parentNode,t);F(t);o(t.parentNode,false)}s.push(t)}function x(M){var O,N,L,t;if(tinymce.inArray(s,M)!==-1){return}M=c(M,I);while(I.is(M.parentNode,"ol,ul,li")){I.split(M.parentNode,M)}s.push(M);M=I.rename(M,"p");L=m(M,false,z.settings.force_br_newlines);if(L===M){O=M.firstChild;while(O){if(I.isBlock(O)){O=I.split(O.parentNode,O);t=true;N=O.nextSibling&&O.nextSibling.firstChild}else{N=O.nextSibling;if(t&&O.tagName==="BR"){I.remove(O)}t=false}O=N}}}e(G,function(t){t=k(t,I);if(t.tagName===v||(t.tagName==="LI"&&t.parentNode.tagName===v)){u=true}else{if(t.tagName===y||(t.tagName==="LI"&&t.parentNode.tagName===y)){H=true}else{w=true}}});if(w&&!H||u||G.length===0){B={LI:A,H1:F,H2:F,H3:F,H4:F,H5:F,H6:F,P:F,BODY:F,DIV:G.length>1?F:D,defaultAction:D,elements:this.selectedBlocks()}}else{B={defaultAction:x,elements:this.selectedBlocks()}}this.process(B)},indent:function(){var u=this.ed,w=u.dom,x=[];function s(z){var y=w.create("li",{style:"list-style-type: none;"});w.insertAfter(y,z);return y}function t(B){var y=s(B),D=w.getParent(B,"ol,ul"),C=D.tagName,E=w.getStyle(D,"list-style-type"),A={},z;if(E!==""){A.style="list-style-type: "+E+";"}z=w.create(C,A);y.appendChild(z);return z}function v(z){if(!d(u,z,x)){z=c(z,w);var y=t(z);y.appendChild(z);m(y.parentNode,false);m(y,false);x.push(z)}}this.process({LI:v,defaultAction:this.adjustPaddingFunction(true),elements:this.selectedBlocks()})},outdent:function(y,x){var w=this,u=w.ed,z=u.dom,s=[];function A(t){var C,B,D;if(!d(u,t,s)){if(z.getStyle(t,"margin-left")!==""||z.getStyle(t,"padding-left")!==""){return w.adjustPaddingFunction(false)(t)}D=z.getStyle(t,"text-align",true);if(D==="center"||D==="right"){z.setStyle(t,"text-align","left");return}t=c(t,z);C=t.parentNode;B=t.parentNode.parentNode;if(B.tagName==="P"){z.split(B,t.parentNode)}else{z.split(C,t);if(B.tagName==="LI"){z.split(B,t)}else{if(!z.is(B,"ol,ul")){z.rename(t,"p")}}}s.push(t)}}var v=x&&tinymce.is(x,"array")?x:this.selectedBlocks();this.process({LI:A,defaultAction:this.adjustPaddingFunction(false),elements:v});e(s,m)},process:function(y){var F=this,w=F.ed.selection,z=F.ed.dom,E,u;function B(t){var s=tinymce.grep(t.childNodes,function(H){return !(H.nodeName==="BR"||H.nodeName==="SPAN"&&z.getAttrib(H,"data-mce-type")=="bookmark"||H.nodeType==3&&(H.nodeValue==String.fromCharCode(160)||H.nodeValue==""))});return s.length===0}function x(s){z.removeClass(s,"_mce_act_on");if(!s||s.nodeType!==1||E.length>1&&B(s)){return}s=k(s,z);var t=y[s.tagName];if(!t){t=y.defaultAction}t(s)}function v(s){F.splitSafeEach(s.childNodes,x)}function C(s,t){return t>=0&&s.hasChildNodes()&&t<s.childNodes.length&&s.childNodes[t].tagName==="BR"}function D(){var t=w.getNode();var s=z.getParent(t,"td");return s!==null}E=y.elements;u=w.getRng(true);if(!u.collapsed){if(C(u.endContainer,u.endOffset-1)){u.setEnd(u.endContainer,u.endOffset-1);w.setRng(u)}if(C(u.startContainer,u.startOffset)){u.setStart(u.startContainer,u.startOffset+1);w.setRng(u)}}if(tinymce.isIE8){var G=F.ed.selection.getNode();if(G.tagName==="LI"&&!(G.parentNode.lastChild===G)){var A=F.ed.getDoc().createTextNode("\uFEFF");G.appendChild(A)}}g=w.getBookmark();y.OL=y.UL=v;F.splitSafeEach(E,x);w.moveToBookmark(g);g=null;if(!D()){F.ed.execCommand("mceRepaint")}},splitSafeEach:function(t,s){if(tinymce.isGecko&&(/Firefox\/[12]\.[0-9]/.test(navigator.userAgent)||/Firefox\/3\.[0-4]/.test(navigator.userAgent))){this.classBasedEach(t,s)}else{e(t,s)}},classBasedEach:function(v,u){var w=this.ed.dom,s,t;e(v,function(x){w.addClass(x,"_mce_act_on")});s=w.select("._mce_act_on");while(s.length>0){t=s.shift();w.removeClass(t,"_mce_act_on");u(t);s=w.select("._mce_act_on")}},adjustPaddingFunction:function(u){var s,v,t=this.ed;s=t.settings.indentation;v=/[a-z%]+/i.exec(s);s=parseInt(s,10);return function(w){var y,x;y=parseInt(t.dom.getStyle(w,"margin-left")||0,10)+parseInt(t.dom.getStyle(w,"padding-left")||0,10);if(u){x=y+s}else{x=y-s}t.dom.setStyle(w,"padding-left","");t.dom.setStyle(w,"margin-left",x>0?x+v:"")}},selectedBlocks:function(){var s=this.ed;var t=s.selection.getSelectedBlocks();return t.length==0?[s.dom.getRoot()]:t},getInfo:function(){return{longname:"Lists",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/lists",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("lists",tinymce.plugins.Lists)}()); \ No newline at end of file
+(function(){var e=tinymce.each,r=tinymce.dom.Event,g;function p(t,s){while(t&&(t.nodeType===8||(t.nodeType===3&&/^[ \t\n\r]*$/.test(t.nodeValue)))){t=s(t)}return t}function b(s){return p(s,function(t){return t.previousSibling})}function i(s){return p(s,function(t){return t.nextSibling})}function d(s,u,t){return s.dom.getParent(u,function(v){return tinymce.inArray(t,v)!==-1})}function n(s){return s&&(s.tagName==="OL"||s.tagName==="UL")}function c(u,v){var t,w,s;t=b(u.lastChild);while(n(t)){w=t;t=b(w.previousSibling)}if(w){s=v.create("li",{style:"list-style-type: none;"});v.split(u,w);v.insertAfter(s,w);s.appendChild(w);s.appendChild(w);u=s.previousSibling}return u}function m(t,s,u){t=a(t,s,u);return o(t,s,u)}function a(u,s,v){var t=b(u.previousSibling);if(t){return h(t,u,s?t:false,v)}else{return u}}function o(u,t,v){var s=i(u.nextSibling);if(s){return h(u,s,t?s:false,v)}else{return u}}function h(u,s,t,v){if(l(u,s,!!t,v)){return f(u,s,t)}else{if(u&&u.tagName==="LI"&&n(s)){u.appendChild(s)}}return s}function l(u,t,s,v){if(!u||!t){return false}else{if(u.tagName==="LI"&&t.tagName==="LI"){return t.style.listStyleType==="none"||j(t)}else{if(n(u)){return(u.tagName===t.tagName&&(s||u.style.listStyleType===t.style.listStyleType))||q(t)}else{return v&&u.tagName==="P"&&t.tagName==="P"}}}}function q(t){var s=i(t.firstChild),u=b(t.lastChild);return s&&u&&n(t)&&s===u&&(n(s)||s.style.listStyleType==="none"||j(s))}function j(u){var t=i(u.firstChild),s=b(u.lastChild);return t&&s&&t===s&&n(t)}function f(w,v,s){var u=b(w.lastChild),t=i(v.firstChild);if(w.tagName==="P"){w.appendChild(w.ownerDocument.createElement("br"))}while(v.firstChild){w.appendChild(v.firstChild)}if(s){w.style.listStyleType=s.style.listStyleType}v.parentNode.removeChild(v);h(u,t,false);return w}function k(t,u){var s;if(!u.is(t,"li,ol,ul")){s=u.getParent(t,"li");if(s){t=s}}return t}tinymce.create("tinymce.plugins.Lists",{init:function(y){var v="TABBING";var s="EMPTY";var J="ESCAPE";var z="PARAGRAPH";var N="UNKNOWN";var x=N;function E(U){return U.keyCode===tinymce.VK.TAB&&!(U.altKey||U.ctrlKey)&&(y.queryCommandState("InsertUnorderedList")||y.queryCommandState("InsertOrderedList"))}function w(){var U=B();var W=U.parentNode.parentNode;var V=U.parentNode.lastChild===U;return V&&!t(W)&&P(U)}function t(U){if(n(U)){return U.parentNode&&U.parentNode.tagName==="LI"}else{return U.tagName==="LI"}}function F(){return y.selection.isCollapsed()&&P(B())}function B(){var U=y.selection.getStart();return((U.tagName=="BR"||U.tagName=="")&&U.parentNode.tagName=="LI")?U.parentNode:U}function P(U){var V=U.childNodes.length;if(U.tagName==="LI"){return V==0?true:V==1&&(U.firstChild.tagName==""||U.firstChild.tagName=="BR"||H(U))}return false}function H(U){var V=tinymce.grep(U.parentNode.childNodes,function(Y){return Y.tagName=="LI"});var W=U==V[V.length-1];var X=U.firstChild;return tinymce.isIE9&&W&&(X.nodeValue==String.fromCharCode(160)||X.nodeValue==String.fromCharCode(32))}function T(U){return U.keyCode===tinymce.VK.ENTER}function A(U){return T(U)&&!U.shiftKey}function M(U){if(E(U)){return v}else{if(A(U)&&w()){return N}else{if(A(U)&&F()){return s}else{return N}}}}function D(U,V){if(x==v||x==s||tinymce.isGecko&&x==J){r.cancel(V)}}function C(){var U=y.selection.getRng(true);var V=U.startContainer;if(V.nodeType==3){var W=V.nodeValue;if(tinymce.isIE9&&W.length>1&&W.charCodeAt(W.length-1)==32){return(U.endOffset==W.length-1)}else{return(U.endOffset==W.length)}}else{if(V.nodeType==1){return U.endOffset==V.childNodes.length}}return false}function I(){var W=y.selection.getNode();var V="h1,h2,h3,h4,h5,h6,p,div";var U=y.dom.is(W,V)&&W.parentNode.tagName==="LI"&&W.parentNode.lastChild===W;return y.selection.isCollapsed()&&U&&C()}function K(W,Y){if(A(Y)&&I()){var X=W.selection.getNode();var V=W.dom.create("li");var U=W.dom.getParent(X,"li");W.dom.insertAfter(V,U);if(tinymce.isIE6||tinymce.isIE7||tinyMCE.isIE8){W.selection.setCursorLocation(V,1)}else{W.selection.setCursorLocation(V,0)}Y.preventDefault()}}function u(X,Z){var ac;if(!tinymce.isGecko){return}var V=X.selection.getStart();if(Z.keyCode!=tinymce.VK.BACKSPACE||V.tagName!=="IMG"){return}function W(ag){var ah=ag.firstChild;var af=null;do{if(!ah){break}if(ah.tagName==="LI"){af=ah}}while(ah=ah.nextSibling);return af}function ae(ag,af){while(ag.childNodes.length>0){af.appendChild(ag.childNodes[0])}}ac=V.parentNode.previousSibling;if(!ac){return}var aa;if(ac.tagName==="UL"||ac.tagName==="OL"){aa=ac}else{if(ac.previousSibling&&(ac.previousSibling.tagName==="UL"||ac.previousSibling.tagName==="OL")){aa=ac.previousSibling}else{return}}var ad=W(aa);var U=X.dom.createRng();U.setStart(ad,1);U.setEnd(ad,1);X.selection.setRng(U);X.selection.collapse(true);var Y=X.selection.getBookmark();var ab=V.parentNode.cloneNode(true);if(ab.tagName==="P"||ab.tagName==="DIV"){ae(ab,ad)}else{ad.appendChild(ab)}V.parentNode.parentNode.removeChild(V.parentNode);X.selection.moveToBookmark(Y)}function G(U){var V=y.dom.getParent(U,"ol,ul");if(V!=null){var W=V.lastChild;y.selection.setCursorLocation(W,0)}}this.ed=y;y.addCommand("Indent",this.indent,this);y.addCommand("Outdent",this.outdent,this);y.addCommand("InsertUnorderedList",function(){this.applyList("UL","OL")},this);y.addCommand("InsertOrderedList",function(){this.applyList("OL","UL")},this);y.onInit.add(function(){y.editorCommands.addCommands({outdent:function(){var V=y.selection,W=y.dom;function U(X){X=W.getParent(X,W.isBlock);return X&&(parseInt(y.dom.getStyle(X,"margin-left")||0,10)+parseInt(y.dom.getStyle(X,"padding-left")||0,10))>0}return U(V.getStart())||U(V.getEnd())||y.queryCommandState("InsertOrderedList")||y.queryCommandState("InsertUnorderedList")}},"state")});y.onKeyUp.add(function(V,W){if(x==v){V.execCommand(W.shiftKey?"Outdent":"Indent",true,null);x=N;return r.cancel(W)}else{if(x==s){var U=B();var Y=V.settings.list_outdent_on_enter===true||W.shiftKey;V.execCommand(Y?"Outdent":"Indent",true,null);if(tinymce.isIE){G(U)}return r.cancel(W)}else{if(x==J){if(tinymce.isIE6||tinymce.isIE7||tinymce.isIE8){var X=V.getDoc().createTextNode("\uFEFF");V.selection.getNode().appendChild(X)}else{if(tinymce.isIE9||tinymce.isGecko){V.execCommand("Outdent");return r.cancel(W)}}}}}});function L(V,U){var W=y.getDoc().createTextNode("\uFEFF");V.insertBefore(W,U);y.selection.setCursorLocation(W,0);y.execCommand("mceRepaint")}function R(V,X){if(T(X)){var U=B();if(U){var W=U.parentNode;var Y=W&&W.parentNode;if(Y&&Y.nodeName=="LI"&&Y.firstChild==W&&U==W.firstChild){L(Y,W)}}}}function S(V,X){if(T(X)){var U=B();if(V.dom.select("ul li",U).length===1){var W=U.firstChild;L(U,W)}}}function Q(W,aa){function X(ab){var ad=[];var ae=new tinymce.dom.TreeWalker(ab.firstChild,ab);for(var ac=ae.current();ac;ac=ae.next()){if(W.dom.is(ac,"ol,ul,li")){ad.push(ac)}}return ad}if(aa.keyCode==tinymce.VK.BACKSPACE){var U=B();if(U){var Z=W.dom.getParent(U,"ol,ul"),V=W.selection.getRng();if(Z&&Z.firstChild===U&&V.startOffset==0){var Y=X(U);Y.unshift(U);W.execCommand("Outdent",false,Y);W.undoManager.add();return r.cancel(aa)}}}}function O(V,X){var U=B();if(X.keyCode===tinymce.VK.BACKSPACE&&V.dom.is(U,"li")&&U.parentNode.firstChild!==U){if(V.dom.select("ul,ol",U).length===1){var Z=U.previousSibling;V.dom.remove(V.dom.select("br",U));V.dom.remove(U,true);var W=tinymce.grep(Z.childNodes,function(aa){return aa.nodeType===3});if(W.length===1){var Y=W[0];V.selection.setCursorLocation(Y,Y.length)}V.undoManager.add();return r.cancel(X)}}}y.onKeyDown.add(function(U,V){x=M(V)});y.onKeyDown.add(D);y.onKeyDown.add(u);y.onKeyDown.add(K);if(tinymce.isGecko){y.onKeyUp.add(R)}if(tinymce.isIE8){y.onKeyUp.add(S)}if(tinymce.isGecko||tinymce.isWebKit){y.onKeyDown.add(Q)}if(tinymce.isWebKit){y.onKeyDown.add(O)}},applyList:function(y,v){var C=this,z=C.ed,I=z.dom,s=[],H=false,u=false,w=false,B,G=z.selection.getSelectedBlocks();function E(t){if(t&&t.tagName==="BR"){I.remove(t)}}function F(M){var N=I.create(y),t;function L(O){if(O.style.marginLeft||O.style.paddingLeft){C.adjustPaddingFunction(false)(O)}}if(M.tagName==="LI"){}else{if(M.tagName==="P"||M.tagName==="DIV"||M.tagName==="BODY"){K(M,function(P,O){J(P,O,M.tagName==="BODY"?null:P.parentNode);t=P.parentNode;L(t);E(O)});if(t){if(t.tagName==="LI"&&(M.tagName==="P"||G.length>1)){I.split(t.parentNode.parentNode,t.parentNode)}m(t.parentNode,true)}return}else{t=I.create("li");I.insertAfter(t,M);t.appendChild(M);L(M);M=t}}I.insertAfter(N,M);N.appendChild(M);m(N,true);s.push(M)}function J(P,L,N){var t,O=P,M;while(!I.isBlock(P.parentNode)&&P.parentNode!==I.getRoot()){P=I.split(P.parentNode,P.previousSibling);P=P.nextSibling;O=P}if(N){t=N.cloneNode(true);P.parentNode.insertBefore(t,P);while(t.firstChild){I.remove(t.firstChild)}t=I.rename(t,"li")}else{t=I.create("li");P.parentNode.insertBefore(t,P)}while(O&&O!=L){M=O.nextSibling;t.appendChild(O);O=M}if(t.childNodes.length===0){t.innerHTML='<br _mce_bogus="1" />'}F(t)}function K(Q,T){var N,R,O=3,L=1,t="br,ul,ol,p,div,h1,h2,h3,h4,h5,h6,table,blockquote,address,pre,form,center,dl";function P(X,U){var V=I.createRng(),W;g.keep=true;z.selection.moveToBookmark(g);g.keep=false;W=z.selection.getRng(true);if(!U){U=X.parentNode.lastChild}V.setStartBefore(X);V.setEndAfter(U);return !(V.compareBoundaryPoints(O,W)>0||V.compareBoundaryPoints(L,W)<=0)}function S(U){if(U.nextSibling){return U.nextSibling}if(!I.isBlock(U.parentNode)&&U.parentNode!==I.getRoot()){return S(U.parentNode)}}N=Q.firstChild;var M=false;e(I.select(t,Q),function(U){if(U.hasAttribute&&U.hasAttribute("_mce_bogus")){return true}if(P(N,U)){I.addClass(U,"_mce_tagged_br");N=S(U)}});M=(N&&P(N,undefined));N=Q.firstChild;e(I.select(t,Q),function(V){var U=S(V);if(V.hasAttribute&&V.hasAttribute("_mce_bogus")){return true}if(I.hasClass(V,"_mce_tagged_br")){T(N,V,R);R=null}else{R=V}N=U});if(M){T(N,undefined,R)}}function D(t){K(t,function(M,L,N){J(M,L);E(L);E(N)})}function A(t){if(tinymce.inArray(s,t)!==-1){return}if(t.parentNode.tagName===v){I.split(t.parentNode,t);F(t);o(t.parentNode,false)}s.push(t)}function x(M){var O,N,L,t;if(tinymce.inArray(s,M)!==-1){return}M=c(M,I);while(I.is(M.parentNode,"ol,ul,li")){I.split(M.parentNode,M)}s.push(M);M=I.rename(M,"p");L=m(M,false,z.settings.force_br_newlines);if(L===M){O=M.firstChild;while(O){if(I.isBlock(O)){O=I.split(O.parentNode,O);t=true;N=O.nextSibling&&O.nextSibling.firstChild}else{N=O.nextSibling;if(t&&O.tagName==="BR"){I.remove(O)}t=false}O=N}}}e(G,function(t){t=k(t,I);if(t.tagName===v||(t.tagName==="LI"&&t.parentNode.tagName===v)){u=true}else{if(t.tagName===y||(t.tagName==="LI"&&t.parentNode.tagName===y)){H=true}else{w=true}}});if(w&&!H||u||G.length===0){B={LI:A,H1:F,H2:F,H3:F,H4:F,H5:F,H6:F,P:F,BODY:F,DIV:G.length>1?F:D,defaultAction:D,elements:this.selectedBlocks()}}else{B={defaultAction:x,elements:this.selectedBlocks(),processEvenIfEmpty:true}}this.process(B)},indent:function(){var u=this.ed,w=u.dom,x=[];function s(z){var y=w.create("li",{style:"list-style-type: none;"});w.insertAfter(y,z);return y}function t(B){var y=s(B),D=w.getParent(B,"ol,ul"),C=D.tagName,E=w.getStyle(D,"list-style-type"),A={},z;if(E!==""){A.style="list-style-type: "+E+";"}z=w.create(C,A);y.appendChild(z);return z}function v(z){if(!d(u,z,x)){z=c(z,w);var y=t(z);y.appendChild(z);m(y.parentNode,false);m(y,false);x.push(z)}}this.process({LI:v,defaultAction:this.adjustPaddingFunction(true),elements:this.selectedBlocks()})},outdent:function(y,x){var w=this,u=w.ed,z=u.dom,s=[];function A(t){var C,B,D;if(!d(u,t,s)){if(z.getStyle(t,"margin-left")!==""||z.getStyle(t,"padding-left")!==""){return w.adjustPaddingFunction(false)(t)}D=z.getStyle(t,"text-align",true);if(D==="center"||D==="right"){z.setStyle(t,"text-align","left");return}t=c(t,z);C=t.parentNode;B=t.parentNode.parentNode;if(B.tagName==="P"){z.split(B,t.parentNode)}else{z.split(C,t);if(B.tagName==="LI"){z.split(B,t)}else{if(!z.is(B,"ol,ul")){z.rename(t,"p")}}}s.push(t)}}var v=x&&tinymce.is(x,"array")?x:this.selectedBlocks();this.process({LI:A,defaultAction:this.adjustPaddingFunction(false),elements:v});e(s,m)},process:function(y){var F=this,w=F.ed.selection,z=F.ed.dom,E,u;function B(t){var s=tinymce.grep(t.childNodes,function(H){return !(H.nodeName==="BR"||H.nodeName==="SPAN"&&z.getAttrib(H,"data-mce-type")=="bookmark"||H.nodeType==3&&(H.nodeValue==String.fromCharCode(160)||H.nodeValue==""))});return s.length===0}function x(s){z.removeClass(s,"_mce_act_on");if(!s||s.nodeType!==1||!y.processEvenIfEmpty&&E.length>1&&B(s)){return}s=k(s,z);var t=y[s.tagName];if(!t){t=y.defaultAction}t(s)}function v(s){F.splitSafeEach(s.childNodes,x,true)}function C(s,t){return t>=0&&s.hasChildNodes()&&t<s.childNodes.length&&s.childNodes[t].tagName==="BR"}function D(){var t=w.getNode();var s=z.getParent(t,"td");return s!==null}E=y.elements;u=w.getRng(true);if(!u.collapsed){if(C(u.endContainer,u.endOffset-1)){u.setEnd(u.endContainer,u.endOffset-1);w.setRng(u)}if(C(u.startContainer,u.startOffset)){u.setStart(u.startContainer,u.startOffset+1);w.setRng(u)}}if(tinymce.isIE8){var G=F.ed.selection.getNode();if(G.tagName==="LI"&&!(G.parentNode.lastChild===G)){var A=F.ed.getDoc().createTextNode("\uFEFF");G.appendChild(A)}}g=w.getBookmark();y.OL=y.UL=v;F.splitSafeEach(E,x);w.moveToBookmark(g);g=null;if(!D()){F.ed.execCommand("mceRepaint")}},splitSafeEach:function(u,t,s){if(s||(tinymce.isGecko&&(/Firefox\/[12]\.[0-9]/.test(navigator.userAgent)||/Firefox\/3\.[0-4]/.test(navigator.userAgent)))){this.classBasedEach(u,t)}else{e(u,t)}},classBasedEach:function(v,u){var w=this.ed.dom,s,t;e(v,function(x){w.addClass(x,"_mce_act_on")});s=w.select("._mce_act_on");while(s.length>0){t=s.shift();w.removeClass(t,"_mce_act_on");u(t);s=w.select("._mce_act_on")}},adjustPaddingFunction:function(u){var s,v,t=this.ed;s=t.settings.indentation;v=/[a-z%]+/i.exec(s);s=parseInt(s,10);return function(w){var y,x;y=parseInt(t.dom.getStyle(w,"margin-left")||0,10)+parseInt(t.dom.getStyle(w,"padding-left")||0,10);if(u){x=y+s}else{x=y-s}t.dom.setStyle(w,"padding-left","");t.dom.setStyle(w,"margin-left",x>0?x+v:"")}},selectedBlocks:function(){var s=this.ed,t=s.selection.getSelectedBlocks();return t.length==0?[s.dom.getRoot()]:t},getInfo:function(){return{longname:"Lists",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/lists",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("lists",tinymce.plugins.Lists)}()); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin_src.js
index a3bd16cab..1000ef745 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/lists/editor_plugin_src.js
@@ -149,29 +149,11 @@
var state = LIST_UNKNOWN;
function isTabInList(e) {
- // Don't indent on Ctrl+Tab or Alt+Tab
+ // Don't indent on Ctrl+Tab or Alt+Tab
return e.keyCode === tinymce.VK.TAB && !(e.altKey || e.ctrlKey) &&
(ed.queryCommandState('InsertUnorderedList') || ed.queryCommandState('InsertOrderedList'));
}
- function isCursorAtEndOfContainer() {
- var range = ed.selection.getRng();
- var startContainer = range.startContainer;
- if (startContainer.nodeType == 3) {
- return (range.endOffset == startContainer.nodeValue.length);
- } else if (startContainer.nodeType == 1) {
- return range.endOffset == startContainer.childNodes.length;
- }
- return false;
- }
-
- // If we are at the end of a paragraph in a list item, pressing enter should create a new list item instead of a new paragraph.
- function isEndOfParagraph() {
- var node = ed.selection.getNode();
- var isLastParagraphOfLi = node.tagName === 'P' && node.parentNode.tagName === 'LI' && node.parentNode.lastChild === node;
- return ed.selection.isCollapsed() && isLastParagraphOfLi && isCursorAtEndOfContainer();
- }
-
function isOnLastListItem() {
var li = getLi();
var grandParent = li.parentNode.parentNode;
@@ -215,22 +197,22 @@
function isEnter(e) {
return e.keyCode === tinymce.VK.ENTER;
- }
+ }
- function isEnterWithoutShift(e) {
- return isEnter(e) && !e.shiftKey;
- }
+ function isEnterWithoutShift(e) {
+ return isEnter(e) && !e.shiftKey;
+ }
function getListKeyState(e) {
if (isTabInList(e)) {
return LIST_TABBING;
} else if (isEnterWithoutShift(e) && isOnLastListItem()) {
- return LIST_ESCAPE;
+ // Returns LIST_UNKNOWN since breaking out of lists is handled by the EnterKey.js logic now
+ //return LIST_ESCAPE;
+ return LIST_UNKNOWN;
} else if (isEnterWithoutShift(e) && isInEmptyListItem()) {
return LIST_EMPTY_ITEM;
- } else if (isEnterWithoutShift(e) && isEndOfParagraph()) {
- return LIST_PARAGRAPH;
- } else {
+ } else {
return LIST_UNKNOWN;
}
}
@@ -241,32 +223,54 @@
Event.cancel(e);
}
}
-
- // Creates a new list item after the current selection's list item parent
- function createNewLi(ed, e) {
- if (state == LIST_PARAGRAPH) {
+
+ function isCursorAtEndOfContainer() {
+ var range = ed.selection.getRng(true);
+ var startContainer = range.startContainer;
+ if (startContainer.nodeType == 3) {
+ var value = startContainer.nodeValue;
+ if (tinymce.isIE9 && value.length > 1 && value.charCodeAt(value.length-1) == 32) {
+ // IE9 places a space on the end of the text in some cases so ignore last char
+ return (range.endOffset == value.length-1);
+ } else {
+ return (range.endOffset == value.length);
+ }
+ } else if (startContainer.nodeType == 1) {
+ return range.endOffset == startContainer.childNodes.length;
+ }
+ return false;
+ }
+
+ /*
+ If we are at the end of a list item surrounded with an element, pressing enter should create a
+ new list item instead without splitting the element e.g. don't want to create new P or H1 tag
+ */
+ function isEndOfListItem() {
+ var node = ed.selection.getNode();
+ var validElements = 'h1,h2,h3,h4,h5,h6,p,div';
+ var isLastParagraphOfLi = ed.dom.is(node, validElements) && node.parentNode.tagName === 'LI' && node.parentNode.lastChild === node;
+ return ed.selection.isCollapsed() && isLastParagraphOfLi && isCursorAtEndOfContainer();
+ }
+
+ // Creates a new list item after the current selection's list item parent
+ function createNewLi(ed, e) {
+ if (isEnterWithoutShift(e) && isEndOfListItem()) {
var node = ed.selection.getNode();
var li = ed.dom.create("li");
var parentLi = ed.dom.getParent(node, 'li');
ed.dom.insertAfter(li, parentLi);
- // Move caret to new list element.
- if (tinyMCE.isIE8) {
- li.appendChild(ed.dom.create("&nbsp;")); // IE needs an element within the bullet point
- ed.selection.setCursorLocation(li, 1);
- } else if (tinyMCE.isGecko) {
- // This setTimeout is a hack as FF behaves badly if there is no content after the bullet point
- setTimeout(function () {
- var n = ed.getDoc().createTextNode('\uFEFF');
- li.appendChild(n);
- ed.selection.setCursorLocation(li, 0);
- }, 0);
- } else {
- ed.selection.setCursorLocation(li, 0);
- }
- Event.cancel(e);
+ // Move caret to new list element.
+ if (tinymce.isIE6 || tinymce.isIE7 || tinyMCE.isIE8) {
+ // Removed this line since it would create an odd <&nbsp;> tag and placing the caret inside an empty LI is handled and should be handled by the selection logic
+ //li.appendChild(ed.dom.create("&nbsp;")); // IE needs an element within the bullet point
+ ed.selection.setCursorLocation(li, 1);
+ } else {
+ ed.selection.setCursorLocation(li, 0);
+ }
+ e.preventDefault();
}
- }
+ }
function imageJoiningListItem(ed, e) {
var prevSibling;
@@ -341,7 +345,8 @@
var list = ed.dom.getParent(li, 'ol,ul');
if (list != null) {
var lastLi = list.lastChild;
- lastLi.appendChild(ed.getDoc().createElement(''));
+ // Removed this line since IE9 would report an DOM character error and placing the caret inside an empty LI is handled and should be handled by the selection logic
+ //lastLi.appendChild(ed.getDoc().createElement(''));
ed.selection.setCursorLocation(lastLi, 0);
}
}
@@ -374,8 +379,8 @@
ed.onKeyUp.add(function(ed, e) {
if (state == LIST_TABBING) {
ed.execCommand(e.shiftKey ? 'Outdent' : 'Indent', true, null);
- state = LIST_UNKNOWN;
- return Event.cancel(e);
+ state = LIST_UNKNOWN;
+ return Event.cancel(e);
} else if (state == LIST_EMPTY_ITEM) {
var li = getLi();
var shouldOutdent = ed.settings.list_outdent_on_enter === true || e.shiftKey;
@@ -386,8 +391,8 @@
return Event.cancel(e);
} else if (state == LIST_ESCAPE) {
- if (tinymce.isIE8) {
- // append a zero sized nbsp so that caret is positioned correctly in IE8 after escaping and applying formatting.
+ if (tinymce.isIE6 || tinymce.isIE7 || tinymce.isIE8) {
+ // append a zero sized nbsp so that caret is positioned correctly in IE after escaping and applying formatting.
// if there is no text then applying formatting for e.g a H1 to the P tag immediately following list after
// escaping from it will cause the caret to be positioned on the last li instead of staying the in P tag.
var n = ed.getDoc().createTextNode('\uFEFF');
@@ -397,7 +402,7 @@
// Gecko does not create a paragraph outdenting inside a TD so default behaviour is cancelled and we outdent ourselves
ed.execCommand('Outdent');
return Event.cancel(e);
- }
+ }
}
});
@@ -435,9 +440,9 @@
}
function fixDeletingFirstCharOfList(ed, e) {
- function listElements(list, li) {
+ function listElements(li) {
var elements = [];
- var walker = new tinymce.dom.TreeWalker(li, list);
+ var walker = new tinymce.dom.TreeWalker(li.firstChild, li);
for (var node = walker.current(); node; node = walker.next()) {
if (ed.dom.is(node, 'ol,ul,li')) {
elements.push(node);
@@ -449,9 +454,11 @@
if (e.keyCode == tinymce.VK.BACKSPACE) {
var li = getLi();
if (li) {
- var list = ed.dom.getParent(li, 'ol,ul');
- if (list && list.firstChild === li) {
- var elements = listElements(list, li);
+ var list = ed.dom.getParent(li, 'ol,ul'),
+ rng = ed.selection.getRng();
+ if (list && list.firstChild === li && rng.startOffset == 0) {
+ var elements = listElements(li);
+ elements.unshift(li);
ed.execCommand("Outdent", false, elements);
ed.undoManager.add();
return Event.cancel(e);
@@ -460,10 +467,28 @@
}
}
+ function fixDeletingEmptyLiInWebkit(ed, e) {
+ var li = getLi();
+ if (e.keyCode === tinymce.VK.BACKSPACE && ed.dom.is(li, 'li') && li.parentNode.firstChild!==li) {
+ if (ed.dom.select('ul,ol', li).length === 1) {
+ var prevLi = li.previousSibling;
+ ed.dom.remove(ed.dom.select('br', li));
+ ed.dom.remove(li, true);
+ var textNodes = tinymce.grep(prevLi.childNodes, function(n){ return n.nodeType === 3 });
+ if (textNodes.length === 1) {
+ var textNode = textNodes[0];
+ ed.selection.setCursorLocation(textNode, textNode.length);
+ }
+ ed.undoManager.add();
+ return Event.cancel(e);
+ }
+ }
+ }
+
ed.onKeyDown.add(function(_, e) { state = getListKeyState(e); });
ed.onKeyDown.add(cancelDefaultEvents);
ed.onKeyDown.add(imageJoiningListItem);
- ed.onKeyDown.add(createNewLi);
+ ed.onKeyDown.add(createNewLi);
if (tinymce.isGecko) {
ed.onKeyUp.add(fixIndentedListItemForGecko);
@@ -474,6 +499,9 @@
if (tinymce.isGecko || tinymce.isWebKit) {
ed.onKeyDown.add(fixDeletingFirstCharOfList);
}
+ if (tinymce.isWebKit) {
+ ed.onKeyDown.add(fixDeletingEmptyLiInWebkit);
+ }
},
applyList: function(targetListType, oppositeListType) {
@@ -542,7 +570,7 @@
li = dom.create('li');
start.parentNode.insertBefore(li, start);
}
- while (n && n != end) {
+ while (n && n != end) {
tmp = n.nextSibling;
li.appendChild(n);
n = tmp;
@@ -696,7 +724,8 @@
} else {
actions = {
defaultAction: convertListItemToParagraph,
- elements: this.selectedBlocks()
+ elements: this.selectedBlocks(),
+ processEvenIfEmpty: true
};
}
this.process(actions);
@@ -800,7 +829,7 @@
function processElement(element) {
dom.removeClass(element, '_mce_act_on');
- if (!element || element.nodeType !== 1 || selectedBlocks.length > 1 && isEmptyElement(element)) {
+ if (!element || element.nodeType !== 1 || ! actions.processEvenIfEmpty && selectedBlocks.length > 1 && isEmptyElement(element)) {
return;
}
element = findItemToOperateOn(element, dom);
@@ -812,7 +841,7 @@
}
function recurse(element) {
- t.splitSafeEach(element.childNodes, processElement);
+ t.splitSafeEach(element.childNodes, processElement, true);
}
function brAtEdgeOfSelection(container, offset) {
@@ -863,9 +892,11 @@
}
},
- splitSafeEach: function(elements, f) {
- if (tinymce.isGecko && (/Firefox\/[12]\.[0-9]/.test(navigator.userAgent) ||
- /Firefox\/3\.[0-4]/.test(navigator.userAgent))) {
+ splitSafeEach: function(elements, f, forceClassBase) {
+ if (forceClassBase ||
+ (tinymce.isGecko &&
+ (/Firefox\/[12]\.[0-9]/.test(navigator.userAgent) ||
+ /Firefox\/3\.[0-4]/.test(navigator.userAgent)))) {
this.classBasedEach(elements, f);
} else {
each(elements, f);
@@ -906,8 +937,7 @@
},
selectedBlocks: function() {
- var ed = this.ed
- var selectedBlocks = ed.selection.getSelectedBlocks();
+ var ed = this.ed, selectedBlocks = ed.selection.getSelectedBlocks();
return selectedBlocks.length == 0 ? [ ed.dom.getRoot() ] : selectedBlocks;
},
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin.js
index 37b4320bd..9ac42e0d2 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin.js
@@ -1 +1 @@
-(function(){var d=tinymce.explode("id,name,width,height,style,align,class,hspace,vspace,bgcolor,type"),h=tinymce.makeMap(d.join(",")),b=tinymce.html.Node,f,a,g=tinymce.util.JSON,e;f=[["Flash","d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["ShockWave","166b1bca-3f9c-11cf-8075-444553540000","application/x-director","http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],["WindowsMedia","6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a","application/x-mplayer2","http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],["QuickTime","02bf25d5-8c17-4b23-bc80-d3488abddc6b","video/quicktime","http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],["RealMedia","cfcdaa03-8be4-11cf-b84b-0020afbbccfa","audio/x-pn-realaudio-plugin","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["Java","8ad9c840-044e-11d1-b3e9-00805f499d93","application/x-java-applet","http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],["Silverlight","dfeaf541-f3e1-4c24-acac-99c30715084a","application/x-silverlight-2"],["Iframe"],["Video"],["EmbeddedAudio"],["Audio"]];function c(m){var l,j,k;if(m&&!m.splice){j=[];for(k=0;true;k++){if(m[k]){j[k]=m[k]}else{break}}return j}return m}tinymce.create("tinymce.plugins.MediaPlugin",{init:function(n,j){var r=this,l={},m,p,q,k;function o(i){return i&&i.nodeName==="IMG"&&n.dom.hasClass(i,"mceItemMedia")}r.editor=n;r.url=j;a="";for(m=0;m<f.length;m++){k=f[m][0];q={name:k,clsids:tinymce.explode(f[m][1]||""),mimes:tinymce.explode(f[m][2]||""),codebase:f[m][3]};for(p=0;p<q.clsids.length;p++){l["clsid:"+q.clsids[p]]=q}for(p=0;p<q.mimes.length;p++){l[q.mimes[p]]=q}l["mceItem"+k]=q;l[k.toLowerCase()]=q;a+=(a?"|":"")+k}tinymce.each(n.getParam("media_types","video=mp4,m4v,ogv,webm;silverlight=xap;flash=swf,flv;shockwave=dcr;quicktime=mov,qt,mpg,mpeg;shockwave=dcr;windowsmedia=avi,wmv,wm,asf,asx,wmx,wvx;realmedia=rm,ra,ram;java=jar;audio=mp3,ogg").split(";"),function(v){var s,u,t;v=v.split(/=/);u=tinymce.explode(v[1].toLowerCase());for(s=0;s<u.length;s++){t=l[v[0].toLowerCase()];if(t){l[u[s]]=t}}});a=new RegExp("write("+a+")\\(([^)]+)\\)");r.lookup=l;n.onPreInit.add(function(){n.schema.addValidElements("object[id|style|width|height|classid|codebase|*],param[name|value],embed[id|style|width|height|type|src|*],video[*],audio[*],source[*]");n.parser.addNodeFilter("object,embed,video,audio,script,iframe",function(s){var t=s.length;while(t--){r.objectToImg(s[t])}});n.serializer.addNodeFilter("img",function(s,u,t){var v=s.length,w;while(v--){w=s[v];if((w.attr("class")||"").indexOf("mceItemMedia")!==-1){r.imgToObject(w,t)}}})});n.onInit.add(function(){if(n.theme&&n.theme.onResolveName){n.theme.onResolveName.add(function(i,s){if(s.name==="img"&&n.dom.hasClass(s.node,"mceItemMedia")){s.name="media"}})}if(n&&n.plugins.contextmenu){n.plugins.contextmenu.onContextMenu.add(function(s,t,i){if(i.nodeName==="IMG"&&i.className.indexOf("mceItemMedia")!==-1){t.add({title:"media.edit",icon:"media",cmd:"mceMedia"})}})}});n.addCommand("mceMedia",function(){var s,i;i=n.selection.getNode();if(o(i)){s=n.dom.getAttrib(i,"data-mce-json");if(s){s=g.parse(s);tinymce.each(d,function(t){var u=n.dom.getAttrib(i,t);if(u){s[t]=u}});s.type=r.getType(i.className).name.toLowerCase()}}if(!s){s={type:"flash",video:{sources:[]},params:{}}}n.windowManager.open({file:j+"/media.htm",width:430+parseInt(n.getLang("media.delta_width",0)),height:500+parseInt(n.getLang("media.delta_height",0)),inline:1},{plugin_url:j,data:s})});n.addButton("media",{title:"media.desc",cmd:"mceMedia"});n.onNodeChange.add(function(s,i,t){i.setActive("media",o(t))})},convertUrl:function(k,n){var j=this,m=j.editor,l=m.settings,o=l.url_converter,i=l.url_converter_scope||j;if(!k){return k}if(n){return m.documentBaseURI.toAbsolute(k)}return o.call(i,k,"src","object")},getInfo:function(){return{longname:"Media",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media",version:tinymce.majorVersion+"."+tinymce.minorVersion}},dataToImg:function(m,k){var r=this,o=r.editor,p=o.documentBaseURI,j,q,n,l;m.params.src=r.convertUrl(m.params.src,k);q=m.video.attrs;if(q){q.src=r.convertUrl(q.src,k)}if(q){q.poster=r.convertUrl(q.poster,k)}j=c(m.video.sources);if(j){for(l=0;l<j.length;l++){j[l].src=r.convertUrl(j[l].src,k)}}n=r.editor.dom.create("img",{id:m.id,style:m.style,align:m.align,hspace:m.hspace,vspace:m.vspace,src:r.editor.theme.url+"/img/trans.gif","class":"mceItemMedia mceItem"+r.getType(m.type).name,"data-mce-json":g.serialize(m,"'")});n.width=m.width||(m.type=="audio"?"300":"320");n.height=m.height||(m.type=="audio"?"32":"240");return n},dataToHtml:function(i,j){return this.editor.serializer.serialize(this.dataToImg(i,j),{forced_root_block:"",force_absolute:j})},htmlToData:function(k){var j,i,l;l={type:"flash",video:{sources:[]},params:{}};j=this.editor.parser.parse(k);i=j.getAll("img")[0];if(i){l=g.parse(i.attr("data-mce-json"));l.type=this.getType(i.attr("class")).name.toLowerCase();tinymce.each(d,function(m){var n=i.attr(m);if(n){l[m]=n}})}return l},getType:function(m){var k,j,l;j=tinymce.explode(m," ");for(k=0;k<j.length;k++){l=this.lookup[j[k]];if(l){return l}}},imgToObject:function(z,o){var u=this,p=u.editor,C,H,j,t,I,y,G,w,k,E,s,q,A,D,m,x,l,B,F;function r(i,n){var M,L,N,K,J;J=p.getParam("flash_video_player_url",u.convertUrl(u.url+"/moxieplayer.swf"));if(J){M=p.documentBaseURI;G.params.src=J;if(p.getParam("flash_video_player_absvideourl",true)){i=M.toAbsolute(i||"",true);n=M.toAbsolute(n||"",true)}N="";L=p.getParam("flash_video_player_flashvars",{url:"$url",poster:"$poster"});tinymce.each(L,function(P,O){P=P.replace(/\$url/,i||"");P=P.replace(/\$poster/,n||"");if(P.length>0){N+=(N?"&":"")+O+"="+escape(P)}});if(N.length){G.params.flashvars=N}K=p.getParam("flash_video_player_params",{allowfullscreen:true,allowscriptaccess:true});tinymce.each(K,function(P,O){G.params[O]=""+P})}}G=z.attr("data-mce-json");if(!G){return}G=g.parse(G);q=this.getType(z.attr("class"));B=z.attr("data-mce-style");if(!B){B=z.attr("style");if(B){B=p.dom.serializeStyle(p.dom.parseStyle(B,"img"))}}if(q.name==="Iframe"){x=new b("iframe",1);tinymce.each(d,function(i){var n=z.attr(i);if(i=="class"&&n){n=n.replace(/mceItem.+ ?/g,"")}if(n&&n.length>0){x.attr(i,n)}});for(I in G.params){x.attr(I,G.params[I])}x.attr({style:B,src:G.params.src});z.replace(x);return}if(this.editor.settings.media_use_script){x=new b("script",1).attr("type","text/javascript");y=new b("#text",3);y.value="write"+q.name+"("+g.serialize(tinymce.extend(G.params,{width:z.attr("width"),height:z.attr("height")}))+");";x.append(y);z.replace(x);return}if(q.name==="Video"&&G.video.sources[0]){C=new b("video",1).attr(tinymce.extend({id:z.attr("id"),width:z.attr("width"),height:z.attr("height"),style:B},G.video.attrs));if(G.video.attrs){l=G.video.attrs.poster}k=G.video.sources=c(G.video.sources);for(A=0;A<k.length;A++){if(/\.mp4$/.test(k[A].src)){m=k[A].src}}if(!k[0].type){C.attr("src",k[0].src);k.splice(0,1)}for(A=0;A<k.length;A++){w=new b("source",1).attr(k[A]);w.shortEnded=true;C.append(w)}if(m){r(m,l);q=u.getType("flash")}else{G.params.src=""}}if(q.name==="Audio"&&G.video.sources[0]){F=new b("audio",1).attr(tinymce.extend({id:z.attr("id"),width:z.attr("width"),height:z.attr("height"),style:B},G.video.attrs));if(G.video.attrs){l=G.video.attrs.poster}k=G.video.sources=c(G.video.sources);if(!k[0].type){F.attr("src",k[0].src);k.splice(0,1)}for(A=0;A<k.length;A++){w=new b("source",1).attr(k[A]);w.shortEnded=true;F.append(w)}G.params.src=""}if(q.name==="EmbeddedAudio"){j=new b("embed",1);j.shortEnded=true;j.attr({id:z.attr("id"),width:z.attr("width"),height:z.attr("height"),style:B,type:z.attr("type")});for(I in G.params){j.attr(I,G.params[I])}tinymce.each(d,function(i){if(G[i]&&i!="type"){j.attr(i,G[i])}});G.params.src=""}if(G.params.src){if(/\.flv$/i.test(G.params.src)){r(G.params.src,"")}if(o&&o.force_absolute){G.params.src=p.documentBaseURI.toAbsolute(G.params.src)}H=new b("object",1).attr({id:z.attr("id"),width:z.attr("width"),height:z.attr("height"),style:B});tinymce.each(d,function(i){var n=G[i];if(i=="class"&&n){n=n.replace(/mceItem.+ ?/g,"")}if(n&&i!="type"){H.attr(i,n)}});for(I in G.params){s=new b("param",1);s.shortEnded=true;y=G.params[I];if(I==="src"&&q.name==="WindowsMedia"){I="url"}s.attr({name:I,value:y});H.append(s)}if(this.editor.getParam("media_strict",true)){H.attr({data:G.params.src,type:q.mimes[0]})}else{H.attr({classid:"clsid:"+q.clsids[0],codebase:q.codebase});j=new b("embed",1);j.shortEnded=true;j.attr({id:z.attr("id"),width:z.attr("width"),height:z.attr("height"),style:B,type:q.mimes[0]});for(I in G.params){j.attr(I,G.params[I])}tinymce.each(d,function(i){if(G[i]&&i!="type"){j.attr(i,G[i])}});H.append(j)}if(G.object_html){y=new b("#text",3);y.raw=true;y.value=G.object_html;H.append(y)}if(C){C.append(H)}}if(C){if(G.video_html){y=new b("#text",3);y.raw=true;y.value=G.video_html;C.append(y)}}if(F){if(G.video_html){y=new b("#text",3);y.raw=true;y.value=G.video_html;F.append(y)}}var v=C||F||H||j;if(v){z.replace(v)}else{z.remove()}},objectToImg:function(C){var L,k,F,s,M,N,y,A,x,G,E,t,q,I,B,l,K,o,H=this.lookup,m,z,v=this.editor.settings.url_converter,n=this.editor.settings.url_converter_scope,w,r,D,j;function u(i){return new tinymce.html.Serializer({inner:true,validate:false}).serialize(i)}function J(O,i){return H[(O.attr(i)||"").toLowerCase()]}function p(O){var i=O.replace(/^.*\.([^.]+)$/,"$1");return H[i.toLowerCase()||""]}if(!C.parent){return}if(C.name==="script"){if(C.firstChild){m=a.exec(C.firstChild.value)}if(!m){return}o=m[1];K={video:{},params:g.parse(m[2])};A=K.params.width;x=K.params.height}K=K||{video:{},params:{}};M=new b("img",1);M.attr({src:this.editor.theme.url+"/img/trans.gif"});N=C.name;if(N==="video"||N=="audio"){F=C;L=C.getAll("object")[0];k=C.getAll("embed")[0];A=F.attr("width");x=F.attr("height");y=F.attr("id");K.video={attrs:{},sources:[]};z=K.video.attrs;for(N in F.attributes.map){z[N]=F.attributes.map[N]}B=C.attr("src");if(B){K.video.sources.push({src:v.call(n,B,"src",C.name)})}l=F.getAll("source");for(E=0;E<l.length;E++){B=l[E].remove();K.video.sources.push({src:v.call(n,B.attr("src"),"src","source"),type:B.attr("type"),media:B.attr("media")})}if(z.poster){z.poster=v.call(n,z.poster,"poster",C.name)}}if(C.name==="object"){L=C;k=C.getAll("embed")[0]}if(C.name==="embed"){k=C}if(C.name==="iframe"){s=C;o="Iframe"}if(L){A=A||L.attr("width");x=x||L.attr("height");G=G||L.attr("style");y=y||L.attr("id");w=w||L.attr("hspace");r=r||L.attr("vspace");D=D||L.attr("align");j=j||L.attr("bgcolor");K.name=L.attr("name");I=L.getAll("param");for(E=0;E<I.length;E++){q=I[E];N=q.remove().attr("name");if(!h[N]){K.params[N]=q.attr("value")}}K.params.src=K.params.src||L.attr("data")}if(k){A=A||k.attr("width");x=x||k.attr("height");G=G||k.attr("style");y=y||k.attr("id");w=w||k.attr("hspace");r=r||k.attr("vspace");D=D||k.attr("align");j=j||k.attr("bgcolor");for(N in k.attributes.map){if(!h[N]&&!K.params[N]){K.params[N]=k.attributes.map[N]}}}if(s){A=s.attr("width");x=s.attr("height");G=G||s.attr("style");y=s.attr("id");w=s.attr("hspace");r=s.attr("vspace");D=s.attr("align");j=s.attr("bgcolor");tinymce.each(d,function(i){M.attr(i,s.attr(i))});for(N in s.attributes.map){if(!h[N]&&!K.params[N]){K.params[N]=s.attributes.map[N]}}}if(K.params.movie){K.params.src=K.params.src||K.params.movie;delete K.params.movie}if(K.params.src){K.params.src=v.call(n,K.params.src,"src","object")}if(F){if(C.name==="video"){o=H.video.name}else{if(C.name==="audio"){o=H.audio.name}}}if(L&&!o){o=(J(L,"clsid")||J(L,"classid")||J(L,"type")||{}).name}if(k&&!o){o=(J(k,"type")||p(K.params.src)||{}).name}if(k&&o=="EmbeddedAudio"){K.params.type=k.attr("type")}C.replace(M);if(k){k.remove()}if(L){t=u(L.remove());if(t){K.object_html=t}}if(F){t=u(F.remove());if(t){K.video_html=t}}K.hspace=w;K.vspace=r;K.align=D;K.bgcolor=j;M.attr({id:y,"class":"mceItemMedia mceItem"+(o||"Flash"),style:G,width:A||(C.name=="audio"?"300":"320"),height:x||(C.name=="audio"?"32":"240"),hspace:w,vspace:r,align:D,bgcolor:j,"data-mce-json":g.serialize(K,"'")})}});tinymce.PluginManager.add("media",tinymce.plugins.MediaPlugin)})(); \ No newline at end of file
+(function(){var b=tinymce.explode("id,name,width,height,style,align,class,hspace,vspace,bgcolor,type"),a=tinymce.makeMap(b.join(",")),f=tinymce.html.Node,d,i,h=tinymce.util.JSON,g;d=[["Flash","d27cdb6e-ae6d-11cf-96b8-444553540000","application/x-shockwave-flash","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["ShockWave","166b1bca-3f9c-11cf-8075-444553540000","application/x-director","http://download.macromedia.com/pub/shockwave/cabs/director/sw.cab#version=8,5,1,0"],["WindowsMedia","6bf52a52-394a-11d3-b153-00c04f79faa6,22d6f312-b0f6-11d0-94ab-0080c74c7e95,05589fa1-c356-11ce-bf01-00aa0055595a","application/x-mplayer2","http://activex.microsoft.com/activex/controls/mplayer/en/nsmp2inf.cab#Version=5,1,52,701"],["QuickTime","02bf25d5-8c17-4b23-bc80-d3488abddc6b","video/quicktime","http://www.apple.com/qtactivex/qtplugin.cab#version=6,0,2,0"],["RealMedia","cfcdaa03-8be4-11cf-b84b-0020afbbccfa","audio/x-pn-realaudio-plugin","http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,40,0"],["Java","8ad9c840-044e-11d1-b3e9-00805f499d93","application/x-java-applet","http://java.sun.com/products/plugin/autodl/jinstall-1_5_0-windows-i586.cab#Version=1,5,0,0"],["Silverlight","dfeaf541-f3e1-4c24-acac-99c30715084a","application/x-silverlight-2"],["Iframe"],["Video"],["EmbeddedAudio"],["Audio"]];function e(j){return typeof(j)=="string"?j.replace(/[^0-9%]/g,""):j}function c(m){var l,j,k;if(m&&!m.splice){j=[];for(k=0;true;k++){if(m[k]){j[k]=m[k]}else{break}}return j}return m}tinymce.create("tinymce.plugins.MediaPlugin",{init:function(n,j){var r=this,l={},m,p,q,k;function o(s){return s&&s.nodeName==="IMG"&&n.dom.hasClass(s,"mceItemMedia")}r.editor=n;r.url=j;i="";for(m=0;m<d.length;m++){k=d[m][0];q={name:k,clsids:tinymce.explode(d[m][1]||""),mimes:tinymce.explode(d[m][2]||""),codebase:d[m][3]};for(p=0;p<q.clsids.length;p++){l["clsid:"+q.clsids[p]]=q}for(p=0;p<q.mimes.length;p++){l[q.mimes[p]]=q}l["mceItem"+k]=q;l[k.toLowerCase()]=q;i+=(i?"|":"")+k}tinymce.each(n.getParam("media_types","video=mp4,m4v,ogv,webm;silverlight=xap;flash=swf,flv;shockwave=dcr;quicktime=mov,qt,mpg,mpeg;shockwave=dcr;windowsmedia=avi,wmv,wm,asf,asx,wmx,wvx;realmedia=rm,ra,ram;java=jar;audio=mp3,ogg").split(";"),function(v){var s,u,t;v=v.split(/=/);u=tinymce.explode(v[1].toLowerCase());for(s=0;s<u.length;s++){t=l[v[0].toLowerCase()];if(t){l[u[s]]=t}}});i=new RegExp("write("+i+")\\(([^)]+)\\)");r.lookup=l;n.onPreInit.add(function(){n.schema.addValidElements("object[id|style|width|height|classid|codebase|*],param[name|value],embed[id|style|width|height|type|src|*],video[*],audio[*],source[*]");n.parser.addNodeFilter("object,embed,video,audio,script,iframe",function(s){var t=s.length;while(t--){r.objectToImg(s[t])}});n.serializer.addNodeFilter("img",function(s,u,t){var v=s.length,w;while(v--){w=s[v];if((w.attr("class")||"").indexOf("mceItemMedia")!==-1){r.imgToObject(w,t)}}})});n.onInit.add(function(){if(n.theme&&n.theme.onResolveName){n.theme.onResolveName.add(function(s,t){if(t.name==="img"&&n.dom.hasClass(t.node,"mceItemMedia")){t.name="media"}})}if(n&&n.plugins.contextmenu){n.plugins.contextmenu.onContextMenu.add(function(t,u,s){if(s.nodeName==="IMG"&&s.className.indexOf("mceItemMedia")!==-1){u.add({title:"media.edit",icon:"media",cmd:"mceMedia"})}})}});n.addCommand("mceMedia",function(){var t,s;s=n.selection.getNode();if(o(s)){t=n.dom.getAttrib(s,"data-mce-json");if(t){t=h.parse(t);tinymce.each(b,function(u){var v=n.dom.getAttrib(s,u);if(v){t[u]=v}});t.type=r.getType(s.className).name.toLowerCase()}}if(!t){t={type:"flash",video:{sources:[]},params:{}}}n.windowManager.open({file:j+"/media.htm",width:430+parseInt(n.getLang("media.delta_width",0)),height:500+parseInt(n.getLang("media.delta_height",0)),inline:1},{plugin_url:j,data:t})});n.addButton("media",{title:"media.desc",cmd:"mceMedia"});n.onNodeChange.add(function(t,s,u){s.setActive("media",o(u))})},convertUrl:function(l,o){var k=this,n=k.editor,m=n.settings,p=m.url_converter,j=m.url_converter_scope||k;if(!l){return l}if(o){return n.documentBaseURI.toAbsolute(l)}return p.call(j,l,"src","object")},getInfo:function(){return{longname:"Media",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/media",version:tinymce.majorVersion+"."+tinymce.minorVersion}},dataToImg:function(m,k){var r=this,o=r.editor,p=o.documentBaseURI,j,q,n,l;m.params.src=r.convertUrl(m.params.src,k);q=m.video.attrs;if(q){q.src=r.convertUrl(q.src,k)}if(q){q.poster=r.convertUrl(q.poster,k)}j=c(m.video.sources);if(j){for(l=0;l<j.length;l++){j[l].src=r.convertUrl(j[l].src,k)}}n=r.editor.dom.create("img",{id:m.id,style:m.style,align:m.align,hspace:m.hspace,vspace:m.vspace,src:r.editor.theme.url+"/img/trans.gif","class":"mceItemMedia mceItem"+r.getType(m.type).name,"data-mce-json":h.serialize(m,"'")});n.width=m.width=e(m.width||(m.type=="audio"?"300":"320"));n.height=m.height=e(m.height||(m.type=="audio"?"32":"240"));return n},dataToHtml:function(j,k){return this.editor.serializer.serialize(this.dataToImg(j,k),{forced_root_block:"",force_absolute:k})},htmlToData:function(l){var k,j,m;m={type:"flash",video:{sources:[]},params:{}};k=this.editor.parser.parse(l);j=k.getAll("img")[0];if(j){m=h.parse(j.attr("data-mce-json"));m.type=this.getType(j.attr("class")).name.toLowerCase();tinymce.each(b,function(n){var o=j.attr(n);if(o){m[n]=o}})}return m},getType:function(m){var k,j,l;j=tinymce.explode(m," ");for(k=0;k<j.length;k++){l=this.lookup[j[k]];if(l){return l}}},imgToObject:function(z,o){var u=this,p=u.editor,C,H,j,t,I,y,G,w,k,E,s,q,A,D,m,x,l,B,F;function r(n,J){var N,M,O,L,K;K=p.getParam("flash_video_player_url",u.convertUrl(u.url+"/moxieplayer.swf"));if(K){N=p.documentBaseURI;G.params.src=K;if(p.getParam("flash_video_player_absvideourl",true)){n=N.toAbsolute(n||"",true);J=N.toAbsolute(J||"",true)}O="";M=p.getParam("flash_video_player_flashvars",{url:"$url",poster:"$poster"});tinymce.each(M,function(Q,P){Q=Q.replace(/\$url/,n||"");Q=Q.replace(/\$poster/,J||"");if(Q.length>0){O+=(O?"&":"")+P+"="+escape(Q)}});if(O.length){G.params.flashvars=O}L=p.getParam("flash_video_player_params",{allowfullscreen:true,allowscriptaccess:true});tinymce.each(L,function(Q,P){G.params[P]=""+Q})}}G=z.attr("data-mce-json");if(!G){return}G=h.parse(G);q=this.getType(z.attr("class"));B=z.attr("data-mce-style");if(!B){B=z.attr("style");if(B){B=p.dom.serializeStyle(p.dom.parseStyle(B,"img"))}}G.width=z.attr("width")||G.width;G.height=z.attr("height")||G.height;if(q.name==="Iframe"){x=new f("iframe",1);tinymce.each(b,function(n){var J=z.attr(n);if(n=="class"&&J){J=J.replace(/mceItem.+ ?/g,"")}if(J&&J.length>0){x.attr(n,J)}});for(I in G.params){x.attr(I,G.params[I])}x.attr({style:B,src:G.params.src});z.replace(x);return}if(this.editor.settings.media_use_script){x=new f("script",1).attr("type","text/javascript");y=new f("#text",3);y.value="write"+q.name+"("+h.serialize(tinymce.extend(G.params,{width:z.attr("width"),height:z.attr("height")}))+");";x.append(y);z.replace(x);return}if(q.name==="Video"&&G.video.sources[0]){C=new f("video",1).attr(tinymce.extend({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B},G.video.attrs));if(G.video.attrs){l=G.video.attrs.poster}k=G.video.sources=c(G.video.sources);for(A=0;A<k.length;A++){if(/\.mp4$/.test(k[A].src)){m=k[A].src}}if(!k[0].type){C.attr("src",k[0].src);k.splice(0,1)}for(A=0;A<k.length;A++){w=new f("source",1).attr(k[A]);w.shortEnded=true;C.append(w)}if(m){r(m,l);q=u.getType("flash")}else{G.params.src=""}}if(q.name==="Audio"&&G.video.sources[0]){F=new f("audio",1).attr(tinymce.extend({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B},G.video.attrs));if(G.video.attrs){l=G.video.attrs.poster}k=G.video.sources=c(G.video.sources);if(!k[0].type){F.attr("src",k[0].src);k.splice(0,1)}for(A=0;A<k.length;A++){w=new f("source",1).attr(k[A]);w.shortEnded=true;F.append(w)}G.params.src=""}if(q.name==="EmbeddedAudio"){j=new f("embed",1);j.shortEnded=true;j.attr({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B,type:z.attr("type")});for(I in G.params){j.attr(I,G.params[I])}tinymce.each(b,function(n){if(G[n]&&n!="type"){j.attr(n,G[n])}});G.params.src=""}if(G.params.src){if(/\.flv$/i.test(G.params.src)){r(G.params.src,"")}if(o&&o.force_absolute){G.params.src=p.documentBaseURI.toAbsolute(G.params.src)}H=new f("object",1).attr({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B});tinymce.each(b,function(n){var J=G[n];if(n=="class"&&J){J=J.replace(/mceItem.+ ?/g,"")}if(J&&n!="type"){H.attr(n,J)}});for(I in G.params){s=new f("param",1);s.shortEnded=true;y=G.params[I];if(I==="src"&&q.name==="WindowsMedia"){I="url"}s.attr({name:I,value:y});H.append(s)}if(this.editor.getParam("media_strict",true)){H.attr({data:G.params.src,type:q.mimes[0]})}else{H.attr({classid:"clsid:"+q.clsids[0],codebase:q.codebase});j=new f("embed",1);j.shortEnded=true;j.attr({id:z.attr("id"),width:e(z.attr("width")),height:e(z.attr("height")),style:B,type:q.mimes[0]});for(I in G.params){j.attr(I,G.params[I])}tinymce.each(b,function(n){if(G[n]&&n!="type"){j.attr(n,G[n])}});H.append(j)}if(G.object_html){y=new f("#text",3);y.raw=true;y.value=G.object_html;H.append(y)}if(C){C.append(H)}}if(C){if(G.video_html){y=new f("#text",3);y.raw=true;y.value=G.video_html;C.append(y)}}if(F){if(G.video_html){y=new f("#text",3);y.raw=true;y.value=G.video_html;F.append(y)}}var v=C||F||H||j;if(v){z.replace(v)}else{z.remove()}},objectToImg:function(C){var L,k,F,s,M,N,y,A,x,G,E,t,q,I,B,l,K,o,H=this.lookup,m,z,v=this.editor.settings.url_converter,n=this.editor.settings.url_converter_scope,w,r,D,j;function u(O){return new tinymce.html.Serializer({inner:true,validate:false}).serialize(O)}function J(P,O){return H[(P.attr(O)||"").toLowerCase()]}function p(P){var O=P.replace(/^.*\.([^.]+)$/,"$1");return H[O.toLowerCase()||""]}if(!C.parent){return}if(C.name==="script"){if(C.firstChild){m=i.exec(C.firstChild.value)}if(!m){return}o=m[1];K={video:{},params:h.parse(m[2])};A=K.params.width;x=K.params.height}K=K||{video:{},params:{}};M=new f("img",1);M.attr({src:this.editor.theme.url+"/img/trans.gif"});N=C.name;if(N==="video"||N=="audio"){F=C;L=C.getAll("object")[0];k=C.getAll("embed")[0];A=F.attr("width");x=F.attr("height");y=F.attr("id");K.video={attrs:{},sources:[]};z=K.video.attrs;for(N in F.attributes.map){z[N]=F.attributes.map[N]}B=C.attr("src");if(B){K.video.sources.push({src:v.call(n,B,"src",C.name)})}l=F.getAll("source");for(E=0;E<l.length;E++){B=l[E].remove();K.video.sources.push({src:v.call(n,B.attr("src"),"src","source"),type:B.attr("type"),media:B.attr("media")})}if(z.poster){z.poster=v.call(n,z.poster,"poster",C.name)}}if(C.name==="object"){L=C;k=C.getAll("embed")[0]}if(C.name==="embed"){k=C}if(C.name==="iframe"){s=C;o="Iframe"}if(L){A=A||L.attr("width");x=x||L.attr("height");G=G||L.attr("style");y=y||L.attr("id");w=w||L.attr("hspace");r=r||L.attr("vspace");D=D||L.attr("align");j=j||L.attr("bgcolor");K.name=L.attr("name");I=L.getAll("param");for(E=0;E<I.length;E++){q=I[E];N=q.remove().attr("name");if(!a[N]){K.params[N]=q.attr("value")}}K.params.src=K.params.src||L.attr("data")}if(k){A=A||k.attr("width");x=x||k.attr("height");G=G||k.attr("style");y=y||k.attr("id");w=w||k.attr("hspace");r=r||k.attr("vspace");D=D||k.attr("align");j=j||k.attr("bgcolor");for(N in k.attributes.map){if(!a[N]&&!K.params[N]){K.params[N]=k.attributes.map[N]}}}if(s){A=e(s.attr("width"));x=e(s.attr("height"));G=G||s.attr("style");y=s.attr("id");w=s.attr("hspace");r=s.attr("vspace");D=s.attr("align");j=s.attr("bgcolor");tinymce.each(b,function(O){M.attr(O,s.attr(O))});for(N in s.attributes.map){if(!a[N]&&!K.params[N]){K.params[N]=s.attributes.map[N]}}}if(K.params.movie){K.params.src=K.params.src||K.params.movie;delete K.params.movie}if(K.params.src){K.params.src=v.call(n,K.params.src,"src","object")}if(F){if(C.name==="video"){o=H.video.name}else{if(C.name==="audio"){o=H.audio.name}}}if(L&&!o){o=(J(L,"clsid")||J(L,"classid")||J(L,"type")||{}).name}if(k&&!o){o=(J(k,"type")||p(K.params.src)||{}).name}if(k&&o=="EmbeddedAudio"){K.params.type=k.attr("type")}C.replace(M);if(k){k.remove()}if(L){t=u(L.remove());if(t){K.object_html=t}}if(F){t=u(F.remove());if(t){K.video_html=t}}K.hspace=w;K.vspace=r;K.align=D;K.bgcolor=j;M.attr({id:y,"class":"mceItemMedia mceItem"+(o||"Flash"),style:G,width:A||(C.name=="audio"?"300":"320"),height:x||(C.name=="audio"?"32":"240"),hspace:w,vspace:r,align:D,bgcolor:j,"data-mce-json":h.serialize(K,"'")})}});tinymce.PluginManager.add("media",tinymce.plugins.MediaPlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin_src.js
index ea79db18a..33a58050e 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/editor_plugin_src.js
@@ -28,6 +28,10 @@
["Audio"]
];
+ function normalizeSize(size) {
+ return typeof(size) == "string" ? size.replace(/[^0-9%]/g, '') : size;
+ }
+
function toArray(obj) {
var undef, out, i;
@@ -258,8 +262,8 @@
'data-mce-json' : JSON.serialize(data, "'")
});
- img.width = data.width || (data.type == 'audio' ? "300" : "320");
- img.height = data.height || (data.type == 'audio' ? "32" : "240");
+ img.width = data.width = normalizeSize(data.width || (data.type == 'audio' ? "300" : "320"));
+ img.height = data.height = normalizeSize(data.height || (data.type == 'audio' ? "32" : "240"));
return img;
},
@@ -378,7 +382,7 @@
data = JSON.parse(data);
typeItem = this.getType(node.attr('class'));
- style = node.attr('data-mce-style')
+ style = node.attr('data-mce-style');
if (!style) {
style = node.attr('style');
@@ -386,6 +390,10 @@
style = editor.dom.serializeStyle(editor.dom.parseStyle(style, 'img'));
}
+ // Use node width/height to override the data width/height when the placeholder is resized
+ data.width = node.attr('width') || data.width;
+ data.height = node.attr('height') || data.height;
+
// Handle iframe
if (typeItem.name === 'Iframe') {
replacement = new Node('iframe', 1);
@@ -434,8 +442,8 @@
// Create new object element
video = new Node('video', 1).attr(tinymce.extend({
id : node.attr('id'),
- width: node.attr('width'),
- height: node.attr('height'),
+ width: normalizeSize(node.attr('width')),
+ height: normalizeSize(node.attr('height')),
style : style
}, data.video.attrs));
@@ -473,8 +481,8 @@
// Create new object element
audio = new Node('audio', 1).attr(tinymce.extend({
id : node.attr('id'),
- width: node.attr('width'),
- height: node.attr('height'),
+ width: normalizeSize(node.attr('width')),
+ height: normalizeSize(node.attr('height')),
style : style
}, data.video.attrs));
@@ -502,8 +510,8 @@
embed.shortEnded = true;
embed.attr({
id: node.attr('id'),
- width: node.attr('width'),
- height: node.attr('height'),
+ width: normalizeSize(node.attr('width')),
+ height: normalizeSize(node.attr('height')),
style : style,
type: node.attr('type')
});
@@ -531,8 +539,8 @@
// Create new object element
object = new Node('object', 1).attr({
id : node.attr('id'),
- width: node.attr('width'),
- height: node.attr('height'),
+ width: normalizeSize(node.attr('width')),
+ height: normalizeSize(node.attr('height')),
style : style
});
@@ -576,8 +584,8 @@
embed.shortEnded = true;
embed.attr({
id: node.attr('id'),
- width: node.attr('width'),
- height: node.attr('height'),
+ width: normalizeSize(node.attr('width')),
+ height: normalizeSize(node.attr('height')),
style : style,
type: typeItem.mimes[0]
});
@@ -793,8 +801,8 @@
if (iframe) {
// Get width/height
- width = iframe.attr('width');
- height = iframe.attr('height');
+ width = normalizeSize(iframe.attr('width'));
+ height = normalizeSize(iframe.attr('height'));
style = style || iframe.attr('style');
id = iframe.attr('id');
hspace = iframe.attr('hspace');
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/js/media.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/js/media.js
index 45d88fe1b..89cea2a41 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/js/media.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/media/js/media.js
@@ -48,7 +48,7 @@
}
function setVal(id, value, name) {
- if (typeof(value) != 'undefined') {
+ if (typeof(value) != 'undefined' && value != null) {
var elm = get(id);
if (elm.nodeName == "SELECT")
@@ -78,7 +78,7 @@
get('video_altsource2_filebrowser').innerHTML = getBrowserHTML('video_filebrowser_altsource2','video_altsource2','media','media');
get('audio_altsource1_filebrowser').innerHTML = getBrowserHTML('audio_filebrowser_altsource1','audio_altsource1','media','media');
get('audio_altsource2_filebrowser').innerHTML = getBrowserHTML('audio_filebrowser_altsource2','audio_altsource2','media','media');
- get('video_poster_filebrowser').innerHTML = getBrowserHTML('filebrowser_poster','video_poster','media','image');
+ get('video_poster_filebrowser').innerHTML = getBrowserHTML('filebrowser_poster','video_poster','image','media');
html = self.getMediaListHTML('medialist', 'src', 'media', 'media');
if (html == "")
@@ -176,14 +176,14 @@
formItemName = type == 'global' ? name : type + '_' + name;
if (type == 'global')
- list = data;
- else if (type == 'video' || type == 'audio') {
+ list = data;
+ else if (type == 'video' || type == 'audio') {
list = data.video.attrs;
if (!list && !to_form)
- data.video.attrs = list = {};
+ data.video.attrs = list = {};
} else
- list = data.params;
+ list = data.params;
if (list) {
if (to_form) {
@@ -295,34 +295,77 @@
} else {
src = getVal("src");
- // YouTube *NEW*
- if (src.match(/youtu.be\/[a-z1-9.-_]+/)) {
+ // YouTube Embed
+ if (src.match(/youtube\.com\/embed\/\w+/)) {
data.width = 425;
data.height = 350;
data.params.frameborder = '0';
data.type = 'iframe';
- src = 'http://www.youtube.com/embed/' + src.match(/youtu.be\/([a-z1-9.-_]+)/)[1];
setVal('src', src);
setVal('media_type', data.type);
+ } else {
+ // YouTube *NEW*
+ if (src.match(/youtu\.be\/[a-z1-9.-_]+/)) {
+ data.width = 425;
+ data.height = 350;
+ data.params.frameborder = '0';
+ data.type = 'iframe';
+ src = 'http://www.youtube.com/embed/' + src.match(/youtu.be\/([a-z1-9.-_]+)/)[1];
+ setVal('src', src);
+ setVal('media_type', data.type);
+ }
+
+ // YouTube
+ if (src.match(/youtube\.com(.+)v=([^&]+)/)) {
+ data.width = 425;
+ data.height = 350;
+ data.params.frameborder = '0';
+ data.type = 'iframe';
+ src = 'http://www.youtube.com/embed/' + src.match(/v=([^&]+)/)[1];
+ setVal('src', src);
+ setVal('media_type', data.type);
+ }
}
- // YouTube
- if (src.match(/youtube.com(.+)v=([^&]+)/)) {
+ // Google video
+ if (src.match(/video\.google\.com(.+)docid=([^&]+)/)) {
+ data.width = 425;
+ data.height = 326;
+ data.type = 'flash';
+ src = 'http://video.google.com/googleplayer.swf?docId=' + src.match(/docid=([^&]+)/)[1] + '&hl=en';
+ setVal('src', src);
+ setVal('media_type', data.type);
+ }
+
+ // Vimeo
+ if (src.match(/vimeo\.com\/([0-9]+)/)) {
data.width = 425;
data.height = 350;
data.params.frameborder = '0';
data.type = 'iframe';
- src = 'http://www.youtube.com/embed/' + src.match(/v=([^&]+)/)[1];
+ src = 'http://player.vimeo.com/video/' + src.match(/vimeo.com\/([0-9]+)/)[1];
setVal('src', src);
setVal('media_type', data.type);
}
-
- // Google video
- if (src.match(/video.google.com(.+)docid=([^&]+)/)) {
+
+ // stream.cz
+ if (src.match(/stream\.cz\/((?!object).)*\/([0-9]+)/)) {
data.width = 425;
- data.height = 326;
- data.type = 'flash';
- src = 'http://video.google.com/googleplayer.swf?docId=' + src.match(/docid=([^&]+)/)[1] + '&hl=en';
+ data.height = 350;
+ data.params.frameborder = '0';
+ data.type = 'iframe';
+ src = 'http://www.stream.cz/object/' + src.match(/stream.cz\/[^/]+\/([0-9]+)/)[1];
+ setVal('src', src);
+ setVal('media_type', data.type);
+ }
+
+ // Google maps
+ if (src.match(/maps\.google\.([a-z]{2,3})\/maps\/(.+)msid=(.+)/)) {
+ data.width = 425;
+ data.height = 350;
+ data.params.frameborder = '0';
+ data.type = 'iframe';
+ src = 'http://maps.google.com/maps/ms?msid=' + src.match(/msid=(.+)/)[1] + "&output=embed";
setVal('src', src);
setVal('media_type', data.type);
}
@@ -427,24 +470,30 @@
},
getMediaTypeHTML : function(editor) {
- function option(media_type){
+ function option(media_type, element) {
+ if (!editor.schema.getElementRule(element || media_type)) {
+ return '';
+ }
+
return '<option value="'+media_type+'">'+tinyMCEPopup.editor.translate("media_dlg."+media_type)+'</option>'
}
+
var html = "";
+
html += '<select id="media_type" name="media_type" onchange="Media.formToData(\'type\');">';
html += option("video");
html += option("audio");
- html += option("flash");
- html += option("quicktime");
- html += option("shockwave");
- html += option("windowsmedia");
- html += option("realmedia");
+ html += option("flash", "object");
+ html += option("quicktime", "object");
+ html += option("shockwave", "object");
+ html += option("windowsmedia", "object");
+ html += option("realmedia", "object");
html += option("iframe");
if (editor.getParam('media_embedded_audio', false)) {
- html += option('embeddedaudio');
+ html += option('embeddedaudio', "object");
}
-
+
html += '</select>';
return html;
},
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin.js
index 2d60138ee..da411ebc0 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin.js
@@ -1 +1 @@
-(function(){var a=tinymce.dom.Event;tinymce.create("tinymce.plugins.NonEditablePlugin",{init:function(d,e){var f=this,c,b,g;f.editor=d;c=d.getParam("noneditable_editable_class","mceEditable");b=d.getParam("noneditable_noneditable_class","mceNonEditable");d.onNodeChange.addToTop(function(i,h,l){var k,j;k=i.dom.getParent(i.selection.getStart(),function(m){return i.dom.hasClass(m,b)});j=i.dom.getParent(i.selection.getEnd(),function(m){return i.dom.hasClass(m,b)});if(k||j){g=1;f._setDisabled(1);return false}else{if(g==1){f._setDisabled(0);g=0}}})},getInfo:function(){return{longname:"Non editable elements",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/noneditable",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_block:function(c,d){var b=d.keyCode;if((b>32&&b<41)||(b>111&&b<124)){return}return a.cancel(d)},_setDisabled:function(d){var c=this,b=c.editor;tinymce.each(b.controlManager.controls,function(e){e.setDisabled(d)});if(d!==c.disabled){if(d){b.onKeyDown.addToTop(c._block);b.onKeyPress.addToTop(c._block);b.onKeyUp.addToTop(c._block);b.onPaste.addToTop(c._block);b.onContextMenu.addToTop(c._block)}else{b.onKeyDown.remove(c._block);b.onKeyPress.remove(c._block);b.onKeyUp.remove(c._block);b.onPaste.remove(c._block);b.onContextMenu.remove(c._block)}c.disabled=d}}});tinymce.PluginManager.add("noneditable",tinymce.plugins.NonEditablePlugin)})(); \ No newline at end of file
+(function(){var c=tinymce.dom.TreeWalker;var a="contenteditable",d="data-mce-"+a;var e=tinymce.VK;function b(n){var j=n.dom,p=n.selection,r,o="mce_noneditablecaret",r="\uFEFF";function m(t){var s;if(t.nodeType===1){s=t.getAttribute(d);if(s&&s!=="inherit"){return s}s=t.contentEditable;if(s!=="inherit"){return s}}return null}function g(s){var t;while(s){t=m(s);if(t){return t==="false"?s:null}s=s.parentNode}}function l(s){while(s){if(s.id===o){return s}s=s.parentNode}}function k(s){var t;if(s){t=new c(s,s);for(s=t.current();s;s=t.next()){if(s.nodeType===3){return s}}}}function f(v,u){var s,t;if(m(v)==="false"){if(j.isBlock(v)){p.select(v);return}}t=j.createRng();if(m(v)==="true"){if(!v.firstChild){v.appendChild(n.getDoc().createTextNode("\u00a0"))}v=v.firstChild;u=true}s=j.create("span",{id:o,"data-mce-bogus":true},r);if(u){v.parentNode.insertBefore(s,v)}else{j.insertAfter(s,v)}t.setStart(s.firstChild,1);t.collapse(true);p.setRng(t);return s}function i(s){var v,t,u;if(s){rng=p.getRng(true);rng.setStartBefore(s);rng.setEndBefore(s);v=k(s);if(v&&v.nodeValue.charAt(0)==r){v=v.deleteData(0,1)}j.remove(s,true);p.setRng(rng)}else{t=l(p.getStart());while((s=j.get(o))&&s!==u){if(t!==s){v=k(s);if(v&&v.nodeValue.charAt(0)==r){v=v.deleteData(0,1)}j.remove(s,true)}u=s}}}function q(){var s,w,u,t,v;function x(B,D){var A,F,E,C,z;A=t.startContainer;F=t.startOffset;if(A.nodeType==3){z=A.nodeValue.length;if((F>0&&F<z)||(D?F==z:F==0)){return}}else{if(F<A.childNodes.length){var G=!D&&F>0?F-1:F;A=A.childNodes[G];if(A.hasChildNodes()){A=A.firstChild}}else{return !D?B:null}}E=new c(A,B);while(C=E[D?"prev":"next"]()){if(C.nodeType===3&&C.nodeValue.length>0){return}else{if(m(C)==="true"){return C}}}return B}i();u=p.isCollapsed();s=g(p.getStart());w=g(p.getEnd());if(s||w){t=p.getRng(true);if(u){s=s||w;var y=p.getStart();if(v=x(s,true)){f(v,true)}else{if(v=x(s,false)){f(v,false)}else{p.select(s)}}}else{t=p.getRng(true);if(s){t.setStartBefore(s)}if(w){t.setEndAfter(w)}p.setRng(t)}}}function h(z,B){var F=B.keyCode,x,C,D,v;function u(H,G){while(H=H[G?"previousSibling":"nextSibling"]){if(H.nodeType!==3||H.nodeValue.length>0){return H}}}function y(G,H){p.select(G);p.collapse(H)}function t(K){var J,I,M,H;function G(O){var N=I;while(N){if(N===O){return}N=N.parentNode}j.remove(O);q()}function L(){var O,P,N=z.schema.getNonEmptyElements();P=new tinymce.dom.TreeWalker(I,z.getBody());while(O=(K?P.prev():P.next())){if(N[O.nodeName.toLowerCase()]){break}if(O.nodeType===3&&tinymce.trim(O.nodeValue).length>0){break}if(m(O)==="false"){G(O);return true}}if(g(O)){return true}return false}if(p.isCollapsed()){J=p.getRng(true);I=J.startContainer;M=J.startOffset;I=l(I)||I;if(H=g(I)){G(H);return false}if(I.nodeType==3&&(K?M>0:M<I.nodeValue.length)){return true}if(I.nodeType==1){I=I.childNodes[M]||I}if(L()){return false}}return true}D=p.getStart();v=p.getEnd();x=g(D)||g(v);if(x&&(F<112||F>124)&&F!=e.DELETE&&F!=e.BACKSPACE){if((tinymce.isMac?B.metaKey:B.ctrlKey)&&(F==67||F==88||F==86)){return}B.preventDefault();if(F==e.LEFT||F==e.RIGHT){var w=F==e.LEFT;if(z.dom.isBlock(x)){var A=w?x.previousSibling:x.nextSibling;var s=new c(A,A);var E=w?s.prev():s.next();y(E,!w)}else{y(x,w)}}}else{if(F==e.LEFT||F==e.RIGHT||F==e.BACKSPACE||F==e.DELETE){C=l(D);if(C){if(F==e.LEFT||F==e.BACKSPACE){x=u(C,true);if(x&&m(x)==="false"){B.preventDefault();if(F==e.LEFT){y(x,true)}else{j.remove(x);return}}else{i(C)}}if(F==e.RIGHT||F==e.DELETE){x=u(C);if(x&&m(x)==="false"){B.preventDefault();if(F==e.RIGHT){y(x,false)}else{j.remove(x);return}}else{i(C)}}}if((F==e.BACKSPACE||F==e.DELETE)&&!t(F==e.BACKSPACE)){B.preventDefault();return false}}}}n.onMouseDown.addToTop(function(s,u){var t=s.selection.getNode();if(m(t)==="false"&&t==u.target){q()}});n.onMouseUp.addToTop(q);n.onKeyDown.addToTop(h);n.onKeyUp.addToTop(q)}tinymce.create("tinymce.plugins.NonEditablePlugin",{init:function(i,k){var h,g,j;function f(m,n){var o=j.length,p=n.content,l=tinymce.trim(g);if(n.format=="raw"){return}while(o--){p=p.replace(j[o],function(s){var r=arguments,q=r[r.length-2];if(q>0&&p.charAt(q-1)=='"'){return s}return'<span class="'+l+'" data-mce-content="'+m.dom.encode(r[0])+'">'+m.dom.encode(typeof(r[1])==="string"?r[1]:r[0])+"</span>"})}n.content=p}h=" "+tinymce.trim(i.getParam("noneditable_editable_class","mceEditable"))+" ";g=" "+tinymce.trim(i.getParam("noneditable_noneditable_class","mceNonEditable"))+" ";j=i.getParam("noneditable_regexp");if(j&&!j.length){j=[j]}i.onPreInit.add(function(){b(i);if(j){i.selection.onBeforeSetContent.add(f);i.onBeforeSetContent.add(f)}i.parser.addAttributeFilter("class",function(l){var m=l.length,n,o;while(m--){o=l[m];n=" "+o.attr("class")+" ";if(n.indexOf(h)!==-1){o.attr(d,"true")}else{if(n.indexOf(g)!==-1){o.attr(d,"false")}}}});i.serializer.addAttributeFilter(d,function(l,m){var n=l.length,o;while(n--){o=l[n];if(j&&o.attr("data-mce-content")){o.name="#text";o.type=3;o.raw=true;o.value=o.attr("data-mce-content")}else{o.attr(a,null);o.attr(d,null)}}});i.parser.addAttributeFilter(a,function(l,m){var n=l.length,o;while(n--){o=l[n];o.attr(d,o.attr(a));o.attr(a,null)}})})},getInfo:function(){return{longname:"Non editable elements",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/noneditable",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("noneditable",tinymce.plugins.NonEditablePlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin_src.js
index 916dce29c..a18bcd786 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/noneditable/editor_plugin_src.js
@@ -9,37 +9,515 @@
*/
(function() {
- var Event = tinymce.dom.Event;
+ var TreeWalker = tinymce.dom.TreeWalker;
+ var externalName = 'contenteditable', internalName = 'data-mce-' + externalName;
+ var VK = tinymce.VK;
+
+ function handleContentEditableSelection(ed) {
+ var dom = ed.dom, selection = ed.selection, invisibleChar, caretContainerId = 'mce_noneditablecaret', invisibleChar = '\uFEFF';
+
+ // Returns the content editable state of a node "true/false" or null
+ function getContentEditable(node) {
+ var contentEditable;
+
+ // Ignore non elements
+ if (node.nodeType === 1) {
+ // Check for fake content editable
+ contentEditable = node.getAttribute(internalName);
+ if (contentEditable && contentEditable !== "inherit") {
+ return contentEditable;
+ }
+
+ // Check for real content editable
+ contentEditable = node.contentEditable;
+ if (contentEditable !== "inherit") {
+ return contentEditable;
+ }
+ }
+
+ return null;
+ };
+
+ // Returns the noneditable parent or null if there is a editable before it or if it wasn't found
+ function getNonEditableParent(node) {
+ var state;
+
+ while (node) {
+ state = getContentEditable(node);
+ if (state) {
+ return state === "false" ? node : null;
+ }
+
+ node = node.parentNode;
+ }
+ };
+
+ // Get caret container parent for the specified node
+ function getParentCaretContainer(node) {
+ while (node) {
+ if (node.id === caretContainerId) {
+ return node;
+ }
+
+ node = node.parentNode;
+ }
+ };
+
+ // Finds the first text node in the specified node
+ function findFirstTextNode(node) {
+ var walker;
+
+ if (node) {
+ walker = new TreeWalker(node, node);
+
+ for (node = walker.current(); node; node = walker.next()) {
+ if (node.nodeType === 3) {
+ return node;
+ }
+ }
+ }
+ };
+
+ // Insert caret container before/after target or expand selection to include block
+ function insertCaretContainerOrExpandToBlock(target, before) {
+ var caretContainer, rng;
+
+ // Select block
+ if (getContentEditable(target) === "false") {
+ if (dom.isBlock(target)) {
+ selection.select(target);
+ return;
+ }
+ }
+
+ rng = dom.createRng();
+
+ if (getContentEditable(target) === "true") {
+ if (!target.firstChild) {
+ target.appendChild(ed.getDoc().createTextNode('\u00a0'));
+ }
+
+ target = target.firstChild;
+ before = true;
+ }
+
+ //caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style:'border: 1px solid red'}, invisibleChar);
+ caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true}, invisibleChar);
+
+ if (before) {
+ target.parentNode.insertBefore(caretContainer, target);
+ } else {
+ dom.insertAfter(caretContainer, target);
+ }
+
+ rng.setStart(caretContainer.firstChild, 1);
+ rng.collapse(true);
+ selection.setRng(rng);
+
+ return caretContainer;
+ };
+
+ // Removes any caret container except the one we might be in
+ function removeCaretContainer(caretContainer) {
+ var child, currentCaretContainer, lastContainer;
+
+ if (caretContainer) {
+ rng = selection.getRng(true);
+ rng.setStartBefore(caretContainer);
+ rng.setEndBefore(caretContainer);
+
+ child = findFirstTextNode(caretContainer);
+ if (child && child.nodeValue.charAt(0) == invisibleChar) {
+ child = child.deleteData(0, 1);
+ }
+
+ dom.remove(caretContainer, true);
+
+ selection.setRng(rng);
+ } else {
+ currentCaretContainer = getParentCaretContainer(selection.getStart());
+ while ((caretContainer = dom.get(caretContainerId)) && caretContainer !== lastContainer) {
+ if (currentCaretContainer !== caretContainer) {
+ child = findFirstTextNode(caretContainer);
+ if (child && child.nodeValue.charAt(0) == invisibleChar) {
+ child = child.deleteData(0, 1);
+ }
+
+ dom.remove(caretContainer, true);
+ }
+
+ lastContainer = caretContainer;
+ }
+ }
+ };
+
+ // Modifies the selection to include contentEditable false elements or insert caret containers
+ function moveSelection() {
+ var nonEditableStart, nonEditableEnd, isCollapsed, rng, element;
+
+ // Checks if there is any contents to the left/right side of caret returns the noneditable element or any editable element if it finds one inside
+ function hasSideContent(element, left) {
+ var container, offset, walker, node, len;
+
+ container = rng.startContainer;
+ offset = rng.startOffset;
+
+ // If endpoint is in middle of text node then expand to beginning/end of element
+ if (container.nodeType == 3) {
+ len = container.nodeValue.length;
+ if ((offset > 0 && offset < len) || (left ? offset == len : offset == 0)) {
+ return;
+ }
+ } else {
+ // Can we resolve the node by index
+ if (offset < container.childNodes.length) {
+ // Browser represents caret position as the offset at the start of an element. When moving right
+ // this is the element we are moving into so we consider our container to be child node at offset-1
+ var pos = !left && offset > 0 ? offset-1 : offset;
+ container = container.childNodes[pos];
+ if (container.hasChildNodes()) {
+ container = container.firstChild;
+ }
+ } else {
+ // If not then the caret is at the last position in it's container and the caret container should be inserted after the noneditable element
+ return !left ? element : null;
+ }
+ }
+
+ // Walk left/right to look for contents
+ walker = new TreeWalker(container, element);
+ while (node = walker[left ? 'prev' : 'next']()) {
+ if (node.nodeType === 3 && node.nodeValue.length > 0) {
+ return;
+ } else if (getContentEditable(node) === "true") {
+ // Found contentEditable=true element return this one to we can move the caret inside it
+ return node;
+ }
+ }
+
+ return element;
+ };
+
+ // Remove any existing caret containers
+ removeCaretContainer();
+
+ // Get noneditable start/end elements
+ isCollapsed = selection.isCollapsed();
+ nonEditableStart = getNonEditableParent(selection.getStart());
+ nonEditableEnd = getNonEditableParent(selection.getEnd());
+
+ // Is any fo the range endpoints noneditable
+ if (nonEditableStart || nonEditableEnd) {
+ rng = selection.getRng(true);
+
+ // If it's a caret selection then look left/right to see if we need to move the caret out side or expand
+ if (isCollapsed) {
+ nonEditableStart = nonEditableStart || nonEditableEnd;
+ var start = selection.getStart();
+ if (element = hasSideContent(nonEditableStart, true)) {
+ // We have no contents to the left of the caret then insert a caret container before the noneditable element
+ insertCaretContainerOrExpandToBlock(element, true);
+ } else if (element = hasSideContent(nonEditableStart, false)) {
+ // We have no contents to the right of the caret then insert a caret container after the noneditable element
+ insertCaretContainerOrExpandToBlock(element, false);
+ } else {
+ // We are in the middle of a noneditable so expand to select it
+ selection.select(nonEditableStart);
+ }
+ } else {
+ rng = selection.getRng(true);
+
+ // Expand selection to include start non editable element
+ if (nonEditableStart) {
+ rng.setStartBefore(nonEditableStart);
+ }
+
+ // Expand selection to include end non editable element
+ if (nonEditableEnd) {
+ rng.setEndAfter(nonEditableEnd);
+ }
+
+ selection.setRng(rng);
+ }
+ }
+ };
+
+ function handleKey(ed, e) {
+ var keyCode = e.keyCode, nonEditableParent, caretContainer, startElement, endElement;
+
+ function getNonEmptyTextNodeSibling(node, prev) {
+ while (node = node[prev ? 'previousSibling' : 'nextSibling']) {
+ if (node.nodeType !== 3 || node.nodeValue.length > 0) {
+ return node;
+ }
+ }
+ };
+
+ function positionCaretOnElement(element, start) {
+ selection.select(element);
+ selection.collapse(start);
+ }
+
+ function canDelete(backspace) {
+ var rng, container, offset, nonEditableParent;
+
+ function removeNodeIfNotParent(node) {
+ var parent = container;
+
+ while (parent) {
+ if (parent === node) {
+ return;
+ }
+
+ parent = parent.parentNode;
+ }
+
+ dom.remove(node);
+ moveSelection();
+ }
+
+ function isNextPrevTreeNodeNonEditable() {
+ var node, walker, nonEmptyElements = ed.schema.getNonEmptyElements();
+
+ walker = new tinymce.dom.TreeWalker(container, ed.getBody());
+ while (node = (backspace ? walker.prev() : walker.next())) {
+ // Found IMG/INPUT etc
+ if (nonEmptyElements[node.nodeName.toLowerCase()]) {
+ break;
+ }
+
+ // Found text node with contents
+ if (node.nodeType === 3 && tinymce.trim(node.nodeValue).length > 0) {
+ break;
+ }
+
+ // Found non editable node
+ if (getContentEditable(node) === "false") {
+ removeNodeIfNotParent(node);
+ return true;
+ }
+ }
+
+ // Check if the content node is within a non editable parent
+ if (getNonEditableParent(node)) {
+ return true;
+ }
+
+ return false;
+ }
+
+ if (selection.isCollapsed()) {
+ rng = selection.getRng(true);
+ container = rng.startContainer;
+ offset = rng.startOffset;
+ container = getParentCaretContainer(container) || container;
+
+ // Is in noneditable parent
+ if (nonEditableParent = getNonEditableParent(container)) {
+ removeNodeIfNotParent(nonEditableParent);
+ return false;
+ }
+
+ // Check if the caret is in the middle of a text node
+ if (container.nodeType == 3 && (backspace ? offset > 0 : offset < container.nodeValue.length)) {
+ return true;
+ }
+
+ // Resolve container index
+ if (container.nodeType == 1) {
+ container = container.childNodes[offset] || container;
+ }
+
+ // Check if previous or next tree node is non editable then block the event
+ if (isNextPrevTreeNodeNonEditable()) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ startElement = selection.getStart()
+ endElement = selection.getEnd();
+
+ // Disable all key presses in contentEditable=false except delete or backspace
+ nonEditableParent = getNonEditableParent(startElement) || getNonEditableParent(endElement);
+ if (nonEditableParent && (keyCode < 112 || keyCode > 124) && keyCode != VK.DELETE && keyCode != VK.BACKSPACE) {
+ // Is Ctrl+c, Ctrl+v or Ctrl+x then use default browser behavior
+ if ((tinymce.isMac ? e.metaKey : e.ctrlKey) && (keyCode == 67 || keyCode == 88 || keyCode == 86)) {
+ return;
+ }
+
+ e.preventDefault();
+
+ // Arrow left/right select the element and collapse left/right
+ if (keyCode == VK.LEFT || keyCode == VK.RIGHT) {
+ var left = keyCode == VK.LEFT;
+ // If a block element find previous or next element to position the caret
+ if (ed.dom.isBlock(nonEditableParent)) {
+ var targetElement = left ? nonEditableParent.previousSibling : nonEditableParent.nextSibling;
+ var walker = new TreeWalker(targetElement, targetElement);
+ var caretElement = left ? walker.prev() : walker.next();
+ positionCaretOnElement(caretElement, !left);
+ } else {
+ positionCaretOnElement(nonEditableParent, left);
+ }
+ }
+ } else {
+ // Is arrow left/right, backspace or delete
+ if (keyCode == VK.LEFT || keyCode == VK.RIGHT || keyCode == VK.BACKSPACE || keyCode == VK.DELETE) {
+ caretContainer = getParentCaretContainer(startElement);
+ if (caretContainer) {
+ // Arrow left or backspace
+ if (keyCode == VK.LEFT || keyCode == VK.BACKSPACE) {
+ nonEditableParent = getNonEmptyTextNodeSibling(caretContainer, true);
+
+ if (nonEditableParent && getContentEditable(nonEditableParent) === "false") {
+ e.preventDefault();
+
+ if (keyCode == VK.LEFT) {
+ positionCaretOnElement(nonEditableParent, true);
+ } else {
+ dom.remove(nonEditableParent);
+ return;
+ }
+ } else {
+ removeCaretContainer(caretContainer);
+ }
+ }
+
+ // Arrow right or delete
+ if (keyCode == VK.RIGHT || keyCode == VK.DELETE) {
+ nonEditableParent = getNonEmptyTextNodeSibling(caretContainer);
+
+ if (nonEditableParent && getContentEditable(nonEditableParent) === "false") {
+ e.preventDefault();
+
+ if (keyCode == VK.RIGHT) {
+ positionCaretOnElement(nonEditableParent, false);
+ } else {
+ dom.remove(nonEditableParent);
+ return;
+ }
+ } else {
+ removeCaretContainer(caretContainer);
+ }
+ }
+ }
+
+ if ((keyCode == VK.BACKSPACE || keyCode == VK.DELETE) && !canDelete(keyCode == VK.BACKSPACE)) {
+ e.preventDefault();
+ return false;
+ }
+ }
+ }
+ };
+
+ ed.onMouseDown.addToTop(function(ed, e) {
+ var node = ed.selection.getNode();
+
+ if (getContentEditable(node) === "false" && node == e.target) {
+ // Expand selection on mouse down we can't block the default event since it's used for drag/drop
+ moveSelection();
+ }
+ });
+
+ ed.onMouseUp.addToTop(moveSelection);
+ ed.onKeyDown.addToTop(handleKey);
+ ed.onKeyUp.addToTop(moveSelection);
+ };
tinymce.create('tinymce.plugins.NonEditablePlugin', {
init : function(ed, url) {
- var t = this, editClass, nonEditClass, state;
+ var editClass, nonEditClass, nonEditableRegExps;
+
+ // Converts configured regexps to noneditable span items
+ function convertRegExpsToNonEditable(ed, args) {
+ var i = nonEditableRegExps.length, content = args.content, cls = tinymce.trim(nonEditClass);
+
+ // Don't replace the variables when raw is used for example on undo/redo
+ if (args.format == "raw") {
+ return;
+ }
+
+ while (i--) {
+ content = content.replace(nonEditableRegExps[i], function(match) {
+ var args = arguments, index = args[args.length - 2];
+
+ // Is value inside an attribute then don't replace
+ if (index > 0 && content.charAt(index - 1) == '"') {
+ return match;
+ }
+
+ return '<span class="' + cls + '" data-mce-content="' + ed.dom.encode(args[0]) + '">' + ed.dom.encode(typeof(args[1]) === "string" ? args[1] : args[0]) + '</span>';
+ });
+ }
- t.editor = ed;
- editClass = ed.getParam("noneditable_editable_class", "mceEditable");
- nonEditClass = ed.getParam("noneditable_noneditable_class", "mceNonEditable");
+ args.content = content;
+ };
+
+ editClass = " " + tinymce.trim(ed.getParam("noneditable_editable_class", "mceEditable")) + " ";
+ nonEditClass = " " + tinymce.trim(ed.getParam("noneditable_noneditable_class", "mceNonEditable")) + " ";
- ed.onNodeChange.addToTop(function(ed, cm, n) {
- var sc, ec;
+ // Setup noneditable regexps array
+ nonEditableRegExps = ed.getParam("noneditable_regexp");
+ if (nonEditableRegExps && !nonEditableRegExps.length) {
+ nonEditableRegExps = [nonEditableRegExps];
+ }
- // Block if start or end is inside a non editable element
- sc = ed.dom.getParent(ed.selection.getStart(), function(n) {
- return ed.dom.hasClass(n, nonEditClass);
+ ed.onPreInit.add(function() {
+ handleContentEditableSelection(ed);
+
+ if (nonEditableRegExps) {
+ ed.selection.onBeforeSetContent.add(convertRegExpsToNonEditable);
+ ed.onBeforeSetContent.add(convertRegExpsToNonEditable);
+ }
+
+ // Apply contentEditable true/false on elements with the noneditable/editable classes
+ ed.parser.addAttributeFilter('class', function(nodes) {
+ var i = nodes.length, className, node;
+
+ while (i--) {
+ node = nodes[i];
+ className = " " + node.attr("class") + " ";
+
+ if (className.indexOf(editClass) !== -1) {
+ node.attr(internalName, "true");
+ } else if (className.indexOf(nonEditClass) !== -1) {
+ node.attr(internalName, "false");
+ }
+ }
});
- ec = ed.dom.getParent(ed.selection.getEnd(), function(n) {
- return ed.dom.hasClass(n, nonEditClass);
+ // Remove internal name
+ ed.serializer.addAttributeFilter(internalName, function(nodes, name) {
+ var i = nodes.length, node;
+
+ while (i--) {
+ node = nodes[i];
+
+ if (nonEditableRegExps && node.attr('data-mce-content')) {
+ node.name = "#text";
+ node.type = 3;
+ node.raw = true;
+ node.value = node.attr('data-mce-content');
+ } else {
+ node.attr(externalName, null);
+ node.attr(internalName, null);
+ }
+ }
});
- // Block or unblock
- if (sc || ec) {
- state = 1;
- t._setDisabled(1);
- return false;
- } else if (state == 1) {
- t._setDisabled(0);
- state = 0;
- }
+ // Convert external name into internal name
+ ed.parser.addAttributeFilter(externalName, function(nodes, name) {
+ var i = nodes.length, node;
+
+ while (i--) {
+ node = nodes[i];
+ node.attr(internalName, node.attr(externalName));
+ node.attr(externalName, null);
+ }
+ });
});
},
@@ -51,42 +529,6 @@
infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/noneditable',
version : tinymce.majorVersion + "." + tinymce.minorVersion
};
- },
-
- _block : function(ed, e) {
- var k = e.keyCode;
-
- // Don't block arrow keys, pg up/down, and F1-F12
- if ((k > 32 && k < 41) || (k > 111 && k < 124))
- return;
-
- return Event.cancel(e);
- },
-
- _setDisabled : function(s) {
- var t = this, ed = t.editor;
-
- tinymce.each(ed.controlManager.controls, function(c) {
- c.setDisabled(s);
- });
-
- if (s !== t.disabled) {
- if (s) {
- ed.onKeyDown.addToTop(t._block);
- ed.onKeyPress.addToTop(t._block);
- ed.onKeyUp.addToTop(t._block);
- ed.onPaste.addToTop(t._block);
- ed.onContextMenu.addToTop(t._block);
- } else {
- ed.onKeyDown.remove(t._block);
- ed.onKeyPress.remove(t._block);
- ed.onKeyUp.remove(t._block);
- ed.onPaste.remove(t._block);
- ed.onContextMenu.remove(t._block);
- }
-
- t.disabled = s;
- }
}
});
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin.js
index e47a5c630..0ab05ebbb 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin.js
@@ -1 +1 @@
-(function(){var c=tinymce.each,a={paste_auto_cleanup_on_paste:true,paste_enable_default_filters:true,paste_block_drop:false,paste_retain_style_properties:"none",paste_strip_class_attributes:"mso",paste_remove_spans:false,paste_remove_styles:false,paste_remove_styles_if_webkit:true,paste_convert_middot_lists:true,paste_convert_headers_to_strong:false,paste_dialog_width:"450",paste_dialog_height:"400",paste_text_use_dialog:false,paste_text_sticky:false,paste_text_sticky_default:false,paste_text_notifyalways:false,paste_text_linebreaktype:"combined",paste_text_replacements:[[/\u2026/g,"..."],[/[\x93\x94\u201c\u201d]/g,'"'],[/[\x60\x91\x92\u2018\u2019]/g,"'"]]};function b(d,e){return d.getParam(e,a[e])}tinymce.create("tinymce.plugins.PastePlugin",{init:function(d,e){var f=this;f.editor=d;f.url=e;f.onPreProcess=new tinymce.util.Dispatcher(f);f.onPostProcess=new tinymce.util.Dispatcher(f);f.onPreProcess.add(f._preProcess);f.onPostProcess.add(f._postProcess);f.onPreProcess.add(function(i,j){d.execCallback("paste_preprocess",i,j)});f.onPostProcess.add(function(i,j){d.execCallback("paste_postprocess",i,j)});d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){return false}});d.pasteAsPlainText=b(d,"paste_text_sticky_default");function h(l,j){var k=d.dom,i;f.onPreProcess.dispatch(f,l);l.node=k.create("div",0,l.content);if(tinymce.isGecko){i=d.selection.getRng(true);if(i.startContainer==i.endContainer&&i.startContainer.nodeType==3){if(l.node.childNodes.length===1&&/^(p|h[1-6]|pre)$/i.test(l.node.firstChild.nodeName)&&l.content.indexOf("__MCE_ITEM__")===-1){k.remove(l.node.firstChild,true)}}}f.onPostProcess.dispatch(f,l);l.content=d.serializer.serialize(l.node,{getInner:1,forced_root_block:""});if((!j)&&(d.pasteAsPlainText)){f._insertPlainText(l.content);if(!b(d,"paste_text_sticky")){d.pasteAsPlainText=false;d.controlManager.setActive("pastetext",false)}}else{f._insert(l.content)}}d.addCommand("mceInsertClipboardContent",function(i,j){h(j,true)});if(!b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(j,i){var k=tinymce.util.Cookie;d.pasteAsPlainText=!d.pasteAsPlainText;d.controlManager.setActive("pastetext",d.pasteAsPlainText);if((d.pasteAsPlainText)&&(!k.get("tinymcePasteText"))){if(b(d,"paste_text_sticky")){d.windowManager.alert(d.translate("paste.plaintext_mode_sticky"))}else{d.windowManager.alert(d.translate("paste.plaintext_mode"))}if(!b(d,"paste_text_notifyalways")){k.set("tinymcePasteText","1",new Date(new Date().getFullYear()+1,12,31))}}})}d.addButton("pastetext",{title:"paste.paste_text_desc",cmd:"mcePasteText"});d.addButton("selectall",{title:"paste.selectall_desc",cmd:"selectall"});function g(s){var l,p,j,t,k=d.selection,o=d.dom,q=d.getBody(),i,r;if(s.clipboardData||o.doc.dataTransfer){r=(s.clipboardData||o.doc.dataTransfer).getData("Text");if(d.pasteAsPlainText){s.preventDefault();h({content:o.encode(r).replace(/\r?\n/g,"<br />")});return}}if(o.get("_mcePaste")){return}l=o.add(q,"div",{id:"_mcePaste","class":"mcePaste","data-mce-bogus":"1"},"\uFEFF\uFEFF");if(q!=d.getDoc().body){i=o.getPos(d.selection.getStart(),q).y}else{i=q.scrollTop+o.getViewPort(d.getWin()).y}o.setStyles(l,{position:"absolute",left:tinymce.isGecko?-40:0,top:i-25,width:1,height:1,overflow:"hidden"});if(tinymce.isIE){t=k.getRng();j=o.doc.body.createTextRange();j.moveToElementText(l);j.execCommand("Paste");o.remove(l);if(l.innerHTML==="\uFEFF\uFEFF"){d.execCommand("mcePasteWord");s.preventDefault();return}k.setRng(t);k.setContent("");setTimeout(function(){h({content:l.innerHTML})},0);return tinymce.dom.Event.cancel(s)}else{function m(n){n.preventDefault()}o.bind(d.getDoc(),"mousedown",m);o.bind(d.getDoc(),"keydown",m);p=d.selection.getRng();l=l.firstChild;j=d.getDoc().createRange();j.setStart(l,0);j.setEnd(l,2);k.setRng(j);window.setTimeout(function(){var u="",n;if(!o.select("div.mcePaste > div.mcePaste").length){n=o.select("div.mcePaste");c(n,function(w){var v=w.firstChild;if(v&&v.nodeName=="DIV"&&v.style.marginTop&&v.style.backgroundColor){o.remove(v,1)}c(o.select("span.Apple-style-span",w),function(x){o.remove(x,1)});c(o.select("br[data-mce-bogus]",w),function(x){o.remove(x)});if(w.parentNode.className!="mcePaste"){u+=w.innerHTML}})}else{u="<p>"+o.encode(r).replace(/\r?\n\r?\n/g,"</p><p>").replace(/\r?\n/g,"<br />")+"</p>"}c(o.select("div.mcePaste"),function(v){o.remove(v)});if(p){k.setRng(p)}h({content:u});o.unbind(d.getDoc(),"mousedown",m);o.unbind(d.getDoc(),"keydown",m)},0)}}if(b(d,"paste_auto_cleanup_on_paste")){if(tinymce.isOpera||/Firefox\/2/.test(navigator.userAgent)){d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){g(j)}})}else{d.onPaste.addToTop(function(i,j){return g(j)})}}d.onInit.add(function(){d.controlManager.setActive("pastetext",d.pasteAsPlainText);if(b(d,"paste_block_drop")){d.dom.bind(d.getBody(),["dragend","dragover","draggesture","dragdrop","drop","drag"],function(i){i.preventDefault();i.stopPropagation();return false})}});f._legacySupport()},getInfo:function(){return{longname:"Paste text/word",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_preProcess:function(g,e){var k=this.editor,j=e.content,p=tinymce.grep,n=tinymce.explode,f=tinymce.trim,l,i;function d(h){c(h,function(o){if(o.constructor==RegExp){j=j.replace(o,"")}else{j=j.replace(o[0],o[1])}})}if(k.settings.paste_enable_default_filters==false){return}if(tinymce.isIE&&document.documentMode>=9){d([[/(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g,"$1"]]);d([[/<br><br>/g,"<BR><BR>"],[/<br>/g," "],[/<BR><BR>/g,"<br>"]])}if(/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(j)||e.wordContent){e.wordContent=true;d([/^\s*(&nbsp;)+/gi,/(&nbsp;|<br[^>]*>)+\s*$/gi]);if(b(k,"paste_convert_headers_to_strong")){j=j.replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"<p><strong>$1</strong></p>")}if(b(k,"paste_convert_middot_lists")){d([[/<!--\[if !supportLists\]-->/gi,"$&__MCE_ITEM__"],[/(<span[^>]+(?:mso-list:|:\s*symbol)[^>]+>)/gi,"$1__MCE_ITEM__"],[/(<p[^>]+(?:MsoListParagraph)[^>]+>)/gi,"$1__MCE_ITEM__"]])}d([/<!--[\s\S]+?-->/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/&nbsp;/gi,"\u00a0"]]);do{l=j.length;j=j.replace(/(<[a-z][^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi,"$1")}while(l!=j.length);if(b(k,"paste_retain_style_properties").replace(/^none$/i,"").length==0){j=j.replace(/<\/?span[^>]*>/gi,"")}else{d([[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,function(o,h){return(h.length>0)?h.replace(/./," ").slice(Math.floor(h.length/2)).split("").join("\u00a0"):""}],[/(<[a-z][^>]*)\sstyle="([^"]*)"/gi,function(t,h,r){var u=[],o=0,q=n(f(r).replace(/&quot;/gi,"'"),";");c(q,function(s){var w,y,z=n(s,":");function x(A){return A+((A!=="0")&&(/\d$/.test(A)))?"px":""}if(z.length==2){w=z[0].toLowerCase();y=z[1].toLowerCase();switch(w){case"mso-padding-alt":case"mso-padding-top-alt":case"mso-padding-right-alt":case"mso-padding-bottom-alt":case"mso-padding-left-alt":case"mso-margin-alt":case"mso-margin-top-alt":case"mso-margin-right-alt":case"mso-margin-bottom-alt":case"mso-margin-left-alt":case"mso-table-layout-alt":case"mso-height":case"mso-width":case"mso-vertical-align-alt":u[o++]=w.replace(/^mso-|-alt$/g,"")+":"+x(y);return;case"horiz-align":u[o++]="text-align:"+y;return;case"vert-align":u[o++]="vertical-align:"+y;return;case"font-color":case"mso-foreground":u[o++]="color:"+y;return;case"mso-background":case"mso-highlight":u[o++]="background:"+y;return;case"mso-default-height":u[o++]="min-height:"+x(y);return;case"mso-default-width":u[o++]="min-width:"+x(y);return;case"mso-padding-between-alt":u[o++]="border-collapse:separate;border-spacing:"+x(y);return;case"text-line-through":if((y=="single")||(y=="double")){u[o++]="text-decoration:line-through"}return;case"mso-zero-height":if(y=="yes"){u[o++]="display:none"}return}if(/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(w)){return}u[o++]=w+":"+z[1]}});if(o>0){return h+' style="'+u.join(";")+'"'}else{return h}}]])}}if(b(k,"paste_convert_headers_to_strong")){d([[/<h[1-6][^>]*>/gi,"<p><strong>"],[/<\/h[1-6][^>]*>/gi,"</strong></p>"]])}d([[/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi,""]]);i=b(k,"paste_strip_class_attributes");if(i!=="none"){function m(q,o){if(i==="all"){return""}var h=p(n(o.replace(/^(["'])(.*)\1$/,"$2")," "),function(r){return(/^(?!mso)/i.test(r))});return h.length?' class="'+h.join(" ")+'"':""}j=j.replace(/ class="([^"]+)"/gi,m);j=j.replace(/ class=([\-\w]+)/gi,m)}if(b(k,"paste_remove_spans")){j=j.replace(/<\/?span[^>]*>/gi,"")}e.content=j},_postProcess:function(g,i){var f=this,e=f.editor,h=e.dom,d;if(e.settings.paste_enable_default_filters==false){return}if(i.wordContent){c(h.select("a",i.node),function(j){if(!j.href||j.href.indexOf("#_Toc")!=-1){h.remove(j,1)}});if(b(e,"paste_convert_middot_lists")){f._convertLists(g,i)}d=b(e,"paste_retain_style_properties");if((tinymce.is(d,"string"))&&(d!=="all")&&(d!=="*")){d=tinymce.explode(d.replace(/^none$/i,""));c(h.select("*",i.node),function(m){var n={},k=0,l,o,j;if(d){for(l=0;l<d.length;l++){o=d[l];j=h.getStyle(m,o);if(j){n[o]=j;k++}}}h.setAttrib(m,"style","");if(d&&k>0){h.setStyles(m,n)}else{if(m.nodeName=="SPAN"&&!m.className){h.remove(m,true)}}})}}if(b(e,"paste_remove_styles")||(b(e,"paste_remove_styles_if_webkit")&&tinymce.isWebKit)){c(h.select("*[style]",i.node),function(j){j.removeAttribute("style");j.removeAttribute("data-mce-style")})}else{if(tinymce.isWebKit){c(h.select("*",i.node),function(j){j.removeAttribute("data-mce-style")})}}},_convertLists:function(g,e){var i=g.editor.dom,h,l,d=-1,f,m=[],k,j;c(i.select("p",e.node),function(t){var q,u="",s,r,n,o;for(q=t.firstChild;q&&q.nodeType==3;q=q.nextSibling){u+=q.nodeValue}u=t.innerHTML.replace(/<\/?\w+[^>]*>/gi,"").replace(/&nbsp;/g,"\u00a0");if(/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(u)){s="ul"}if(/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(u)){s="ol"}if(s){f=parseFloat(t.style.marginLeft||0);if(f>d){m.push(f)}if(!h||s!=k){h=i.create(s);i.insertAfter(h,t)}else{if(f>d){h=l.appendChild(i.create(s))}else{if(f<d){n=tinymce.inArray(m,f);o=i.getParents(h.parentNode,s);h=o[o.length-1-n]||h}}}c(i.select("span",t),function(v){var p=v.innerHTML.replace(/<\/?\w+[^>]*>/gi,"");if(s=="ul"&&/^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(p)){i.remove(v)}else{if(/^__MCE_ITEM__[\s\S]*\w+\.(&nbsp;|\u00a0)*\s*/.test(p)){i.remove(v)}}});r=t.innerHTML;if(s=="ul"){r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*(&nbsp;|\u00a0)+\s*/,"")}else{r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^\s*\w+\.(&nbsp;|\u00a0)+\s*/,"")}l=h.appendChild(i.create("li",0,r));i.remove(t);d=f;k=s}else{h=d=0}});j=e.node.innerHTML;if(j.indexOf("__MCE_ITEM__")!=-1){e.node.innerHTML=j.replace(/__MCE_ITEM__/g,"")}},_insert:function(f,d){var e=this.editor,g=e.selection.getRng();if(!e.selection.isCollapsed()&&g.startContainer!=g.endContainer){e.getDoc().execCommand("Delete",false,null)}e.execCommand("mceInsertContent",false,f,{skip_undo:d})},_insertPlainText:function(g){var d=this.editor,e=b(d,"paste_text_linebreaktype"),i=b(d,"paste_text_replacements"),f=tinymce.is;function h(j){c(j,function(k){if(k.constructor==RegExp){g=g.replace(k,"")}else{g=g.replace(k[0],k[1])}})}if((typeof(g)==="string")&&(g.length>0)){if(/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(g)){h([/[\n\r]+/g])}else{h([/\r+/g])}h([[/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi,"\n\n"],[/<br[^>]*>|<\/tr>/gi,"\n"],[/<\/t[dh]>\s*<t[dh][^>]*>/gi,"\t"],/<[a-z!\/?][^>]*>/gi,[/&nbsp;/gi," "],[/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi,"$1"],[/\n{3,}/g,"\n\n"]]);g=d.dom.decode(tinymce.html.Entities.encodeRaw(g));if(f(i,"array")){h(i)}else{if(f(i,"string")){h(new RegExp(i,"gi"))}}if(e=="none"){h([[/\n+/g," "]])}else{if(e=="br"){h([[/\n/g,"<br />"]])}else{if(e=="p"){h([[/\n+/g,"</p><p>"],[/^(.*<\/p>)(<p>)$/,"<p>$1"]])}else{h([[/\n\n/g,"</p><p>"],[/^(.*<\/p>)(<p>)$/,"<p>$1"],[/\n/g,"<br />"]])}}}d.execCommand("mceInsertContent",false,g)}},_legacySupport:function(){var e=this,d=e.editor;d.addCommand("mcePasteWord",function(){d.windowManager.open({file:e.url+"/pasteword.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})});if(b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(){d.windowManager.open({file:e.url+"/pastetext.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})})}d.addButton("pasteword",{title:"paste.paste_word_desc",cmd:"mcePasteWord"})}});tinymce.PluginManager.add("paste",tinymce.plugins.PastePlugin)})(); \ No newline at end of file
+(function(){var c=tinymce.each,a={paste_auto_cleanup_on_paste:true,paste_enable_default_filters:true,paste_block_drop:false,paste_retain_style_properties:"none",paste_strip_class_attributes:"mso",paste_remove_spans:false,paste_remove_styles:false,paste_remove_styles_if_webkit:true,paste_convert_middot_lists:true,paste_convert_headers_to_strong:false,paste_dialog_width:"450",paste_dialog_height:"400",paste_max_consecutive_linebreaks:2,paste_text_use_dialog:false,paste_text_sticky:false,paste_text_sticky_default:false,paste_text_notifyalways:false,paste_text_linebreaktype:"combined",paste_text_replacements:[[/\u2026/g,"..."],[/[\x93\x94\u201c\u201d]/g,'"'],[/[\x60\x91\x92\u2018\u2019]/g,"'"]]};function b(d,e){return d.getParam(e,a[e])}tinymce.create("tinymce.plugins.PastePlugin",{init:function(d,e){var f=this;f.editor=d;f.url=e;f.onPreProcess=new tinymce.util.Dispatcher(f);f.onPostProcess=new tinymce.util.Dispatcher(f);f.onPreProcess.add(f._preProcess);f.onPostProcess.add(f._postProcess);f.onPreProcess.add(function(i,j){d.execCallback("paste_preprocess",i,j)});f.onPostProcess.add(function(i,j){d.execCallback("paste_postprocess",i,j)});d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){return false}});d.pasteAsPlainText=b(d,"paste_text_sticky_default");function h(l,j){var k=d.dom,i;f.onPreProcess.dispatch(f,l);l.node=k.create("div",0,l.content);if(tinymce.isGecko){i=d.selection.getRng(true);if(i.startContainer==i.endContainer&&i.startContainer.nodeType==3){if(l.node.childNodes.length===1&&/^(p|h[1-6]|pre)$/i.test(l.node.firstChild.nodeName)&&l.content.indexOf("__MCE_ITEM__")===-1){k.remove(l.node.firstChild,true)}}}f.onPostProcess.dispatch(f,l);l.content=d.serializer.serialize(l.node,{getInner:1,forced_root_block:""});if((!j)&&(d.pasteAsPlainText)){f._insertPlainText(l.content);if(!b(d,"paste_text_sticky")){d.pasteAsPlainText=false;d.controlManager.setActive("pastetext",false)}}else{f._insert(l.content)}}d.addCommand("mceInsertClipboardContent",function(i,j){h(j,true)});if(!b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(j,i){var k=tinymce.util.Cookie;d.pasteAsPlainText=!d.pasteAsPlainText;d.controlManager.setActive("pastetext",d.pasteAsPlainText);if((d.pasteAsPlainText)&&(!k.get("tinymcePasteText"))){if(b(d,"paste_text_sticky")){d.windowManager.alert(d.translate("paste.plaintext_mode_sticky"))}else{d.windowManager.alert(d.translate("paste.plaintext_mode"))}if(!b(d,"paste_text_notifyalways")){k.set("tinymcePasteText","1",new Date(new Date().getFullYear()+1,12,31))}}})}d.addButton("pastetext",{title:"paste.paste_text_desc",cmd:"mcePasteText"});d.addButton("selectall",{title:"paste.selectall_desc",cmd:"selectall"});function g(s){var l,p,j,t,k=d.selection,o=d.dom,q=d.getBody(),i,r;if(s.clipboardData||o.doc.dataTransfer){r=(s.clipboardData||o.doc.dataTransfer).getData("Text");if(d.pasteAsPlainText){s.preventDefault();h({content:o.encode(r).replace(/\r?\n/g,"<br />")});return}}if(o.get("_mcePaste")){return}l=o.add(q,"div",{id:"_mcePaste","class":"mcePaste","data-mce-bogus":"1"},"\uFEFF\uFEFF");if(q!=d.getDoc().body){i=o.getPos(d.selection.getStart(),q).y}else{i=q.scrollTop+o.getViewPort(d.getWin()).y}o.setStyles(l,{position:"absolute",left:tinymce.isGecko?-40:0,top:i-25,width:1,height:1,overflow:"hidden"});if(tinymce.isIE){t=k.getRng();j=o.doc.body.createTextRange();j.moveToElementText(l);j.execCommand("Paste");o.remove(l);if(l.innerHTML==="\uFEFF\uFEFF"){d.execCommand("mcePasteWord");s.preventDefault();return}k.setRng(t);k.setContent("");setTimeout(function(){h({content:l.innerHTML})},0);return tinymce.dom.Event.cancel(s)}else{function m(n){n.preventDefault()}o.bind(d.getDoc(),"mousedown",m);o.bind(d.getDoc(),"keydown",m);p=d.selection.getRng();l=l.firstChild;j=d.getDoc().createRange();j.setStart(l,0);j.setEnd(l,2);k.setRng(j);window.setTimeout(function(){var u="",n;if(!o.select("div.mcePaste > div.mcePaste").length){n=o.select("div.mcePaste");c(n,function(w){var v=w.firstChild;if(v&&v.nodeName=="DIV"&&v.style.marginTop&&v.style.backgroundColor){o.remove(v,1)}c(o.select("span.Apple-style-span",w),function(x){o.remove(x,1)});c(o.select("br[data-mce-bogus]",w),function(x){o.remove(x)});if(w.parentNode.className!="mcePaste"){u+=w.innerHTML}})}else{u="<p>"+o.encode(r).replace(/\r?\n\r?\n/g,"</p><p>").replace(/\r?\n/g,"<br />")+"</p>"}c(o.select("div.mcePaste"),function(v){o.remove(v)});if(p){k.setRng(p)}h({content:u});o.unbind(d.getDoc(),"mousedown",m);o.unbind(d.getDoc(),"keydown",m)},0)}}if(b(d,"paste_auto_cleanup_on_paste")){if(tinymce.isOpera||/Firefox\/2/.test(navigator.userAgent)){d.onKeyDown.addToTop(function(i,j){if(((tinymce.isMac?j.metaKey:j.ctrlKey)&&j.keyCode==86)||(j.shiftKey&&j.keyCode==45)){g(j)}})}else{d.onPaste.addToTop(function(i,j){return g(j)})}}d.onInit.add(function(){d.controlManager.setActive("pastetext",d.pasteAsPlainText);if(b(d,"paste_block_drop")){d.dom.bind(d.getBody(),["dragend","dragover","draggesture","dragdrop","drop","drag"],function(i){i.preventDefault();i.stopPropagation();return false})}});f._legacySupport()},getInfo:function(){return{longname:"Paste text/word",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/paste",version:tinymce.majorVersion+"."+tinymce.minorVersion}},_preProcess:function(g,e){var k=this.editor,j=e.content,p=tinymce.grep,n=tinymce.explode,f=tinymce.trim,l,i;function d(h){c(h,function(o){if(o.constructor==RegExp){j=j.replace(o,"")}else{j=j.replace(o[0],o[1])}})}if(k.settings.paste_enable_default_filters==false){return}if(tinymce.isIE&&document.documentMode>=9&&/<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/.test(e.content)){d([[/(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g,"$1"]]);d([[/<br><br>/g,"<BR><BR>"],[/<br>/g," "],[/<BR><BR>/g,"<br>"]])}if(/class="?Mso|style="[^"]*\bmso-|w:WordDocument/i.test(j)||e.wordContent){e.wordContent=true;d([/^\s*(&nbsp;)+/gi,/(&nbsp;|<br[^>]*>)+\s*$/gi]);if(b(k,"paste_convert_headers_to_strong")){j=j.replace(/<p [^>]*class="?MsoHeading"?[^>]*>(.*?)<\/p>/gi,"<p><strong>$1</strong></p>")}if(b(k,"paste_convert_middot_lists")){d([[/<!--\[if !supportLists\]-->/gi,"$&__MCE_ITEM__"],[/(<span[^>]+(?:mso-list:|:\s*symbol)[^>]+>)/gi,"$1__MCE_ITEM__"],[/(<p[^>]+(?:MsoListParagraph)[^>]+>)/gi,"$1__MCE_ITEM__"]])}d([/<!--[\s\S]+?-->/gi,/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi,[/<(\/?)s>/gi,"<$1strike>"],[/&nbsp;/gi,"\u00a0"]]);do{l=j.length;j=j.replace(/(<[a-z][^>]*\s)(?:id|name|language|type|on\w+|\w+:\w+)=(?:"[^"]*"|\w+)\s?/gi,"$1")}while(l!=j.length);if(b(k,"paste_retain_style_properties").replace(/^none$/i,"").length==0){j=j.replace(/<\/?span[^>]*>/gi,"")}else{d([[/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi,function(o,h){return(h.length>0)?h.replace(/./," ").slice(Math.floor(h.length/2)).split("").join("\u00a0"):""}],[/(<[a-z][^>]*)\sstyle="([^"]*)"/gi,function(t,h,r){var u=[],o=0,q=n(f(r).replace(/&quot;/gi,"'"),";");c(q,function(s){var w,y,z=n(s,":");function x(A){return A+((A!=="0")&&(/\d$/.test(A)))?"px":""}if(z.length==2){w=z[0].toLowerCase();y=z[1].toLowerCase();switch(w){case"mso-padding-alt":case"mso-padding-top-alt":case"mso-padding-right-alt":case"mso-padding-bottom-alt":case"mso-padding-left-alt":case"mso-margin-alt":case"mso-margin-top-alt":case"mso-margin-right-alt":case"mso-margin-bottom-alt":case"mso-margin-left-alt":case"mso-table-layout-alt":case"mso-height":case"mso-width":case"mso-vertical-align-alt":u[o++]=w.replace(/^mso-|-alt$/g,"")+":"+x(y);return;case"horiz-align":u[o++]="text-align:"+y;return;case"vert-align":u[o++]="vertical-align:"+y;return;case"font-color":case"mso-foreground":u[o++]="color:"+y;return;case"mso-background":case"mso-highlight":u[o++]="background:"+y;return;case"mso-default-height":u[o++]="min-height:"+x(y);return;case"mso-default-width":u[o++]="min-width:"+x(y);return;case"mso-padding-between-alt":u[o++]="border-collapse:separate;border-spacing:"+x(y);return;case"text-line-through":if((y=="single")||(y=="double")){u[o++]="text-decoration:line-through"}return;case"mso-zero-height":if(y=="yes"){u[o++]="display:none"}return}if(/^(mso|column|font-emph|lang|layout|line-break|list-image|nav|panose|punct|row|ruby|sep|size|src|tab-|table-border|text-(?!align|decor|indent|trans)|top-bar|version|vnd|word-break)/.test(w)){return}u[o++]=w+":"+z[1]}});if(o>0){return h+' style="'+u.join(";")+'"'}else{return h}}]])}}if(b(k,"paste_convert_headers_to_strong")){d([[/<h[1-6][^>]*>/gi,"<p><strong>"],[/<\/h[1-6][^>]*>/gi,"</strong></p>"]])}d([[/Version:[\d.]+\nStartHTML:\d+\nEndHTML:\d+\nStartFragment:\d+\nEndFragment:\d+/gi,""]]);i=b(k,"paste_strip_class_attributes");if(i!=="none"){function m(q,o){if(i==="all"){return""}var h=p(n(o.replace(/^(["'])(.*)\1$/,"$2")," "),function(r){return(/^(?!mso)/i.test(r))});return h.length?' class="'+h.join(" ")+'"':""}j=j.replace(/ class="([^"]+)"/gi,m);j=j.replace(/ class=([\-\w]+)/gi,m)}if(b(k,"paste_remove_spans")){j=j.replace(/<\/?span[^>]*>/gi,"")}e.content=j},_postProcess:function(g,i){var f=this,e=f.editor,h=e.dom,d;if(e.settings.paste_enable_default_filters==false){return}if(i.wordContent){c(h.select("a",i.node),function(j){if(!j.href||j.href.indexOf("#_Toc")!=-1){h.remove(j,1)}});if(b(e,"paste_convert_middot_lists")){f._convertLists(g,i)}d=b(e,"paste_retain_style_properties");if((tinymce.is(d,"string"))&&(d!=="all")&&(d!=="*")){d=tinymce.explode(d.replace(/^none$/i,""));c(h.select("*",i.node),function(m){var n={},k=0,l,o,j;if(d){for(l=0;l<d.length;l++){o=d[l];j=h.getStyle(m,o);if(j){n[o]=j;k++}}}h.setAttrib(m,"style","");if(d&&k>0){h.setStyles(m,n)}else{if(m.nodeName=="SPAN"&&!m.className){h.remove(m,true)}}})}}if(b(e,"paste_remove_styles")||(b(e,"paste_remove_styles_if_webkit")&&tinymce.isWebKit)){c(h.select("*[style]",i.node),function(j){j.removeAttribute("style");j.removeAttribute("data-mce-style")})}else{if(tinymce.isWebKit){c(h.select("*",i.node),function(j){j.removeAttribute("data-mce-style")})}}},_convertLists:function(g,e){var i=g.editor.dom,h,l,d=-1,f,m=[],k,j;c(i.select("p",e.node),function(t){var q,u="",s,r,n,o;for(q=t.firstChild;q&&q.nodeType==3;q=q.nextSibling){u+=q.nodeValue}u=t.innerHTML.replace(/<\/?\w+[^>]*>/gi,"").replace(/&nbsp;/g,"\u00a0");if(/^(__MCE_ITEM__)+[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*\u00a0*/.test(u)){s="ul"}if(/^__MCE_ITEM__\s*\w+\.\s*\u00a0+/.test(u)){s="ol"}if(s){f=parseFloat(t.style.marginLeft||0);if(f>d){m.push(f)}if(!h||s!=k){h=i.create(s);i.insertAfter(h,t)}else{if(f>d){h=l.appendChild(i.create(s))}else{if(f<d){n=tinymce.inArray(m,f);o=i.getParents(h.parentNode,s);h=o[o.length-1-n]||h}}}c(i.select("span",t),function(v){var p=v.innerHTML.replace(/<\/?\w+[^>]*>/gi,"");if(s=="ul"&&/^__MCE_ITEM__[\u2022\u00b7\u00a7\u00d8o\u25CF]/.test(p)){i.remove(v)}else{if(/^__MCE_ITEM__[\s\S]*\w+\.(&nbsp;|\u00a0)*\s*/.test(p)){i.remove(v)}}});r=t.innerHTML;if(s=="ul"){r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^[\u2022\u00b7\u00a7\u00d8o\u25CF]\s*(&nbsp;|\u00a0)+\s*/,"")}else{r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^\s*\w+\.(&nbsp;|\u00a0)+\s*/,"")}l=h.appendChild(i.create("li",0,r));i.remove(t);d=f;k=s}else{h=d=0}});j=e.node.innerHTML;if(j.indexOf("__MCE_ITEM__")!=-1){e.node.innerHTML=j.replace(/__MCE_ITEM__/g,"")}},_insert:function(f,d){var e=this.editor,g=e.selection.getRng();if(!e.selection.isCollapsed()&&g.startContainer!=g.endContainer){e.getDoc().execCommand("Delete",false,null)}e.execCommand("mceInsertContent",false,f,{skip_undo:d})},_insertPlainText:function(j){var h=this.editor,f=b(h,"paste_text_linebreaktype"),k=b(h,"paste_text_replacements"),g=tinymce.is;function e(m){c(m,function(n){if(n.constructor==RegExp){j=j.replace(n,"")}else{j=j.replace(n[0],n[1])}})}if((typeof(j)==="string")&&(j.length>0)){if(/<(?:p|br|h[1-6]|ul|ol|dl|table|t[rdh]|div|blockquote|fieldset|pre|address|center)[^>]*>/i.test(j)){e([/[\n\r]+/g])}else{e([/\r+/g])}e([[/<\/(?:p|h[1-6]|ul|ol|dl|table|div|blockquote|fieldset|pre|address|center)>/gi,"\n\n"],[/<br[^>]*>|<\/tr>/gi,"\n"],[/<\/t[dh]>\s*<t[dh][^>]*>/gi,"\t"],/<[a-z!\/?][^>]*>/gi,[/&nbsp;/gi," "],[/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi,"$1"]]);var d=Number(b(h,"paste_max_consecutive_linebreaks"));if(d>-1){var l=new RegExp("\n{"+(d+1)+",}","g");var i="";while(i.length<d){i+="\n"}e([[l,i]])}j=h.dom.decode(tinymce.html.Entities.encodeRaw(j));if(g(k,"array")){e(k)}else{if(g(k,"string")){e(new RegExp(k,"gi"))}}if(f=="none"){e([[/\n+/g," "]])}else{if(f=="br"){e([[/\n/g,"<br />"]])}else{if(f=="p"){e([[/\n+/g,"</p><p>"],[/^(.*<\/p>)(<p>)$/,"<p>$1"]])}else{e([[/\n\n/g,"</p><p>"],[/^(.*<\/p>)(<p>)$/,"<p>$1"],[/\n/g,"<br />"]])}}}h.execCommand("mceInsertContent",false,j)}},_legacySupport:function(){var e=this,d=e.editor;d.addCommand("mcePasteWord",function(){d.windowManager.open({file:e.url+"/pasteword.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})});if(b(d,"paste_text_use_dialog")){d.addCommand("mcePasteText",function(){d.windowManager.open({file:e.url+"/pastetext.htm",width:parseInt(b(d,"paste_dialog_width")),height:parseInt(b(d,"paste_dialog_height")),inline:1})})}d.addButton("pasteword",{title:"paste.paste_word_desc",cmd:"mcePasteWord"})}});tinymce.PluginManager.add("paste",tinymce.plugins.PastePlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin_src.js
index 73fe7fe9a..0154eceb5 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/paste/editor_plugin_src.js
@@ -23,6 +23,7 @@
paste_convert_headers_to_strong : false,
paste_dialog_width : "450",
paste_dialog_height : "400",
+ paste_max_consecutive_linebreaks: 2,
paste_text_use_dialog : false,
paste_text_sticky : false,
paste_text_sticky_default : false,
@@ -359,7 +360,7 @@
}
// IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
- if (tinymce.isIE && document.documentMode >= 9) {
+ if (tinymce.isIE && document.documentMode >= 9 && /<(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)/.test(o.content)) {
// IE9 adds BRs before/after block elements when contents is pasted from word or for example another browser
process([[/(?:<br>&nbsp;[\s\r\n]+|<br>)*(<\/?(h[1-6r]|p|div|address|pre|form|table|tbody|thead|tfoot|th|tr|td|li|ol|ul|caption|blockquote|center|dl|dt|dd|dir|fieldset)[^>]*>)(?:<br>&nbsp;[\s\r\n]+|<br>)*/g, '$1']]);
@@ -790,10 +791,23 @@
[/<\/t[dh]>\s*<t[dh][^>]*>/gi, "\t"], // Table cells get tabs betweem them
/<[a-z!\/?][^>]*>/gi, // Delete all remaining tags
[/&nbsp;/gi, " "], // Convert non-break spaces to regular spaces (remember, *plain text*)
- [/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi, "$1"],// Cool little RegExp deletes whitespace around linebreak chars.
- [/\n{3,}/g, "\n\n"] // Max. 2 consecutive linebreaks
+ [/(?:(?!\n)\s)*(\n+)(?:(?!\n)\s)*/gi, "$1"] // Cool little RegExp deletes whitespace around linebreak chars.
]);
+ var maxLinebreaks = Number(getParam(ed, "paste_max_consecutive_linebreaks"));
+ if (maxLinebreaks > -1) {
+ var maxLinebreaksRegex = new RegExp("\n{" + (maxLinebreaks + 1) + ",}", "g");
+ var linebreakReplacement = "";
+
+ while (linebreakReplacement.length < maxLinebreaks) {
+ linebreakReplacement += "\n";
+ }
+
+ process([
+ [maxLinebreaksRegex, linebreakReplacement] // Limit max consecutive linebreaks
+ ]);
+ }
+
content = ed.dom.decode(tinymce.html.Entities.encodeRaw(content));
// Perform default or custom replacements
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm
index 5a22d8aa4..2443a9184 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/searchreplace/searchreplace.htm
@@ -93,7 +93,7 @@
<input type="submit" id="insert" name="insert" value="{#searchreplace_dlg.findnext}" />
<input type="button" class="button" id="replaceBtn" name="replaceBtn" value="{#searchreplace_dlg.replace}" onclick="SearchReplaceDialog.searchNext('current');" />
<input type="button" class="button" id="replaceAllBtn" name="replaceAllBtn" value="{#searchreplace_dlg.replaceall}" onclick="SearchReplaceDialog.searchNext('all');" />
- <input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();" />
+ <input type="button" id="cancel" name="close" value="{#close}" onclick="tinyMCEPopup.close();" />
</div>
</form>
</body>
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js
index 71fbb68a6..48549c923 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin.js
@@ -1 +1 @@
-(function(){var a=tinymce.util.JSONRequest,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.SpellcheckerPlugin",{getInfo:function(){return{longname:"Spellchecker",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker",version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(e,f){var g=this,d;g.url=f;g.editor=e;g.rpcUrl=e.getParam("spellchecker_rpc_url","{backend}");if(g.rpcUrl=="{backend}"){if(tinymce.isIE){return}g.hasSupport=true;e.onContextMenu.addToTop(function(h,i){if(g.active){return false}})}e.addCommand("mceSpellCheck",function(){if(g.rpcUrl=="{backend}"){g.editor.getBody().spellcheck=g.active=!g.active;return}if(!g.active){e.setProgressState(1);g._sendRPC("checkWords",[g.selectedLang,g._getWords()],function(h){if(h.length>0){g.active=1;g._markWords(h);e.setProgressState(0);e.nodeChanged()}else{e.setProgressState(0);if(e.getParam("spellchecker_report_no_misspellings",true)){e.windowManager.alert("spellchecker.no_mpell")}}})}else{g._done()}});if(e.settings.content_css!==false){e.contentCSS.push(f+"/css/content.css")}e.onClick.add(g._showMenu,g);e.onContextMenu.add(g._showMenu,g);e.onBeforeGetContent.add(function(){if(g.active){g._removeWords()}});e.onNodeChange.add(function(i,h){h.setActive("spellchecker",g.active)});e.onSetContent.add(function(){g._done()});e.onBeforeGetContent.add(function(){g._done()});e.onBeforeExecCommand.add(function(h,i){if(i=="mceFullScreen"){g._done()}});g.languages={};c(e.getParam("spellchecker_languages","+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv","hash"),function(i,h){if(h.indexOf("+")===0){h=h.substring(1);g.selectedLang=i}g.languages[h]=i})},createControl:function(h,d){var f=this,g,e=f.editor;if(h=="spellchecker"){if(f.rpcUrl=="{backend}"){if(f.hasSupport){g=d.createButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f})}return g}g=d.createSplitButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f});g.onRenderMenu.add(function(j,i){i.add({title:"spellchecker.langs","class":"mceMenuItemTitle"}).setDisabled(1);c(f.languages,function(n,m){var p={icon:1},l;p.onclick=function(){if(n==f.selectedLang){return}l.setSelected(1);f.selectedItem.setSelected(0);f.selectedItem=l;f.selectedLang=n};p.title=m;l=i.add(p);l.setSelected(n==f.selectedLang);if(n==f.selectedLang){f.selectedItem=l}})});return g}},_walk:function(i,g){var h=this.editor.getDoc(),e;if(h.createTreeWalker){e=h.createTreeWalker(i,NodeFilter.SHOW_TEXT,null,false);while((i=e.nextNode())!=null){g.call(this,i)}}else{tinymce.walk(i,g,"childNodes")}},_getSeparators:function(){var e="",d,f=this.editor.getParam("spellchecker_word_separator_chars",'\\s!"#$%&()*+,-./:;<=>?@[]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(d=0;d<f.length;d++){e+="\\"+f.charAt(d)}return e},_getWords:function(){var e=this.editor,g=[],d="",f={},h=[];this._walk(e.getBody(),function(i){if(i.nodeType==3){d+=i.nodeValue+" "}});if(e.getParam("spellchecker_word_pattern")){h=d.match("("+e.getParam("spellchecker_word_pattern")+")","gi")}else{d=d.replace(new RegExp("([0-9]|["+this._getSeparators()+"])","g")," ");d=tinymce.trim(d.replace(/(\s+)/g," "));h=d.split(" ")}c(h,function(i){if(!f[i]){g.push(i);f[i]=1}});return g},_removeWords:function(e){var f=this.editor,h=f.dom,g=f.selection,d=g.getBookmark();c(h.select("span").reverse(),function(i){if(i&&(h.hasClass(i,"mceItemHiddenSpellWord")||h.hasClass(i,"mceItemHidden"))){if(!e||h.decode(i.innerHTML)==e){h.remove(i,1)}}});g.moveToBookmark(d)},_markWords:function(l){var g=this.editor,f=g.dom,j=g.getDoc(),h=g.selection,i=h.getBookmark(),d=[],k=l.join("|"),m=this._getSeparators(),e=new RegExp("(^|["+m+"])("+k+")(?=["+m+"]|$)","g");this._walk(g.getBody(),function(o){if(o.nodeType==3){d.push(o)}});c(d,function(t){var r,q,o,s,p=t.nodeValue;if(e.test(p)){p=f.encode(p);q=f.create("span",{"class":"mceItemHidden"});if(tinymce.isIE){p=p.replace(e,"$1<mcespell>$2</mcespell>");while((s=p.indexOf("<mcespell>"))!=-1){o=p.substring(0,s);if(o.length){r=j.createTextNode(f.decode(o));q.appendChild(r)}p=p.substring(s+10);s=p.indexOf("</mcespell>");o=p.substring(0,s);p=p.substring(s+11);q.appendChild(f.create("span",{"class":"mceItemHiddenSpellWord"},o))}if(p.length){r=j.createTextNode(f.decode(p));q.appendChild(r)}}else{q.innerHTML=p.replace(e,'$1<span class="mceItemHiddenSpellWord">$2</span>')}f.replace(q,t)}});h.moveToBookmark(i)},_showMenu:function(h,j){var i=this,h=i.editor,d=i._menu,l,k=h.dom,g=k.getViewPort(h.getWin()),f=j.target;j=0;if(!d){d=h.controlManager.createDropMenu("spellcheckermenu",{"class":"mceNoIcons"});i._menu=d}if(k.hasClass(f,"mceItemHiddenSpellWord")){d.removeAll();d.add({title:"spellchecker.wait","class":"mceMenuItemTitle"}).setDisabled(1);i._sendRPC("getSuggestions",[i.selectedLang,k.decode(f.innerHTML)],function(m){var e;d.removeAll();if(m.length>0){d.add({title:"spellchecker.sug","class":"mceMenuItemTitle"}).setDisabled(1);c(m,function(n){d.add({title:n,onclick:function(){k.replace(h.getDoc().createTextNode(n),f);i._checkDone()}})});d.addSeparator()}else{d.add({title:"spellchecker.no_sug","class":"mceMenuItemTitle"}).setDisabled(1)}if(h.getParam("show_ignore_words",true)){e=i.editor.getParam("spellchecker_enable_ignore_rpc","");d.add({title:"spellchecker.ignore_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});d.add({title:"spellchecker.ignore_words",onclick:function(){var n=f.innerHTML;i._removeWords(k.decode(n));i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWords",[i.selectedLang,n],function(o){h.setProgressState(0)})}}})}if(i.editor.getParam("spellchecker_enable_learn_rpc")){d.add({title:"spellchecker.learn_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();h.setProgressState(1);i._sendRPC("learnWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}})}d.update()});l=b.getPos(h.getContentAreaContainer());d.settings.offset_x=l.x;d.settings.offset_y=l.y;h.selection.select(f);l=k.getPos(f);d.showMenu(l.x,l.y+f.offsetHeight-g.y);return tinymce.dom.Event.cancel(j)}else{d.hideMenu()}},_checkDone:function(){var e=this,d=e.editor,g=d.dom,f;c(g.select("span"),function(h){if(h&&g.hasClass(h,"mceItemHiddenSpellWord")){f=true;return false}});if(!f){e._done()}},_done:function(){var d=this,e=d.active;if(d.active){d.active=0;d._removeWords();if(d._menu){d._menu.hideMenu()}if(e){d.editor.nodeChanged()}}},_sendRPC:function(e,g,d){var f=this;a.sendRPC({url:f.rpcUrl,method:e,params:g,success:d,error:function(i,h){f.editor.setProgressState(0);f.editor.windowManager.alert(i.errstr||("Error response: "+h.responseText))}})}});tinymce.PluginManager.add("spellchecker",tinymce.plugins.SpellcheckerPlugin)})(); \ No newline at end of file
+(function(){var a=tinymce.util.JSONRequest,c=tinymce.each,b=tinymce.DOM;tinymce.create("tinymce.plugins.SpellcheckerPlugin",{getInfo:function(){return{longname:"Spellchecker",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/spellchecker",version:tinymce.majorVersion+"."+tinymce.minorVersion}},init:function(e,f){var g=this,d;g.url=f;g.editor=e;g.rpcUrl=e.getParam("spellchecker_rpc_url","{backend}");if(g.rpcUrl=="{backend}"){if(tinymce.isIE){return}g.hasSupport=true;e.onContextMenu.addToTop(function(h,i){if(g.active){return false}})}e.addCommand("mceSpellCheck",function(){if(g.rpcUrl=="{backend}"){g.editor.getBody().spellcheck=g.active=!g.active;return}if(!g.active){e.setProgressState(1);g._sendRPC("checkWords",[g.selectedLang,g._getWords()],function(h){if(h.length>0){g.active=1;g._markWords(h);e.setProgressState(0);e.nodeChanged()}else{e.setProgressState(0);if(e.getParam("spellchecker_report_no_misspellings",true)){e.windowManager.alert("spellchecker.no_mpell")}}})}else{g._done()}});if(e.settings.content_css!==false){e.contentCSS.push(f+"/css/content.css")}e.onClick.add(g._showMenu,g);e.onContextMenu.add(g._showMenu,g);e.onBeforeGetContent.add(function(){if(g.active){g._removeWords()}});e.onNodeChange.add(function(i,h){h.setActive("spellchecker",g.active)});e.onSetContent.add(function(){g._done()});e.onBeforeGetContent.add(function(){g._done()});e.onBeforeExecCommand.add(function(h,i){if(i=="mceFullScreen"){g._done()}});g.languages={};c(e.getParam("spellchecker_languages","+English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr,German=de,Italian=it,Polish=pl,Portuguese=pt,Spanish=es,Swedish=sv","hash"),function(i,h){if(h.indexOf("+")===0){h=h.substring(1);g.selectedLang=i}g.languages[h]=i})},createControl:function(h,d){var f=this,g,e=f.editor;if(h=="spellchecker"){if(f.rpcUrl=="{backend}"){if(f.hasSupport){g=d.createButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f})}return g}g=d.createSplitButton(h,{title:"spellchecker.desc",cmd:"mceSpellCheck",scope:f});g.onRenderMenu.add(function(j,i){i.add({title:"spellchecker.langs","class":"mceMenuItemTitle"}).setDisabled(1);c(f.languages,function(n,m){var p={icon:1},l;p.onclick=function(){if(n==f.selectedLang){return}l.setSelected(1);f.selectedItem.setSelected(0);f.selectedItem=l;f.selectedLang=n};p.title=m;l=i.add(p);l.setSelected(n==f.selectedLang);if(n==f.selectedLang){f.selectedItem=l}})});return g}},_walk:function(i,g){var h=this.editor.getDoc(),e;if(h.createTreeWalker){e=h.createTreeWalker(i,NodeFilter.SHOW_TEXT,null,false);while((i=e.nextNode())!=null){g.call(this,i)}}else{tinymce.walk(i,g,"childNodes")}},_getSeparators:function(){var e="",d,f=this.editor.getParam("spellchecker_word_separator_chars",'\\s!"#$%&()*+,-./:;<=>?@[]^_{|}§©«®±¶·¸»¼½¾¿×÷¤\u201d\u201c');for(d=0;d<f.length;d++){e+="\\"+f.charAt(d)}return e},_getWords:function(){var e=this.editor,g=[],d="",f={},h=[];this._walk(e.getBody(),function(i){if(i.nodeType==3){d+=i.nodeValue+" "}});if(e.getParam("spellchecker_word_pattern")){h=d.match("("+e.getParam("spellchecker_word_pattern")+")","gi")}else{d=d.replace(new RegExp("([0-9]|["+this._getSeparators()+"])","g")," ");d=tinymce.trim(d.replace(/(\s+)/g," "));h=d.split(" ")}c(h,function(i){if(!f[i]){g.push(i);f[i]=1}});return g},_removeWords:function(d){var e=this.editor,h=e.dom,g=e.selection,f=g.getRng(true);c(h.select("span").reverse(),function(i){if(i&&(h.hasClass(i,"mceItemHiddenSpellWord")||h.hasClass(i,"mceItemHidden"))){if(!d||h.decode(i.innerHTML)==d){h.remove(i,1)}}});g.setRng(f)},_markWords:function(l){var h=this.editor,g=h.dom,j=h.getDoc(),i=h.selection,d=i.getRng(true),e=[],k=l.join("|"),m=this._getSeparators(),f=new RegExp("(^|["+m+"])("+k+")(?=["+m+"]|$)","g");this._walk(h.getBody(),function(o){if(o.nodeType==3){e.push(o)}});c(e,function(t){var r,q,o,s,p=t.nodeValue;if(f.test(p)){p=g.encode(p);q=g.create("span",{"class":"mceItemHidden"});if(tinymce.isIE){p=p.replace(f,"$1<mcespell>$2</mcespell>");while((s=p.indexOf("<mcespell>"))!=-1){o=p.substring(0,s);if(o.length){r=j.createTextNode(g.decode(o));q.appendChild(r)}p=p.substring(s+10);s=p.indexOf("</mcespell>");o=p.substring(0,s);p=p.substring(s+11);q.appendChild(g.create("span",{"class":"mceItemHiddenSpellWord"},o))}if(p.length){r=j.createTextNode(g.decode(p));q.appendChild(r)}}else{q.innerHTML=p.replace(f,'$1<span class="mceItemHiddenSpellWord">$2</span>')}g.replace(q,t)}});i.setRng(d)},_showMenu:function(h,j){var i=this,h=i.editor,d=i._menu,l,k=h.dom,g=k.getViewPort(h.getWin()),f=j.target;j=0;if(!d){d=h.controlManager.createDropMenu("spellcheckermenu",{"class":"mceNoIcons"});i._menu=d}if(k.hasClass(f,"mceItemHiddenSpellWord")){d.removeAll();d.add({title:"spellchecker.wait","class":"mceMenuItemTitle"}).setDisabled(1);i._sendRPC("getSuggestions",[i.selectedLang,k.decode(f.innerHTML)],function(m){var e;d.removeAll();if(m.length>0){d.add({title:"spellchecker.sug","class":"mceMenuItemTitle"}).setDisabled(1);c(m,function(n){d.add({title:n,onclick:function(){k.replace(h.getDoc().createTextNode(n),f);i._checkDone()}})});d.addSeparator()}else{d.add({title:"spellchecker.no_sug","class":"mceMenuItemTitle"}).setDisabled(1)}if(h.getParam("show_ignore_words",true)){e=i.editor.getParam("spellchecker_enable_ignore_rpc","");d.add({title:"spellchecker.ignore_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}}});d.add({title:"spellchecker.ignore_words",onclick:function(){var n=f.innerHTML;i._removeWords(k.decode(n));i._checkDone();if(e){h.setProgressState(1);i._sendRPC("ignoreWords",[i.selectedLang,n],function(o){h.setProgressState(0)})}}})}if(i.editor.getParam("spellchecker_enable_learn_rpc")){d.add({title:"spellchecker.learn_word",onclick:function(){var n=f.innerHTML;k.remove(f,1);i._checkDone();h.setProgressState(1);i._sendRPC("learnWord",[i.selectedLang,n],function(o){h.setProgressState(0)})}})}d.update()});l=b.getPos(h.getContentAreaContainer());d.settings.offset_x=l.x;d.settings.offset_y=l.y;h.selection.select(f);l=k.getPos(f);d.showMenu(l.x,l.y+f.offsetHeight-g.y);return tinymce.dom.Event.cancel(j)}else{d.hideMenu()}},_checkDone:function(){var e=this,d=e.editor,g=d.dom,f;c(g.select("span"),function(h){if(h&&g.hasClass(h,"mceItemHiddenSpellWord")){f=true;return false}});if(!f){e._done()}},_done:function(){var d=this,e=d.active;if(d.active){d.active=0;d._removeWords();if(d._menu){d._menu.hideMenu()}if(e){d.editor.nodeChanged()}}},_sendRPC:function(e,g,d){var f=this;a.sendRPC({url:f.rpcUrl,method:e,params:g,success:d,error:function(i,h){f.editor.setProgressState(0);f.editor.windowManager.alert(i.errstr||("Error response: "+h.responseText))}})}});tinymce.PluginManager.add("spellchecker",tinymce.plugins.SpellcheckerPlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js
index fb32af434..86fdfceb4 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/spellchecker/editor_plugin_src.js
@@ -208,7 +208,7 @@
},
_removeWords : function(w) {
- var ed = this.editor, dom = ed.dom, se = ed.selection, b = se.getBookmark();
+ var ed = this.editor, dom = ed.dom, se = ed.selection, r = se.getRng(true);
each(dom.select('span').reverse(), function(n) {
if (n && (dom.hasClass(n, 'mceItemHiddenSpellWord') || dom.hasClass(n, 'mceItemHidden'))) {
@@ -217,11 +217,11 @@
}
});
- se.moveToBookmark(b);
+ se.setRng(r);
},
_markWords : function(wl) {
- var ed = this.editor, dom = ed.dom, doc = ed.getDoc(), se = ed.selection, b = se.getBookmark(), nl = [],
+ var ed = this.editor, dom = ed.dom, doc = ed.getDoc(), se = ed.selection, r = se.getRng(true), nl = [],
w = wl.join('|'), re = this._getSeparators(), rx = new RegExp('(^|[' + re + '])(' + w + ')(?=[' + re + ']|$)', 'g');
// Collect all text nodes
@@ -279,7 +279,7 @@
}
});
- se.moveToBookmark(b);
+ se.setRng(r);
},
_showMenu : function(ed, e) {
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/css/props.css b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/css/props.css
index eb1f26496..3b8f0ee77 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/css/props.css
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/css/props.css
@@ -5,6 +5,7 @@ select, #block_text_indent, #box_width, #box_height, #box_padding_top, #box_padd
#box_margin_top, #box_margin_right, #box_margin_bottom, #box_margin_left, #positioning_width, #positioning_height, #positioning_zindex {width:70px;}
#positioning_placement_top, #positioning_placement_right, #positioning_placement_bottom, #positioning_placement_left {width:70px;}
#positioning_clip_top, #positioning_clip_right, #positioning_clip_bottom, #positioning_clip_left {width:70px;}
+.panel_toggle_insert_span {padding-top:10px;}
.panel_wrapper div.current {padding-top:10px;height:230px;}
.delim {border-left:1px solid gray;}
.tdelim {border-bottom:1px solid gray;}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin.js
index cab2153c4..dda9f928b 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin.js
@@ -1 +1 @@
-(function(){tinymce.create("tinymce.plugins.StylePlugin",{init:function(a,b){a.addCommand("mceStyleProps",function(){a.windowManager.open({file:b+"/props.htm",width:480+parseInt(a.getLang("style.delta_width",0)),height:320+parseInt(a.getLang("style.delta_height",0)),inline:1},{plugin_url:b,style_text:a.selection.getNode().style.cssText})});a.addCommand("mceSetElementStyle",function(d,c){if(e=a.selection.getNode()){a.dom.setAttrib(e,"style",c);a.execCommand("mceRepaint")}});a.onNodeChange.add(function(d,c,f){c.setDisabled("styleprops",f.nodeName==="BODY")});a.addButton("styleprops",{title:"style.desc",cmd:"mceStyleProps"})},getInfo:function(){return{longname:"Style",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/style",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("style",tinymce.plugins.StylePlugin)})(); \ No newline at end of file
+(function(){tinymce.create("tinymce.plugins.StylePlugin",{init:function(a,b){a.addCommand("mceStyleProps",function(){var c=false;var f=a.selection.getSelectedBlocks();var d=[];if(f.length===1){d.push(a.selection.getNode().style.cssText)}else{tinymce.each(f,function(g){d.push(a.dom.getAttrib(g,"style"))});c=true}a.windowManager.open({file:b+"/props.htm",width:480+parseInt(a.getLang("style.delta_width",0)),height:340+parseInt(a.getLang("style.delta_height",0)),inline:1},{applyStyleToBlocks:c,plugin_url:b,styles:d})});a.addCommand("mceSetElementStyle",function(d,c){if(e=a.selection.getNode()){a.dom.setAttrib(e,"style",c);a.execCommand("mceRepaint")}});a.onNodeChange.add(function(d,c,f){c.setDisabled("styleprops",f.nodeName==="BODY")});a.addButton("styleprops",{title:"style.desc",cmd:"mceStyleProps"})},getInfo:function(){return{longname:"Style",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/style",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("style",tinymce.plugins.StylePlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin_src.js
index 5f7755f18..eaa7c7713 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/editor_plugin_src.js
@@ -13,14 +13,30 @@
init : function(ed, url) {
// Register commands
ed.addCommand('mceStyleProps', function() {
+
+ var applyStyleToBlocks = false;
+ var blocks = ed.selection.getSelectedBlocks();
+ var styles = [];
+
+ if (blocks.length === 1) {
+ styles.push(ed.selection.getNode().style.cssText);
+ }
+ else {
+ tinymce.each(blocks, function(block) {
+ styles.push(ed.dom.getAttrib(block, 'style'));
+ });
+ applyStyleToBlocks = true;
+ }
+
ed.windowManager.open({
file : url + '/props.htm',
width : 480 + parseInt(ed.getLang('style.delta_width', 0)),
- height : 320 + parseInt(ed.getLang('style.delta_height', 0)),
+ height : 340 + parseInt(ed.getLang('style.delta_height', 0)),
inline : 1
}, {
+ applyStyleToBlocks : applyStyleToBlocks,
plugin_url : url,
- style_text : ed.selection.getNode().style.cssText
+ styles : styles
});
});
@@ -52,4 +68,4 @@
// Register plugin
tinymce.PluginManager.add('style', tinymce.plugins.StylePlugin);
-})(); \ No newline at end of file
+})();
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/js/props.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/js/props.js
index 6800a9a9a..0a8a8ec3e 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/js/props.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/js/props.js
@@ -27,10 +27,41 @@ var defaultBorderStyle = "none;solid;dashed;dotted;double;groove;ridge;inset;out
var defaultBorderWidth = "thin;medium;thick";
var defaultListType = "disc;circle;square;decimal;lower-roman;upper-roman;lower-alpha;upper-alpha;none";
-function init() {
+function aggregateStyles(allStyles) {
+ var mergedStyles = {};
+
+ tinymce.each(allStyles, function(style) {
+ if (style !== '') {
+ var parsedStyles = tinyMCEPopup.editor.dom.parseStyle(style);
+ for (var name in parsedStyles) {
+ if (parsedStyles.hasOwnProperty(name)) {
+ if (mergedStyles[name] === undefined) {
+ mergedStyles[name] = parsedStyles[name];
+ }
+ else if (name === 'text-decoration') {
+ if (mergedStyles[name].indexOf(parsedStyles[name]) === -1) {
+ mergedStyles[name] = mergedStyles[name] +' '+ parsedStyles[name];
+ }
+ }
+ }
+ }
+ }
+ });
+
+ return mergedStyles;
+}
+
+var applyActionIsInsert;
+var existingStyles;
+
+function init(ed) {
var ce = document.getElementById('container'), h;
- ce.style.cssText = tinyMCEPopup.getWindowArg('style_text');
+ existingStyles = aggregateStyles(tinyMCEPopup.getWindowArg('styles'));
+ ce.style.cssText = tinyMCEPopup.editor.dom.serializeStyle(existingStyles);
+
+ applyActionIsInsert = ed.getParam("edit_css_style_insert_span", false);
+ document.getElementById('toggle_insert_span').checked = applyActionIsInsert;
h = getBrowserHTML('background_image_browser','background_image','image','advimage');
document.getElementById("background_image_browser").innerHTML = h;
@@ -144,6 +175,8 @@ function setupFormData() {
f.text_overline.checked = inStr(ce.style.textDecoration, 'overline');
f.text_linethrough.checked = inStr(ce.style.textDecoration, 'line-through');
f.text_blink.checked = inStr(ce.style.textDecoration, 'blink');
+ f.text_none.checked = inStr(ce.style.textDecoration, 'none');
+ updateTextDecorations();
// Setup background fields
@@ -366,13 +399,41 @@ function hasEqualValues(a) {
return true;
}
+function toggleApplyAction() {
+ applyActionIsInsert = ! applyActionIsInsert;
+}
+
function applyAction() {
var ce = document.getElementById('container'), ed = tinyMCEPopup.editor;
generateCSS();
tinyMCEPopup.restoreSelection();
- ed.dom.setAttrib(ed.selection.getSelectedBlocks(), 'style', tinyMCEPopup.editor.dom.serializeStyle(tinyMCEPopup.editor.dom.parseStyle(ce.style.cssText)));
+
+ var newStyles = tinyMCEPopup.editor.dom.parseStyle(ce.style.cssText);
+
+ if (applyActionIsInsert) {
+ ed.formatter.register('plugin_style', {
+ inline: 'span', styles: existingStyles
+ });
+ ed.formatter.remove('plugin_style');
+
+ ed.formatter.register('plugin_style', {
+ inline: 'span', styles: newStyles
+ });
+ ed.formatter.apply('plugin_style');
+ } else {
+ var nodes;
+
+ if (tinyMCEPopup.getWindowArg('applyStyleToBlocks')) {
+ nodes = ed.selection.getSelectedBlocks();
+ }
+ else {
+ nodes = ed.selection.getNode();
+ }
+
+ ed.dom.setAttrib(nodes, 'style', tinyMCEPopup.editor.dom.serializeStyle(newStyles));
+ }
}
function updateAction() {
@@ -632,4 +693,17 @@ function synch(fr, to) {
selectByValue(f, to + "_measurement", f.elements[fr + "_measurement"].value);
}
+function updateTextDecorations(){
+ var el = document.forms[0].elements;
+
+ var textDecorations = ["text_underline", "text_overline", "text_linethrough", "text_blink"];
+ var noneChecked = el["text_none"].checked;
+ tinymce.each(textDecorations, function(id) {
+ el[id].disabled = noneChecked;
+ if (noneChecked) {
+ el[id].checked = false;
+ }
+ });
+}
+
tinyMCEPopup.onInit.add(init);
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/langs/en_dlg.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/langs/en_dlg.js
index 9a1d4a223..35881b3ac 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/langs/en_dlg.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/langs/en_dlg.js
@@ -1 +1 @@
-tinyMCE.addI18n('en.style_dlg',{"text_lineheight":"Line Height","text_variant":"Variant","text_style":"Style","text_weight":"Weight","text_size":"Size","text_font":"Font","text_props":"Text","positioning_tab":"Positioning","list_tab":"List","border_tab":"Border","box_tab":"Box","block_tab":"Block","background_tab":"Background","text_tab":"Text",apply:"Apply",title:"Edit CSS Style",clip:"Clip",placement:"Placement",overflow:"Overflow",zindex:"Z-index",visibility:"Visibility","positioning_type":"Type",position:"Position","bullet_image":"Bullet Image","list_type":"Type",color:"Color",height:"Height",width:"Width",style:"Style",margin:"Margin",left:"Left",bottom:"Bottom",right:"Right",top:"Top",same:"Same for All",padding:"Padding","box_clear":"Clear","box_float":"Float","box_height":"Height","box_width":"Width","block_display":"Display","block_whitespace":"Whitespace","block_text_indent":"Text Indent","block_text_align":"Text Align","block_vertical_alignment":"Vertical Alignment","block_letterspacing":"Letter Spacing","block_wordspacing":"Word Spacing","background_vpos":"Vertical Position","background_hpos":"Horizontal Position","background_attachment":"Attachment","background_repeat":"Repeat","background_image":"Background Image","background_color":"Background Color","text_none":"None","text_blink":"Blink","text_case":"Case","text_striketrough":"Strikethrough","text_underline":"Underline","text_overline":"Overline","text_decoration":"Decoration","text_color":"Color",text:"Text",background:"Background",block:"Block",box:"Box",border:"Border",list:"List"}); \ No newline at end of file
+tinyMCE.addI18n('en.style_dlg',{"text_lineheight":"Line Height","text_variant":"Variant","text_style":"Style","text_weight":"Weight","text_size":"Size","text_font":"Font","text_props":"Text","positioning_tab":"Positioning","list_tab":"List","border_tab":"Border","box_tab":"Box","block_tab":"Block","background_tab":"Background","text_tab":"Text",apply:"Apply",toggle_insert_span:"Insert span at selection",title:"Edit CSS Style",clip:"Clip",placement:"Placement",overflow:"Overflow",zindex:"Z-index",visibility:"Visibility","positioning_type":"Type",position:"Position","bullet_image":"Bullet Image","list_type":"Type",color:"Color",height:"Height",width:"Width",style:"Style",margin:"Margin",left:"Left",bottom:"Bottom",right:"Right",top:"Top",same:"Same for All",padding:"Padding","box_clear":"Clear","box_float":"Float","box_height":"Height","box_width":"Width","block_display":"Display","block_whitespace":"Whitespace","block_text_indent":"Text Indent","block_text_align":"Text Align","block_vertical_alignment":"Vertical Alignment","block_letterspacing":"Letter Spacing","block_wordspacing":"Word Spacing","background_vpos":"Vertical Position","background_hpos":"Horizontal Position","background_attachment":"Attachment","background_repeat":"Repeat","background_image":"Background Image","background_color":"Background Color","text_none":"None","text_blink":"Blink","text_case":"Case","text_striketrough":"Strikethrough","text_underline":"Underline","text_overline":"Overline","text_decoration":"Decoration","text_color":"Color",text:"Text",background:"Background",block:"Block",box:"Box",border:"Border",list:"List"});
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/props.htm b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/props.htm
index 76ab68d89..7dc087a30 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/props.htm
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/props.htm
@@ -118,7 +118,7 @@
<td><label for="text_blink">{#style_dlg.text_blink}</label></td>
</tr>
<tr>
- <td><input id="text_none" name="text_none" class="checkbox" type="checkbox" /></td>
+ <td><input id="text_none" name="text_none" class="checkbox" type="checkbox" onclick="updateTextDecorations();"/></td>
<td><label for="text_none">{#style_dlg.text_none}</label></td>
</tr>
</table>
@@ -825,6 +825,11 @@
</div>
</div>
+<div class="panel_toggle_insert_span">
+ <input type="checkbox" class="checkbox" id="toggle_insert_span" name="toggle_insert_span" onClick="toggleApplyAction();" />
+ <label for="toggle_insert_span">{#style_dlg.toggle_insert_span}</label>
+</div>
+
<div class="mceActionPanel">
<input type="submit" id="insert" name="insert" value="{#update}" />
<input type="button" class="button" id="apply" name="apply" value="{#style_dlg.apply}" onClick="applyAction();" />
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/readme.txt b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/readme.txt
new file mode 100644
index 000000000..5bac30202
--- /dev/null
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/style/readme.txt
@@ -0,0 +1,19 @@
+Edit CSS Style plug-in notes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Unlike WYSIWYG editor functionality that operates only on the selected text,
+typically by inserting new HTML elements with the specified styles.
+This plug-in operates on the HTML blocks surrounding the selected text.
+No new HTML elements are created.
+
+This plug-in only operates on the surrounding blocks and not the nearest
+parent node. This means that if a block encapsulates a node,
+e.g <p><span>text</span></p>, then only the styles in the block are
+recognized, not those in the span.
+
+When selecting text that includes multiple blocks at the same level (peers),
+this plug-in accumulates the specified styles in all of the surrounding blocks
+and populates the dialogue checkboxes accordingly. There is no differentiation
+between styles set in all the blocks versus styles set in some of the blocks.
+
+When the [Update] or [Apply] buttons are pressed, the styles selected in the
+checkboxes are applied to all blocks that surround the selected text.
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js
index 42a82d112..2c5129161 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin.js
@@ -1 +1 @@
-(function(){var c=tinymce.DOM,a=tinymce.dom.Event,d=tinymce.each,b=tinymce.explode;tinymce.create("tinymce.plugins.TabFocusPlugin",{init:function(f,g){function e(i,j){if(j.keyCode===9){return a.cancel(j)}}function h(l,p){var j,m,o,n,k;function q(t){n=c.select(":input:enabled,*[tabindex]");function s(v){return v.nodeName==="BODY"||(v.type!="hidden"&&!(v.style.display=="none")&&!(v.style.visibility=="hidden")&&s(v.parentNode))}function i(v){return v.attributes.tabIndex.specified||v.nodeName=="INPUT"||v.nodeName=="TEXTAREA"}function u(){return tinymce.isIE6||tinymce.isIE7}function r(v){return((!u()||i(v)))&&v.getAttribute("tabindex")!="-1"&&s(v)}d(n,function(w,v){if(w.id==l.id){j=v;return false}});if(t>0){for(m=j+1;m<n.length;m++){if(r(n[m])){return n[m]}}}else{for(m=j-1;m>=0;m--){if(r(n[m])){return n[m]}}}return null}if(p.keyCode===9){k=b(l.getParam("tab_focus",l.getParam("tabfocus_elements",":prev,:next")));if(k.length==1){k[1]=k[0];k[0]=":prev"}if(p.shiftKey){if(k[0]==":prev"){n=q(-1)}else{n=c.get(k[0])}}else{if(k[1]==":next"){n=q(1)}else{n=c.get(k[1])}}if(n){if(n.id&&(l=tinymce.get(n.id||n.name))){l.focus()}else{window.setTimeout(function(){if(!tinymce.isWebKit){window.focus()}n.focus()},10)}return a.cancel(p)}}}f.onKeyUp.add(e);if(tinymce.isGecko){f.onKeyPress.add(h);f.onKeyDown.add(e)}else{f.onKeyDown.add(h)}},getInfo:function(){return{longname:"Tabfocus",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("tabfocus",tinymce.plugins.TabFocusPlugin)})(); \ No newline at end of file
+(function(){var c=tinymce.DOM,a=tinymce.dom.Event,d=tinymce.each,b=tinymce.explode;tinymce.create("tinymce.plugins.TabFocusPlugin",{init:function(f,g){function e(i,j){if(j.keyCode===9){return a.cancel(j)}}function h(l,p){var j,m,o,n,k;function q(t){n=c.select(":input:enabled,*[tabindex]:not(iframe)");function s(v){return v.nodeName==="BODY"||(v.type!="hidden"&&!(v.style.display=="none")&&!(v.style.visibility=="hidden")&&s(v.parentNode))}function i(v){return v.attributes.tabIndex.specified||v.nodeName=="INPUT"||v.nodeName=="TEXTAREA"}function u(){return tinymce.isIE6||tinymce.isIE7}function r(v){return((!u()||i(v)))&&v.getAttribute("tabindex")!="-1"&&s(v)}d(n,function(w,v){if(w.id==l.id){j=v;return false}});if(t>0){for(m=j+1;m<n.length;m++){if(r(n[m])){return n[m]}}}else{for(m=j-1;m>=0;m--){if(r(n[m])){return n[m]}}}return null}if(p.keyCode===9){k=b(l.getParam("tab_focus",l.getParam("tabfocus_elements",":prev,:next")));if(k.length==1){k[1]=k[0];k[0]=":prev"}if(p.shiftKey){if(k[0]==":prev"){n=q(-1)}else{n=c.get(k[0])}}else{if(k[1]==":next"){n=q(1)}else{n=c.get(k[1])}}if(n){if(n.id&&(l=tinymce.get(n.id||n.name))){l.focus()}else{window.setTimeout(function(){if(!tinymce.isWebKit){window.focus()}n.focus()},10)}return a.cancel(p)}}}f.onKeyUp.add(e);if(tinymce.isGecko){f.onKeyPress.add(h);f.onKeyDown.add(e)}else{f.onKeyDown.add(h)}},getInfo:function(){return{longname:"Tabfocus",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/tabfocus",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("tabfocus",tinymce.plugins.TabFocusPlugin)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js
index a1579c85f..94f45320d 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/tabfocus/editor_plugin_src.js
@@ -22,7 +22,7 @@
var x, i, f, el, v;
function find(d) {
- el = DOM.select(':input:enabled,*[tabindex]');
+ el = DOM.select(':input:enabled,*[tabindex]:not(iframe)');
function canSelectRecursive(e) {
return e.nodeName==="BODY" || (e.type != 'hidden' &&
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin.js
index 2f3b0e2d7..4a35a5ef9 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin.js
@@ -1 +1 @@
-(function(d){var e=d.each;function c(g,h){var j=h.ownerDocument,f=j.createRange(),k;f.setStartBefore(h);f.setEnd(g.endContainer,g.endOffset);k=j.createElement("body");k.appendChild(f.cloneContents());return k.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi,"-").replace(/<[^>]+>/g,"").length==0}function a(g,f){return parseInt(g.getAttribute(f)||1)}function b(H,G,K){var g,L,D,o;t();o=G.getParent(K.getStart(),"th,td");if(o){L=F(o);D=I();o=z(L.x,L.y)}function A(N,M){N=N.cloneNode(M);N.removeAttribute("id");return N}function t(){var M=0;g=[];e(["thead","tbody","tfoot"],function(N){var O=G.select("> "+N+" tr",H);e(O,function(P,Q){Q+=M;e(G.select("> td, > th",P),function(W,R){var S,T,U,V;if(g[Q]){while(g[Q][R]){R++}}U=a(W,"rowspan");V=a(W,"colspan");for(T=Q;T<Q+U;T++){if(!g[T]){g[T]=[]}for(S=R;S<R+V;S++){g[T][S]={part:N,real:T==Q&&S==R,elm:W,rowspan:U,colspan:V}}}})});M+=O.length})}function z(M,O){var N;N=g[O];if(N){return N[M]}}function s(O,M,N){if(O){N=parseInt(N);if(N===1){O.removeAttribute(M,1)}else{O.setAttribute(M,N,1)}}}function j(M){return M&&(G.hasClass(M.elm,"mceSelected")||M==o)}function k(){var M=[];e(H.rows,function(N){e(N.cells,function(O){if(G.hasClass(O,"mceSelected")||O==o.elm){M.push(N);return false}})});return M}function r(){var M=G.createRng();M.setStartAfter(H);M.setEndAfter(H);K.setRng(M);G.remove(H)}function f(M){var N;d.walk(M,function(P){var O;if(P.nodeType==3){e(G.getParents(P.parentNode,null,M).reverse(),function(Q){Q=A(Q,false);if(!N){N=O=Q}else{if(O){O.appendChild(Q)}}O=Q});if(O){O.innerHTML=d.isIE?"&nbsp;":'<br data-mce-bogus="1" />'}return false}},"childNodes");M=A(M,false);s(M,"rowSpan",1);s(M,"colSpan",1);if(N){M.appendChild(N)}else{if(!d.isIE){M.innerHTML='<br data-mce-bogus="1" />'}}return M}function q(){var M=G.createRng();e(G.select("tr",H),function(N){if(N.cells.length==0){G.remove(N)}});if(G.select("tr",H).length==0){M.setStartAfter(H);M.setEndAfter(H);K.setRng(M);G.remove(H);return}e(G.select("thead,tbody,tfoot",H),function(N){if(N.rows.length==0){G.remove(N)}});t();row=g[Math.min(g.length-1,L.y)];if(row){K.select(row[Math.min(row.length-1,L.x)].elm,true);K.collapse(true)}}function u(S,Q,U,R){var P,N,M,O,T;P=g[Q][S].elm.parentNode;for(M=1;M<=U;M++){P=G.getNext(P,"tr");if(P){for(N=S;N>=0;N--){T=g[Q+M][N].elm;if(T.parentNode==P){for(O=1;O<=R;O++){G.insertAfter(f(T),T)}break}}if(N==-1){for(O=1;O<=R;O++){P.insertBefore(f(P.cells[0]),P.cells[0])}}}}}function C(){e(g,function(M,N){e(M,function(P,O){var S,R,T,Q;if(j(P)){P=P.elm;S=a(P,"colspan");R=a(P,"rowspan");if(S>1||R>1){s(P,"rowSpan",1);s(P,"colSpan",1);for(Q=0;Q<S-1;Q++){G.insertAfter(f(P),P)}u(O,N,R-1,S)}}})})}function p(V,S,Y){var P,O,X,W,U,R,T,M,V,N,Q;if(V){pos=F(V);P=pos.x;O=pos.y;X=P+(S-1);W=O+(Y-1)}else{P=L.x;O=L.y;X=D.x;W=D.y}T=z(P,O);M=z(X,W);if(T&&M&&T.part==M.part){C();t();T=z(P,O).elm;s(T,"colSpan",(X-P)+1);s(T,"rowSpan",(W-O)+1);for(R=O;R<=W;R++){for(U=P;U<=X;U++){if(!g[R]||!g[R][U]){continue}V=g[R][U].elm;if(V!=T){N=d.grep(V.childNodes);e(N,function(Z){T.appendChild(Z)});if(N.length){N=d.grep(T.childNodes);Q=0;e(N,function(Z){if(Z.nodeName=="BR"&&G.getAttrib(Z,"data-mce-bogus")&&Q++<N.length-1){T.removeChild(Z)}})}G.remove(V)}}}q()}}function l(Q){var M,S,P,R,T,U,N,V,O;e(g,function(W,X){e(W,function(Z,Y){if(j(Z)){Z=Z.elm;T=Z.parentNode;U=A(T,false);M=X;if(Q){return false}}});if(Q){return !M}});for(R=0;R<g[0].length;R++){if(!g[M][R]){continue}S=g[M][R].elm;if(S!=P){if(!Q){O=a(S,"rowspan");if(O>1){s(S,"rowSpan",O+1);continue}}else{if(M>0&&g[M-1][R]){V=g[M-1][R].elm;O=a(V,"rowSpan");if(O>1){s(V,"rowSpan",O+1);continue}}}N=f(S);s(N,"colSpan",S.colSpan);U.appendChild(N);P=S}}if(U.hasChildNodes()){if(!Q){G.insertAfter(U,T)}else{T.parentNode.insertBefore(U,T)}}}function h(N){var O,M;e(g,function(P,Q){e(P,function(S,R){if(j(S)){O=R;if(N){return false}}});if(N){return !O}});e(g,function(S,T){var P,Q,R;if(!S[O]){return}P=S[O].elm;if(P!=M){R=a(P,"colspan");Q=a(P,"rowspan");if(R==1){if(!N){G.insertAfter(f(P),P);u(O,T,Q-1,R)}else{P.parentNode.insertBefore(f(P),P);u(O,T,Q-1,R)}}else{s(P,"colSpan",P.colSpan+1)}M=P}})}function n(){var M=[];e(g,function(N,O){e(N,function(Q,P){if(j(Q)&&d.inArray(M,P)===-1){e(g,function(T){var R=T[P].elm,S;S=a(R,"colSpan");if(S>1){s(R,"colSpan",S-1)}else{G.remove(R)}});M.push(P)}})});q()}function m(){var N;function M(Q){var P,R,O;P=G.getNext(Q,"tr");e(Q.cells,function(S){var T=a(S,"rowSpan");if(T>1){s(S,"rowSpan",T-1);R=F(S);u(R.x,R.y,1,1)}});R=F(Q.cells[0]);e(g[R.y],function(S){var T;S=S.elm;if(S!=O){T=a(S,"rowSpan");if(T<=1){G.remove(S)}else{s(S,"rowSpan",T-1)}O=S}})}N=k();e(N.reverse(),function(O){M(O)});q()}function E(){var M=k();G.remove(M);q();return M}function J(){var M=k();e(M,function(O,N){M[N]=A(O,true)});return M}function B(O,N){var P=k(),M=P[N?0:P.length-1],Q=M.cells.length;e(g,function(S){var R;Q=0;e(S,function(U,T){if(U.real){Q+=U.colspan}if(U.elm.parentNode==M){R=1}});if(R){return false}});if(!N){O.reverse()}e(O,function(T){var S=T.cells.length,R;for(i=0;i<S;i++){R=T.cells[i];s(R,"colSpan",1);s(R,"rowSpan",1)}for(i=S;i<Q;i++){T.appendChild(f(T.cells[S-1]))}for(i=Q;i<S;i++){G.remove(T.cells[i])}if(N){M.parentNode.insertBefore(T,M)}else{G.insertAfter(T,M)}})}function F(M){var N;e(g,function(O,P){e(O,function(R,Q){if(R.elm==M){N={x:Q,y:P};return false}});return !N});return N}function w(M){L=F(M)}function I(){var O,N,M;N=M=0;e(g,function(P,Q){e(P,function(S,R){var U,T;if(j(S)){S=g[Q][R];if(R>N){N=R}if(Q>M){M=Q}if(S.real){U=S.colspan-1;T=S.rowspan-1;if(U){if(R+U>N){N=R+U}}if(T){if(Q+T>M){M=Q+T}}}}})});return{x:N,y:M}}function v(S){var P,O,U,T,N,M,Q,R;D=F(S);if(L&&D){P=Math.min(L.x,D.x);O=Math.min(L.y,D.y);U=Math.max(L.x,D.x);T=Math.max(L.y,D.y);N=U;M=T;for(y=O;y<=M;y++){S=g[y][P];if(!S.real){if(P-(S.colspan-1)<P){P-=S.colspan-1}}}for(x=P;x<=N;x++){S=g[O][x];if(!S.real){if(O-(S.rowspan-1)<O){O-=S.rowspan-1}}}for(y=O;y<=T;y++){for(x=P;x<=U;x++){S=g[y][x];if(S.real){Q=S.colspan-1;R=S.rowspan-1;if(Q){if(x+Q>N){N=x+Q}}if(R){if(y+R>M){M=y+R}}}}}G.removeClass(G.select("td.mceSelected,th.mceSelected"),"mceSelected");for(y=O;y<=M;y++){for(x=P;x<=N;x++){if(g[y][x]){G.addClass(g[y][x].elm,"mceSelected")}}}}}d.extend(this,{deleteTable:r,split:C,merge:p,insertRow:l,insertCol:h,deleteCols:n,deleteRows:m,cutRows:E,copyRows:J,pasteRows:B,getPos:F,setStartCell:w,setEndCell:v})}d.create("tinymce.plugins.TablePlugin",{init:function(g,h){var f,m,j=true;function l(p){var o=g.selection,n=g.dom.getParent(p||o.getNode(),"table");if(n){return new b(n,g.dom,o)}}function k(){g.getBody().style.webkitUserSelect="";if(j){g.dom.removeClass(g.dom.select("td.mceSelected,th.mceSelected"),"mceSelected");j=false}}e([["table","table.desc","mceInsertTable",true],["delete_table","table.del","mceTableDelete"],["delete_col","table.delete_col_desc","mceTableDeleteCol"],["delete_row","table.delete_row_desc","mceTableDeleteRow"],["col_after","table.col_after_desc","mceTableInsertColAfter"],["col_before","table.col_before_desc","mceTableInsertColBefore"],["row_after","table.row_after_desc","mceTableInsertRowAfter"],["row_before","table.row_before_desc","mceTableInsertRowBefore"],["row_props","table.row_desc","mceTableRowProps",true],["cell_props","table.cell_desc","mceTableCellProps",true],["split_cells","table.split_cells_desc","mceTableSplitCells",true],["merge_cells","table.merge_cells_desc","mceTableMergeCells",true]],function(n){g.addButton(n[0],{title:n[1],cmd:n[2],ui:n[3]})});if(!d.isIE){g.onClick.add(function(n,o){o=o.target;if(o.nodeName==="TABLE"){n.selection.select(o);n.nodeChanged()}})}g.onPreProcess.add(function(o,p){var n,q,r,t=o.dom,s;n=t.select("table",p.node);q=n.length;while(q--){r=n[q];t.setAttrib(r,"data-mce-style","");if((s=t.getAttrib(r,"width"))){t.setStyle(r,"width",s);t.setAttrib(r,"width","")}if((s=t.getAttrib(r,"height"))){t.setStyle(r,"height",s);t.setAttrib(r,"height","")}}});g.onNodeChange.add(function(q,o,s){var r;s=q.selection.getStart();r=q.dom.getParent(s,"td,th,caption");o.setActive("table",s.nodeName==="TABLE"||!!r);if(r&&r.nodeName==="CAPTION"){r=0}o.setDisabled("delete_table",!r);o.setDisabled("delete_col",!r);o.setDisabled("delete_table",!r);o.setDisabled("delete_row",!r);o.setDisabled("col_after",!r);o.setDisabled("col_before",!r);o.setDisabled("row_after",!r);o.setDisabled("row_before",!r);o.setDisabled("row_props",!r);o.setDisabled("cell_props",!r);o.setDisabled("split_cells",!r);o.setDisabled("merge_cells",!r)});g.onInit.add(function(r){var p,t,q=r.dom,u;f=r.windowManager;r.onMouseDown.add(function(w,z){if(z.button!=2){k();t=q.getParent(z.target,"td,th");p=q.getParent(t,"table")}});q.bind(r.getDoc(),"mouseover",function(C){var A,z,B=C.target;if(t&&(u||B!=t)&&(B.nodeName=="TD"||B.nodeName=="TH")){z=q.getParent(B,"table");if(z==p){if(!u){u=l(z);u.setStartCell(t);r.getBody().style.webkitUserSelect="none"}u.setEndCell(B);j=true}A=r.selection.getSel();try{if(A.removeAllRanges){A.removeAllRanges()}else{A.empty()}}catch(w){}C.preventDefault()}});r.onMouseUp.add(function(F,G){var z,B=F.selection,H,I=B.getSel(),w,C,A,E;if(t){if(u){F.getBody().style.webkitUserSelect=""}function D(J,L){var K=new d.dom.TreeWalker(J,J);do{if(J.nodeType==3&&d.trim(J.nodeValue).length!=0){if(L){z.setStart(J,0)}else{z.setEnd(J,J.nodeValue.length)}return}if(J.nodeName=="BR"){if(L){z.setStartBefore(J)}else{z.setEndBefore(J)}return}}while(J=(L?K.next():K.prev()))}H=q.select("td.mceSelected,th.mceSelected");if(H.length>0){z=q.createRng();C=H[0];E=H[H.length-1];z.setStartBefore(C);z.setEndAfter(C);D(C,1);w=new d.dom.TreeWalker(C,q.getParent(H[0],"table"));do{if(C.nodeName=="TD"||C.nodeName=="TH"){if(!q.hasClass(C,"mceSelected")){break}A=C}}while(C=w.next());D(A);B.setRng(z)}F.nodeChanged();t=u=p=null}});r.onKeyUp.add(function(w,z){k()});r.onKeyDown.add(function(w,z){n(w)});r.onMouseDown.add(function(w,z){if(z.button!=2){n(w)}});function o(D,z,A,F){var B=3,G=D.dom.getParent(z.startContainer,"TABLE"),C,w,E;if(G){C=G.parentNode}w=z.startContainer.nodeType==B&&z.startOffset==0&&z.endOffset==0&&F&&(A.nodeName=="TR"||A==C);E=(A.nodeName=="TD"||A.nodeName=="TH")&&!F;return w||E}function n(A){if(!d.isWebKit){return}var z=A.selection.getRng();var C=A.selection.getNode();var B=A.dom.getParent(z.startContainer,"TD,TH");if(!o(A,z,C,B)){return}if(!B){B=C}var w=B.lastChild;while(w.lastChild){w=w.lastChild}z.setEnd(w,w.nodeValue.length);A.selection.setRng(z)}r.plugins.table.fixTableCellSelection=n;if(r&&r.plugins.contextmenu){r.plugins.contextmenu.onContextMenu.add(function(A,w,C){var D,B=r.selection,z=B.getNode()||r.getBody();if(r.dom.getParent(C,"td")||r.dom.getParent(C,"th")||r.dom.select("td.mceSelected,th.mceSelected").length){w.removeAll();if(z.nodeName=="A"&&!r.dom.getAttrib(z,"name")){w.add({title:"advanced.link_desc",icon:"link",cmd:r.plugins.advlink?"mceAdvLink":"mceLink",ui:true});w.add({title:"advanced.unlink_desc",icon:"unlink",cmd:"UnLink"});w.addSeparator()}if(z.nodeName=="IMG"&&z.className.indexOf("mceItem")==-1){w.add({title:"advanced.image_desc",icon:"image",cmd:r.plugins.advimage?"mceAdvImage":"mceImage",ui:true});w.addSeparator()}w.add({title:"table.desc",icon:"table",cmd:"mceInsertTable",value:{action:"insert"}});w.add({title:"table.props_desc",icon:"table_props",cmd:"mceInsertTable"});w.add({title:"table.del",icon:"delete_table",cmd:"mceTableDelete"});w.addSeparator();D=w.addMenu({title:"table.cell"});D.add({title:"table.cell_desc",icon:"cell_props",cmd:"mceTableCellProps"});D.add({title:"table.split_cells_desc",icon:"split_cells",cmd:"mceTableSplitCells"});D.add({title:"table.merge_cells_desc",icon:"merge_cells",cmd:"mceTableMergeCells"});D=w.addMenu({title:"table.row"});D.add({title:"table.row_desc",icon:"row_props",cmd:"mceTableRowProps"});D.add({title:"table.row_before_desc",icon:"row_before",cmd:"mceTableInsertRowBefore"});D.add({title:"table.row_after_desc",icon:"row_after",cmd:"mceTableInsertRowAfter"});D.add({title:"table.delete_row_desc",icon:"delete_row",cmd:"mceTableDeleteRow"});D.addSeparator();D.add({title:"table.cut_row_desc",icon:"cut",cmd:"mceTableCutRow"});D.add({title:"table.copy_row_desc",icon:"copy",cmd:"mceTableCopyRow"});D.add({title:"table.paste_row_before_desc",icon:"paste",cmd:"mceTablePasteRowBefore"}).setDisabled(!m);D.add({title:"table.paste_row_after_desc",icon:"paste",cmd:"mceTablePasteRowAfter"}).setDisabled(!m);D=w.addMenu({title:"table.col"});D.add({title:"table.col_before_desc",icon:"col_before",cmd:"mceTableInsertColBefore"});D.add({title:"table.col_after_desc",icon:"col_after",cmd:"mceTableInsertColAfter"});D.add({title:"table.delete_col_desc",icon:"delete_col",cmd:"mceTableDeleteCol"})}else{w.add({title:"table.desc",icon:"table",cmd:"mceInsertTable"})}})}if(d.isWebKit){function v(C,N){var L=d.VK;var Q=N.keyCode;function O(Y,U,S){var T=Y?"previousSibling":"nextSibling";var Z=C.dom.getParent(U,"tr");var X=Z[T];if(X){z(C,U,X,Y);d.dom.Event.cancel(S);return true}else{var aa=C.dom.getParent(Z,"table");var W=Z.parentNode;var R=W.nodeName.toLowerCase();if(R==="tbody"||R===(Y?"tfoot":"thead")){var V=w(Y,aa,W,"tbody");if(V!==null){return K(Y,V,U,S)}}return M(Y,Z,T,aa,S)}}function w(V,T,U,X){var S=C.dom.select(">"+X,T);var R=S.indexOf(U);if(V&&R===0||!V&&R===S.length-1){return B(V,T)}else{if(R===-1){var W=U.tagName.toLowerCase()==="thead"?0:S.length-1;return S[W]}else{return S[R+(V?-1:1)]}}}function B(U,T){var S=U?"thead":"tfoot";var R=C.dom.select(">"+S,T);return R.length!==0?R[0]:null}function K(V,T,S,U){var R=J(T,V);R&&z(C,S,R,V);d.dom.Event.cancel(U);return true}function M(Y,U,R,X,W){var S=X[R];if(S){F(S);return true}else{var V=C.dom.getParent(X,"td,th");if(V){return O(Y,V,W)}else{var T=J(U,!Y);F(T);return d.dom.Event.cancel(W)}}}function J(S,R){return S&&S[R?"lastChild":"firstChild"]}function F(R){C.selection.setCursorLocation(R,0)}function A(){return Q==L.UP||Q==L.DOWN}function D(R){var T=R.selection.getNode();var S=R.dom.getParent(T,"tr");return S!==null}function P(S){var R=0;var T=S;while(T.previousSibling){T=T.previousSibling;R=R+a(T,"colspan")}return R}function E(T,R){var U=0;var S=0;e(T.children,function(V,W){U=U+a(V,"colspan");S=W;if(U>R){return false}});return S}function z(T,W,Y,V){var X=P(T.dom.getParent(W,"td,th"));var S=E(Y,X);var R=Y.childNodes[S];var U=J(R,V);F(U||R)}function H(R){var T=C.selection.getNode();var U=C.dom.getParent(T,"td,th");var S=C.dom.getParent(R,"td,th");return U&&U!==S&&I(U,S)}function I(S,R){return C.dom.getParent(S,"TABLE")===C.dom.getParent(R,"TABLE")}if(A()&&D(C)){var G=C.selection.getNode();setTimeout(function(){if(H(G)){O(!N.shiftKey&&Q===L.UP,G,N)}},0)}}r.onKeyDown.add(v)}if(!d.isIE){function s(){var w;for(w=r.getBody().lastChild;w&&w.nodeType==3&&!w.nodeValue.length;w=w.previousSibling){}if(w&&w.nodeName=="TABLE"){r.dom.add(r.getBody(),"p",null,'<br mce_bogus="1" />')}}if(d.isGecko){r.onKeyDown.add(function(z,B){var w,A,C=z.dom;if(B.keyCode==37||B.keyCode==38){w=z.selection.getRng();A=C.getParent(w.startContainer,"table");if(A&&z.getBody().firstChild==A){if(c(w,A)){w=C.createRng();w.setStartBefore(A);w.setEndBefore(A);z.selection.setRng(w);B.preventDefault()}}}})}r.onKeyUp.add(s);r.onSetContent.add(s);r.onVisualAid.add(s);r.onPreProcess.add(function(w,A){var z=A.node.lastChild;if(z&&z.childNodes.length==1&&z.firstChild.nodeName=="BR"){w.dom.remove(z)}});s();r.startContent=r.getContent({format:"raw"})}});e({mceTableSplitCells:function(n){n.split()},mceTableMergeCells:function(o){var p,q,n;n=g.dom.getParent(g.selection.getNode(),"th,td");if(n){p=n.rowSpan;q=n.colSpan}if(!g.dom.select("td.mceSelected,th.mceSelected").length){f.open({url:h+"/merge_cells.htm",width:240+parseInt(g.getLang("table.merge_cells_delta_width",0)),height:110+parseInt(g.getLang("table.merge_cells_delta_height",0)),inline:1},{rows:p,cols:q,onaction:function(r){o.merge(n,r.cols,r.rows)},plugin_url:h})}else{o.merge()}},mceTableInsertRowBefore:function(n){n.insertRow(true)},mceTableInsertRowAfter:function(n){n.insertRow()},mceTableInsertColBefore:function(n){n.insertCol(true)},mceTableInsertColAfter:function(n){n.insertCol()},mceTableDeleteCol:function(n){n.deleteCols()},mceTableDeleteRow:function(n){n.deleteRows()},mceTableCutRow:function(n){m=n.cutRows()},mceTableCopyRow:function(n){m=n.copyRows()},mceTablePasteRowBefore:function(n){n.pasteRows(m,true)},mceTablePasteRowAfter:function(n){n.pasteRows(m)},mceTableDelete:function(n){n.deleteTable()}},function(o,n){g.addCommand(n,function(){var p=l();if(p){o(p);g.execCommand("mceRepaint");k()}})});e({mceInsertTable:function(n){f.open({url:h+"/table.htm",width:400+parseInt(g.getLang("table.table_delta_width",0)),height:320+parseInt(g.getLang("table.table_delta_height",0)),inline:1},{plugin_url:h,action:n?n.action:0})},mceTableRowProps:function(){f.open({url:h+"/row.htm",width:400+parseInt(g.getLang("table.rowprops_delta_width",0)),height:295+parseInt(g.getLang("table.rowprops_delta_height",0)),inline:1},{plugin_url:h})},mceTableCellProps:function(){f.open({url:h+"/cell.htm",width:400+parseInt(g.getLang("table.cellprops_delta_width",0)),height:295+parseInt(g.getLang("table.cellprops_delta_height",0)),inline:1},{plugin_url:h})}},function(o,n){g.addCommand(n,function(p,q){o(q)})})}});d.PluginManager.add("table",d.plugins.TablePlugin)})(tinymce); \ No newline at end of file
+(function(d){var e=d.each;function c(g,h){var j=h.ownerDocument,f=j.createRange(),k;f.setStartBefore(h);f.setEnd(g.endContainer,g.endOffset);k=j.createElement("body");k.appendChild(f.cloneContents());return k.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi,"-").replace(/<[^>]+>/g,"").length==0}function a(g,f){return parseInt(g.getAttribute(f)||1)}function b(H,G,K){var g,L,D,o;t();o=G.getParent(K.getStart(),"th,td");if(o){L=F(o);D=I();o=z(L.x,L.y)}function A(N,M){N=N.cloneNode(M);N.removeAttribute("id");return N}function t(){var M=0;g=[];e(["thead","tbody","tfoot"],function(N){var O=G.select("> "+N+" tr",H);e(O,function(P,Q){Q+=M;e(G.select("> td, > th",P),function(W,R){var S,T,U,V;if(g[Q]){while(g[Q][R]){R++}}U=a(W,"rowspan");V=a(W,"colspan");for(T=Q;T<Q+U;T++){if(!g[T]){g[T]=[]}for(S=R;S<R+V;S++){g[T][S]={part:N,real:T==Q&&S==R,elm:W,rowspan:U,colspan:V}}}})});M+=O.length})}function z(M,O){var N;N=g[O];if(N){return N[M]}}function s(O,M,N){if(O){N=parseInt(N);if(N===1){O.removeAttribute(M,1)}else{O.setAttribute(M,N,1)}}}function j(M){return M&&(G.hasClass(M.elm,"mceSelected")||M==o)}function k(){var M=[];e(H.rows,function(N){e(N.cells,function(O){if(G.hasClass(O,"mceSelected")||O==o.elm){M.push(N);return false}})});return M}function r(){var M=G.createRng();M.setStartAfter(H);M.setEndAfter(H);K.setRng(M);G.remove(H)}function f(M){var N;d.walk(M,function(P){var O;if(P.nodeType==3){e(G.getParents(P.parentNode,null,M).reverse(),function(Q){Q=A(Q,false);if(!N){N=O=Q}else{if(O){O.appendChild(Q)}}O=Q});if(O){O.innerHTML=d.isIE?"&nbsp;":'<br data-mce-bogus="1" />'}return false}},"childNodes");M=A(M,false);s(M,"rowSpan",1);s(M,"colSpan",1);if(N){M.appendChild(N)}else{if(!d.isIE){M.innerHTML='<br data-mce-bogus="1" />'}}return M}function q(){var M=G.createRng();e(G.select("tr",H),function(N){if(N.cells.length==0){G.remove(N)}});if(G.select("tr",H).length==0){M.setStartAfter(H);M.setEndAfter(H);K.setRng(M);G.remove(H);return}e(G.select("thead,tbody,tfoot",H),function(N){if(N.rows.length==0){G.remove(N)}});t();row=g[Math.min(g.length-1,L.y)];if(row){K.select(row[Math.min(row.length-1,L.x)].elm,true);K.collapse(true)}}function u(S,Q,U,R){var P,N,M,O,T;P=g[Q][S].elm.parentNode;for(M=1;M<=U;M++){P=G.getNext(P,"tr");if(P){for(N=S;N>=0;N--){T=g[Q+M][N].elm;if(T.parentNode==P){for(O=1;O<=R;O++){G.insertAfter(f(T),T)}break}}if(N==-1){for(O=1;O<=R;O++){P.insertBefore(f(P.cells[0]),P.cells[0])}}}}}function C(){e(g,function(M,N){e(M,function(P,O){var S,R,T,Q;if(j(P)){P=P.elm;S=a(P,"colspan");R=a(P,"rowspan");if(S>1||R>1){s(P,"rowSpan",1);s(P,"colSpan",1);for(Q=0;Q<S-1;Q++){G.insertAfter(f(P),P)}u(O,N,R-1,S)}}})})}function p(V,S,Y){var P,O,X,W,U,R,T,M,V,N,Q;if(V){pos=F(V);P=pos.x;O=pos.y;X=P+(S-1);W=O+(Y-1)}else{L=D=null;e(g,function(Z,aa){e(Z,function(ac,ab){if(j(ac)){if(!L){L={x:ab,y:aa}}D={x:ab,y:aa}}})});P=L.x;O=L.y;X=D.x;W=D.y}T=z(P,O);M=z(X,W);if(T&&M&&T.part==M.part){C();t();T=z(P,O).elm;s(T,"colSpan",(X-P)+1);s(T,"rowSpan",(W-O)+1);for(R=O;R<=W;R++){for(U=P;U<=X;U++){if(!g[R]||!g[R][U]){continue}V=g[R][U].elm;if(V!=T){N=d.grep(V.childNodes);e(N,function(Z){T.appendChild(Z)});if(N.length){N=d.grep(T.childNodes);Q=0;e(N,function(Z){if(Z.nodeName=="BR"&&G.getAttrib(Z,"data-mce-bogus")&&Q++<N.length-1){T.removeChild(Z)}})}G.remove(V)}}}q()}}function l(Q){var M,S,P,R,T,U,N,V,O;e(g,function(W,X){e(W,function(Z,Y){if(j(Z)){Z=Z.elm;T=Z.parentNode;U=A(T,false);M=X;if(Q){return false}}});if(Q){return !M}});for(R=0;R<g[0].length;R++){if(!g[M][R]){continue}S=g[M][R].elm;if(S!=P){if(!Q){O=a(S,"rowspan");if(O>1){s(S,"rowSpan",O+1);continue}}else{if(M>0&&g[M-1][R]){V=g[M-1][R].elm;O=a(V,"rowSpan");if(O>1){s(V,"rowSpan",O+1);continue}}}N=f(S);s(N,"colSpan",S.colSpan);U.appendChild(N);P=S}}if(U.hasChildNodes()){if(!Q){G.insertAfter(U,T)}else{T.parentNode.insertBefore(U,T)}}}function h(N){var O,M;e(g,function(P,Q){e(P,function(S,R){if(j(S)){O=R;if(N){return false}}});if(N){return !O}});e(g,function(S,T){var P,Q,R;if(!S[O]){return}P=S[O].elm;if(P!=M){R=a(P,"colspan");Q=a(P,"rowspan");if(R==1){if(!N){G.insertAfter(f(P),P);u(O,T,Q-1,R)}else{P.parentNode.insertBefore(f(P),P);u(O,T,Q-1,R)}}else{s(P,"colSpan",P.colSpan+1)}M=P}})}function n(){var M=[];e(g,function(N,O){e(N,function(Q,P){if(j(Q)&&d.inArray(M,P)===-1){e(g,function(T){var R=T[P].elm,S;S=a(R,"colSpan");if(S>1){s(R,"colSpan",S-1)}else{G.remove(R)}});M.push(P)}})});q()}function m(){var N;function M(Q){var P,R,O;P=G.getNext(Q,"tr");e(Q.cells,function(S){var T=a(S,"rowSpan");if(T>1){s(S,"rowSpan",T-1);R=F(S);u(R.x,R.y,1,1)}});R=F(Q.cells[0]);e(g[R.y],function(S){var T;S=S.elm;if(S!=O){T=a(S,"rowSpan");if(T<=1){G.remove(S)}else{s(S,"rowSpan",T-1)}O=S}})}N=k();e(N.reverse(),function(O){M(O)});q()}function E(){var M=k();G.remove(M);q();return M}function J(){var M=k();e(M,function(O,N){M[N]=A(O,true)});return M}function B(O,N){if(!O){return}var P=k(),M=P[N?0:P.length-1],Q=M.cells.length;e(g,function(S){var R;Q=0;e(S,function(U,T){if(U.real){Q+=U.colspan}if(U.elm.parentNode==M){R=1}});if(R){return false}});if(!N){O.reverse()}e(O,function(T){var S=T.cells.length,R;for(i=0;i<S;i++){R=T.cells[i];s(R,"colSpan",1);s(R,"rowSpan",1)}for(i=S;i<Q;i++){T.appendChild(f(T.cells[S-1]))}for(i=Q;i<S;i++){G.remove(T.cells[i])}if(N){M.parentNode.insertBefore(T,M)}else{G.insertAfter(T,M)}});G.removeClass(G.select("td.mceSelected,th.mceSelected"),"mceSelected")}function F(M){var N;e(g,function(O,P){e(O,function(R,Q){if(R.elm==M){N={x:Q,y:P};return false}});return !N});return N}function w(M){L=F(M)}function I(){var O,N,M;N=M=0;e(g,function(P,Q){e(P,function(S,R){var U,T;if(j(S)){S=g[Q][R];if(R>N){N=R}if(Q>M){M=Q}if(S.real){U=S.colspan-1;T=S.rowspan-1;if(U){if(R+U>N){N=R+U}}if(T){if(Q+T>M){M=Q+T}}}}})});return{x:N,y:M}}function v(S){var P,O,U,T,N,M,Q,R;D=F(S);if(L&&D){P=Math.min(L.x,D.x);O=Math.min(L.y,D.y);U=Math.max(L.x,D.x);T=Math.max(L.y,D.y);N=U;M=T;for(y=O;y<=M;y++){S=g[y][P];if(!S.real){if(P-(S.colspan-1)<P){P-=S.colspan-1}}}for(x=P;x<=N;x++){S=g[O][x];if(!S.real){if(O-(S.rowspan-1)<O){O-=S.rowspan-1}}}for(y=O;y<=T;y++){for(x=P;x<=U;x++){S=g[y][x];if(S.real){Q=S.colspan-1;R=S.rowspan-1;if(Q){if(x+Q>N){N=x+Q}}if(R){if(y+R>M){M=y+R}}}}}G.removeClass(G.select("td.mceSelected,th.mceSelected"),"mceSelected");for(y=O;y<=M;y++){for(x=P;x<=N;x++){if(g[y][x]){G.addClass(g[y][x].elm,"mceSelected")}}}}}d.extend(this,{deleteTable:r,split:C,merge:p,insertRow:l,insertCol:h,deleteCols:n,deleteRows:m,cutRows:E,copyRows:J,pasteRows:B,getPos:F,setStartCell:w,setEndCell:v})}d.create("tinymce.plugins.TablePlugin",{init:function(g,h){var f,m,j=true;function l(p){var o=g.selection,n=g.dom.getParent(p||o.getNode(),"table");if(n){return new b(n,g.dom,o)}}function k(){g.getBody().style.webkitUserSelect="";if(j){g.dom.removeClass(g.dom.select("td.mceSelected,th.mceSelected"),"mceSelected");j=false}}e([["table","table.desc","mceInsertTable",true],["delete_table","table.del","mceTableDelete"],["delete_col","table.delete_col_desc","mceTableDeleteCol"],["delete_row","table.delete_row_desc","mceTableDeleteRow"],["col_after","table.col_after_desc","mceTableInsertColAfter"],["col_before","table.col_before_desc","mceTableInsertColBefore"],["row_after","table.row_after_desc","mceTableInsertRowAfter"],["row_before","table.row_before_desc","mceTableInsertRowBefore"],["row_props","table.row_desc","mceTableRowProps",true],["cell_props","table.cell_desc","mceTableCellProps",true],["split_cells","table.split_cells_desc","mceTableSplitCells",true],["merge_cells","table.merge_cells_desc","mceTableMergeCells",true]],function(n){g.addButton(n[0],{title:n[1],cmd:n[2],ui:n[3]})});if(!d.isIE){g.onClick.add(function(n,o){o=o.target;if(o.nodeName==="TABLE"){n.selection.select(o);n.nodeChanged()}})}g.onPreProcess.add(function(o,p){var n,q,r,t=o.dom,s;n=t.select("table",p.node);q=n.length;while(q--){r=n[q];t.setAttrib(r,"data-mce-style","");if((s=t.getAttrib(r,"width"))){t.setStyle(r,"width",s);t.setAttrib(r,"width","")}if((s=t.getAttrib(r,"height"))){t.setStyle(r,"height",s);t.setAttrib(r,"height","")}}});g.onNodeChange.add(function(q,o,s){var r;s=q.selection.getStart();r=q.dom.getParent(s,"td,th,caption");o.setActive("table",s.nodeName==="TABLE"||!!r);if(r&&r.nodeName==="CAPTION"){r=0}o.setDisabled("delete_table",!r);o.setDisabled("delete_col",!r);o.setDisabled("delete_table",!r);o.setDisabled("delete_row",!r);o.setDisabled("col_after",!r);o.setDisabled("col_before",!r);o.setDisabled("row_after",!r);o.setDisabled("row_before",!r);o.setDisabled("row_props",!r);o.setDisabled("cell_props",!r);o.setDisabled("split_cells",!r);o.setDisabled("merge_cells",!r)});g.onInit.add(function(r){var p,t,q=r.dom,u;f=r.windowManager;r.onMouseDown.add(function(w,z){if(z.button!=2){k();t=q.getParent(z.target,"td,th");p=q.getParent(t,"table")}});q.bind(r.getDoc(),"mouseover",function(C){var A,z,B=C.target;if(t&&(u||B!=t)&&(B.nodeName=="TD"||B.nodeName=="TH")){z=q.getParent(B,"table");if(z==p){if(!u){u=l(z);u.setStartCell(t);r.getBody().style.webkitUserSelect="none"}u.setEndCell(B);j=true}A=r.selection.getSel();try{if(A.removeAllRanges){A.removeAllRanges()}else{A.empty()}}catch(w){}C.preventDefault()}});r.onMouseUp.add(function(F,G){var z,B=F.selection,H,I=B.getSel(),w,C,A,E;if(t){if(u){F.getBody().style.webkitUserSelect=""}function D(J,L){var K=new d.dom.TreeWalker(J,J);do{if(J.nodeType==3&&d.trim(J.nodeValue).length!=0){if(L){z.setStart(J,0)}else{z.setEnd(J,J.nodeValue.length)}return}if(J.nodeName=="BR"){if(L){z.setStartBefore(J)}else{z.setEndBefore(J)}return}}while(J=(L?K.next():K.prev()))}H=q.select("td.mceSelected,th.mceSelected");if(H.length>0){z=q.createRng();C=H[0];E=H[H.length-1];z.setStartBefore(C);z.setEndAfter(C);D(C,1);w=new d.dom.TreeWalker(C,q.getParent(H[0],"table"));do{if(C.nodeName=="TD"||C.nodeName=="TH"){if(!q.hasClass(C,"mceSelected")){break}A=C}}while(C=w.next());D(A);B.setRng(z)}F.nodeChanged();t=u=p=null}});r.onKeyUp.add(function(w,z){k()});r.onKeyDown.add(function(w,z){n(w)});r.onMouseDown.add(function(w,z){if(z.button!=2){n(w)}});function o(D,z,A,F){var B=3,G=D.dom.getParent(z.startContainer,"TABLE"),C,w,E;if(G){C=G.parentNode}w=z.startContainer.nodeType==B&&z.startOffset==0&&z.endOffset==0&&F&&(A.nodeName=="TR"||A==C);E=(A.nodeName=="TD"||A.nodeName=="TH")&&!F;return w||E}function n(A){if(!d.isWebKit){return}var z=A.selection.getRng();var C=A.selection.getNode();var B=A.dom.getParent(z.startContainer,"TD,TH");if(!o(A,z,C,B)){return}if(!B){B=C}var w=B.lastChild;while(w.lastChild){w=w.lastChild}z.setEnd(w,w.nodeValue.length);A.selection.setRng(z)}r.plugins.table.fixTableCellSelection=n;if(r&&r.plugins.contextmenu){r.plugins.contextmenu.onContextMenu.add(function(A,w,C){var D,B=r.selection,z=B.getNode()||r.getBody();if(r.dom.getParent(C,"td")||r.dom.getParent(C,"th")||r.dom.select("td.mceSelected,th.mceSelected").length){w.removeAll();if(z.nodeName=="A"&&!r.dom.getAttrib(z,"name")){w.add({title:"advanced.link_desc",icon:"link",cmd:r.plugins.advlink?"mceAdvLink":"mceLink",ui:true});w.add({title:"advanced.unlink_desc",icon:"unlink",cmd:"UnLink"});w.addSeparator()}if(z.nodeName=="IMG"&&z.className.indexOf("mceItem")==-1){w.add({title:"advanced.image_desc",icon:"image",cmd:r.plugins.advimage?"mceAdvImage":"mceImage",ui:true});w.addSeparator()}w.add({title:"table.desc",icon:"table",cmd:"mceInsertTable",value:{action:"insert"}});w.add({title:"table.props_desc",icon:"table_props",cmd:"mceInsertTable"});w.add({title:"table.del",icon:"delete_table",cmd:"mceTableDelete"});w.addSeparator();D=w.addMenu({title:"table.cell"});D.add({title:"table.cell_desc",icon:"cell_props",cmd:"mceTableCellProps"});D.add({title:"table.split_cells_desc",icon:"split_cells",cmd:"mceTableSplitCells"});D.add({title:"table.merge_cells_desc",icon:"merge_cells",cmd:"mceTableMergeCells"});D=w.addMenu({title:"table.row"});D.add({title:"table.row_desc",icon:"row_props",cmd:"mceTableRowProps"});D.add({title:"table.row_before_desc",icon:"row_before",cmd:"mceTableInsertRowBefore"});D.add({title:"table.row_after_desc",icon:"row_after",cmd:"mceTableInsertRowAfter"});D.add({title:"table.delete_row_desc",icon:"delete_row",cmd:"mceTableDeleteRow"});D.addSeparator();D.add({title:"table.cut_row_desc",icon:"cut",cmd:"mceTableCutRow"});D.add({title:"table.copy_row_desc",icon:"copy",cmd:"mceTableCopyRow"});D.add({title:"table.paste_row_before_desc",icon:"paste",cmd:"mceTablePasteRowBefore"}).setDisabled(!m);D.add({title:"table.paste_row_after_desc",icon:"paste",cmd:"mceTablePasteRowAfter"}).setDisabled(!m);D=w.addMenu({title:"table.col"});D.add({title:"table.col_before_desc",icon:"col_before",cmd:"mceTableInsertColBefore"});D.add({title:"table.col_after_desc",icon:"col_after",cmd:"mceTableInsertColAfter"});D.add({title:"table.delete_col_desc",icon:"delete_col",cmd:"mceTableDeleteCol"})}else{w.add({title:"table.desc",icon:"table",cmd:"mceInsertTable"})}})}if(d.isWebKit){function v(C,N){var L=d.VK;var Q=N.keyCode;function O(Y,U,S){var T=Y?"previousSibling":"nextSibling";var Z=C.dom.getParent(U,"tr");var X=Z[T];if(X){z(C,U,X,Y);d.dom.Event.cancel(S);return true}else{var aa=C.dom.getParent(Z,"table");var W=Z.parentNode;var R=W.nodeName.toLowerCase();if(R==="tbody"||R===(Y?"tfoot":"thead")){var V=w(Y,aa,W,"tbody");if(V!==null){return K(Y,V,U,S)}}return M(Y,Z,T,aa,S)}}function w(V,T,U,X){var S=C.dom.select(">"+X,T);var R=S.indexOf(U);if(V&&R===0||!V&&R===S.length-1){return B(V,T)}else{if(R===-1){var W=U.tagName.toLowerCase()==="thead"?0:S.length-1;return S[W]}else{return S[R+(V?-1:1)]}}}function B(U,T){var S=U?"thead":"tfoot";var R=C.dom.select(">"+S,T);return R.length!==0?R[0]:null}function K(V,T,S,U){var R=J(T,V);R&&z(C,S,R,V);d.dom.Event.cancel(U);return true}function M(Y,U,R,X,W){var S=X[R];if(S){F(S);return true}else{var V=C.dom.getParent(X,"td,th");if(V){return O(Y,V,W)}else{var T=J(U,!Y);F(T);return d.dom.Event.cancel(W)}}}function J(S,R){var T=S&&S[R?"lastChild":"firstChild"];return T&&T.nodeName==="BR"?C.dom.getParent(T,"td,th"):T}function F(R){C.selection.setCursorLocation(R,0)}function A(){return Q==L.UP||Q==L.DOWN}function D(R){var T=R.selection.getNode();var S=R.dom.getParent(T,"tr");return S!==null}function P(S){var R=0;var T=S;while(T.previousSibling){T=T.previousSibling;R=R+a(T,"colspan")}return R}function E(T,R){var U=0;var S=0;e(T.children,function(V,W){U=U+a(V,"colspan");S=W;if(U>R){return false}});return S}function z(T,W,Y,V){var X=P(T.dom.getParent(W,"td,th"));var S=E(Y,X);var R=Y.childNodes[S];var U=J(R,V);F(U||R)}function H(R){var T=C.selection.getNode();var U=C.dom.getParent(T,"td,th");var S=C.dom.getParent(R,"td,th");return U&&U!==S&&I(U,S)}function I(S,R){return C.dom.getParent(S,"TABLE")===C.dom.getParent(R,"TABLE")}if(A()&&D(C)){var G=C.selection.getNode();setTimeout(function(){if(H(G)){O(!N.shiftKey&&Q===L.UP,G,N)}},0)}}r.onKeyDown.add(v)}function s(){var w;for(w=r.getBody().lastChild;w&&w.nodeType==3&&!w.nodeValue.length;w=w.previousSibling){}if(w&&w.nodeName=="TABLE"){if(r.settings.forced_root_block){r.dom.add(r.getBody(),r.settings.forced_root_block,null,d.isIE?"&nbsp;":'<br data-mce-bogus="1" />')}else{r.dom.add(r.getBody(),"br",{"data-mce-bogus":"1"})}}}if(d.isGecko){r.onKeyDown.add(function(z,B){var w,A,C=z.dom;if(B.keyCode==37||B.keyCode==38){w=z.selection.getRng();A=C.getParent(w.startContainer,"table");if(A&&z.getBody().firstChild==A){if(c(w,A)){w=C.createRng();w.setStartBefore(A);w.setEndBefore(A);z.selection.setRng(w);B.preventDefault()}}}})}r.onKeyUp.add(s);r.onSetContent.add(s);r.onVisualAid.add(s);r.onPreProcess.add(function(w,A){var z=A.node.lastChild;if(z&&(z.nodeName=="BR"||(z.childNodes.length==1&&(z.firstChild.nodeName=="BR"||z.firstChild.nodeValue=="\u00a0")))&&z.previousSibling&&z.previousSibling.nodeName=="TABLE"){w.dom.remove(z)}});s();r.startContent=r.getContent({format:"raw"})});e({mceTableSplitCells:function(n){n.split()},mceTableMergeCells:function(o){var p,q,n;n=g.dom.getParent(g.selection.getNode(),"th,td");if(n){p=n.rowSpan;q=n.colSpan}if(!g.dom.select("td.mceSelected,th.mceSelected").length){f.open({url:h+"/merge_cells.htm",width:240+parseInt(g.getLang("table.merge_cells_delta_width",0)),height:110+parseInt(g.getLang("table.merge_cells_delta_height",0)),inline:1},{rows:p,cols:q,onaction:function(r){o.merge(n,r.cols,r.rows)},plugin_url:h})}else{o.merge()}},mceTableInsertRowBefore:function(n){n.insertRow(true)},mceTableInsertRowAfter:function(n){n.insertRow()},mceTableInsertColBefore:function(n){n.insertCol(true)},mceTableInsertColAfter:function(n){n.insertCol()},mceTableDeleteCol:function(n){n.deleteCols()},mceTableDeleteRow:function(n){n.deleteRows()},mceTableCutRow:function(n){m=n.cutRows()},mceTableCopyRow:function(n){m=n.copyRows()},mceTablePasteRowBefore:function(n){n.pasteRows(m,true)},mceTablePasteRowAfter:function(n){n.pasteRows(m)},mceTableDelete:function(n){n.deleteTable()}},function(o,n){g.addCommand(n,function(){var p=l();if(p){o(p);g.execCommand("mceRepaint");k()}})});e({mceInsertTable:function(n){f.open({url:h+"/table.htm",width:400+parseInt(g.getLang("table.table_delta_width",0)),height:320+parseInt(g.getLang("table.table_delta_height",0)),inline:1},{plugin_url:h,action:n?n.action:0})},mceTableRowProps:function(){f.open({url:h+"/row.htm",width:400+parseInt(g.getLang("table.rowprops_delta_width",0)),height:295+parseInt(g.getLang("table.rowprops_delta_height",0)),inline:1},{plugin_url:h})},mceTableCellProps:function(){f.open({url:h+"/cell.htm",width:400+parseInt(g.getLang("table.cellprops_delta_width",0)),height:295+parseInt(g.getLang("table.cellprops_delta_height",0)),inline:1},{plugin_url:h})}},function(o,n){g.addCommand(n,function(p,q){o(q)})})}});d.PluginManager.add("table",d.plugins.TablePlugin)})(tinymce); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin_src.js
index 8170e4ed4..532b79c6f 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/editor_plugin_src.js
@@ -287,6 +287,21 @@
endX = startX + (cols - 1);
endY = startY + (rows - 1);
} else {
+ startPos = endPos = null;
+
+ // Calculate start/end pos by checking for selected cells in grid works better with context menu
+ each(grid, function(row, y) {
+ each(row, function(cell, x) {
+ if (isCellSelected(cell)) {
+ if (!startPos) {
+ startPos = {x: x, y: y};
+ }
+
+ endPos = {x: x, y: y};
+ }
+ });
+ });
+
// Use selection
startX = startPos.x;
startY = startPos.y;
@@ -551,6 +566,10 @@
};
function pasteRows(rows, before) {
+ // If we don't have any rows in the clipboard, return immediately
+ if(!rows)
+ return;
+
var selectedRows = getSelectedRows(),
targetRow = selectedRows[before ? 0 : selectedRows.length - 1],
targetCellCount = targetRow.cells.length;
@@ -599,6 +618,9 @@
else
dom.insertAfter(row, targetRow);
});
+
+ // Remove current selection
+ dom.removeClass(dom.select('td.mceSelected,th.mceSelected'), 'mceSelected');
};
function getPos(target) {
@@ -1144,7 +1166,9 @@
}
function getChildForDirection(parent, up) {
- return parent && parent[up ? 'lastChild' : 'firstChild'];
+ var child = parent && parent[up ? 'lastChild' : 'firstChild'];
+ // BR is not a valid table child to return in this case we return the table cell
+ return child && child.nodeName === 'BR' ? ed.dom.getParent(child, 'td,th') : child;
}
function moveCursorToStartOfElement(n) {
@@ -1214,62 +1238,86 @@
ed.onKeyDown.add(moveSelection);
}
-
+
// Fixes an issue on Gecko where it's impossible to place the caret behind a table
// This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
- if (!tinymce.isIE) {
- function fixTableCaretPos() {
- var last;
+ function fixTableCaretPos() {
+ var last;
- // Skip empty text nodes form the end
- for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ;
+ // Skip empty text nodes form the end
+ for (last = ed.getBody().lastChild; last && last.nodeType == 3 && !last.nodeValue.length; last = last.previousSibling) ;
- if (last && last.nodeName == 'TABLE')
- ed.dom.add(ed.getBody(), 'p', null, '<br mce_bogus="1" />');
- };
+ if (last && last.nodeName == 'TABLE') {
+ if (ed.settings.forced_root_block)
+ ed.dom.add(ed.getBody(), ed.settings.forced_root_block, null, tinymce.isIE ? '&nbsp;' : '<br data-mce-bogus="1" />');
+ else
+ ed.dom.add(ed.getBody(), 'br', {'data-mce-bogus': '1'});
+ }
+ };
- // Fixes an bug where it's impossible to place the caret before a table in Gecko
- // this fix solves it by detecting when the caret is at the beginning of such a table
- // and then manually moves the caret infront of the table
- if (tinymce.isGecko) {
- ed.onKeyDown.add(function(ed, e) {
- var rng, table, dom = ed.dom;
+ // Fixes an bug where it's impossible to place the caret before a table in Gecko
+ // this fix solves it by detecting when the caret is at the beginning of such a table
+ // and then manually moves the caret infront of the table
+ if (tinymce.isGecko) {
+ ed.onKeyDown.add(function(ed, e) {
+ var rng, table, dom = ed.dom;
- // On gecko it's not possible to place the caret before a table
- if (e.keyCode == 37 || e.keyCode == 38) {
- rng = ed.selection.getRng();
- table = dom.getParent(rng.startContainer, 'table');
+ // On gecko it's not possible to place the caret before a table
+ if (e.keyCode == 37 || e.keyCode == 38) {
+ rng = ed.selection.getRng();
+ table = dom.getParent(rng.startContainer, 'table');
- if (table && ed.getBody().firstChild == table) {
- if (isAtStart(rng, table)) {
- rng = dom.createRng();
+ if (table && ed.getBody().firstChild == table) {
+ if (isAtStart(rng, table)) {
+ rng = dom.createRng();
- rng.setStartBefore(table);
- rng.setEndBefore(table);
+ rng.setStartBefore(table);
+ rng.setEndBefore(table);
- ed.selection.setRng(rng);
+ ed.selection.setRng(rng);
- e.preventDefault();
- }
+ e.preventDefault();
}
}
- });
- }
+ }
+ });
+ }
- ed.onKeyUp.add(fixTableCaretPos);
- ed.onSetContent.add(fixTableCaretPos);
- ed.onVisualAid.add(fixTableCaretPos);
+ ed.onKeyUp.add(fixTableCaretPos);
+ ed.onSetContent.add(fixTableCaretPos);
+ ed.onVisualAid.add(fixTableCaretPos);
- ed.onPreProcess.add(function(ed, o) {
- var last = o.node.lastChild;
+ ed.onPreProcess.add(function(ed, o) {
+ var last = o.node.lastChild;
+
+ if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 && (last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))) && last.previousSibling && last.previousSibling.nodeName == "TABLE") {
+ ed.dom.remove(last);
+ }
+ });
- if (last && last.childNodes.length == 1 && last.firstChild.nodeName == 'BR')
- ed.dom.remove(last);
- });
- fixTableCaretPos();
- ed.startContent = ed.getContent({format : 'raw'});
+ /**
+ * Fixes bug in Gecko where shift-enter in table cell does not place caret on new line
+ *
+ * Removed: Since the new enter logic seems to fix this one.
+ */
+ /*
+ if (tinymce.isGecko) {
+ ed.onKeyDown.add(function(ed, e) {
+ if (e.keyCode === tinymce.VK.ENTER && e.shiftKey) {
+ var node = ed.selection.getRng().startContainer;
+ var tableCell = dom.getParent(node, 'td,th');
+ if (tableCell) {
+ var zeroSizedNbsp = ed.getDoc().createTextNode("\uFEFF");
+ dom.insertAfter(zeroSizedNbsp, node);
+ }
+ }
+ });
}
+ */
+
+ fixTableCaretPos();
+ ed.startContent = ed.getContent({format : 'raw'});
});
// Register action commands
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/cell.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/cell.js
index d6f329059..02ecf22c8 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/cell.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/cell.js
@@ -137,7 +137,7 @@ function updateAction() {
do {
if (cell == tdElm)
break;
- col += cell.getAttribute("colspan");
+ col += cell.getAttribute("colspan")?cell.getAttribute("colspan"):1;
} while ((cell = nextCell(cell)) != null);
for (var i=0; i<rows.length; i++) {
@@ -152,7 +152,7 @@ function updateAction() {
cell = updateCell(cell, true);
break;
}
- curr += cell.getAttribute("colspan");
+ curr += cell.getAttribute("colspan")?cell.getAttribute("colspan"):1;
} while ((cell = nextCell(cell)) != null);
}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/row.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/row.js
index a13d69592..0c678de46 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/row.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/row.js
@@ -25,6 +25,7 @@ function init() {
var dir = dom.getAttrib(trElm, 'dir');
selectByValue(formObj, 'rowtype', rowtype);
+ setActionforRowType(formObj, rowtype);
// Any cells selected
if (dom.select('td.mceSelected,th.mceSelected', trElm).length == 0) {
@@ -234,4 +235,20 @@ function changedColor() {
formObj.style.value = dom.serializeStyle(st);
}
+function changedRowType() {
+ var formObj = document.forms[0];
+ var rowtype = getSelectValue(formObj, 'rowtype');
+
+ setActionforRowType(formObj, rowtype);
+
+}
+
+function setActionforRowType(formObj, rowtype) {
+ if (rowtype === "tbody") {
+ formObj.action.disabled = false;
+ } else {
+ selectByValue(formObj, 'action', "row");
+ formObj.action.disabled = true;
+ }
+}
tinyMCEPopup.onInit.add(init);
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/table.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/table.js
index b21eaa6e7..1db243b63 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/table.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/js/table.js
@@ -144,7 +144,7 @@ function insertTable() {
//elm.outerHTML = elm.outerHTML;
inst.nodeChanged();
- inst.execCommand('mceEndUndoLevel');
+ inst.execCommand('mceEndUndoLevel', false, {}, {skip_undo: true});
// Repaint if dimensions changed
if (formObj.width.value != orgTableWidth || formObj.height.value != orgTableHeight)
@@ -242,8 +242,16 @@ function insertTable() {
} else
inst.execCommand('mceInsertContent', false, html);
- tinymce.each(dom.select('table[data-mce-new]'), function(node) {
+ tinymce.each(dom.select('table[data-mce-new]'), function(node) {
var tdorth = dom.select('td,th', node);
+
+ // Fixes a bug in IE where the caret cannot be placed after the table if the table is at the end of the document
+ if (tinymce.isIE && node.nextSibling == null) {
+ if (inst.settings.forced_root_block)
+ dom.insertAfter(dom.create(inst.settings.forced_root_block), node);
+ else
+ dom.insertAfter(dom.create('br', {'data-mce-bogus': '1'}), node);
+ }
try {
// IE9 might fail to do this selection
@@ -256,7 +264,7 @@ function insertTable() {
});
inst.addVisual();
- inst.execCommand('mceEndUndoLevel');
+ inst.execCommand('mceEndUndoLevel', false, {}, {skip_undo: true});
tinyMCEPopup.close();
}
@@ -299,6 +307,15 @@ function init() {
var formObj = document.forms[0];
var elm = dom.getParent(inst.selection.getNode(), "table");
+ // Hide advanced fields that isn't available in the schema
+ tinymce.each("summary id rules dir style frame".split(" "), function(name) {
+ var tr = tinyMCEPopup.dom.getParent(name, "tr") || tinyMCEPopup.dom.getParent("t" + name, "tr");
+
+ if (tr && !tinyMCEPopup.editor.schema.isValid("table", name)) {
+ tr.style.display = 'none';
+ }
+ });
+
action = tinyMCEPopup.getWindowArg('action');
if (!action)
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/row.htm b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/row.htm
index 1885401f6..6ebef2842 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/row.htm
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/table/row.htm
@@ -28,7 +28,7 @@
<tr>
<td><label for="rowtype">{#table_dlg.rowtype}</label></td>
<td class="col2">
- <select id="rowtype" name="rowtype" class="mceFocus">
+ <select id="rowtype" name="rowtype" class="mceFocus" onChange="changedRowType();">
<option value="thead">{#table_dlg.thead}</option>
<option value="tbody">{#table_dlg.tbody}</option>
<option value="tfoot">{#table_dlg.tfoot}</option>
@@ -83,8 +83,8 @@
<table role="presentation" border="0" cellpadding="0" cellspacing="4">
<tr>
- <td class="column1"><label for="id">{#table_dlg.id}</label></td>
- <td><input id="id" name="id" type="text" value="" style="width: 200px" /></td>
+ <td class="column1"><label for="id">{#table_dlg.id}</label></td>
+ <td><input id="id" name="id" type="text" value="" style="width: 200px" /></td>
</tr>
<tr>
@@ -93,25 +93,25 @@
</tr>
<tr>
- <td class="column1"><label for="dir">{#table_dlg.langdir}</label></td>
+ <td class="column1"><label for="dir">{#table_dlg.langdir}</label></td>
<td>
- <select id="dir" name="dir" style="width: 200px">
- <option value="">{#not_set}</option>
- <option value="ltr">{#table_dlg.ltr}</option>
- <option value="rtl">{#table_dlg.rtl}</option>
+ <select id="dir" name="dir" style="width: 200px">
+ <option value="">{#not_set}</option>
+ <option value="ltr">{#table_dlg.ltr}</option>
+ <option value="rtl">{#table_dlg.rtl}</option>
</select>
- </td>
+ </td>
</tr>
<tr>
- <td class="column1"><label for="lang">{#table_dlg.langcode}</label></td>
+ <td class="column1"><label for="lang">{#table_dlg.langcode}</label></td>
<td>
<input id="lang" name="lang" type="text" value="" style="width: 200px" />
- </td>
+ </td>
</tr>
<tr>
- <td class="column1"><label for="backgroundimage">{#table_dlg.bgimage}</label></td>
+ <td class="column1"><label for="backgroundimage">{#table_dlg.bgimage}</label></td>
<td>
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
<tr>
@@ -119,11 +119,11 @@
<td id="backgroundimagebrowsercontainer">&nbsp;</td>
</tr>
</table>
- </td>
+ </td>
</tr>
<tr>
- <td class="column1"><label for="bgcolor" id="bgcolor_label">{#table_dlg.bgcolor}</label></td>
+ <td class="column1"><label for="bgcolor" id="bgcolor_label">{#table_dlg.bgcolor}</label></td>
<td>
<span role="group" aria-labelledby="bgcolor_label">
<table role="presentation" border="0" cellpadding="0" cellspacing="0">
@@ -133,7 +133,7 @@
</tr>
</table>
</span>
- </td>
+ </td>
</tr>
</table>
</fieldset>
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/css/visualblocks.css b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/css/visualblocks.css
new file mode 100644
index 000000000..76bc92b50
--- /dev/null
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/css/visualblocks.css
@@ -0,0 +1,21 @@
+p, h1, h2, h3, h4, h5, h6, hgroup, aside, div, section, article, blockquote, address, pre, figure {display: block; padding-top: 10px; border: 1px dashed #BBB; background: transparent no-repeat}
+p, h1, h2, h3, h4, h5, h6, hgroup, aside, div, section, article, address, pre, figure {margin-left: 3px}
+section, article, address, hgroup, aside, figure {margin: 0 0 1em 3px}
+
+p {background-image: url(data:image/gif;base64,R0lGODlhCQAJAJEAAAAAAP///7u7u////yH5BAEAAAMALAAAAAAJAAkAAAIQnG+CqCN/mlyvsRUpThG6AgA7)}
+h1 {background-image: url(data:image/gif;base64,R0lGODlhDQAKAIABALu7u////yH5BAEAAAEALAAAAAANAAoAAAIXjI8GybGu1JuxHoAfRNRW3TWXyF2YiRUAOw==)}
+h2 {background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8Hybbx4oOuqgTynJd6bGlWg3DkJzoaUAAAOw==)}
+h3 {background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIZjI8Hybbx4oOuqgTynJf2Ln2NOHpQpmhAAQA7)}
+h4 {background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxInR0zqeAdhtJlXwV1oCll2HaWgAAOw==)}
+h5 {background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjane4iq5GlW05GgIkIZUAAAOw==)}
+h6 {background-image: url(data:image/gif;base64,R0lGODlhDgAKAIABALu7u////yH5BAEAAAEALAAAAAAOAAoAAAIajI8HybbxIoiuwjan04jep1iZ1XRlAo5bVgAAOw==)}
+div {background-image: url(data:image/gif;base64,R0lGODlhEgAKAIABALu7u////yH5BAEAAAEALAAAAAASAAoAAAIfjI9poI0cgDywrhuxfbrzDEbQM2Ei5aRjmoySW4pAAQA7)}
+section {background-image: url(data:image/gif;base64,R0lGODlhKAAKAIABALu7u////yH5BAEAAAEALAAAAAAoAAoAAAI5jI+pywcNY3sBWHdNrplytD2ellDeSVbp+GmWqaDqDMepc8t17Y4vBsK5hDyJMcI6KkuYU+jpjLoKADs=)}
+article {background-image: url(data:image/gif;base64,R0lGODlhKgAKAIABALu7u////yH5BAEAAAEALAAAAAAqAAoAAAI6jI+pywkNY3wG0GBvrsd2tXGYSGnfiF7ikpXemTpOiJScasYoDJJrjsG9gkCJ0ag6KhmaIe3pjDYBBQA7)}
+blockquote {background-image: url(data:image/gif;base64,R0lGODlhPgAKAIABALu7u////yH5BAEAAAEALAAAAAA+AAoAAAJPjI+py+0Knpz0xQDyuUhvfoGgIX5iSKZYgq5uNL5q69asZ8s5rrf0yZmpNkJZzFesBTu8TOlDVAabUyatguVhWduud3EyiUk45xhTTgMBBQA7)}
+address {background-image: url(data:image/gif;base64,R0lGODlhLQAKAIABALu7u////yH5BAEAAAEALAAAAAAtAAoAAAI/jI+pywwNozSP1gDyyZcjb3UaRpXkWaXmZW4OqKLhBmLs+K263DkJK7OJeifh7FicKD9A1/IpGdKkyFpNmCkAADs=)}
+pre {background-image: url(data:image/gif;base64,R0lGODlhFQAKAIABALu7uwAAACH5BAEAAAEALAAAAAAVAAoAAAIjjI+ZoN0cgDwSmnpz1NCueYERhnibZVKLNnbOq8IvKpJtVQAAOw==)}
+hgroup {background-image: url(data:image/gif;base64,R0lGODlhJwAKAIABALu7uwAAACH5BAEAAAEALAAAAAAnAAoAAAI3jI+pywYNI3uB0gpsRtt5fFnfNZaVSYJil4Wo03Hv6Z62uOCgiXH1kZIIJ8NiIxRrAZNMZAtQAAA7)}
+aside {background-image: url(data:image/gif;base64,R0lGODlhHgAKAIABAKqqqv///yH5BAEAAAEALAAAAAAeAAoAAAItjI+pG8APjZOTzgtqy7I3f1yehmQcFY4WKZbqByutmW4aHUd6vfcVbgudgpYCADs=)}
+figure {background-image: url(data:image/gif;base64,R0lGODlhJAAKAIAAALu7u////yH5BAEAAAEALAAAAAAkAAoAAAI0jI+py+2fwAHUSFvD3RlvG4HIp4nX5JFSpnZUJ6LlrM52OE7uSWosBHScgkSZj7dDKnWAAgA7)}
+figcaption {border: 1px dashed #BBB}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin.js
new file mode 100644
index 000000000..c65eaf2b4
--- /dev/null
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin.js
@@ -0,0 +1 @@
+(function(){tinymce.create("tinymce.plugins.VisualBlocks",{init:function(a,b){var c;if(!window.NodeList){return}a.addCommand("mceVisualBlocks",function(){var e=a.dom,d;if(!c){c=e.uniqueId();d=e.create("link",{id:c,rel:"stylesheet",href:b+"/css/visualblocks.css"});a.getDoc().getElementsByTagName("head")[0].appendChild(d)}else{d=e.get(c);d.disabled=!d.disabled}a.controlManager.setActive("visualblocks",!d.disabled)});a.addButton("visualblocks",{title:"visualblocks.desc",cmd:"mceVisualBlocks"});a.onInit.add(function(){if(a.settings.visualblocks_default_state){a.execCommand("mceVisualBlocks",false,null,{skip_focus:true})}})},getInfo:function(){return{longname:"Visual blocks",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/visualblocks",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("visualblocks",tinymce.plugins.VisualBlocks)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin_src.js
new file mode 100644
index 000000000..b9d2ab2e1
--- /dev/null
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/visualblocks/editor_plugin_src.js
@@ -0,0 +1,63 @@
+/**
+ * editor_plugin_src.js
+ *
+ * Copyright 2012, Moxiecode Systems AB
+ * Released under LGPL License.
+ *
+ * License: http://tinymce.moxiecode.com/license
+ * Contributing: http://tinymce.moxiecode.com/contributing
+ */
+
+(function() {
+ tinymce.create('tinymce.plugins.VisualBlocks', {
+ init : function(ed, url) {
+ var cssId;
+
+ // We don't support older browsers like IE6/7 and they don't provide prototypes for DOM objects
+ if (!window.NodeList) {
+ return;
+ }
+
+ ed.addCommand('mceVisualBlocks', function() {
+ var dom = ed.dom, linkElm;
+
+ if (!cssId) {
+ cssId = dom.uniqueId();
+ linkElm = dom.create('link', {
+ id: cssId,
+ rel : 'stylesheet',
+ href : url + '/css/visualblocks.css'
+ });
+
+ ed.getDoc().getElementsByTagName('head')[0].appendChild(linkElm);
+ } else {
+ linkElm = dom.get(cssId);
+ linkElm.disabled = !linkElm.disabled;
+ }
+
+ ed.controlManager.setActive('visualblocks', !linkElm.disabled);
+ });
+
+ ed.addButton('visualblocks', {title : 'visualblocks.desc', cmd : 'mceVisualBlocks'});
+
+ ed.onInit.add(function() {
+ if (ed.settings.visualblocks_default_state) {
+ ed.execCommand('mceVisualBlocks', false, null, {skip_focus : true});
+ }
+ });
+ },
+
+ getInfo : function() {
+ return {
+ longname : 'Visual blocks',
+ author : 'Moxiecode Systems AB',
+ authorurl : 'http://tinymce.moxiecode.com',
+ infourl : 'http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/visualblocks',
+ version : tinymce.majorVersion + "." + tinymce.minorVersion
+ };
+ }
+ });
+
+ // Register plugin
+ tinymce.PluginManager.add('visualblocks', tinymce.plugins.VisualBlocks);
+})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin.js
index a752ad32a..42ece2092 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin.js
@@ -1 +1 @@
-(function(){tinymce.create("tinymce.plugins.WordCount",{block:0,id:null,countre:null,cleanre:null,init:function(a,b){var c=this,d=0;c.countre=a.getParam("wordcount_countregex",/[\w\u2019\'-]+/g);c.cleanre=a.getParam("wordcount_cleanregex",/[0-9.(),;:!?%#$?\'\"_+=\\\/-]*/g);c.id=a.id+"-word-count";a.onPostRender.add(function(f,e){var g,h;h=f.getParam("wordcount_target_id");if(!h){g=tinymce.DOM.get(f.id+"_path_row");if(g){tinymce.DOM.add(g.parentNode,"div",{style:"float: right"},f.getLang("wordcount.words","Words: ")+'<span id="'+c.id+'">0</span>')}}else{tinymce.DOM.add(h,"span",{},'<span id="'+c.id+'">0</span>')}});a.onInit.add(function(e){e.selection.onSetContent.add(function(){c._count(e)});c._count(e)});a.onSetContent.add(function(e){c._count(e)});a.onKeyUp.add(function(f,g){if(g.keyCode==d){return}if(13==g.keyCode||8==d||46==d){c._count(f)}d=g.keyCode})},_getCount:function(c){var a=0;var b=c.getContent({format:"raw"});if(b){b=b.replace(/\.\.\./g," ");b=b.replace(/<.[^<>]*?>/g," ").replace(/&nbsp;|&#160;/gi," ");b=b.replace(/(\w+)(&.+?;)+(\w+)/,"$1$3").replace(/&.+?;/g," ");b=b.replace(this.cleanre,"");var d=b.match(this.countre);if(d){a=d.length}}return a},_count:function(a){var b=this;if(b.block){return}b.block=1;setTimeout(function(){if(!a.destroyed){var c=b._getCount(a);tinymce.DOM.setHTML(b.id,c.toString());setTimeout(function(){b.block=0},2000)}},1)},getInfo:function(){return{longname:"Word Count plugin",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/wordcount",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("wordcount",tinymce.plugins.WordCount)})(); \ No newline at end of file
+(function(){tinymce.create("tinymce.plugins.WordCount",{block:0,id:null,countre:null,cleanre:null,init:function(c,d){var e=this,f=0,g=tinymce.VK;e.countre=c.getParam("wordcount_countregex",/[\w\u2019\'-]+/g);e.cleanre=c.getParam("wordcount_cleanregex",/[0-9.(),;:!?%#$?\'\"_+=\\\/-]*/g);e.update_rate=c.getParam("wordcount_update_rate",2000);e.update_on_delete=c.getParam("wordcount_update_on_delete",false);e.id=c.id+"-word-count";c.onPostRender.add(function(i,h){var j,k;k=i.getParam("wordcount_target_id");if(!k){j=tinymce.DOM.get(i.id+"_path_row");if(j){tinymce.DOM.add(j.parentNode,"div",{style:"float: right"},i.getLang("wordcount.words","Words: ")+'<span id="'+e.id+'">0</span>')}}else{tinymce.DOM.add(k,"span",{},'<span id="'+e.id+'">0</span>')}});c.onInit.add(function(h){h.selection.onSetContent.add(function(){e._count(h)});e._count(h)});c.onSetContent.add(function(h){e._count(h)});function b(h){return h!==f&&(h===g.ENTER||f===g.SPACEBAR||a(f))}function a(h){return h===g.DELETE||h===g.BACKSPACE}c.onKeyUp.add(function(h,i){if(b(i.keyCode)||e.update_on_delete&&a(i.keyCode)){e._count(h)}f=i.keyCode})},_getCount:function(c){var a=0;var b=c.getContent({format:"raw"});if(b){b=b.replace(/\.\.\./g," ");b=b.replace(/<.[^<>]*?>/g," ").replace(/&nbsp;|&#160;/gi," ");b=b.replace(/(\w+)(&.+?;)+(\w+)/,"$1$3").replace(/&.+?;/g," ");b=b.replace(this.cleanre,"");var d=b.match(this.countre);if(d){a=d.length}}return a},_count:function(a){var b=this;if(b.block){return}b.block=1;setTimeout(function(){if(!a.destroyed){var c=b._getCount(a);tinymce.DOM.setHTML(b.id,c.toString());setTimeout(function(){b.block=0},b.update_rate)}},1)},getInfo:function(){return{longname:"Word Count plugin",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",infourl:"http://wiki.moxiecode.com/index.php/TinyMCE:Plugins/wordcount",version:tinymce.majorVersion+"."+tinymce.minorVersion}}});tinymce.PluginManager.add("wordcount",tinymce.plugins.WordCount)})(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js
index e94743bae..34b265553 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/plugins/wordcount/editor_plugin_src.js
@@ -16,10 +16,12 @@
cleanre : null,
init : function(ed, url) {
- var t = this, last = 0;
+ var t = this, last = 0, VK = tinymce.VK;
t.countre = ed.getParam('wordcount_countregex', /[\w\u2019\'-]+/g); // u2019 == &rsquo;
t.cleanre = ed.getParam('wordcount_cleanregex', /[0-9.(),;:!?%#$?\'\"_+=\\\/-]*/g);
+ t.update_rate = ed.getParam('wordcount_update_rate', 2000);
+ t.update_on_delete = ed.getParam('wordcount_update_on_delete', false);
t.id = ed.id + '-word-count';
ed.onPostRender.add(function(ed, cm) {
@@ -49,12 +51,18 @@
t._count(ed);
});
- ed.onKeyUp.add(function(ed, e) {
- if (e.keyCode == last)
- return;
+ function checkKeys(key) {
+ return key !== last && (key === VK.ENTER || last === VK.SPACEBAR || checkDelOrBksp(last));
+ }
+
+ function checkDelOrBksp(key) {
+ return key === VK.DELETE || key === VK.BACKSPACE;
+ }
- if (13 == e.keyCode || 8 == last || 46 == last)
+ ed.onKeyUp.add(function(ed, e) {
+ if (checkKeys(e.keyCode) || t.update_on_delete && checkDelOrBksp(e.keyCode)) {
t._count(ed);
+ }
last = e.keyCode;
});
@@ -94,7 +102,7 @@
if (!ed.destroyed) {
var tc = t._getCount(ed);
tinymce.DOM.setHTML(t.id, tc.toString());
- setTimeout(function() {t.block = 0;}, 2000);
+ setTimeout(function() {t.block = 0;}, t.update_rate);
}
}, 1);
},
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/color_picker.htm b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/color_picker.htm
index ad1bb0f6c..b625531a6 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/color_picker.htm
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/color_picker.htm
@@ -62,12 +62,8 @@
<div class="mceActionPanel">
<input type="submit" id="insert" name="insert" value="{#apply}" />
-
- <div id="preview"></div>
-
- <div id="previewblock">
- <label for="color">{#advanced_dlg.colorpicker_color}</label> <input id="color" type="text" size="8" class="text mceFocus" aria-required="true" />
- </div>
+ <input type="button" id="cancel" name="cancel" value="{#cancel}" onclick="tinyMCEPopup.close();"/>
+ <div id="preview_wrapper"><div id="previewblock"><label for="color">{#advanced_dlg.colorpicker_color}</label> <input id="color" type="text" size="8" class="text mceFocus" aria-required="true" /></div><span id="preview"></span></div>
</div>
</form>
</body>
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template.js
index 812578d0b..4b8d56375 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template.js
@@ -1 +1 @@
-(function(e){var d=e.DOM,b=e.dom.Event,h=e.extend,f=e.each,a=e.util.Cookie,g,c=e.explode;e.ThemeManager.requireLangPack("advanced");e.create("tinymce.themes.AdvancedTheme",{sizes:[8,10,12,14,18,24,36],controls:{bold:["bold_desc","Bold"],italic:["italic_desc","Italic"],underline:["underline_desc","Underline"],strikethrough:["striketrough_desc","Strikethrough"],justifyleft:["justifyleft_desc","JustifyLeft"],justifycenter:["justifycenter_desc","JustifyCenter"],justifyright:["justifyright_desc","JustifyRight"],justifyfull:["justifyfull_desc","JustifyFull"],bullist:["bullist_desc","InsertUnorderedList"],numlist:["numlist_desc","InsertOrderedList"],outdent:["outdent_desc","Outdent"],indent:["indent_desc","Indent"],cut:["cut_desc","Cut"],copy:["copy_desc","Copy"],paste:["paste_desc","Paste"],undo:["undo_desc","Undo"],redo:["redo_desc","Redo"],link:["link_desc","mceLink"],unlink:["unlink_desc","unlink"],image:["image_desc","mceImage"],cleanup:["cleanup_desc","mceCleanup"],help:["help_desc","mceHelp"],code:["code_desc","mceCodeEditor"],hr:["hr_desc","InsertHorizontalRule"],removeformat:["removeformat_desc","RemoveFormat"],sub:["sub_desc","subscript"],sup:["sup_desc","superscript"],forecolor:["forecolor_desc","ForeColor"],forecolorpicker:["forecolor_desc","mceForeColor"],backcolor:["backcolor_desc","HiliteColor"],backcolorpicker:["backcolor_desc","mceBackColor"],charmap:["charmap_desc","mceCharMap"],visualaid:["visualaid_desc","mceToggleVisualAid"],anchor:["anchor_desc","mceInsertAnchor"],newdocument:["newdocument_desc","mceNewDocument"],blockquote:["blockquote_desc","mceBlockQuote"]},stateControls:["bold","italic","underline","strikethrough","bullist","numlist","justifyleft","justifycenter","justifyright","justifyfull","sub","sup","blockquote"],init:function(j,k){var l=this,m,i,n;l.editor=j;l.url=k;l.onResolveName=new e.util.Dispatcher(this);j.forcedHighContrastMode=j.settings.detect_highcontrast&&l._isHighContrast();j.settings.skin=j.forcedHighContrastMode?"highcontrast":j.settings.skin;l.settings=m=h({theme_advanced_path:true,theme_advanced_toolbar_location:"bottom",theme_advanced_buttons1:"bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",theme_advanced_buttons2:"bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",theme_advanced_buttons3:"hr,removeformat,visualaid,|,sub,sup,|,charmap",theme_advanced_blockformats:"p,address,pre,h1,h2,h3,h4,h5,h6",theme_advanced_toolbar_align:"center",theme_advanced_fonts:"Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",theme_advanced_more_colors:1,theme_advanced_row_height:23,theme_advanced_resize_horizontal:1,theme_advanced_resizing_use_cookie:1,theme_advanced_font_sizes:"1,2,3,4,5,6,7",theme_advanced_font_selector:"span",theme_advanced_show_current_color:0,readonly:j.settings.readonly},j.settings);if(!m.font_size_style_values){m.font_size_style_values="8pt,10pt,12pt,14pt,18pt,24pt,36pt"}if(e.is(m.theme_advanced_font_sizes,"string")){m.font_size_style_values=e.explode(m.font_size_style_values);m.font_size_classes=e.explode(m.font_size_classes||"");n={};j.settings.theme_advanced_font_sizes=m.theme_advanced_font_sizes;f(j.getParam("theme_advanced_font_sizes","","hash"),function(q,p){var o;if(p==q&&q>=1&&q<=7){p=q+" ("+l.sizes[q-1]+"pt)";o=m.font_size_classes[q-1];q=m.font_size_style_values[q-1]||(l.sizes[q-1]+"pt")}if(/^\s*\./.test(q)){o=q.replace(/\./g,"")}n[p]=o?{"class":o}:{fontSize:q}});m.theme_advanced_font_sizes=n}if((i=m.theme_advanced_path_location)&&i!="none"){m.theme_advanced_statusbar_location=m.theme_advanced_path_location}if(m.theme_advanced_statusbar_location=="none"){m.theme_advanced_statusbar_location=0}if(j.settings.content_css!==false){j.contentCSS.push(j.baseURI.toAbsolute(k+"/skins/"+j.settings.skin+"/content.css"))}j.onInit.add(function(){if(!j.settings.readonly){j.onNodeChange.add(l._nodeChanged,l);j.onKeyUp.add(l._updateUndoStatus,l);j.onMouseUp.add(l._updateUndoStatus,l);j.dom.bind(j.dom.getRoot(),"dragend",function(){l._updateUndoStatus(j)})}});j.onSetProgressState.add(function(q,o,r){var s,t=q.id,p;if(o){l.progressTimer=setTimeout(function(){s=q.getContainer();s=s.insertBefore(d.create("DIV",{style:"position:relative"}),s.firstChild);p=d.get(q.id+"_tbl");d.add(s,"div",{id:t+"_blocker","class":"mceBlocker",style:{width:p.clientWidth+2,height:p.clientHeight+2}});d.add(s,"div",{id:t+"_progress","class":"mceProgress",style:{left:p.clientWidth/2,top:p.clientHeight/2}})},r||0)}else{d.remove(t+"_blocker");d.remove(t+"_progress");clearTimeout(l.progressTimer)}});d.loadCSS(m.editor_css?j.documentBaseURI.toAbsolute(m.editor_css):k+"/skins/"+j.settings.skin+"/ui.css");if(m.skin_variant){d.loadCSS(k+"/skins/"+j.settings.skin+"/ui_"+m.skin_variant+".css")}},_isHighContrast:function(){var i,j=d.add(d.getRoot(),"div",{style:"background-color: rgb(171,239,86);"});i=(d.getStyle(j,"background-color",true)+"").toLowerCase().replace(/ /g,"");d.remove(j);return i!="rgb(171,239,86)"&&i!="#abef56"},createControl:function(l,i){var j,k;if(k=i.createControl(l)){return k}switch(l){case"styleselect":return this._createStyleSelect();case"formatselect":return this._createBlockFormats();case"fontselect":return this._createFontSelect();case"fontsizeselect":return this._createFontSizeSelect();case"forecolor":return this._createForeColorMenu();case"backcolor":return this._createBackColorMenu()}if((j=this.controls[l])){return i.createButton(l,{title:"advanced."+j[0],cmd:j[1],ui:j[2],value:j[3]})}},execCommand:function(k,j,l){var i=this["_"+k];if(i){i.call(this,j,l);return true}return false},_importClasses:function(k){var i=this.editor,j=i.controlManager.get("styleselect");if(j.getLength()==0){f(i.dom.getClasses(),function(n,l){var m="style_"+l;i.formatter.register(m,{inline:"span",attributes:{"class":n["class"]},selector:"*"});j.add(n["class"],m)})}},_createStyleSelect:function(m){var k=this,i=k.editor,j=i.controlManager,l;l=j.createListBox("styleselect",{title:"advanced.style_select",onselect:function(o){var p,n=[];f(l.items,function(q){n.push(q.value)});i.focus();i.undoManager.add();p=i.formatter.matchAll(n);if(!o||p[0]==o){if(p[0]){i.formatter.remove(p[0])}}else{i.formatter.apply(o)}i.undoManager.add();i.nodeChanged();return false}});i.onInit.add(function(){var o=0,n=i.getParam("style_formats");if(n){f(n,function(p){var q,r=0;f(p,function(){r++});if(r>1){q=p.name=p.name||"style_"+(o++);i.formatter.register(q,p);l.add(p.title,q)}else{l.add(p.title)}})}else{f(i.getParam("theme_advanced_styles","","hash"),function(r,q){var p;if(r){p="style_"+(o++);i.formatter.register(p,{inline:"span",classes:r,selector:"*"});l.add(k.editor.translate(q),p)}})}});if(l.getLength()==0){l.onPostRender.add(function(o,p){if(!l.NativeListBox){b.add(p.id+"_text","focus",k._importClasses,k);b.add(p.id+"_text","mousedown",k._importClasses,k);b.add(p.id+"_open","focus",k._importClasses,k);b.add(p.id+"_open","mousedown",k._importClasses,k)}else{b.add(p.id,"focus",k._importClasses,k)}})}return l},_createFontSelect:function(){var k,j=this,i=j.editor;k=i.controlManager.createListBox("fontselect",{title:"advanced.fontdefault",onselect:function(l){var m=k.items[k.selectedIndex];if(!l&&m){i.execCommand("FontName",false,m.value);return}i.execCommand("FontName",false,l);k.select(function(n){return l==n});if(m&&m.value==l){k.select(null)}return false}});if(k){f(i.getParam("theme_advanced_fonts",j.settings.theme_advanced_fonts,"hash"),function(m,l){k.add(i.translate(l),m,{style:m.indexOf("dings")==-1?"font-family:"+m:""})})}return k},_createFontSizeSelect:function(){var m=this,k=m.editor,n,l=0,j=[];n=k.controlManager.createListBox("fontsizeselect",{title:"advanced.font_size",onselect:function(i){var o=n.items[n.selectedIndex];if(!i&&o){o=o.value;if(o["class"]){k.formatter.toggle("fontsize_class",{value:o["class"]});k.undoManager.add();k.nodeChanged()}else{k.execCommand("FontSize",false,o.fontSize)}return}if(i["class"]){k.focus();k.undoManager.add();k.formatter.toggle("fontsize_class",{value:i["class"]});k.undoManager.add();k.nodeChanged()}else{k.execCommand("FontSize",false,i.fontSize)}n.select(function(p){return i==p});if(o&&(o.value.fontSize==i.fontSize||o.value["class"]==i["class"])){n.select(null)}return false}});if(n){f(m.settings.theme_advanced_font_sizes,function(o,i){var p=o.fontSize;if(p>=1&&p<=7){p=m.sizes[parseInt(p)-1]+"pt"}n.add(i,o,{style:"font-size:"+p,"class":"mceFontSize"+(l++)+(" "+(o["class"]||""))})})}return n},_createBlockFormats:function(){var k,i={p:"advanced.paragraph",address:"advanced.address",pre:"advanced.pre",h1:"advanced.h1",h2:"advanced.h2",h3:"advanced.h3",h4:"advanced.h4",h5:"advanced.h5",h6:"advanced.h6",div:"advanced.div",blockquote:"advanced.blockquote",code:"advanced.code",dt:"advanced.dt",dd:"advanced.dd",samp:"advanced.samp"},j=this;k=j.editor.controlManager.createListBox("formatselect",{title:"advanced.block",onselect:function(l){j.editor.execCommand("FormatBlock",false,l);return false}});if(k){f(j.editor.getParam("theme_advanced_blockformats",j.settings.theme_advanced_blockformats,"hash"),function(m,l){k.add(j.editor.translate(l!=m?l:i[m]),m,{"class":"mce_formatPreview mce_"+m})})}return k},_createForeColorMenu:function(){var m,j=this,k=j.settings,l={},i;if(k.theme_advanced_more_colors){l.more_colors_func=function(){j._mceColorPicker(0,{color:m.value,func:function(n){m.setColor(n)}})}}if(i=k.theme_advanced_text_colors){l.colors=i}if(k.theme_advanced_default_foreground_color){l.default_color=k.theme_advanced_default_foreground_color}l.title="advanced.forecolor_desc";l.cmd="ForeColor";l.scope=this;m=j.editor.controlManager.createColorSplitButton("forecolor",l);return m},_createBackColorMenu:function(){var m,j=this,k=j.settings,l={},i;if(k.theme_advanced_more_colors){l.more_colors_func=function(){j._mceColorPicker(0,{color:m.value,func:function(n){m.setColor(n)}})}}if(i=k.theme_advanced_background_colors){l.colors=i}if(k.theme_advanced_default_background_color){l.default_color=k.theme_advanced_default_background_color}l.title="advanced.backcolor_desc";l.cmd="HiliteColor";l.scope=this;m=j.editor.controlManager.createColorSplitButton("backcolor",l);return m},renderUI:function(k){var m,l,q,v=this,r=v.editor,w=v.settings,u,j,i;if(r.settings){r.settings.aria_label=w.aria_label+r.getLang("advanced.help_shortcut")}m=j=d.create("span",{role:"application","aria-labelledby":r.id+"_voice",id:r.id+"_parent","class":"mceEditor "+r.settings.skin+"Skin"+(w.skin_variant?" "+r.settings.skin+"Skin"+v._ufirst(w.skin_variant):"")});d.add(m,"span",{"class":"mceVoiceLabel",style:"display:none;",id:r.id+"_voice"},w.aria_label);if(!d.boxModel){m=d.add(m,"div",{"class":"mceOldBoxModel"})}m=u=d.add(m,"table",{role:"presentation",id:r.id+"_tbl","class":"mceLayout",cellSpacing:0,cellPadding:0});m=q=d.add(m,"tbody");switch((w.theme_advanced_layout_manager||"").toLowerCase()){case"rowlayout":l=v._rowLayout(w,q,k);break;case"customlayout":l=r.execCallback("theme_advanced_custom_layout",w,q,k,j);break;default:l=v._simpleLayout(w,q,k,j)}m=k.targetNode;i=u.rows;d.addClass(i[0],"mceFirst");d.addClass(i[i.length-1],"mceLast");f(d.select("tr",q),function(o){d.addClass(o.firstChild,"mceFirst");d.addClass(o.childNodes[o.childNodes.length-1],"mceLast")});if(d.get(w.theme_advanced_toolbar_container)){d.get(w.theme_advanced_toolbar_container).appendChild(j)}else{d.insertAfter(j,m)}b.add(r.id+"_path_row","click",function(n){n=n.target;if(n.nodeName=="A"){v._sel(n.className.replace(/^.*mcePath_([0-9]+).*$/,"$1"));return b.cancel(n)}});if(!r.getParam("accessibility_focus")){b.add(d.add(j,"a",{href:"#"},"<!-- IE -->"),"focus",function(){tinyMCE.get(r.id).focus()})}if(w.theme_advanced_toolbar_location=="external"){k.deltaHeight=0}v.deltaHeight=k.deltaHeight;k.targetNode=null;r.onKeyDown.add(function(p,n){var s=121,o=122;if(n.altKey){if(n.keyCode===s){if(e.isWebKit){window.focus()}v.toolbarGroup.focus();return b.cancel(n)}else{if(n.keyCode===o){d.get(p.id+"_path_row").focus();return b.cancel(n)}}}});r.addShortcut("alt+0","","mceShortcuts",v);return{iframeContainer:l,editorContainer:r.id+"_parent",sizeContainer:u,deltaHeight:k.deltaHeight}},getInfo:function(){return{longname:"Advanced theme",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",version:e.majorVersion+"."+e.minorVersion}},resizeBy:function(i,j){var k=d.get(this.editor.id+"_ifr");this.resizeTo(k.clientWidth+i,k.clientHeight+j)},resizeTo:function(i,m,k){var j=this.editor,l=this.settings,n=d.get(j.id+"_tbl"),o=d.get(j.id+"_ifr");i=Math.max(l.theme_advanced_resizing_min_width||100,i);m=Math.max(l.theme_advanced_resizing_min_height||100,m);i=Math.min(l.theme_advanced_resizing_max_width||65535,i);m=Math.min(l.theme_advanced_resizing_max_height||65535,m);d.setStyle(n,"height","");d.setStyle(o,"height",m);if(l.theme_advanced_resize_horizontal){d.setStyle(n,"width","");d.setStyle(o,"width",i);if(i<n.clientWidth){i=n.clientWidth;d.setStyle(o,"width",n.clientWidth)}}if(k&&l.theme_advanced_resizing_use_cookie){a.setHash("TinyMCE_"+j.id+"_size",{cw:i,ch:m})}},destroy:function(){var i=this.editor.id;b.clear(i+"_resize");b.clear(i+"_path_row");b.clear(i+"_external_close")},_simpleLayout:function(y,r,k,i){var x=this,u=x.editor,v=y.theme_advanced_toolbar_location,m=y.theme_advanced_statusbar_location,l,j,q,w;if(y.readonly){l=d.add(r,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"});return j}if(v=="top"){x._addToolbars(r,k)}if(v=="external"){l=w=d.create("div",{style:"position:relative"});l=d.add(l,"div",{id:u.id+"_external","class":"mceExternalToolbar"});d.add(l,"a",{id:u.id+"_external_close",href:"javascript:;","class":"mceExternalClose"});l=d.add(l,"table",{id:u.id+"_tblext",cellSpacing:0,cellPadding:0});q=d.add(l,"tbody");if(i.firstChild.className=="mceOldBoxModel"){i.firstChild.appendChild(w)}else{i.insertBefore(w,i.firstChild)}x._addToolbars(q,k);u.onMouseUp.add(function(){var o=d.get(u.id+"_external");d.show(o);d.hide(g);var n=b.add(u.id+"_external_close","click",function(){d.hide(u.id+"_external");b.remove(u.id+"_external_close","click",n)});d.show(o);d.setStyle(o,"top",0-d.getRect(u.id+"_tblext").h-1);d.hide(o);d.show(o);o.style.filter="";g=u.id+"_external";o=null})}if(m=="top"){x._addStatusBar(r,k)}if(!y.theme_advanced_toolbar_container){l=d.add(r,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"})}if(v=="bottom"){x._addToolbars(r,k)}if(m=="bottom"){x._addStatusBar(r,k)}return j},_rowLayout:function(w,m,k){var v=this,p=v.editor,u,x,i=p.controlManager,l,j,r,q;u=w.theme_advanced_containers_default_class||"";x=w.theme_advanced_containers_default_align||"center";f(c(w.theme_advanced_containers||""),function(s,o){var n=w["theme_advanced_container_"+s]||"";switch(s.toLowerCase()){case"mceeditor":l=d.add(m,"tr");l=j=d.add(l,"td",{"class":"mceIframeContainer"});break;case"mceelementpath":v._addStatusBar(m,k);break;default:q=(w["theme_advanced_container_"+s+"_align"]||x).toLowerCase();q="mce"+v._ufirst(q);l=d.add(d.add(m,"tr"),"td",{"class":"mceToolbar "+(w["theme_advanced_container_"+s+"_class"]||u)+" "+q||x});r=i.createToolbar("toolbar"+o);v._addControls(n,r);d.setHTML(l,r.renderHTML());k.deltaHeight-=w.theme_advanced_row_height}});return j},_addControls:function(j,i){var k=this,l=k.settings,m,n=k.editor.controlManager;if(l.theme_advanced_disable&&!k._disabled){m={};f(c(l.theme_advanced_disable),function(o){m[o]=1});k._disabled=m}else{m=k._disabled}f(c(j),function(p){var o;if(m&&m[p]){return}if(p=="tablecontrols"){f(["table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","split_cells","merge_cells"],function(q){q=k.createControl(q,n);if(q){i.add(q)}});return}o=k.createControl(p,n);if(o){i.add(o)}})},_addToolbars:function(x,k){var A=this,p,m,r=A.editor,B=A.settings,z,j=r.controlManager,u,l,q=[],y,w;w=j.createToolbarGroup("toolbargroup",{name:r.getLang("advanced.toolbar"),tab_focus_toolbar:r.getParam("theme_advanced_tab_focus_toolbar")});A.toolbarGroup=w;y=B.theme_advanced_toolbar_align.toLowerCase();y="mce"+A._ufirst(y);l=d.add(d.add(x,"tr",{role:"presentation"}),"td",{"class":"mceToolbar "+y,role:"presentation"});for(p=1;(z=B["theme_advanced_buttons"+p]);p++){m=j.createToolbar("toolbar"+p,{"class":"mceToolbarRow"+p});if(B["theme_advanced_buttons"+p+"_add"]){z+=","+B["theme_advanced_buttons"+p+"_add"]}if(B["theme_advanced_buttons"+p+"_add_before"]){z=B["theme_advanced_buttons"+p+"_add_before"]+","+z}A._addControls(z,m);w.add(m);k.deltaHeight-=B.theme_advanced_row_height}q.push(w.renderHTML());q.push(d.createHTML("a",{href:"#",accesskey:"z",title:r.getLang("advanced.toolbar_focus"),onfocus:"tinyMCE.getInstanceById('"+r.id+"').focus();"},"<!-- IE -->"));d.setHTML(l,q.join(""))},_addStatusBar:function(m,j){var k,v=this,p=v.editor,w=v.settings,i,q,u,l;k=d.add(m,"tr");k=l=d.add(k,"td",{"class":"mceStatusbar"});k=d.add(k,"div",{id:p.id+"_path_row",role:"group","aria-labelledby":p.id+"_path_voice"});if(w.theme_advanced_path){d.add(k,"span",{id:p.id+"_path_voice"},p.translate("advanced.path"));d.add(k,"span",{},": ")}else{d.add(k,"span",{},"&#160;")}if(w.theme_advanced_resizing){d.add(l,"a",{id:p.id+"_resize",href:"javascript:;",onclick:"return false;","class":"mceResize",tabIndex:"-1"});if(w.theme_advanced_resizing_use_cookie){p.onPostRender.add(function(){var n=a.getHash("TinyMCE_"+p.id+"_size"),r=d.get(p.id+"_tbl");if(!n){return}v.resizeTo(n.cw,n.ch)})}p.onPostRender.add(function(){b.add(p.id+"_resize","click",function(n){n.preventDefault()});b.add(p.id+"_resize","mousedown",function(D){var t,r,s,o,C,z,A,F,n,E,x;function y(G){G.preventDefault();n=A+(G.screenX-C);E=F+(G.screenY-z);v.resizeTo(n,E)}function B(G){b.remove(d.doc,"mousemove",t);b.remove(p.getDoc(),"mousemove",r);b.remove(d.doc,"mouseup",s);b.remove(p.getDoc(),"mouseup",o);n=A+(G.screenX-C);E=F+(G.screenY-z);v.resizeTo(n,E,true)}D.preventDefault();C=D.screenX;z=D.screenY;x=d.get(v.editor.id+"_ifr");A=n=x.clientWidth;F=E=x.clientHeight;t=b.add(d.doc,"mousemove",y);r=b.add(p.getDoc(),"mousemove",y);s=b.add(d.doc,"mouseup",B);o=b.add(p.getDoc(),"mouseup",B)})})}j.deltaHeight-=21;k=m=null},_updateUndoStatus:function(j){var i=j.controlManager,k=j.undoManager;i.setDisabled("undo",!k.hasUndo()&&!k.typing);i.setDisabled("redo",!k.hasRedo())},_nodeChanged:function(m,r,D,q,E){var y=this,C,F=0,x,G,z=y.settings,w,k,u,B,l,j,i;e.each(y.stateControls,function(n){r.setActive(n,m.queryCommandState(y.controls[n][1]))});function o(p){var s,n=E.parents,t=p;if(typeof(p)=="string"){t=function(v){return v.nodeName==p}}for(s=0;s<n.length;s++){if(t(n[s])){return n[s]}}}r.setActive("visualaid",m.hasVisual);y._updateUndoStatus(m);r.setDisabled("outdent",!m.queryCommandState("Outdent"));C=o("A");if(G=r.get("link")){if(!C||!C.name){G.setDisabled(!C&&q);G.setActive(!!C)}}if(G=r.get("unlink")){G.setDisabled(!C&&q);G.setActive(!!C&&!C.name)}if(G=r.get("anchor")){G.setActive(!q&&!!C&&C.name)}C=o("IMG");if(G=r.get("image")){G.setActive(!q&&!!C&&D.className.indexOf("mceItem")==-1)}if(G=r.get("styleselect")){y._importClasses();j=[];f(G.items,function(n){j.push(n.value)});i=m.formatter.matchAll(j);G.select(i[0])}if(G=r.get("formatselect")){C=o(d.isBlock);if(C){G.select(C.nodeName.toLowerCase())}}o(function(p){if(p.nodeName==="SPAN"){if(!w&&p.className){w=p.className}}if(m.dom.is(p,z.theme_advanced_font_selector)){if(!k&&p.style.fontSize){k=p.style.fontSize}if(!u&&p.style.fontFamily){u=p.style.fontFamily.replace(/[\"\']+/g,"").replace(/^([^,]+).*/,"$1").toLowerCase()}if(!B&&p.style.color){B=p.style.color}if(!l&&p.style.backgroundColor){l=p.style.backgroundColor}}return false});if(G=r.get("fontselect")){G.select(function(n){return n.replace(/^([^,]+).*/,"$1").toLowerCase()==u})}if(G=r.get("fontsizeselect")){if(z.theme_advanced_runtime_fontsize&&!k&&!w){k=m.dom.getStyle(D,"fontSize",true)}G.select(function(n){if(n.fontSize&&n.fontSize===k){return true}if(n["class"]&&n["class"]===w){return true}})}if(z.theme_advanced_show_current_color){function A(p,n){if(G=r.get(p)){if(!n){n=G.settings.default_color}if(n!==G.value){G.displayColor(n)}}}A("forecolor",B);A("backcolor",l)}if(z.theme_advanced_show_current_color){function A(p,n){if(G=r.get(p)){if(!n){n=G.settings.default_color}if(n!==G.value){G.displayColor(n)}}}A("forecolor",B);A("backcolor",l)}if(z.theme_advanced_path&&z.theme_advanced_statusbar_location){C=d.get(m.id+"_path")||d.add(m.id+"_path_row","span",{id:m.id+"_path"});if(y.statusKeyboardNavigation){y.statusKeyboardNavigation.destroy();y.statusKeyboardNavigation=null}d.setHTML(C,"");o(function(H){var p=H.nodeName.toLowerCase(),s,v,t="";if(H.nodeType!=1||p==="br"||H.getAttribute("data-mce-bogus")||d.hasClass(H,"mceItemHidden")||d.hasClass(H,"mceItemRemoved")){return}if(e.isIE&&H.scopeName!=="HTML"){p=H.scopeName+":"+p}p=p.replace(/mce\:/g,"");switch(p){case"b":p="strong";break;case"i":p="em";break;case"img":if(x=d.getAttrib(H,"src")){t+="src: "+x+" "}break;case"a":if(x=d.getAttrib(H,"name")){t+="name: "+x+" ";p+="#"+x}if(x=d.getAttrib(H,"href")){t+="href: "+x+" "}break;case"font":if(x=d.getAttrib(H,"face")){t+="font: "+x+" "}if(x=d.getAttrib(H,"size")){t+="size: "+x+" "}if(x=d.getAttrib(H,"color")){t+="color: "+x+" "}break;case"span":if(x=d.getAttrib(H,"style")){t+="style: "+x+" "}break}if(x=d.getAttrib(H,"id")){t+="id: "+x+" "}if(x=H.className){x=x.replace(/\b\s*(webkit|mce|Apple-)\w+\s*\b/g,"");if(x){t+="class: "+x+" ";if(d.isBlock(H)||p=="img"||p=="span"){p+="."+x}}}p=p.replace(/(html:)/g,"");p={name:p,node:H,title:t};y.onResolveName.dispatch(y,p);t=p.title;p=p.name;v=d.create("a",{href:"javascript:;",role:"button",onmousedown:"return false;",title:t,"class":"mcePath_"+(F++)},p);if(C.hasChildNodes()){C.insertBefore(d.create("span",{"aria-hidden":"true"},"\u00a0\u00bb "),C.firstChild);C.insertBefore(v,C.firstChild)}else{C.appendChild(v)}},m.getBody());if(d.select("a",C).length>0){y.statusKeyboardNavigation=new e.ui.KeyboardNavigation({root:m.id+"_path_row",items:d.select("a",C),excludeFromTabOrder:true,onCancel:function(){m.focus()}},d)}}},_sel:function(i){this.editor.execCommand("mceSelectNodeDepth",false,i)},_mceInsertAnchor:function(k,j){var i=this.editor;i.windowManager.open({url:this.url+"/anchor.htm",width:320+parseInt(i.getLang("advanced.anchor_delta_width",0)),height:90+parseInt(i.getLang("advanced.anchor_delta_height",0)),inline:true},{theme_url:this.url})},_mceCharMap:function(){var i=this.editor;i.windowManager.open({url:this.url+"/charmap.htm",width:550+parseInt(i.getLang("advanced.charmap_delta_width",0)),height:260+parseInt(i.getLang("advanced.charmap_delta_height",0)),inline:true},{theme_url:this.url})},_mceHelp:function(){var i=this.editor;i.windowManager.open({url:this.url+"/about.htm",width:480,height:380,inline:true},{theme_url:this.url})},_mceShortcuts:function(){var i=this.editor;i.windowManager.open({url:this.url+"/shortcuts.htm",width:480,height:380,inline:true},{theme_url:this.url})},_mceColorPicker:function(k,j){var i=this.editor;j=j||{};i.windowManager.open({url:this.url+"/color_picker.htm",width:375+parseInt(i.getLang("advanced.colorpicker_delta_width",0)),height:250+parseInt(i.getLang("advanced.colorpicker_delta_height",0)),close_previous:false,inline:true},{input_color:j.color,func:j.func,theme_url:this.url})},_mceCodeEditor:function(j,k){var i=this.editor;i.windowManager.open({url:this.url+"/source_editor.htm",width:parseInt(i.getParam("theme_advanced_source_editor_width",720)),height:parseInt(i.getParam("theme_advanced_source_editor_height",580)),inline:true,resizable:true,maximizable:true},{theme_url:this.url})},_mceImage:function(j,k){var i=this.editor;if(i.dom.getAttrib(i.selection.getNode(),"class").indexOf("mceItem")!=-1){return}i.windowManager.open({url:this.url+"/image.htm",width:355+parseInt(i.getLang("advanced.image_delta_width",0)),height:275+parseInt(i.getLang("advanced.image_delta_height",0)),inline:true},{theme_url:this.url})},_mceLink:function(j,k){var i=this.editor;i.windowManager.open({url:this.url+"/link.htm",width:310+parseInt(i.getLang("advanced.link_delta_width",0)),height:200+parseInt(i.getLang("advanced.link_delta_height",0)),inline:true},{theme_url:this.url})},_mceNewDocument:function(){var i=this.editor;i.windowManager.confirm("advanced.newdocument",function(j){if(j){i.execCommand("mceSetContent",false,"")}})},_mceForeColor:function(){var i=this;this._mceColorPicker(0,{color:i.fgColor,func:function(j){i.fgColor=j;i.editor.execCommand("ForeColor",false,j)}})},_mceBackColor:function(){var i=this;this._mceColorPicker(0,{color:i.bgColor,func:function(j){i.bgColor=j;i.editor.execCommand("HiliteColor",false,j)}})},_ufirst:function(i){return i.substring(0,1).toUpperCase()+i.substring(1)}});e.ThemeManager.add("advanced",e.themes.AdvancedTheme)}(tinymce)); \ No newline at end of file
+(function(h){var i=h.DOM,g=h.dom.Event,c=h.extend,f=h.each,a=h.util.Cookie,e,d=h.explode;function b(p,m){var k,l,o=p.dom,j="",n,r;previewStyles=p.settings.preview_styles;if(previewStyles===false){return""}if(!previewStyles){previewStyles="font-family font-size font-weight text-decoration text-transform color background-color"}function q(s){return s.replace(/%(\w+)/g,"")}k=m.block||m.inline||"span";l=o.create(k);f(m.styles,function(t,s){t=q(t);if(t){o.setStyle(l,s,t)}});f(m.attributes,function(t,s){t=q(t);if(t){o.setAttrib(l,s,t)}});f(m.classes,function(s){s=q(s);if(!o.hasClass(l,s)){o.addClass(l,s)}});o.setStyles(l,{position:"absolute",left:-65535});p.getBody().appendChild(l);n=o.getStyle(p.getBody(),"fontSize",true);n=/px$/.test(n)?parseInt(n,10):0;f(previewStyles.split(" "),function(s){var t=o.getStyle(l,s,true);if(s=="background-color"&&/transparent|rgba\s*\([^)]+,\s*0\)/.test(t)){t=o.getStyle(p.getBody(),s,true);if(o.toHex(t).toLowerCase()=="#ffffff"){return}}if(s=="font-size"){if(/em|%$/.test(t)){if(n===0){return}t=parseFloat(t,10)/(/%$/.test(t)?100:1);t=(t*n)+"px"}}j+=s+":"+t+";"});o.remove(l);return j}h.ThemeManager.requireLangPack("advanced");h.create("tinymce.themes.AdvancedTheme",{sizes:[8,10,12,14,18,24,36],controls:{bold:["bold_desc","Bold"],italic:["italic_desc","Italic"],underline:["underline_desc","Underline"],strikethrough:["striketrough_desc","Strikethrough"],justifyleft:["justifyleft_desc","JustifyLeft"],justifycenter:["justifycenter_desc","JustifyCenter"],justifyright:["justifyright_desc","JustifyRight"],justifyfull:["justifyfull_desc","JustifyFull"],bullist:["bullist_desc","InsertUnorderedList"],numlist:["numlist_desc","InsertOrderedList"],outdent:["outdent_desc","Outdent"],indent:["indent_desc","Indent"],cut:["cut_desc","Cut"],copy:["copy_desc","Copy"],paste:["paste_desc","Paste"],undo:["undo_desc","Undo"],redo:["redo_desc","Redo"],link:["link_desc","mceLink"],unlink:["unlink_desc","unlink"],image:["image_desc","mceImage"],cleanup:["cleanup_desc","mceCleanup"],help:["help_desc","mceHelp"],code:["code_desc","mceCodeEditor"],hr:["hr_desc","InsertHorizontalRule"],removeformat:["removeformat_desc","RemoveFormat"],sub:["sub_desc","subscript"],sup:["sup_desc","superscript"],forecolor:["forecolor_desc","ForeColor"],forecolorpicker:["forecolor_desc","mceForeColor"],backcolor:["backcolor_desc","HiliteColor"],backcolorpicker:["backcolor_desc","mceBackColor"],charmap:["charmap_desc","mceCharMap"],visualaid:["visualaid_desc","mceToggleVisualAid"],anchor:["anchor_desc","mceInsertAnchor"],newdocument:["newdocument_desc","mceNewDocument"],blockquote:["blockquote_desc","mceBlockQuote"]},stateControls:["bold","italic","underline","strikethrough","bullist","numlist","justifyleft","justifycenter","justifyright","justifyfull","sub","sup","blockquote"],init:function(k,l){var m=this,n,j,p;m.editor=k;m.url=l;m.onResolveName=new h.util.Dispatcher(this);n=k.settings;k.forcedHighContrastMode=k.settings.detect_highcontrast&&m._isHighContrast();k.settings.skin=k.forcedHighContrastMode?"highcontrast":k.settings.skin;if(!n.theme_advanced_buttons1){n=c({theme_advanced_buttons1:"bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",theme_advanced_buttons2:"bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",theme_advanced_buttons3:"hr,removeformat,visualaid,|,sub,sup,|,charmap"},n)}m.settings=n=c({theme_advanced_path:true,theme_advanced_toolbar_location:"top",theme_advanced_blockformats:"p,address,pre,h1,h2,h3,h4,h5,h6",theme_advanced_toolbar_align:"left",theme_advanced_statusbar_location:"bottom",theme_advanced_fonts:"Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",theme_advanced_more_colors:1,theme_advanced_row_height:23,theme_advanced_resize_horizontal:1,theme_advanced_resizing_use_cookie:1,theme_advanced_font_sizes:"1,2,3,4,5,6,7",theme_advanced_font_selector:"span",theme_advanced_show_current_color:0,readonly:k.settings.readonly},n);if(!n.font_size_style_values){n.font_size_style_values="8pt,10pt,12pt,14pt,18pt,24pt,36pt"}if(h.is(n.theme_advanced_font_sizes,"string")){n.font_size_style_values=h.explode(n.font_size_style_values);n.font_size_classes=h.explode(n.font_size_classes||"");p={};k.settings.theme_advanced_font_sizes=n.theme_advanced_font_sizes;f(k.getParam("theme_advanced_font_sizes","","hash"),function(r,q){var o;if(q==r&&r>=1&&r<=7){q=r+" ("+m.sizes[r-1]+"pt)";o=n.font_size_classes[r-1];r=n.font_size_style_values[r-1]||(m.sizes[r-1]+"pt")}if(/^\s*\./.test(r)){o=r.replace(/\./g,"")}p[q]=o?{"class":o}:{fontSize:r}});n.theme_advanced_font_sizes=p}if((j=n.theme_advanced_path_location)&&j!="none"){n.theme_advanced_statusbar_location=n.theme_advanced_path_location}if(n.theme_advanced_statusbar_location=="none"){n.theme_advanced_statusbar_location=0}if(k.settings.content_css!==false){k.contentCSS.push(k.baseURI.toAbsolute(l+"/skins/"+k.settings.skin+"/content.css"))}k.onInit.add(function(){if(!k.settings.readonly){k.onNodeChange.add(m._nodeChanged,m);k.onKeyUp.add(m._updateUndoStatus,m);k.onMouseUp.add(m._updateUndoStatus,m);k.dom.bind(k.dom.getRoot(),"dragend",function(){m._updateUndoStatus(k)})}});k.onSetProgressState.add(function(r,o,s){var t,u=r.id,q;if(o){m.progressTimer=setTimeout(function(){t=r.getContainer();t=t.insertBefore(i.create("DIV",{style:"position:relative"}),t.firstChild);q=i.get(r.id+"_tbl");i.add(t,"div",{id:u+"_blocker","class":"mceBlocker",style:{width:q.clientWidth+2,height:q.clientHeight+2}});i.add(t,"div",{id:u+"_progress","class":"mceProgress",style:{left:q.clientWidth/2,top:q.clientHeight/2}})},s||0)}else{i.remove(u+"_blocker");i.remove(u+"_progress");clearTimeout(m.progressTimer)}});i.loadCSS(n.editor_css?k.documentBaseURI.toAbsolute(n.editor_css):l+"/skins/"+k.settings.skin+"/ui.css");if(n.skin_variant){i.loadCSS(l+"/skins/"+k.settings.skin+"/ui_"+n.skin_variant+".css")}},_isHighContrast:function(){var j,k=i.add(i.getRoot(),"div",{style:"background-color: rgb(171,239,86);"});j=(i.getStyle(k,"background-color",true)+"").toLowerCase().replace(/ /g,"");i.remove(k);return j!="rgb(171,239,86)"&&j!="#abef56"},createControl:function(m,j){var k,l;if(l=j.createControl(m)){return l}switch(m){case"styleselect":return this._createStyleSelect();case"formatselect":return this._createBlockFormats();case"fontselect":return this._createFontSelect();case"fontsizeselect":return this._createFontSizeSelect();case"forecolor":return this._createForeColorMenu();case"backcolor":return this._createBackColorMenu()}if((k=this.controls[m])){return j.createButton(m,{title:"advanced."+k[0],cmd:k[1],ui:k[2],value:k[3]})}},execCommand:function(l,k,m){var j=this["_"+l];if(j){j.call(this,k,m);return true}return false},_importClasses:function(l){var j=this.editor,k=j.controlManager.get("styleselect");if(k.getLength()==0){f(j.dom.getClasses(),function(q,m){var p="style_"+m,n;n={inline:"span",attributes:{"class":q["class"]},selector:"*"};j.formatter.register(p,n);k.add(q["class"],p,{style:function(){return b(j,n)}})})}},_createStyleSelect:function(o){var l=this,j=l.editor,k=j.controlManager,m;m=k.createListBox("styleselect",{title:"advanced.style_select",onselect:function(q){var r,n=[],p;f(m.items,function(s){n.push(s.value)});j.focus();j.undoManager.add();r=j.formatter.matchAll(n);h.each(r,function(s){if(!q||s==q){if(s){j.formatter.remove(s)}p=true}});if(!p){j.formatter.apply(q)}j.undoManager.add();j.nodeChanged();return false}});j.onPreInit.add(function(){var p=0,n=j.getParam("style_formats");if(n){f(n,function(q){var r,s=0;f(q,function(){s++});if(s>1){r=q.name=q.name||"style_"+(p++);j.formatter.register(r,q);m.add(q.title,r,{style:function(){return b(j,q)}})}else{m.add(q.title)}})}else{f(j.getParam("theme_advanced_styles","","hash"),function(t,s){var r,q;if(t){r="style_"+(p++);q={inline:"span",classes:t,selector:"*"};j.formatter.register(r,q);m.add(l.editor.translate(s),r,{style:function(){return b(j,q)}})}})}});if(m.getLength()==0){m.onPostRender.add(function(p,q){if(!m.NativeListBox){g.add(q.id+"_text","focus",l._importClasses,l);g.add(q.id+"_text","mousedown",l._importClasses,l);g.add(q.id+"_open","focus",l._importClasses,l);g.add(q.id+"_open","mousedown",l._importClasses,l)}else{g.add(q.id,"focus",l._importClasses,l)}})}return m},_createFontSelect:function(){var l,k=this,j=k.editor;l=j.controlManager.createListBox("fontselect",{title:"advanced.fontdefault",onselect:function(m){var n=l.items[l.selectedIndex];if(!m&&n){j.execCommand("FontName",false,n.value);return}j.execCommand("FontName",false,m);l.select(function(o){return m==o});if(n&&n.value==m){l.select(null)}return false}});if(l){f(j.getParam("theme_advanced_fonts",k.settings.theme_advanced_fonts,"hash"),function(n,m){l.add(j.translate(m),n,{style:n.indexOf("dings")==-1?"font-family:"+n:""})})}return l},_createFontSizeSelect:function(){var m=this,k=m.editor,n,l=0,j=[];n=k.controlManager.createListBox("fontsizeselect",{title:"advanced.font_size",onselect:function(o){var p=n.items[n.selectedIndex];if(!o&&p){p=p.value;if(p["class"]){k.formatter.toggle("fontsize_class",{value:p["class"]});k.undoManager.add();k.nodeChanged()}else{k.execCommand("FontSize",false,p.fontSize)}return}if(o["class"]){k.focus();k.undoManager.add();k.formatter.toggle("fontsize_class",{value:o["class"]});k.undoManager.add();k.nodeChanged()}else{k.execCommand("FontSize",false,o.fontSize)}n.select(function(q){return o==q});if(p&&(p.value.fontSize==o.fontSize||p.value["class"]&&p.value["class"]==o["class"])){n.select(null)}return false}});if(n){f(m.settings.theme_advanced_font_sizes,function(p,o){var q=p.fontSize;if(q>=1&&q<=7){q=m.sizes[parseInt(q)-1]+"pt"}n.add(o,p,{style:"font-size:"+q,"class":"mceFontSize"+(l++)+(" "+(p["class"]||""))})})}return n},_createBlockFormats:function(){var l,j={p:"advanced.paragraph",address:"advanced.address",pre:"advanced.pre",h1:"advanced.h1",h2:"advanced.h2",h3:"advanced.h3",h4:"advanced.h4",h5:"advanced.h5",h6:"advanced.h6",div:"advanced.div",blockquote:"advanced.blockquote",code:"advanced.code",dt:"advanced.dt",dd:"advanced.dd",samp:"advanced.samp"},k=this;l=k.editor.controlManager.createListBox("formatselect",{title:"advanced.block",onselect:function(m){k.editor.execCommand("FormatBlock",false,m);return false}});if(l){f(k.editor.getParam("theme_advanced_blockformats",k.settings.theme_advanced_blockformats,"hash"),function(n,m){l.add(k.editor.translate(m!=n?m:j[n]),n,{"class":"mce_formatPreview mce_"+n,style:function(){return b(k.editor,{block:n})}})})}return l},_createForeColorMenu:function(){var n,k=this,l=k.settings,m={},j;if(l.theme_advanced_more_colors){m.more_colors_func=function(){k._mceColorPicker(0,{color:n.value,func:function(o){n.setColor(o)}})}}if(j=l.theme_advanced_text_colors){m.colors=j}if(l.theme_advanced_default_foreground_color){m.default_color=l.theme_advanced_default_foreground_color}m.title="advanced.forecolor_desc";m.cmd="ForeColor";m.scope=this;n=k.editor.controlManager.createColorSplitButton("forecolor",m);return n},_createBackColorMenu:function(){var n,k=this,l=k.settings,m={},j;if(l.theme_advanced_more_colors){m.more_colors_func=function(){k._mceColorPicker(0,{color:n.value,func:function(o){n.setColor(o)}})}}if(j=l.theme_advanced_background_colors){m.colors=j}if(l.theme_advanced_default_background_color){m.default_color=l.theme_advanced_default_background_color}m.title="advanced.backcolor_desc";m.cmd="HiliteColor";m.scope=this;n=k.editor.controlManager.createColorSplitButton("backcolor",m);return n},renderUI:function(l){var q,m,r,w=this,u=w.editor,x=w.settings,v,k,j;if(u.settings){u.settings.aria_label=x.aria_label+u.getLang("advanced.help_shortcut")}q=k=i.create("span",{role:"application","aria-labelledby":u.id+"_voice",id:u.id+"_parent","class":"mceEditor "+u.settings.skin+"Skin"+(x.skin_variant?" "+u.settings.skin+"Skin"+w._ufirst(x.skin_variant):"")+(u.settings.directionality=="rtl"?" mceRtl":"")});i.add(q,"span",{"class":"mceVoiceLabel",style:"display:none;",id:u.id+"_voice"},x.aria_label);if(!i.boxModel){q=i.add(q,"div",{"class":"mceOldBoxModel"})}q=v=i.add(q,"table",{role:"presentation",id:u.id+"_tbl","class":"mceLayout",cellSpacing:0,cellPadding:0});q=r=i.add(q,"tbody");switch((x.theme_advanced_layout_manager||"").toLowerCase()){case"rowlayout":m=w._rowLayout(x,r,l);break;case"customlayout":m=u.execCallback("theme_advanced_custom_layout",x,r,l,k);break;default:m=w._simpleLayout(x,r,l,k)}q=l.targetNode;j=v.rows;i.addClass(j[0],"mceFirst");i.addClass(j[j.length-1],"mceLast");f(i.select("tr",r),function(o){i.addClass(o.firstChild,"mceFirst");i.addClass(o.childNodes[o.childNodes.length-1],"mceLast")});if(i.get(x.theme_advanced_toolbar_container)){i.get(x.theme_advanced_toolbar_container).appendChild(k)}else{i.insertAfter(k,q)}g.add(u.id+"_path_row","click",function(n){n=n.target;if(n.nodeName=="A"){w._sel(n.className.replace(/^.*mcePath_([0-9]+).*$/,"$1"));return false}});if(!u.getParam("accessibility_focus")){g.add(i.add(k,"a",{href:"#"},"<!-- IE -->"),"focus",function(){tinyMCE.get(u.id).focus()})}if(x.theme_advanced_toolbar_location=="external"){l.deltaHeight=0}w.deltaHeight=l.deltaHeight;l.targetNode=null;u.onKeyDown.add(function(p,n){var s=121,o=122;if(n.altKey){if(n.keyCode===s){if(h.isWebKit){window.focus()}w.toolbarGroup.focus();return g.cancel(n)}else{if(n.keyCode===o){i.get(p.id+"_path_row").focus();return g.cancel(n)}}}});u.addShortcut("alt+0","","mceShortcuts",w);return{iframeContainer:m,editorContainer:u.id+"_parent",sizeContainer:v,deltaHeight:l.deltaHeight}},getInfo:function(){return{longname:"Advanced theme",author:"Moxiecode Systems AB",authorurl:"http://tinymce.moxiecode.com",version:h.majorVersion+"."+h.minorVersion}},resizeBy:function(j,k){var l=i.get(this.editor.id+"_ifr");this.resizeTo(l.clientWidth+j,l.clientHeight+k)},resizeTo:function(j,n,l){var k=this.editor,m=this.settings,o=i.get(k.id+"_tbl"),p=i.get(k.id+"_ifr");j=Math.max(m.theme_advanced_resizing_min_width||100,j);n=Math.max(m.theme_advanced_resizing_min_height||100,n);j=Math.min(m.theme_advanced_resizing_max_width||65535,j);n=Math.min(m.theme_advanced_resizing_max_height||65535,n);i.setStyle(o,"height","");i.setStyle(p,"height",n);if(m.theme_advanced_resize_horizontal){i.setStyle(o,"width","");i.setStyle(p,"width",j);if(j<o.clientWidth){j=o.clientWidth;i.setStyle(p,"width",o.clientWidth)}}if(l&&m.theme_advanced_resizing_use_cookie){a.setHash("TinyMCE_"+k.id+"_size",{cw:j,ch:n})}},destroy:function(){var j=this.editor.id;g.clear(j+"_resize");g.clear(j+"_path_row");g.clear(j+"_external_close")},_simpleLayout:function(z,u,l,j){var y=this,v=y.editor,w=z.theme_advanced_toolbar_location,q=z.theme_advanced_statusbar_location,m,k,r,x;if(z.readonly){m=i.add(u,"tr");m=k=i.add(m,"td",{"class":"mceIframeContainer"});return k}if(w=="top"){y._addToolbars(u,l)}if(w=="external"){m=x=i.create("div",{style:"position:relative"});m=i.add(m,"div",{id:v.id+"_external","class":"mceExternalToolbar"});i.add(m,"a",{id:v.id+"_external_close",href:"javascript:;","class":"mceExternalClose"});m=i.add(m,"table",{id:v.id+"_tblext",cellSpacing:0,cellPadding:0});r=i.add(m,"tbody");if(j.firstChild.className=="mceOldBoxModel"){j.firstChild.appendChild(x)}else{j.insertBefore(x,j.firstChild)}y._addToolbars(r,l);v.onMouseUp.add(function(){var o=i.get(v.id+"_external");i.show(o);i.hide(e);var n=g.add(v.id+"_external_close","click",function(){i.hide(v.id+"_external");g.remove(v.id+"_external_close","click",n);return false});i.show(o);i.setStyle(o,"top",0-i.getRect(v.id+"_tblext").h-1);i.hide(o);i.show(o);o.style.filter="";e=v.id+"_external";o=null})}if(q=="top"){y._addStatusBar(u,l)}if(!z.theme_advanced_toolbar_container){m=i.add(u,"tr");m=k=i.add(m,"td",{"class":"mceIframeContainer"})}if(w=="bottom"){y._addToolbars(u,l)}if(q=="bottom"){y._addStatusBar(u,l)}return k},_rowLayout:function(x,p,l){var w=this,q=w.editor,v,y,j=q.controlManager,m,k,u,r;v=x.theme_advanced_containers_default_class||"";y=x.theme_advanced_containers_default_align||"center";f(d(x.theme_advanced_containers||""),function(s,o){var n=x["theme_advanced_container_"+s]||"";switch(s.toLowerCase()){case"mceeditor":m=i.add(p,"tr");m=k=i.add(m,"td",{"class":"mceIframeContainer"});break;case"mceelementpath":w._addStatusBar(p,l);break;default:r=(x["theme_advanced_container_"+s+"_align"]||y).toLowerCase();r="mce"+w._ufirst(r);m=i.add(i.add(p,"tr"),"td",{"class":"mceToolbar "+(x["theme_advanced_container_"+s+"_class"]||v)+" "+r||y});u=j.createToolbar("toolbar"+o);w._addControls(n,u);i.setHTML(m,u.renderHTML());l.deltaHeight-=x.theme_advanced_row_height}});return k},_addControls:function(k,j){var l=this,m=l.settings,n,o=l.editor.controlManager;if(m.theme_advanced_disable&&!l._disabled){n={};f(d(m.theme_advanced_disable),function(p){n[p]=1});l._disabled=n}else{n=l._disabled}f(d(k),function(q){var p;if(n&&n[q]){return}if(q=="tablecontrols"){f(["table","|","row_props","cell_props","|","row_before","row_after","delete_row","|","col_before","col_after","delete_col","|","split_cells","merge_cells"],function(r){r=l.createControl(r,o);if(r){j.add(r)}});return}p=l.createControl(q,o);if(p){j.add(p)}})},_addToolbars:function(y,k){var B=this,q,p,u=B.editor,C=B.settings,A,j=u.controlManager,w,l,r=[],z,x,m=false;x=j.createToolbarGroup("toolbargroup",{name:u.getLang("advanced.toolbar"),tab_focus_toolbar:u.getParam("theme_advanced_tab_focus_toolbar")});B.toolbarGroup=x;z=C.theme_advanced_toolbar_align.toLowerCase();z="mce"+B._ufirst(z);l=i.add(i.add(y,"tr",{role:"presentation"}),"td",{"class":"mceToolbar "+z,role:"toolbar"});for(q=1;(A=C["theme_advanced_buttons"+q]);q++){m=true;p=j.createToolbar("toolbar"+q,{"class":"mceToolbarRow"+q});if(C["theme_advanced_buttons"+q+"_add"]){A+=","+C["theme_advanced_buttons"+q+"_add"]}if(C["theme_advanced_buttons"+q+"_add_before"]){A=C["theme_advanced_buttons"+q+"_add_before"]+","+A}B._addControls(A,p);x.add(p);k.deltaHeight-=C.theme_advanced_row_height}if(!m){k.deltaHeight-=C.theme_advanced_row_height}r.push(x.renderHTML());r.push(i.createHTML("a",{href:"#",accesskey:"z",title:u.getLang("advanced.toolbar_focus"),onfocus:"tinyMCE.getInstanceById('"+u.id+"').focus();"},"<!-- IE -->"));i.setHTML(l,r.join(""))},_addStatusBar:function(p,k){var l,w=this,q=w.editor,x=w.settings,j,u,v,m;l=i.add(p,"tr");l=m=i.add(l,"td",{"class":"mceStatusbar"});l=i.add(l,"div",{id:q.id+"_path_row",role:"group","aria-labelledby":q.id+"_path_voice"});if(x.theme_advanced_path){i.add(l,"span",{id:q.id+"_path_voice"},q.translate("advanced.path"));i.add(l,"span",{},": ")}else{i.add(l,"span",{},"&#160;")}if(x.theme_advanced_resizing){i.add(m,"a",{id:q.id+"_resize",href:"javascript:;",onclick:"return false;","class":"mceResize",tabIndex:"-1"});if(x.theme_advanced_resizing_use_cookie){q.onPostRender.add(function(){var n=a.getHash("TinyMCE_"+q.id+"_size"),r=i.get(q.id+"_tbl");if(!n){return}w.resizeTo(n.cw,n.ch)})}q.onPostRender.add(function(){g.add(q.id+"_resize","click",function(n){n.preventDefault()});g.add(q.id+"_resize","mousedown",function(E){var t,r,s,o,D,A,B,G,n,F,y;function z(H){H.preventDefault();n=B+(H.screenX-D);F=G+(H.screenY-A);w.resizeTo(n,F)}function C(H){g.remove(i.doc,"mousemove",t);g.remove(q.getDoc(),"mousemove",r);g.remove(i.doc,"mouseup",s);g.remove(q.getDoc(),"mouseup",o);n=B+(H.screenX-D);F=G+(H.screenY-A);w.resizeTo(n,F,true);q.nodeChanged()}E.preventDefault();D=E.screenX;A=E.screenY;y=i.get(w.editor.id+"_ifr");B=n=y.clientWidth;G=F=y.clientHeight;t=g.add(i.doc,"mousemove",z);r=g.add(q.getDoc(),"mousemove",z);s=g.add(i.doc,"mouseup",C);o=g.add(q.getDoc(),"mouseup",C)})})}k.deltaHeight-=21;l=p=null},_updateUndoStatus:function(k){var j=k.controlManager,l=k.undoManager;j.setDisabled("undo",!l.hasUndo()&&!l.typing);j.setDisabled("redo",!l.hasRedo())},_nodeChanged:function(o,u,E,r,F){var z=this,D,G=0,y,H,A=z.settings,x,l,w,C,m,k,j;h.each(z.stateControls,function(n){u.setActive(n,o.queryCommandState(z.controls[n][1]))});function q(p){var s,n=F.parents,t=p;if(typeof(p)=="string"){t=function(v){return v.nodeName==p}}for(s=0;s<n.length;s++){if(t(n[s])){return n[s]}}}u.setActive("visualaid",o.hasVisual);z._updateUndoStatus(o);u.setDisabled("outdent",!o.queryCommandState("Outdent"));D=q("A");if(H=u.get("link")){H.setDisabled((!D&&r)||(D&&!D.href));H.setActive(!!D&&(!D.name&&!D.id))}if(H=u.get("unlink")){H.setDisabled(!D&&r);H.setActive(!!D&&!D.name&&!D.id)}if(H=u.get("anchor")){H.setActive(!r&&!!D&&(D.name||(D.id&&!D.href)))}D=q("IMG");if(H=u.get("image")){H.setActive(!r&&!!D&&E.className.indexOf("mceItem")==-1)}if(H=u.get("styleselect")){z._importClasses();k=[];f(H.items,function(n){k.push(n.value)});j=o.formatter.matchAll(k);H.select(j[0]);h.each(j,function(p,n){if(n>0){H.mark(p)}})}if(H=u.get("formatselect")){D=q(o.dom.isBlock);if(D){H.select(D.nodeName.toLowerCase())}}q(function(p){if(p.nodeName==="SPAN"){if(!x&&p.className){x=p.className}}if(o.dom.is(p,A.theme_advanced_font_selector)){if(!l&&p.style.fontSize){l=p.style.fontSize}if(!w&&p.style.fontFamily){w=p.style.fontFamily.replace(/[\"\']+/g,"").replace(/^([^,]+).*/,"$1").toLowerCase()}if(!C&&p.style.color){C=p.style.color}if(!m&&p.style.backgroundColor){m=p.style.backgroundColor}}return false});if(H=u.get("fontselect")){H.select(function(n){return n.replace(/^([^,]+).*/,"$1").toLowerCase()==w})}if(H=u.get("fontsizeselect")){if(A.theme_advanced_runtime_fontsize&&!l&&!x){l=o.dom.getStyle(E,"fontSize",true)}H.select(function(n){if(n.fontSize&&n.fontSize===l){return true}if(n["class"]&&n["class"]===x){return true}})}if(A.theme_advanced_show_current_color){function B(p,n){if(H=u.get(p)){if(!n){n=H.settings.default_color}if(n!==H.value){H.displayColor(n)}}}B("forecolor",C);B("backcolor",m)}if(A.theme_advanced_show_current_color){function B(p,n){if(H=u.get(p)){if(!n){n=H.settings.default_color}if(n!==H.value){H.displayColor(n)}}}B("forecolor",C);B("backcolor",m)}if(A.theme_advanced_path&&A.theme_advanced_statusbar_location){D=i.get(o.id+"_path")||i.add(o.id+"_path_row","span",{id:o.id+"_path"});if(z.statusKeyboardNavigation){z.statusKeyboardNavigation.destroy();z.statusKeyboardNavigation=null}i.setHTML(D,"");q(function(I){var p=I.nodeName.toLowerCase(),s,v,t="";if(I.nodeType!=1||p==="br"||I.getAttribute("data-mce-bogus")||i.hasClass(I,"mceItemHidden")||i.hasClass(I,"mceItemRemoved")){return}if(h.isIE&&I.scopeName!=="HTML"&&I.scopeName){p=I.scopeName+":"+p}p=p.replace(/mce\:/g,"");switch(p){case"b":p="strong";break;case"i":p="em";break;case"img":if(y=i.getAttrib(I,"src")){t+="src: "+y+" "}break;case"a":if(y=i.getAttrib(I,"name")){t+="name: "+y+" ";p+="#"+y}if(y=i.getAttrib(I,"href")){t+="href: "+y+" "}break;case"font":if(y=i.getAttrib(I,"face")){t+="font: "+y+" "}if(y=i.getAttrib(I,"size")){t+="size: "+y+" "}if(y=i.getAttrib(I,"color")){t+="color: "+y+" "}break;case"span":if(y=i.getAttrib(I,"style")){t+="style: "+y+" "}break}if(y=i.getAttrib(I,"id")){t+="id: "+y+" "}if(y=I.className){y=y.replace(/\b\s*(webkit|mce|Apple-)\w+\s*\b/g,"");if(y){t+="class: "+y+" ";if(o.dom.isBlock(I)||p=="img"||p=="span"){p+="."+y}}}p=p.replace(/(html:)/g,"");p={name:p,node:I,title:t};z.onResolveName.dispatch(z,p);t=p.title;p=p.name;v=i.create("a",{href:"javascript:;",role:"button",onmousedown:"return false;",title:t,"class":"mcePath_"+(G++)},p);if(D.hasChildNodes()){D.insertBefore(i.create("span",{"aria-hidden":"true"},"\u00a0\u00bb "),D.firstChild);D.insertBefore(v,D.firstChild)}else{D.appendChild(v)}},o.getBody());if(i.select("a",D).length>0){z.statusKeyboardNavigation=new h.ui.KeyboardNavigation({root:o.id+"_path_row",items:i.select("a",D),excludeFromTabOrder:true,onCancel:function(){o.focus()}},i)}}},_sel:function(j){this.editor.execCommand("mceSelectNodeDepth",false,j)},_mceInsertAnchor:function(l,k){var j=this.editor;j.windowManager.open({url:this.url+"/anchor.htm",width:320+parseInt(j.getLang("advanced.anchor_delta_width",0)),height:90+parseInt(j.getLang("advanced.anchor_delta_height",0)),inline:true},{theme_url:this.url})},_mceCharMap:function(){var j=this.editor;j.windowManager.open({url:this.url+"/charmap.htm",width:550+parseInt(j.getLang("advanced.charmap_delta_width",0)),height:265+parseInt(j.getLang("advanced.charmap_delta_height",0)),inline:true},{theme_url:this.url})},_mceHelp:function(){var j=this.editor;j.windowManager.open({url:this.url+"/about.htm",width:480,height:380,inline:true},{theme_url:this.url})},_mceShortcuts:function(){var j=this.editor;j.windowManager.open({url:this.url+"/shortcuts.htm",width:480,height:380,inline:true},{theme_url:this.url})},_mceColorPicker:function(l,k){var j=this.editor;k=k||{};j.windowManager.open({url:this.url+"/color_picker.htm",width:375+parseInt(j.getLang("advanced.colorpicker_delta_width",0)),height:250+parseInt(j.getLang("advanced.colorpicker_delta_height",0)),close_previous:false,inline:true},{input_color:k.color,func:k.func,theme_url:this.url})},_mceCodeEditor:function(k,l){var j=this.editor;j.windowManager.open({url:this.url+"/source_editor.htm",width:parseInt(j.getParam("theme_advanced_source_editor_width",720)),height:parseInt(j.getParam("theme_advanced_source_editor_height",580)),inline:true,resizable:true,maximizable:true},{theme_url:this.url})},_mceImage:function(k,l){var j=this.editor;if(j.dom.getAttrib(j.selection.getNode(),"class","").indexOf("mceItem")!=-1){return}j.windowManager.open({url:this.url+"/image.htm",width:355+parseInt(j.getLang("advanced.image_delta_width",0)),height:275+parseInt(j.getLang("advanced.image_delta_height",0)),inline:true},{theme_url:this.url})},_mceLink:function(k,l){var j=this.editor;j.windowManager.open({url:this.url+"/link.htm",width:310+parseInt(j.getLang("advanced.link_delta_width",0)),height:200+parseInt(j.getLang("advanced.link_delta_height",0)),inline:true},{theme_url:this.url})},_mceNewDocument:function(){var j=this.editor;j.windowManager.confirm("advanced.newdocument",function(k){if(k){j.execCommand("mceSetContent",false,"")}})},_mceForeColor:function(){var j=this;this._mceColorPicker(0,{color:j.fgColor,func:function(k){j.fgColor=k;j.editor.execCommand("ForeColor",false,k)}})},_mceBackColor:function(){var j=this;this._mceColorPicker(0,{color:j.bgColor,func:function(k){j.bgColor=k;j.editor.execCommand("HiliteColor",false,k)}})},_ufirst:function(j){return j.substring(0,1).toUpperCase()+j.substring(1)}});h.ThemeManager.add("advanced",h.themes.AdvancedTheme)}(tinymce)); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template_src.js
index a3713b29e..82166dcb6 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/editor_template_src.js
@@ -11,6 +11,95 @@
(function(tinymce) {
var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend, each = tinymce.each, Cookie = tinymce.util.Cookie, lastExtID, explode = tinymce.explode;
+ // Generates a preview for a format
+ function getPreviewCss(ed, fmt) {
+ var name, previewElm, dom = ed.dom, previewCss = '', parentFontSize, previewStylesName;
+
+ previewStyles = ed.settings.preview_styles;
+
+ // No preview forced
+ if (previewStyles === false)
+ return '';
+
+ // Default preview
+ if (!previewStyles)
+ previewStyles = 'font-family font-size font-weight text-decoration text-transform color background-color';
+
+ // Removes any variables since these can't be previewed
+ function removeVars(val) {
+ return val.replace(/%(\w+)/g, '');
+ };
+
+ // Create block/inline element to use for preview
+ name = fmt.block || fmt.inline || 'span';
+ previewElm = dom.create(name);
+
+ // Add format styles to preview element
+ each(fmt.styles, function(value, name) {
+ value = removeVars(value);
+
+ if (value)
+ dom.setStyle(previewElm, name, value);
+ });
+
+ // Add attributes to preview element
+ each(fmt.attributes, function(value, name) {
+ value = removeVars(value);
+
+ if (value)
+ dom.setAttrib(previewElm, name, value);
+ });
+
+ // Add classes to preview element
+ each(fmt.classes, function(value) {
+ value = removeVars(value);
+
+ if (!dom.hasClass(previewElm, value))
+ dom.addClass(previewElm, value);
+ });
+
+ // Add the previewElm outside the visual area
+ dom.setStyles(previewElm, {position: 'absolute', left: -0xFFFF});
+ ed.getBody().appendChild(previewElm);
+
+ // Get parent container font size so we can compute px values out of em/% for older IE:s
+ parentFontSize = dom.getStyle(ed.getBody(), 'fontSize', true);
+ parentFontSize = /px$/.test(parentFontSize) ? parseInt(parentFontSize, 10) : 0;
+
+ each(previewStyles.split(' '), function(name) {
+ var value = dom.getStyle(previewElm, name, true);
+
+ // If background is transparent then check if the body has a background color we can use
+ if (name == 'background-color' && /transparent|rgba\s*\([^)]+,\s*0\)/.test(value)) {
+ value = dom.getStyle(ed.getBody(), name, true);
+
+ // Ignore white since it's the default color, not the nicest fix
+ if (dom.toHex(value).toLowerCase() == '#ffffff') {
+ return;
+ }
+ }
+
+ // Old IE won't calculate the font size so we need to do that manually
+ if (name == 'font-size') {
+ if (/em|%$/.test(value)) {
+ if (parentFontSize === 0) {
+ return;
+ }
+
+ // Convert font size from em/% to px
+ value = parseFloat(value, 10) / (/%$/.test(value) ? 100 : 1);
+ value = (value * parentFontSize) + 'px';
+ }
+ }
+
+ previewCss += name + ':' + value + ';';
+ });
+
+ dom.remove(previewElm);
+
+ return previewCss;
+ };
+
// Tell it to load theme specific language pack(s)
tinymce.ThemeManager.requireLangPack('advanced');
@@ -61,23 +150,31 @@
init : function(ed, url) {
var t = this, s, v, o;
-
+
t.editor = ed;
t.url = url;
t.onResolveName = new tinymce.util.Dispatcher(this);
+ s = ed.settings;
ed.forcedHighContrastMode = ed.settings.detect_highcontrast && t._isHighContrast();
ed.settings.skin = ed.forcedHighContrastMode ? 'highcontrast' : ed.settings.skin;
+ // Setup default buttons
+ if (!s.theme_advanced_buttons1) {
+ s = extend({
+ theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",
+ theme_advanced_buttons2 : "bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",
+ theme_advanced_buttons3 : "hr,removeformat,visualaid,|,sub,sup,|,charmap"
+ }, s);
+ }
+
// Default settings
t.settings = s = extend({
theme_advanced_path : true,
- theme_advanced_toolbar_location : 'bottom',
- theme_advanced_buttons1 : "bold,italic,underline,strikethrough,|,justifyleft,justifycenter,justifyright,justifyfull,|,styleselect,formatselect",
- theme_advanced_buttons2 : "bullist,numlist,|,outdent,indent,|,undo,redo,|,link,unlink,anchor,image,cleanup,help,code",
- theme_advanced_buttons3 : "hr,removeformat,visualaid,|,sub,sup,|,charmap",
+ theme_advanced_toolbar_location : 'top',
theme_advanced_blockformats : "p,address,pre,h1,h2,h3,h4,h5,h6",
- theme_advanced_toolbar_align : "center",
+ theme_advanced_toolbar_align : "left",
+ theme_advanced_statusbar_location : "bottom",
theme_advanced_fonts : "Andale Mono=andale mono,times;Arial=arial,helvetica,sans-serif;Arial Black=arial black,avant garde;Book Antiqua=book antiqua,palatino;Comic Sans MS=comic sans ms,sans-serif;Courier New=courier new,courier;Georgia=georgia,palatino;Helvetica=helvetica;Impact=impact,chicago;Symbol=symbol;Tahoma=tahoma,arial,helvetica,sans-serif;Terminal=terminal,monaco;Times New Roman=times new roman,times;Trebuchet MS=trebuchet ms,geneva;Verdana=verdana,geneva;Webdings=webdings;Wingdings=wingdings,zapf dingbats",
theme_advanced_more_colors : 1,
theme_advanced_row_height : 23,
@@ -87,7 +184,7 @@
theme_advanced_font_selector : "span",
theme_advanced_show_current_color: 0,
readonly : ed.settings.readonly
- }, ed.settings);
+ }, s);
// Setup default font_size_style_values
if (!s.font_size_style_values)
@@ -219,15 +316,21 @@
if (ctrl.getLength() == 0) {
each(ed.dom.getClasses(), function(o, idx) {
- var name = 'style_' + idx;
+ var name = 'style_' + idx, fmt;
- ed.formatter.register(name, {
+ fmt = {
inline : 'span',
attributes : {'class' : o['class']},
selector : '*'
- });
+ };
+
+ ed.formatter.register(name, fmt);
- ctrl.add(o['class'], name);
+ ctrl.add(o['class'], name, {
+ style: function() {
+ return getPreviewCss(ed, fmt);
+ }
+ });
});
}
},
@@ -239,7 +342,7 @@
ctrl = ctrlMan.createListBox('styleselect', {
title : 'advanced.style_select',
onselect : function(name) {
- var matches, formatNames = [];
+ var matches, formatNames = [], removedFormat;
each(ctrl.items, function(item) {
formatNames.push(item.value);
@@ -248,12 +351,18 @@
ed.focus();
ed.undoManager.add();
- // Toggle off the current format
+ // Toggle off the current format(s)
matches = ed.formatter.matchAll(formatNames);
- if (!name || matches[0] == name) {
- if (matches[0])
- ed.formatter.remove(matches[0]);
- } else
+ tinymce.each(matches, function(match) {
+ if (!name || match == name) {
+ if (match)
+ ed.formatter.remove(match);
+
+ removedFormat = true;
+ }
+ });
+
+ if (!removedFormat)
ed.formatter.apply(name);
ed.undoManager.add();
@@ -264,7 +373,7 @@
});
// Handle specified format
- ed.onInit.add(function() {
+ ed.onPreInit.add(function() {
var counter = 0, formats = ed.getParam('style_formats');
if (formats) {
@@ -276,24 +385,32 @@
if (keys > 1) {
name = fmt.name = fmt.name || 'style_' + (counter++);
ed.formatter.register(name, fmt);
- ctrl.add(fmt.title, name);
+ ctrl.add(fmt.title, name, {
+ style: function() {
+ return getPreviewCss(ed, fmt);
+ }
+ });
} else
ctrl.add(fmt.title);
});
} else {
each(ed.getParam('theme_advanced_styles', '', 'hash'), function(val, key) {
- var name;
+ var name, fmt;
if (val) {
name = 'style_' + (counter++);
-
- ed.formatter.register(name, {
+ fmt = {
inline : 'span',
classes : val,
selector : '*'
- });
+ };
- ctrl.add(t.editor.translate(key), name);
+ ed.formatter.register(name, fmt);
+ ctrl.add(t.editor.translate(key), name, {
+ style: function() {
+ return getPreviewCss(ed, fmt);
+ }
+ });
}
});
}
@@ -386,7 +503,7 @@
return v == sv;
});
- if (cur && (cur.value.fontSize == v.fontSize || cur.value['class'] == v['class'])) {
+ if (cur && (cur.value.fontSize == v.fontSize || cur.value['class'] && cur.value['class'] == v['class'])) {
c.select(null);
}
@@ -433,7 +550,9 @@
if (c) {
each(t.editor.getParam('theme_advanced_blockformats', t.settings.theme_advanced_blockformats, 'hash'), function(v, k) {
- c.add(t.editor.translate(k != v ? k : fmts[v]), v, {'class' : 'mce_formatPreview mce_' + v});
+ c.add(t.editor.translate(k != v ? k : fmts[v]), v, {'class' : 'mce_formatPreview mce_' + v, style: function() {
+ return getPreviewCss(t.editor, {block: v});
+ }});
});
}
@@ -507,7 +626,7 @@
// TODO: ACC Should have an aria-describedby attribute which is user-configurable to describe what this field is actually for.
// Maybe actually inherit it from the original textara?
- n = p = DOM.create('span', {role : 'application', 'aria-labelledby' : ed.id + '_voice', id : ed.id + '_parent', 'class' : 'mceEditor ' + ed.settings.skin + 'Skin' + (s.skin_variant ? ' ' + ed.settings.skin + 'Skin' + t._ufirst(s.skin_variant) : '')});
+ n = p = DOM.create('span', {role : 'application', 'aria-labelledby' : ed.id + '_voice', id : ed.id + '_parent', 'class' : 'mceEditor ' + ed.settings.skin + 'Skin' + (s.skin_variant ? ' ' + ed.settings.skin + 'Skin' + t._ufirst(s.skin_variant) : '') + (ed.settings.directionality == "rtl" ? ' mceRtl' : '')});
DOM.add(n, 'span', {'class': 'mceVoiceLabel', 'style': 'display:none;', id: ed.id + '_voice'}, s.aria_label);
if (!DOM.boxModel)
@@ -552,15 +671,14 @@
if (e.nodeName == 'A') {
t._sel(e.className.replace(/^.*mcePath_([0-9]+).*$/, '$1'));
-
- return Event.cancel(e);
+ return false;
}
});
/*
if (DOM.get(ed.id + '_path_row')) {
Event.add(ed.id + '_tbl', 'mouseover', function(e) {
var re;
-
+
e = e.target;
if (e.nodeName == 'SPAN' && DOM.hasClass(e.parentNode, 'mceButton')) {
@@ -713,6 +831,7 @@
var f = Event.add(ed.id + '_external_close', 'click', function() {
DOM.hide(ed.id + '_external');
Event.remove(ed.id + '_external_close', 'click', f);
+ return false;
});
DOM.show(e);
@@ -825,7 +944,7 @@
},
_addToolbars : function(c, o) {
- var t = this, i, tb, ed = t.editor, s = t.settings, v, cf = ed.controlManager, di, n, h = [], a, toolbarGroup;
+ var t = this, i, tb, ed = t.editor, s = t.settings, v, cf = ed.controlManager, di, n, h = [], a, toolbarGroup, toolbarsExist = false;
toolbarGroup = cf.createToolbarGroup('toolbargroup', {
'name': ed.getLang('advanced.toolbar'),
@@ -837,10 +956,11 @@
a = s.theme_advanced_toolbar_align.toLowerCase();
a = 'mce' + t._ufirst(a);
- n = DOM.add(DOM.add(c, 'tr', {role: 'presentation'}), 'td', {'class' : 'mceToolbar ' + a, "role":"presentation"});
+ n = DOM.add(DOM.add(c, 'tr', {role: 'presentation'}), 'td', {'class' : 'mceToolbar ' + a, "role":"toolbar"});
// Create toolbar and add the controls
for (i=1; (v = s['theme_advanced_buttons' + i]); i++) {
+ toolbarsExist = true;
tb = cf.createToolbar("toolbar" + i, {'class' : 'mceToolbarRow' + i});
if (s['theme_advanced_buttons' + i + '_add'])
@@ -854,6 +974,9 @@
o.deltaHeight -= s.theme_advanced_row_height;
}
+ // Handle case when there are no toolbar buttons and ensure editor height is adjusted accordingly
+ if (!toolbarsExist)
+ o.deltaHeight -= s.theme_advanced_row_height;
h.push(toolbarGroup.renderHTML());
h.push(DOM.createHTML('a', {href : '#', accesskey : 'z', title : ed.getLang("advanced.toolbar_focus"), onfocus : 'tinyMCE.getInstanceById(\'' + ed.id + '\').focus();'}, '<!-- IE -->'));
DOM.setHTML(n, h.join(''));
@@ -863,7 +986,7 @@
var n, t = this, ed = t.editor, s = t.settings, r, mf, me, td;
n = DOM.add(tb, 'tr');
- n = td = DOM.add(n, 'td', {'class' : 'mceStatusbar'});
+ n = td = DOM.add(n, 'td', {'class' : 'mceStatusbar'});
n = DOM.add(n, 'div', {id : ed.id + '_path_row', 'role': 'group', 'aria-labelledby': ed.id + '_path_voice'});
if (s.theme_advanced_path) {
DOM.add(n, 'span', {id: ed.id + '_path_voice'}, ed.translate('advanced.path'));
@@ -871,7 +994,7 @@
} else {
DOM.add(n, 'span', {}, '&#160;');
}
-
+
if (s.theme_advanced_resizing) {
DOM.add(td, 'a', {id : ed.id + '_resize', href : 'javascript:;', onclick : "return false;", 'class' : 'mceResize', tabIndex:"-1"});
@@ -916,6 +1039,8 @@
width = startWidth + (e.screenX - startX);
height = startHeight + (e.screenY - startY);
t.resizeTo(width, height, true);
+
+ ed.nodeChanged();
};
e.preventDefault();
@@ -975,19 +1100,17 @@
p = getParent('A');
if (c = cm.get('link')) {
- if (!p || !p.name) {
- c.setDisabled(!p && co);
- c.setActive(!!p);
- }
+ c.setDisabled((!p && co) || (p && !p.href));
+ c.setActive(!!p && (!p.name && !p.id));
}
if (c = cm.get('unlink')) {
c.setDisabled(!p && co);
- c.setActive(!!p && !p.name);
+ c.setActive(!!p && !p.name && !p.id);
}
if (c = cm.get('anchor')) {
- c.setActive(!co && !!p && p.name);
+ c.setActive(!co && !!p && (p.name || (p.id && !p.href)));
}
p = getParent('IMG');
@@ -1004,10 +1127,15 @@
matches = ed.formatter.matchAll(formatNames);
c.select(matches[0]);
+ tinymce.each(matches, function(match, index) {
+ if (index > 0) {
+ c.mark(match);
+ }
+ });
}
if (c = cm.get('formatselect')) {
- p = getParent(DOM.isBlock);
+ p = getParent(ed.dom.isBlock);
if (p)
c.select(p.nodeName.toLowerCase());
@@ -1026,7 +1154,7 @@
if (!fn && n.style.fontFamily)
fn = n.style.fontFamily.replace(/[\"\']+/g, '').replace(/^([^,]+).*/, '$1').toLowerCase();
-
+
if (!fc && n.style.color)
fc = n.style.color;
@@ -1057,7 +1185,7 @@
return true;
});
}
-
+
if (s.theme_advanced_show_current_color) {
function updateColor(controlId, color) {
if (c = cm.get(controlId)) {
@@ -1105,7 +1233,7 @@
return;
// Handle prefix
- if (tinymce.isIE && n.scopeName !== 'HTML')
+ if (tinymce.isIE && n.scopeName !== 'HTML' && n.scopeName)
na = n.scopeName + ':' + na;
// Remove internal prefix
@@ -1161,12 +1289,12 @@
ti += 'id: ' + v + ' ';
if (v = n.className) {
- v = v.replace(/\b\s*(webkit|mce|Apple-)\w+\s*\b/g, '')
+ v = v.replace(/\b\s*(webkit|mce|Apple-)\w+\s*\b/g, '');
if (v) {
ti += 'class: ' + v + ' ';
- if (DOM.isBlock(n) || na == 'img' || na == 'span')
+ if (ed.dom.isBlock(n) || na == 'img' || na == 'span')
na += '.' + v;
}
}
@@ -1225,7 +1353,7 @@
ed.windowManager.open({
url : this.url + '/charmap.htm',
width : 550 + parseInt(ed.getLang('advanced.charmap_delta_width', 0)),
- height : 260 + parseInt(ed.getLang('advanced.charmap_delta_height', 0)),
+ height : 265 + parseInt(ed.getLang('advanced.charmap_delta_height', 0)),
inline : true
}, {
theme_url : this.url
@@ -1294,7 +1422,7 @@
var ed = this.editor;
// Internal image object like a flash placeholder
- if (ed.dom.getAttrib(ed.selection.getNode(), 'class').indexOf('mceItem') != -1)
+ if (ed.dom.getAttrib(ed.selection.getNode(), 'class', '').indexOf('mceItem') != -1)
return;
ed.windowManager.open({
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/img/icons.gif b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/img/icons.gif
index 641a9e3d3..ca2224901 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/img/icons.gif
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/img/icons.gif
Binary files differ
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js
index 04f41e0ca..2909a3a4d 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/anchor.js
@@ -6,7 +6,7 @@ var AnchorDialog = {
this.editor = ed;
elm = ed.dom.getParent(ed.selection.getNode(), 'A');
- v = ed.dom.getAttrib(elm, 'name');
+ v = ed.dom.getAttrib(elm, 'name') || ed.dom.getAttrib(elm, 'id');
if (v) {
this.action = 'update';
@@ -17,7 +17,7 @@ var AnchorDialog = {
},
update : function() {
- var ed = this.editor, elm, name = document.forms[0].anchorName.value;
+ var ed = this.editor, elm, name = document.forms[0].anchorName.value, attribName;
if (!name || !/^[a-z][a-z0-9\-\_:\.]*$/i.test(name)) {
tinyMCEPopup.alert('advanced_dlg.anchor_invalid');
@@ -29,12 +29,25 @@ var AnchorDialog = {
if (this.action != 'update')
ed.selection.collapse(1);
+ var aRule = ed.schema.getElementRule('a');
+ if (!aRule || aRule.attributes.name) {
+ attribName = 'name';
+ } else {
+ attribName = 'id';
+ }
+
elm = ed.dom.getParent(ed.selection.getNode(), 'A');
if (elm) {
- elm.setAttribute('name', name);
- elm.name = name;
- } else
- ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', {name : name, 'class' : 'mceItemAnchor'}, ''));
+ elm.setAttribute(attribName, name);
+ elm[attribName] = name;
+ ed.undoManager.add();
+ } else {
+ // create with zero-sized nbsp so that in Webkit where anchor is on last line by itself caret cannot be placed after it
+ var attrs = {'class' : 'mceItemAnchor'};
+ attrs[attribName] = name;
+ ed.execCommand('mceInsertContent', 0, ed.dom.createHTML('a', attrs, '\uFEFF'));
+ ed.nodeChanged();
+ }
tinyMCEPopup.close();
}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js
index f51e703b0..cc891c171 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/color_picker.js
@@ -1,329 +1,345 @@
-tinyMCEPopup.requireLangPack();
-
-var detail = 50, strhex = "0123456789abcdef", i, isMouseDown = false, isMouseOver = false;
-
-var colors = [
- "#000000","#000033","#000066","#000099","#0000cc","#0000ff","#330000","#330033",
- "#330066","#330099","#3300cc","#3300ff","#660000","#660033","#660066","#660099",
- "#6600cc","#6600ff","#990000","#990033","#990066","#990099","#9900cc","#9900ff",
- "#cc0000","#cc0033","#cc0066","#cc0099","#cc00cc","#cc00ff","#ff0000","#ff0033",
- "#ff0066","#ff0099","#ff00cc","#ff00ff","#003300","#003333","#003366","#003399",
- "#0033cc","#0033ff","#333300","#333333","#333366","#333399","#3333cc","#3333ff",
- "#663300","#663333","#663366","#663399","#6633cc","#6633ff","#993300","#993333",
- "#993366","#993399","#9933cc","#9933ff","#cc3300","#cc3333","#cc3366","#cc3399",
- "#cc33cc","#cc33ff","#ff3300","#ff3333","#ff3366","#ff3399","#ff33cc","#ff33ff",
- "#006600","#006633","#006666","#006699","#0066cc","#0066ff","#336600","#336633",
- "#336666","#336699","#3366cc","#3366ff","#666600","#666633","#666666","#666699",
- "#6666cc","#6666ff","#996600","#996633","#996666","#996699","#9966cc","#9966ff",
- "#cc6600","#cc6633","#cc6666","#cc6699","#cc66cc","#cc66ff","#ff6600","#ff6633",
- "#ff6666","#ff6699","#ff66cc","#ff66ff","#009900","#009933","#009966","#009999",
- "#0099cc","#0099ff","#339900","#339933","#339966","#339999","#3399cc","#3399ff",
- "#669900","#669933","#669966","#669999","#6699cc","#6699ff","#999900","#999933",
- "#999966","#999999","#9999cc","#9999ff","#cc9900","#cc9933","#cc9966","#cc9999",
- "#cc99cc","#cc99ff","#ff9900","#ff9933","#ff9966","#ff9999","#ff99cc","#ff99ff",
- "#00cc00","#00cc33","#00cc66","#00cc99","#00cccc","#00ccff","#33cc00","#33cc33",
- "#33cc66","#33cc99","#33cccc","#33ccff","#66cc00","#66cc33","#66cc66","#66cc99",
- "#66cccc","#66ccff","#99cc00","#99cc33","#99cc66","#99cc99","#99cccc","#99ccff",
- "#cccc00","#cccc33","#cccc66","#cccc99","#cccccc","#ccccff","#ffcc00","#ffcc33",
- "#ffcc66","#ffcc99","#ffcccc","#ffccff","#00ff00","#00ff33","#00ff66","#00ff99",
- "#00ffcc","#00ffff","#33ff00","#33ff33","#33ff66","#33ff99","#33ffcc","#33ffff",
- "#66ff00","#66ff33","#66ff66","#66ff99","#66ffcc","#66ffff","#99ff00","#99ff33",
- "#99ff66","#99ff99","#99ffcc","#99ffff","#ccff00","#ccff33","#ccff66","#ccff99",
- "#ccffcc","#ccffff","#ffff00","#ffff33","#ffff66","#ffff99","#ffffcc","#ffffff"
-];
-
-var named = {
- '#F0F8FF':'Alice Blue','#FAEBD7':'Antique White','#00FFFF':'Aqua','#7FFFD4':'Aquamarine','#F0FFFF':'Azure','#F5F5DC':'Beige',
- '#FFE4C4':'Bisque','#000000':'Black','#FFEBCD':'Blanched Almond','#0000FF':'Blue','#8A2BE2':'Blue Violet','#A52A2A':'Brown',
- '#DEB887':'Burly Wood','#5F9EA0':'Cadet Blue','#7FFF00':'Chartreuse','#D2691E':'Chocolate','#FF7F50':'Coral','#6495ED':'Cornflower Blue',
- '#FFF8DC':'Cornsilk','#DC143C':'Crimson','#00FFFF':'Cyan','#00008B':'Dark Blue','#008B8B':'Dark Cyan','#B8860B':'Dark Golden Rod',
- '#A9A9A9':'Dark Gray','#A9A9A9':'Dark Grey','#006400':'Dark Green','#BDB76B':'Dark Khaki','#8B008B':'Dark Magenta','#556B2F':'Dark Olive Green',
- '#FF8C00':'Darkorange','#9932CC':'Dark Orchid','#8B0000':'Dark Red','#E9967A':'Dark Salmon','#8FBC8F':'Dark Sea Green','#483D8B':'Dark Slate Blue',
- '#2F4F4F':'Dark Slate Gray','#2F4F4F':'Dark Slate Grey','#00CED1':'Dark Turquoise','#9400D3':'Dark Violet','#FF1493':'Deep Pink','#00BFFF':'Deep Sky Blue',
- '#696969':'Dim Gray','#696969':'Dim Grey','#1E90FF':'Dodger Blue','#B22222':'Fire Brick','#FFFAF0':'Floral White','#228B22':'Forest Green',
- '#FF00FF':'Fuchsia','#DCDCDC':'Gainsboro','#F8F8FF':'Ghost White','#FFD700':'Gold','#DAA520':'Golden Rod','#808080':'Gray','#808080':'Grey',
- '#008000':'Green','#ADFF2F':'Green Yellow','#F0FFF0':'Honey Dew','#FF69B4':'Hot Pink','#CD5C5C':'Indian Red','#4B0082':'Indigo','#FFFFF0':'Ivory',
- '#F0E68C':'Khaki','#E6E6FA':'Lavender','#FFF0F5':'Lavender Blush','#7CFC00':'Lawn Green','#FFFACD':'Lemon Chiffon','#ADD8E6':'Light Blue',
- '#F08080':'Light Coral','#E0FFFF':'Light Cyan','#FAFAD2':'Light Golden Rod Yellow','#D3D3D3':'Light Gray','#D3D3D3':'Light Grey','#90EE90':'Light Green',
- '#FFB6C1':'Light Pink','#FFA07A':'Light Salmon','#20B2AA':'Light Sea Green','#87CEFA':'Light Sky Blue','#778899':'Light Slate Gray','#778899':'Light Slate Grey',
- '#B0C4DE':'Light Steel Blue','#FFFFE0':'Light Yellow','#00FF00':'Lime','#32CD32':'Lime Green','#FAF0E6':'Linen','#FF00FF':'Magenta','#800000':'Maroon',
- '#66CDAA':'Medium Aqua Marine','#0000CD':'Medium Blue','#BA55D3':'Medium Orchid','#9370D8':'Medium Purple','#3CB371':'Medium Sea Green','#7B68EE':'Medium Slate Blue',
- '#00FA9A':'Medium Spring Green','#48D1CC':'Medium Turquoise','#C71585':'Medium Violet Red','#191970':'Midnight Blue','#F5FFFA':'Mint Cream','#FFE4E1':'Misty Rose','#FFE4B5':'Moccasin',
- '#FFDEAD':'Navajo White','#000080':'Navy','#FDF5E6':'Old Lace','#808000':'Olive','#6B8E23':'Olive Drab','#FFA500':'Orange','#FF4500':'Orange Red','#DA70D6':'Orchid',
- '#EEE8AA':'Pale Golden Rod','#98FB98':'Pale Green','#AFEEEE':'Pale Turquoise','#D87093':'Pale Violet Red','#FFEFD5':'Papaya Whip','#FFDAB9':'Peach Puff',
- '#CD853F':'Peru','#FFC0CB':'Pink','#DDA0DD':'Plum','#B0E0E6':'Powder Blue','#800080':'Purple','#FF0000':'Red','#BC8F8F':'Rosy Brown','#4169E1':'Royal Blue',
- '#8B4513':'Saddle Brown','#FA8072':'Salmon','#F4A460':'Sandy Brown','#2E8B57':'Sea Green','#FFF5EE':'Sea Shell','#A0522D':'Sienna','#C0C0C0':'Silver',
- '#87CEEB':'Sky Blue','#6A5ACD':'Slate Blue','#708090':'Slate Gray','#708090':'Slate Grey','#FFFAFA':'Snow','#00FF7F':'Spring Green',
- '#4682B4':'Steel Blue','#D2B48C':'Tan','#008080':'Teal','#D8BFD8':'Thistle','#FF6347':'Tomato','#40E0D0':'Turquoise','#EE82EE':'Violet',
- '#F5DEB3':'Wheat','#FFFFFF':'White','#F5F5F5':'White Smoke','#FFFF00':'Yellow','#9ACD32':'Yellow Green'
-};
-
-var namedLookup = {};
-
-function init() {
- var inputColor = convertRGBToHex(tinyMCEPopup.getWindowArg('input_color')), key, value;
-
- tinyMCEPopup.resizeToInnerSize();
-
- generatePicker();
- generateWebColors();
- generateNamedColors();
-
- if (inputColor) {
- changeFinalColor(inputColor);
-
- col = convertHexToRGB(inputColor);
-
- if (col)
- updateLight(col.r, col.g, col.b);
- }
-
- for (key in named) {
- value = named[key];
- namedLookup[value.replace(/\s+/, '').toLowerCase()] = key.replace(/#/, '').toLowerCase();
- }
-}
-
-function toHexColor(color) {
- var matches, red, green, blue, toInt = parseInt;
-
- function hex(value) {
- value = parseInt(value).toString(16);
-
- return value.length > 1 ? value : '0' + value; // Padd with leading zero
- };
-
- color = color.replace(/[\s#]+/g, '').toLowerCase();
- color = namedLookup[color] || color;
- matches = /^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)|([a-f0-9]{2})([a-f0-9]{2})([a-f0-9]{2})|([a-f0-9])([a-f0-9])([a-f0-9])$/.exec(color);
-
- if (matches) {
- if (matches[1]) {
- red = toInt(matches[1]);
- green = toInt(matches[2]);
- blue = toInt(matches[3]);
- } else if (matches[4]) {
- red = toInt(matches[4], 16);
- green = toInt(matches[5], 16);
- blue = toInt(matches[6], 16);
- } else if (matches[7]) {
- red = toInt(matches[7] + matches[7], 16);
- green = toInt(matches[8] + matches[8], 16);
- blue = toInt(matches[9] + matches[9], 16);
- }
-
- return '#' + hex(red) + hex(green) + hex(blue);
- }
-
- return '';
-}
-
-function insertAction() {
- var color = document.getElementById("color").value, f = tinyMCEPopup.getWindowArg('func');
-
- tinyMCEPopup.restoreSelection();
-
- if (f)
- f(toHexColor(color));
-
- tinyMCEPopup.close();
-}
-
-function showColor(color, name) {
- if (name)
- document.getElementById("colorname").innerHTML = name;
-
- document.getElementById("preview").style.backgroundColor = color;
- document.getElementById("color").value = color.toUpperCase();
-}
-
-function convertRGBToHex(col) {
- var re = new RegExp("rgb\\s*\\(\\s*([0-9]+).*,\\s*([0-9]+).*,\\s*([0-9]+).*\\)", "gi");
-
- if (!col)
- return col;
-
- var rgb = col.replace(re, "$1,$2,$3").split(',');
- if (rgb.length == 3) {
- r = parseInt(rgb[0]).toString(16);
- g = parseInt(rgb[1]).toString(16);
- b = parseInt(rgb[2]).toString(16);
-
- r = r.length == 1 ? '0' + r : r;
- g = g.length == 1 ? '0' + g : g;
- b = b.length == 1 ? '0' + b : b;
-
- return "#" + r + g + b;
- }
-
- return col;
-}
-
-function convertHexToRGB(col) {
- if (col.indexOf('#') != -1) {
- col = col.replace(new RegExp('[^0-9A-F]', 'gi'), '');
-
- r = parseInt(col.substring(0, 2), 16);
- g = parseInt(col.substring(2, 4), 16);
- b = parseInt(col.substring(4, 6), 16);
-
- return {r : r, g : g, b : b};
- }
-
- return null;
-}
-
-function generatePicker() {
- var el = document.getElementById('light'), h = '', i;
-
- for (i = 0; i < detail; i++){
- h += '<div id="gs'+i+'" style="background-color:#000000; width:15px; height:3px; border-style:none; border-width:0px;"'
- + ' onclick="changeFinalColor(this.style.backgroundColor)"'
- + ' onmousedown="isMouseDown = true; return false;"'
- + ' onmouseup="isMouseDown = false;"'
- + ' onmousemove="if (isMouseDown && isMouseOver) changeFinalColor(this.style.backgroundColor); return false;"'
- + ' onmouseover="isMouseOver = true;"'
- + ' onmouseout="isMouseOver = false;"'
- + '></div>';
- }
-
- el.innerHTML = h;
-}
-
-function generateWebColors() {
- var el = document.getElementById('webcolors'), h = '', i;
-
- if (el.className == 'generated')
- return;
-
- // TODO: VoiceOver doesn't seem to support legend as a label referenced by labelledby.
- h += '<div role="listbox" aria-labelledby="webcolors_title" tabindex="0"><table role="presentation" border="0" cellspacing="1" cellpadding="0">'
- + '<tr>';
-
- for (i=0; i<colors.length; i++) {
- h += '<td bgcolor="' + colors[i] + '" width="10" height="10">'
- + '<a href="javascript:insertAction();" role="option" tabindex="-1" aria-labelledby="web_colors_' + i + '" onfocus="showColor(\'' + colors[i] + '\');" onmouseover="showColor(\'' + colors[i] + '\');" style="display:block;width:10px;height:10px;overflow:hidden;">';
- if (tinyMCEPopup.editor.forcedHighContrastMode) {
- h += '<canvas class="mceColorSwatch" height="10" width="10" data-color="' + colors[i] + '"></canvas>';
- }
- h += '<span class="mceVoiceLabel" style="display:none;" id="web_colors_' + i + '">' + colors[i].toUpperCase() + '</span>';
- h += '</a></td>';
- if ((i+1) % 18 == 0)
- h += '</tr><tr>';
- }
-
- h += '</table></div>';
-
- el.innerHTML = h;
- el.className = 'generated';
-
- paintCanvas(el);
- enableKeyboardNavigation(el.firstChild);
-}
-
-function paintCanvas(el) {
- tinyMCEPopup.getWin().tinymce.each(tinyMCEPopup.dom.select('canvas.mceColorSwatch', el), function(canvas) {
- var context;
- if (canvas.getContext && (context = canvas.getContext("2d"))) {
- context.fillStyle = canvas.getAttribute('data-color');
- context.fillRect(0, 0, 10, 10);
- }
- });
-}
-function generateNamedColors() {
- var el = document.getElementById('namedcolors'), h = '', n, v, i = 0;
-
- if (el.className == 'generated')
- return;
-
- for (n in named) {
- v = named[n];
- h += '<a href="javascript:insertAction();" role="option" tabindex="-1" aria-labelledby="named_colors_' + i + '" onfocus="showColor(\'' + n + '\',\'' + v + '\');" onmouseover="showColor(\'' + n + '\',\'' + v + '\');" style="background-color: ' + n + '">';
- if (tinyMCEPopup.editor.forcedHighContrastMode) {
- h += '<canvas class="mceColorSwatch" height="10" width="10" data-color="' + colors[i] + '"></canvas>';
- }
- h += '<span class="mceVoiceLabel" style="display:none;" id="named_colors_' + i + '">' + v + '</span>';
- h += '</a>';
- i++;
- }
-
- el.innerHTML = h;
- el.className = 'generated';
-
- paintCanvas(el);
- enableKeyboardNavigation(el);
-}
-
-function enableKeyboardNavigation(el) {
- tinyMCEPopup.editor.windowManager.createInstance('tinymce.ui.KeyboardNavigation', {
- root: el,
- items: tinyMCEPopup.dom.select('a', el)
- }, tinyMCEPopup.dom);
-}
-
-function dechex(n) {
- return strhex.charAt(Math.floor(n / 16)) + strhex.charAt(n % 16);
-}
-
-function computeColor(e) {
- var x, y, partWidth, partDetail, imHeight, r, g, b, coef, i, finalCoef, finalR, finalG, finalB, pos = tinyMCEPopup.dom.getPos(e.target);
-
- x = e.offsetX ? e.offsetX : (e.target ? e.clientX - pos.x : 0);
- y = e.offsetY ? e.offsetY : (e.target ? e.clientY - pos.y : 0);
-
- partWidth = document.getElementById('colors').width / 6;
- partDetail = detail / 2;
- imHeight = document.getElementById('colors').height;
-
- r = (x >= 0)*(x < partWidth)*255 + (x >= partWidth)*(x < 2*partWidth)*(2*255 - x * 255 / partWidth) + (x >= 4*partWidth)*(x < 5*partWidth)*(-4*255 + x * 255 / partWidth) + (x >= 5*partWidth)*(x < 6*partWidth)*255;
- g = (x >= 0)*(x < partWidth)*(x * 255 / partWidth) + (x >= partWidth)*(x < 3*partWidth)*255 + (x >= 3*partWidth)*(x < 4*partWidth)*(4*255 - x * 255 / partWidth);
- b = (x >= 2*partWidth)*(x < 3*partWidth)*(-2*255 + x * 255 / partWidth) + (x >= 3*partWidth)*(x < 5*partWidth)*255 + (x >= 5*partWidth)*(x < 6*partWidth)*(6*255 - x * 255 / partWidth);
-
- coef = (imHeight - y) / imHeight;
- r = 128 + (r - 128) * coef;
- g = 128 + (g - 128) * coef;
- b = 128 + (b - 128) * coef;
-
- changeFinalColor('#' + dechex(r) + dechex(g) + dechex(b));
- updateLight(r, g, b);
-}
-
-function updateLight(r, g, b) {
- var i, partDetail = detail / 2, finalCoef, finalR, finalG, finalB, color;
-
- for (i=0; i<detail; i++) {
- if ((i>=0) && (i<partDetail)) {
- finalCoef = i / partDetail;
- finalR = dechex(255 - (255 - r) * finalCoef);
- finalG = dechex(255 - (255 - g) * finalCoef);
- finalB = dechex(255 - (255 - b) * finalCoef);
- } else {
- finalCoef = 2 - i / partDetail;
- finalR = dechex(r * finalCoef);
- finalG = dechex(g * finalCoef);
- finalB = dechex(b * finalCoef);
- }
-
- color = finalR + finalG + finalB;
-
- setCol('gs' + i, '#'+color);
- }
-}
-
-function changeFinalColor(color) {
- if (color.indexOf('#') == -1)
- color = convertRGBToHex(color);
-
- setCol('preview', color);
- document.getElementById('color').value = color;
-}
-
-function setCol(e, c) {
- try {
- document.getElementById(e).style.backgroundColor = c;
- } catch (ex) {
- // Ignore IE warning
- }
-}
-
-tinyMCEPopup.onInit.add(init);
+tinyMCEPopup.requireLangPack();
+
+var detail = 50, strhex = "0123456789abcdef", i, isMouseDown = false, isMouseOver = false;
+
+var colors = [
+ "#000000","#000033","#000066","#000099","#0000cc","#0000ff","#330000","#330033",
+ "#330066","#330099","#3300cc","#3300ff","#660000","#660033","#660066","#660099",
+ "#6600cc","#6600ff","#990000","#990033","#990066","#990099","#9900cc","#9900ff",
+ "#cc0000","#cc0033","#cc0066","#cc0099","#cc00cc","#cc00ff","#ff0000","#ff0033",
+ "#ff0066","#ff0099","#ff00cc","#ff00ff","#003300","#003333","#003366","#003399",
+ "#0033cc","#0033ff","#333300","#333333","#333366","#333399","#3333cc","#3333ff",
+ "#663300","#663333","#663366","#663399","#6633cc","#6633ff","#993300","#993333",
+ "#993366","#993399","#9933cc","#9933ff","#cc3300","#cc3333","#cc3366","#cc3399",
+ "#cc33cc","#cc33ff","#ff3300","#ff3333","#ff3366","#ff3399","#ff33cc","#ff33ff",
+ "#006600","#006633","#006666","#006699","#0066cc","#0066ff","#336600","#336633",
+ "#336666","#336699","#3366cc","#3366ff","#666600","#666633","#666666","#666699",
+ "#6666cc","#6666ff","#996600","#996633","#996666","#996699","#9966cc","#9966ff",
+ "#cc6600","#cc6633","#cc6666","#cc6699","#cc66cc","#cc66ff","#ff6600","#ff6633",
+ "#ff6666","#ff6699","#ff66cc","#ff66ff","#009900","#009933","#009966","#009999",
+ "#0099cc","#0099ff","#339900","#339933","#339966","#339999","#3399cc","#3399ff",
+ "#669900","#669933","#669966","#669999","#6699cc","#6699ff","#999900","#999933",
+ "#999966","#999999","#9999cc","#9999ff","#cc9900","#cc9933","#cc9966","#cc9999",
+ "#cc99cc","#cc99ff","#ff9900","#ff9933","#ff9966","#ff9999","#ff99cc","#ff99ff",
+ "#00cc00","#00cc33","#00cc66","#00cc99","#00cccc","#00ccff","#33cc00","#33cc33",
+ "#33cc66","#33cc99","#33cccc","#33ccff","#66cc00","#66cc33","#66cc66","#66cc99",
+ "#66cccc","#66ccff","#99cc00","#99cc33","#99cc66","#99cc99","#99cccc","#99ccff",
+ "#cccc00","#cccc33","#cccc66","#cccc99","#cccccc","#ccccff","#ffcc00","#ffcc33",
+ "#ffcc66","#ffcc99","#ffcccc","#ffccff","#00ff00","#00ff33","#00ff66","#00ff99",
+ "#00ffcc","#00ffff","#33ff00","#33ff33","#33ff66","#33ff99","#33ffcc","#33ffff",
+ "#66ff00","#66ff33","#66ff66","#66ff99","#66ffcc","#66ffff","#99ff00","#99ff33",
+ "#99ff66","#99ff99","#99ffcc","#99ffff","#ccff00","#ccff33","#ccff66","#ccff99",
+ "#ccffcc","#ccffff","#ffff00","#ffff33","#ffff66","#ffff99","#ffffcc","#ffffff"
+];
+
+var named = {
+ '#F0F8FF':'Alice Blue','#FAEBD7':'Antique White','#00FFFF':'Aqua','#7FFFD4':'Aquamarine','#F0FFFF':'Azure','#F5F5DC':'Beige',
+ '#FFE4C4':'Bisque','#000000':'Black','#FFEBCD':'Blanched Almond','#0000FF':'Blue','#8A2BE2':'Blue Violet','#A52A2A':'Brown',
+ '#DEB887':'Burly Wood','#5F9EA0':'Cadet Blue','#7FFF00':'Chartreuse','#D2691E':'Chocolate','#FF7F50':'Coral','#6495ED':'Cornflower Blue',
+ '#FFF8DC':'Cornsilk','#DC143C':'Crimson','#00FFFF':'Cyan','#00008B':'Dark Blue','#008B8B':'Dark Cyan','#B8860B':'Dark Golden Rod',
+ '#A9A9A9':'Dark Gray','#A9A9A9':'Dark Grey','#006400':'Dark Green','#BDB76B':'Dark Khaki','#8B008B':'Dark Magenta','#556B2F':'Dark Olive Green',
+ '#FF8C00':'Darkorange','#9932CC':'Dark Orchid','#8B0000':'Dark Red','#E9967A':'Dark Salmon','#8FBC8F':'Dark Sea Green','#483D8B':'Dark Slate Blue',
+ '#2F4F4F':'Dark Slate Gray','#2F4F4F':'Dark Slate Grey','#00CED1':'Dark Turquoise','#9400D3':'Dark Violet','#FF1493':'Deep Pink','#00BFFF':'Deep Sky Blue',
+ '#696969':'Dim Gray','#696969':'Dim Grey','#1E90FF':'Dodger Blue','#B22222':'Fire Brick','#FFFAF0':'Floral White','#228B22':'Forest Green',
+ '#FF00FF':'Fuchsia','#DCDCDC':'Gainsboro','#F8F8FF':'Ghost White','#FFD700':'Gold','#DAA520':'Golden Rod','#808080':'Gray','#808080':'Grey',
+ '#008000':'Green','#ADFF2F':'Green Yellow','#F0FFF0':'Honey Dew','#FF69B4':'Hot Pink','#CD5C5C':'Indian Red','#4B0082':'Indigo','#FFFFF0':'Ivory',
+ '#F0E68C':'Khaki','#E6E6FA':'Lavender','#FFF0F5':'Lavender Blush','#7CFC00':'Lawn Green','#FFFACD':'Lemon Chiffon','#ADD8E6':'Light Blue',
+ '#F08080':'Light Coral','#E0FFFF':'Light Cyan','#FAFAD2':'Light Golden Rod Yellow','#D3D3D3':'Light Gray','#D3D3D3':'Light Grey','#90EE90':'Light Green',
+ '#FFB6C1':'Light Pink','#FFA07A':'Light Salmon','#20B2AA':'Light Sea Green','#87CEFA':'Light Sky Blue','#778899':'Light Slate Gray','#778899':'Light Slate Grey',
+ '#B0C4DE':'Light Steel Blue','#FFFFE0':'Light Yellow','#00FF00':'Lime','#32CD32':'Lime Green','#FAF0E6':'Linen','#FF00FF':'Magenta','#800000':'Maroon',
+ '#66CDAA':'Medium Aqua Marine','#0000CD':'Medium Blue','#BA55D3':'Medium Orchid','#9370D8':'Medium Purple','#3CB371':'Medium Sea Green','#7B68EE':'Medium Slate Blue',
+ '#00FA9A':'Medium Spring Green','#48D1CC':'Medium Turquoise','#C71585':'Medium Violet Red','#191970':'Midnight Blue','#F5FFFA':'Mint Cream','#FFE4E1':'Misty Rose','#FFE4B5':'Moccasin',
+ '#FFDEAD':'Navajo White','#000080':'Navy','#FDF5E6':'Old Lace','#808000':'Olive','#6B8E23':'Olive Drab','#FFA500':'Orange','#FF4500':'Orange Red','#DA70D6':'Orchid',
+ '#EEE8AA':'Pale Golden Rod','#98FB98':'Pale Green','#AFEEEE':'Pale Turquoise','#D87093':'Pale Violet Red','#FFEFD5':'Papaya Whip','#FFDAB9':'Peach Puff',
+ '#CD853F':'Peru','#FFC0CB':'Pink','#DDA0DD':'Plum','#B0E0E6':'Powder Blue','#800080':'Purple','#FF0000':'Red','#BC8F8F':'Rosy Brown','#4169E1':'Royal Blue',
+ '#8B4513':'Saddle Brown','#FA8072':'Salmon','#F4A460':'Sandy Brown','#2E8B57':'Sea Green','#FFF5EE':'Sea Shell','#A0522D':'Sienna','#C0C0C0':'Silver',
+ '#87CEEB':'Sky Blue','#6A5ACD':'Slate Blue','#708090':'Slate Gray','#708090':'Slate Grey','#FFFAFA':'Snow','#00FF7F':'Spring Green',
+ '#4682B4':'Steel Blue','#D2B48C':'Tan','#008080':'Teal','#D8BFD8':'Thistle','#FF6347':'Tomato','#40E0D0':'Turquoise','#EE82EE':'Violet',
+ '#F5DEB3':'Wheat','#FFFFFF':'White','#F5F5F5':'White Smoke','#FFFF00':'Yellow','#9ACD32':'Yellow Green'
+};
+
+var namedLookup = {};
+
+function init() {
+ var inputColor = convertRGBToHex(tinyMCEPopup.getWindowArg('input_color')), key, value;
+
+ tinyMCEPopup.resizeToInnerSize();
+
+ generatePicker();
+ generateWebColors();
+ generateNamedColors();
+
+ if (inputColor) {
+ changeFinalColor(inputColor);
+
+ col = convertHexToRGB(inputColor);
+
+ if (col)
+ updateLight(col.r, col.g, col.b);
+ }
+
+ for (key in named) {
+ value = named[key];
+ namedLookup[value.replace(/\s+/, '').toLowerCase()] = key.replace(/#/, '').toLowerCase();
+ }
+}
+
+function toHexColor(color) {
+ var matches, red, green, blue, toInt = parseInt;
+
+ function hex(value) {
+ value = parseInt(value).toString(16);
+
+ return value.length > 1 ? value : '0' + value; // Padd with leading zero
+ };
+
+ color = tinymce.trim(color);
+ color = color.replace(/^[#]/, '').toLowerCase(); // remove leading '#'
+ color = namedLookup[color] || color;
+
+ matches = /^rgb\((\d{1,3}),(\d{1,3}),(\d{1,3})\)$/.exec(color);
+
+ if (matches) {
+ red = toInt(matches[1]);
+ green = toInt(matches[2]);
+ blue = toInt(matches[3]);
+ } else {
+ matches = /^([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/.exec(color);
+
+ if (matches) {
+ red = toInt(matches[1], 16);
+ green = toInt(matches[2], 16);
+ blue = toInt(matches[3], 16);
+ } else {
+ matches = /^([0-9a-f])([0-9a-f])([0-9a-f])$/.exec(color);
+
+ if (matches) {
+ red = toInt(matches[1] + matches[1], 16);
+ green = toInt(matches[2] + matches[2], 16);
+ blue = toInt(matches[3] + matches[3], 16);
+ } else {
+ return '';
+ }
+ }
+ }
+
+ return '#' + hex(red) + hex(green) + hex(blue);
+}
+
+function insertAction() {
+ var color = document.getElementById("color").value, f = tinyMCEPopup.getWindowArg('func');
+
+ var hexColor = toHexColor(color);
+
+ if (hexColor === '') {
+ var text = tinyMCEPopup.editor.getLang('advanced_dlg.invalid_color_value');
+ tinyMCEPopup.alert(text + ': ' + color);
+ }
+ else {
+ tinyMCEPopup.restoreSelection();
+
+ if (f)
+ f(hexColor);
+
+ tinyMCEPopup.close();
+ }
+}
+
+function showColor(color, name) {
+ if (name)
+ document.getElementById("colorname").innerHTML = name;
+
+ document.getElementById("preview").style.backgroundColor = color;
+ document.getElementById("color").value = color.toUpperCase();
+}
+
+function convertRGBToHex(col) {
+ var re = new RegExp("rgb\\s*\\(\\s*([0-9]+).*,\\s*([0-9]+).*,\\s*([0-9]+).*\\)", "gi");
+
+ if (!col)
+ return col;
+
+ var rgb = col.replace(re, "$1,$2,$3").split(',');
+ if (rgb.length == 3) {
+ r = parseInt(rgb[0]).toString(16);
+ g = parseInt(rgb[1]).toString(16);
+ b = parseInt(rgb[2]).toString(16);
+
+ r = r.length == 1 ? '0' + r : r;
+ g = g.length == 1 ? '0' + g : g;
+ b = b.length == 1 ? '0' + b : b;
+
+ return "#" + r + g + b;
+ }
+
+ return col;
+}
+
+function convertHexToRGB(col) {
+ if (col.indexOf('#') != -1) {
+ col = col.replace(new RegExp('[^0-9A-F]', 'gi'), '');
+
+ r = parseInt(col.substring(0, 2), 16);
+ g = parseInt(col.substring(2, 4), 16);
+ b = parseInt(col.substring(4, 6), 16);
+
+ return {r : r, g : g, b : b};
+ }
+
+ return null;
+}
+
+function generatePicker() {
+ var el = document.getElementById('light'), h = '', i;
+
+ for (i = 0; i < detail; i++){
+ h += '<div id="gs'+i+'" style="background-color:#000000; width:15px; height:3px; border-style:none; border-width:0px;"'
+ + ' onclick="changeFinalColor(this.style.backgroundColor)"'
+ + ' onmousedown="isMouseDown = true; return false;"'
+ + ' onmouseup="isMouseDown = false;"'
+ + ' onmousemove="if (isMouseDown && isMouseOver) changeFinalColor(this.style.backgroundColor); return false;"'
+ + ' onmouseover="isMouseOver = true;"'
+ + ' onmouseout="isMouseOver = false;"'
+ + '></div>';
+ }
+
+ el.innerHTML = h;
+}
+
+function generateWebColors() {
+ var el = document.getElementById('webcolors'), h = '', i;
+
+ if (el.className == 'generated')
+ return;
+
+ // TODO: VoiceOver doesn't seem to support legend as a label referenced by labelledby.
+ h += '<div role="listbox" aria-labelledby="webcolors_title" tabindex="0"><table role="presentation" border="0" cellspacing="1" cellpadding="0">'
+ + '<tr>';
+
+ for (i=0; i<colors.length; i++) {
+ h += '<td bgcolor="' + colors[i] + '" width="10" height="10">'
+ + '<a href="javascript:insertAction();" role="option" tabindex="-1" aria-labelledby="web_colors_' + i + '" onfocus="showColor(\'' + colors[i] + '\');" onmouseover="showColor(\'' + colors[i] + '\');" style="display:block;width:10px;height:10px;overflow:hidden;">';
+ if (tinyMCEPopup.editor.forcedHighContrastMode) {
+ h += '<canvas class="mceColorSwatch" height="10" width="10" data-color="' + colors[i] + '"></canvas>';
+ }
+ h += '<span class="mceVoiceLabel" style="display:none;" id="web_colors_' + i + '">' + colors[i].toUpperCase() + '</span>';
+ h += '</a></td>';
+ if ((i+1) % 18 == 0)
+ h += '</tr><tr>';
+ }
+
+ h += '</table></div>';
+
+ el.innerHTML = h;
+ el.className = 'generated';
+
+ paintCanvas(el);
+ enableKeyboardNavigation(el.firstChild);
+}
+
+function paintCanvas(el) {
+ tinyMCEPopup.getWin().tinymce.each(tinyMCEPopup.dom.select('canvas.mceColorSwatch', el), function(canvas) {
+ var context;
+ if (canvas.getContext && (context = canvas.getContext("2d"))) {
+ context.fillStyle = canvas.getAttribute('data-color');
+ context.fillRect(0, 0, 10, 10);
+ }
+ });
+}
+function generateNamedColors() {
+ var el = document.getElementById('namedcolors'), h = '', n, v, i = 0;
+
+ if (el.className == 'generated')
+ return;
+
+ for (n in named) {
+ v = named[n];
+ h += '<a href="javascript:insertAction();" role="option" tabindex="-1" aria-labelledby="named_colors_' + i + '" onfocus="showColor(\'' + n + '\',\'' + v + '\');" onmouseover="showColor(\'' + n + '\',\'' + v + '\');" style="background-color: ' + n + '">';
+ if (tinyMCEPopup.editor.forcedHighContrastMode) {
+ h += '<canvas class="mceColorSwatch" height="10" width="10" data-color="' + colors[i] + '"></canvas>';
+ }
+ h += '<span class="mceVoiceLabel" style="display:none;" id="named_colors_' + i + '">' + v + '</span>';
+ h += '</a>';
+ i++;
+ }
+
+ el.innerHTML = h;
+ el.className = 'generated';
+
+ paintCanvas(el);
+ enableKeyboardNavigation(el);
+}
+
+function enableKeyboardNavigation(el) {
+ tinyMCEPopup.editor.windowManager.createInstance('tinymce.ui.KeyboardNavigation', {
+ root: el,
+ items: tinyMCEPopup.dom.select('a', el)
+ }, tinyMCEPopup.dom);
+}
+
+function dechex(n) {
+ return strhex.charAt(Math.floor(n / 16)) + strhex.charAt(n % 16);
+}
+
+function computeColor(e) {
+ var x, y, partWidth, partDetail, imHeight, r, g, b, coef, i, finalCoef, finalR, finalG, finalB, pos = tinyMCEPopup.dom.getPos(e.target);
+
+ x = e.offsetX ? e.offsetX : (e.target ? e.clientX - pos.x : 0);
+ y = e.offsetY ? e.offsetY : (e.target ? e.clientY - pos.y : 0);
+
+ partWidth = document.getElementById('colors').width / 6;
+ partDetail = detail / 2;
+ imHeight = document.getElementById('colors').height;
+
+ r = (x >= 0)*(x < partWidth)*255 + (x >= partWidth)*(x < 2*partWidth)*(2*255 - x * 255 / partWidth) + (x >= 4*partWidth)*(x < 5*partWidth)*(-4*255 + x * 255 / partWidth) + (x >= 5*partWidth)*(x < 6*partWidth)*255;
+ g = (x >= 0)*(x < partWidth)*(x * 255 / partWidth) + (x >= partWidth)*(x < 3*partWidth)*255 + (x >= 3*partWidth)*(x < 4*partWidth)*(4*255 - x * 255 / partWidth);
+ b = (x >= 2*partWidth)*(x < 3*partWidth)*(-2*255 + x * 255 / partWidth) + (x >= 3*partWidth)*(x < 5*partWidth)*255 + (x >= 5*partWidth)*(x < 6*partWidth)*(6*255 - x * 255 / partWidth);
+
+ coef = (imHeight - y) / imHeight;
+ r = 128 + (r - 128) * coef;
+ g = 128 + (g - 128) * coef;
+ b = 128 + (b - 128) * coef;
+
+ changeFinalColor('#' + dechex(r) + dechex(g) + dechex(b));
+ updateLight(r, g, b);
+}
+
+function updateLight(r, g, b) {
+ var i, partDetail = detail / 2, finalCoef, finalR, finalG, finalB, color;
+
+ for (i=0; i<detail; i++) {
+ if ((i>=0) && (i<partDetail)) {
+ finalCoef = i / partDetail;
+ finalR = dechex(255 - (255 - r) * finalCoef);
+ finalG = dechex(255 - (255 - g) * finalCoef);
+ finalB = dechex(255 - (255 - b) * finalCoef);
+ } else {
+ finalCoef = 2 - i / partDetail;
+ finalR = dechex(r * finalCoef);
+ finalG = dechex(g * finalCoef);
+ finalB = dechex(b * finalCoef);
+ }
+
+ color = finalR + finalG + finalB;
+
+ setCol('gs' + i, '#'+color);
+ }
+}
+
+function changeFinalColor(color) {
+ if (color.indexOf('#') == -1)
+ color = convertRGBToHex(color);
+
+ setCol('preview', color);
+ document.getElementById('color').value = color;
+}
+
+function setCol(e, c) {
+ try {
+ document.getElementById(e).style.backgroundColor = c;
+ } catch (ex) {
+ // Ignore IE warning
+ }
+}
+
+tinyMCEPopup.onInit.add(init);
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/image.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/image.js
index 6c2489a16..bb09e75bf 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/image.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/image.js
@@ -104,10 +104,12 @@ var ImageDialog = {
},
updateStyle : function() {
- var dom = tinyMCEPopup.dom, st, v, f = document.forms[0];
+ var dom = tinyMCEPopup.dom, st = {}, v, f = document.forms[0];
if (tinyMCEPopup.editor.settings.inline_styles) {
- st = tinyMCEPopup.dom.parseStyle(this.styleVal);
+ tinymce.each(tinyMCEPopup.dom.parseStyle(this.styleVal), function(value, key) {
+ st[key] = value;
+ });
// Handle align
v = getSelectValue(f, 'align');
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/link.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/link.js
index 53ff409e7..8c1d73c50 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/link.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/link.js
@@ -68,10 +68,16 @@ var LinkDialog = {
} else {
ed.dom.setAttribs(e, {
href : href,
- title : f.linktitle.value,
- target : f.target_list ? getSelectValue(f, "target_list") : null,
- 'class' : f.class_list ? getSelectValue(f, "class_list") : null
+ title : f.linktitle.value
});
+
+ if (f.target_list) {
+ ed.dom.setAttrib(e, 'target', getSelectValue(f, "target_list"));
+ }
+
+ if (f.class_list) {
+ ed.dom.setAttrib(e, 'class', getSelectValue(f, "class_list"));
+ }
}
// Don't move caret if selection was image
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/source_editor.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/source_editor.js
index 84546ad52..dd5e366fa 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/source_editor.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/js/source_editor.js
@@ -16,7 +16,7 @@ function onLoadInit() {
document.getElementById('htmlSource').value = tinyMCEPopup.editor.getContent({source_view : true});
if (tinyMCEPopup.editor.getParam("theme_advanced_source_editor_wrap", true)) {
- setWrap('soft');
+ turnWrapOn();
document.getElementById('wraped').checked = true;
}
@@ -37,11 +37,33 @@ function setWrap(val) {
}
}
-function toggleWordWrap(elm) {
- if (elm.checked)
- setWrap('soft');
- else
+function setWhiteSpaceCss(value) {
+ var el = document.getElementById('htmlSource');
+ tinymce.DOM.setStyle(el, 'white-space', value);
+}
+
+function turnWrapOff() {
+ if (tinymce.isWebKit) {
+ setWhiteSpaceCss('pre');
+ } else {
setWrap('off');
+ }
+}
+
+function turnWrapOn() {
+ if (tinymce.isWebKit) {
+ setWhiteSpaceCss('pre-wrap');
+ } else {
+ setWrap('soft');
+ }
+}
+
+function toggleWordWrap(elm) {
+ if (elm.checked) {
+ turnWrapOn();
+ } else {
+ turnWrapOff();
+ }
}
function resizeInputs() {
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/langs/en_dlg.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/langs/en_dlg.js
index e451f3774..50cd87e3d 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/langs/en_dlg.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/langs/en_dlg.js
@@ -1 +1 @@
-tinyMCE.addI18n('en.advanced_dlg',{"link_list":"Link List","link_is_external":"The URL you entered seems to be an external link. Do you want to add the required http:// prefix?","link_is_email":"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?","link_titlefield":"Title","link_target_blank":"Open Link in a New Window","link_target_same":"Open Link in the Same Window","link_target":"Target","link_url":"Link URL","link_title":"Insert/Edit Link","image_align_right":"Right","image_align_left":"Left","image_align_textbottom":"Text Bottom","image_align_texttop":"Text Top","image_align_bottom":"Bottom","image_align_middle":"Middle","image_align_top":"Top","image_align_baseline":"Baseline","image_align":"Alignment","image_hspace":"Horizontal Space","image_vspace":"Vertical Space","image_dimensions":"Dimensions","image_alt":"Image Description","image_list":"Image List","image_border":"Border","image_src":"Image URL","image_title":"Insert/Edit Image","charmap_title":"Select Special Character","colorpicker_name":"Name:","colorpicker_color":"Color:","colorpicker_named_title":"Named Colors","colorpicker_named_tab":"Named","colorpicker_palette_title":"Palette Colors","colorpicker_palette_tab":"Palette","colorpicker_picker_title":"Color Picker","colorpicker_picker_tab":"Picker","colorpicker_title":"Select a Color","code_wordwrap":"Word Wrap","code_title":"HTML Source Editor","anchor_name":"Anchor Name","anchor_title":"Insert/Edit Anchor","about_loaded":"Loaded Plugins","about_version":"Version","about_author":"Author","about_plugin":"Plugin","about_plugins":"Plugins","about_license":"License","about_help":"Help","about_general":"About","about_title":"About TinyMCE","charmap_usage":"Use left and right arrows to navigate.","anchor_invalid":"Please specify a valid anchor name.","accessibility_help":"Accessibility Help","accessibility_usage_title":"General Usage","invalid_color_value":"Invalid color value"}); \ No newline at end of file
+tinyMCE.addI18n('en.advanced_dlg', {"link_list":"Link List","link_is_external":"The URL you entered seems to be an external link. Do you want to add the required http:// prefix?","link_is_email":"The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?","link_titlefield":"Title","link_target_blank":"Open Link in a New Window","link_target_same":"Open Link in the Same Window","link_target":"Target","link_url":"Link URL","link_title":"Insert/Edit Link","image_align_right":"Right","image_align_left":"Left","image_align_textbottom":"Text Bottom","image_align_texttop":"Text Top","image_align_bottom":"Bottom","image_align_middle":"Middle","image_align_top":"Top","image_align_baseline":"Baseline","image_align":"Alignment","image_hspace":"Horizontal Space","image_vspace":"Vertical Space","image_dimensions":"Dimensions","image_alt":"Image Description","image_list":"Image List","image_border":"Border","image_src":"Image URL","image_title":"Insert/Edit Image","charmap_title":"Select Special Character", "charmap_usage":"Use left and right arrows to navigate.","colorpicker_name":"Name:","colorpicker_color":"Color:","colorpicker_named_title":"Named Colors","colorpicker_named_tab":"Named","colorpicker_palette_title":"Palette Colors","colorpicker_palette_tab":"Palette","colorpicker_picker_title":"Color Picker","colorpicker_picker_tab":"Picker","colorpicker_title":"Select a Color","code_wordwrap":"Word Wrap","code_title":"HTML Source Editor","anchor_name":"Anchor Name","anchor_title":"Insert/Edit Anchor","about_loaded":"Loaded Plugins","about_version":"Version","about_author":"Author","about_plugin":"Plugin","about_plugins":"Plugins","about_license":"License","about_help":"Help","about_general":"About","about_title":"About TinyMCE","anchor_invalid":"Please specify a valid anchor name.","accessibility_help":"Accessibility Help","accessibility_usage_title":"General Usage","invalid_color_value":"Invalid color value","":""});
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css
index f01222650..879786fc1 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/dialog.css
@@ -105,11 +105,12 @@ h3 {font-size:14px;}
#plugintable, #about #plugintable td {border:1px solid #919B9C;}
#plugintable {width:96%; margin-top:10px;}
#pluginscontainer {height:290px; overflow:auto;}
-#colorpicker #preview {float:right; width:50px; height:14px;line-height:1px; border:1px solid black; margin-left:5px;}
+#colorpicker #preview {display:inline-block; padding-left:40px; height:14px; border:1px solid black; margin-left:5px; margin-right: 5px}
+#colorpicker #previewblock {position: relative; top: -3px; padding-left:5px; padding-top: 0px; display:inline}
+#colorpicker #preview_wrapper { text-align:center; padding-top:4px; white-space: nowrap}
#colorpicker #colors {float:left; border:1px solid gray; cursor:crosshair;}
#colorpicker #light {border:1px solid gray; margin-left:5px; float:left;width:15px; height:150px; cursor:crosshair;}
#colorpicker #light div {overflow:hidden;}
-#colorpicker #previewblock {float:right; padding-left:10px; height:20px;}
#colorpicker .panel_wrapper div.current {height:175px;}
#colorpicker #namedcolors {width:150px;}
#colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/ui.css b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/ui.css
index 2b7c2a59a..77083f311 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/ui.css
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/default/ui.css
@@ -83,7 +83,7 @@
.defaultSkin .mce_forecolor span.mceAction, .defaultSkin .mce_backcolor span.mceAction {overflow:hidden; height:16px}
/* Menu */
-.defaultSkin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid #D4D0C8}
+.defaultSkin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid #D4D0C8; direction:ltr}
.defaultSkin .mceNoIcons span.mceIcon {width:0;}
.defaultSkin .mceNoIcons a .mceText {padding-left:10px}
.defaultSkin .mceMenu table {background:#FFF}
@@ -109,6 +109,10 @@
.defaultSkin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; -ms-filter:'alpha(opacity=50)'; filter:alpha(opacity=50); background:#FFF}
.defaultSkin .mceProgress {position:absolute; left:0; top:0; z-index:1001; background:url(img/progress.gif) no-repeat; width:32px; height:32px; margin:-16px 0 0 -16px}
+/* Rtl */
+.mceRtl .mceListBox .mceText {text-align: right; padding: 0 4px 0 0}
+.mceRtl .mceMenuItem .mceText {text-align: right}
+
/* Formats */
.defaultSkin .mce_formatPreview a {font-size:10px}
.defaultSkin .mce_p span.mceText {}
@@ -212,3 +216,4 @@
.defaultSkin span.mce_pagebreak {background-position:0 -40px}
.defaultSkin span.mce_restoredraft {background-position:-20px -40px}
.defaultSkin span.mce_spellchecker {background-position:-540px -20px}
+.defaultSkin span.mce_visualblocks {background-position: -40px -40px}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/dialog.css b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/dialog.css
index b2ed097cd..6d9fc8dd6 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/dialog.css
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/dialog.css
@@ -1,7 +1,7 @@
/* Generic */
body {
font-family:Verdana, Arial, Helvetica, sans-serif; font-size:11px;
-background:#F0F0EE;
+background:#F0F0EE;
color: black;
padding:0;
margin:8px 8px 0 8px;
@@ -94,11 +94,12 @@ h3 {font-size:14px;}
#plugintable, #about #plugintable td {border:1px solid #919B9C;}
#plugintable {width:96%; margin-top:10px;}
#pluginscontainer {height:290px; overflow:auto;}
-#colorpicker #preview {float:right; width:50px; height:14px;line-height:1px; border:1px solid black; margin-left:5px;}
+#colorpicker #preview {display:inline-block; padding-left:40px; height:14px; border:1px solid black; margin-left:5px; margin-right: 5px}
+#colorpicker #previewblock {position: relative; top: -3px; padding-left:5px; padding-top: 0px; display:inline}
+#colorpicker #preview_wrapper { text-align:center; padding-top:4px; white-space: nowrap}
#colorpicker #colors {float:left; border:1px solid gray; cursor:crosshair;}
#colorpicker #light {border:1px solid gray; margin-left:5px; float:left;width:15px; height:150px; cursor:crosshair;}
#colorpicker #light div {overflow:hidden;}
-#colorpicker #previewblock {float:right; padding-left:10px; height:20px;}
#colorpicker .panel_wrapper div.current {height:175px;}
#colorpicker #namedcolors {width:150px;}
#colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/ui.css b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/ui.css
index a2cfcc393..effbbe158 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/ui.css
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/highcontrast/ui.css
@@ -58,7 +58,7 @@
/* Menu */
.highcontrastSkin .mceNoIcons span.mceIcon {width:0;}
-.highcontrastSkin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid; }
+.highcontrastSkin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid; direction:ltr}
.highcontrastSkin .mceMenu table {background:white; color: black}
.highcontrastSkin .mceNoIcons a .mceText {padding-left:10px}
.highcontrastSkin .mceMenu a, .highcontrastSkin .mceMenu span, .highcontrastSkin .mceMenu {display:block;background:white; color: black}
@@ -90,6 +90,10 @@
.highcontrastSkin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=50); background:#FFF}
.highcontrastSkin .mceProgress {position:absolute; left:0; top:0; z-index:1001; background:url(../default/img/progress.gif) no-repeat; width:32px; height:32px; margin:-16px 0 0 -16px}
+/* Rtl */
+.mceRtl .mceListBox .mceText {text-align: right; padding: 0 4px 0 0}
+.mceRtl .mceMenuItem .mceText {text-align: right}
+
/* Formats */
.highcontrastSkin .mce_p span.mceText {}
.highcontrastSkin .mce_address span.mceText {font-style:italic}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css
index ec0877224..a54db98df 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/dialog.css
@@ -105,11 +105,12 @@ h3 {font-size:14px;}
#plugintable, #about #plugintable td {border:1px solid #919B9C;}
#plugintable {width:96%; margin-top:10px;}
#pluginscontainer {height:290px; overflow:auto;}
-#colorpicker #preview {float:right; width:50px; height:14px;line-height:1px; border:1px solid black; margin-left:5px;}
+#colorpicker #preview {display:inline-block; padding-left:40px; height:14px; border:1px solid black; margin-left:5px; margin-right: 5px}
+#colorpicker #previewblock {position: relative; top: -3px; padding-left:5px; padding-top: 0px; display:inline}
+#colorpicker #preview_wrapper { text-align:center; padding-top:4px; white-space: nowrap}
#colorpicker #colors {float:left; border:1px solid gray; cursor:crosshair;}
#colorpicker #light {border:1px solid gray; margin-left:5px; float:left;width:15px; height:150px; cursor:crosshair;}
#colorpicker #light div {overflow:hidden;}
-#colorpicker #previewblock {float:right; padding-left:10px; height:20px;}
#colorpicker .panel_wrapper div.current {height:175px;}
#colorpicker #namedcolors {width:150px;}
#colorpicker #namedcolors a {display:block; float:left; width:10px; height:10px; margin:1px 1px 0 0; overflow:hidden;}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css
index 0916c34e8..a31022371 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/skins/o2k7/ui.css
@@ -86,7 +86,7 @@
.o2k7Skin .mce_forecolor span.mceAction, .o2k7Skin .mce_backcolor span.mceAction {height:15px;overflow:hidden}
/* Menu */
-.o2k7Skin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid #ABC6DD}
+.o2k7Skin .mceMenu {position:absolute; left:0; top:0; z-index:1000; border:1px solid #ABC6DD; direction:ltr}
.o2k7Skin .mceNoIcons span.mceIcon {width:0;}
.o2k7Skin .mceNoIcons a .mceText {padding-left:10px}
.o2k7Skin .mceMenu table {background:#FFF}
@@ -112,6 +112,10 @@
.o2k7Skin .mceBlocker {position:absolute; left:0; top:0; z-index:1000; opacity:0.5; -ms-filter:'alpha(opacity=30)'; filter:alpha(opacity=50); background:#FFF}
.o2k7Skin .mceProgress {position:absolute; left:0; top:0; z-index:1001; background:url(../default/img/progress.gif) no-repeat; width:32px; height:32px; margin:-16px 0 0 -16px}
+/* Rtl */
+.mceRtl .mceListBox .mceText {text-align: right; padding: 0 4px 0 0}
+.mceRtl .mceMenuItem .mceText {text-align: right}
+
/* Formats */
.o2k7Skin .mce_formatPreview a {font-size:10px}
.o2k7Skin .mce_p span.mceText {}
@@ -215,3 +219,4 @@
.o2k7Skin span.mce_pagebreak {background-position:0 -40px}
.o2k7Skin span.mce_restoredraft {background-position:-20px -40px}
.o2k7Skin span.mce_spellchecker {background-position:-540px -20px}
+.o2k7Skin span.mce_visualblocks {background-position: -40px -40px}
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/source_editor.htm b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/source_editor.htm
index 3c6d65808..dd973fcc0 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/source_editor.htm
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/themes/advanced/source_editor.htm
@@ -4,7 +4,7 @@
<script type="text/javascript" src="../../tiny_mce_popup.js"></script>
<script type="text/javascript" src="js/source_editor.js"></script>
</head>
-<body onresize="resizeInputs();" style="display:none; overflow:hidden;">
+<body onresize="resizeInputs();" style="display:none; overflow:hidden;" spellcheck="false">
<form name="source" onsubmit="saveContent();return false;" action="#">
<div style="float: left" class="title"><label for="htmlSource">{#advanced_dlg.code_title}</label></div>
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce.js
index ad3745f86..44d9fd902 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce.js
@@ -1 +1 @@
-(function(d){var a=/^\s*|\s*$/g,e,c="B".replace(/A(.)|B/,"$1")==="$1";var b={majorVersion:"3",minorVersion:"4.7",releaseDate:"2011-11-03",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isOpera=d.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName);s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(d.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m<f.length;m++){if(r=f[m].href){if(/^https?:\/\/[^\/]+$/.test(r)){r+="/"}k=r?r.match(/.*\//)[0]:""}}function h(i){if(i.src&&/tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(i.src)){if(/_(src|dev)\.js/g.test(i.src)){s.suffix="_src"}if((j=i.src.indexOf("?"))!=-1){s.query=i.src.substring(j+1)}s.baseURL=i.src.substring(0,i.src.lastIndexOf("/"));if(k&&s.baseURL.indexOf("://")==-1&&s.baseURL.indexOf("/")!==0){s.baseURL=k+s.baseURL}return s.baseURL}return null}f=q.getElementsByTagName("script");for(m=0;m<f.length;m++){if(h(f[m])){return}}l=q.getElementsByTagName("head")[0];if(l){f=l.getElementsByTagName("script");for(m=0;m<f.length;m++){if(h(f[m])){return}}}return},is:function(g,f){if(!f){return g!==e}if(f=="array"&&(g.hasOwnProperty&&g instanceof Array)){return true}return typeof(g)==f},makeMap:function(f,j,h){var g;f=f||[];j=j||",";if(typeof(f)=="string"){f=f.split(j)}h=h||{};g=f.length;while(g--){h[f[g]]={}}return h},each:function(i,f,h){var j,g;if(!i){return 0}h=h||i;if(i.length!==e){for(j=0,g=i.length;j<g;j++){if(f.call(h,i[j],j,i)===false){return 0}}}else{for(j in i){if(i.hasOwnProperty(j)){if(f.call(h,i[j],j,i)===false){return 0}}}}return 1},map:function(g,h){var i=[];b.each(g,function(f){i.push(h(f))});return i},grep:function(g,h){var i=[];b.each(g,function(f){if(!h||h(f)){i.push(f)}});return i},inArray:function(g,h){var j,f;if(g){for(j=0,f=g.length;j<f;j++){if(g[j]===h){return j}}}return -1},extend:function(k,j){var h,g,f=arguments;for(h=1,g=f.length;h<g;h++){j=f[h];b.each(j,function(i,l){if(i!==e){k[l]=i}})}return k},trim:function(f){return(f?""+f:"").replace(a,"")},create:function(o,f,j){var n=this,g,i,k,l,h,m=0;o=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(o);k=o[3].match(/(^|\.)(\w+)$/i)[2];i=n.createNS(o[3].replace(/\.\w+$/,""),j);if(i[k]){return}if(o[2]=="static"){i[k]=f;if(this.onCreate){this.onCreate(o[2],o[3],i[k])}return}if(!f[k]){f[k]=function(){};m=1}i[k]=f[k];n.extend(i[k].prototype,f);if(o[5]){g=n.resolve(o[5]).prototype;l=o[5].match(/\.(\w+)$/i)[1];h=i[k];if(m){i[k]=function(){return g[l].apply(this,arguments)}}else{i[k]=function(){this.parent=g[l];return h.apply(this,arguments)}}i[k].prototype[k]=i[k];n.each(g,function(p,q){i[k].prototype[q]=g[q]});n.each(f,function(p,q){if(g[q]){i[k].prototype[q]=function(){this.parent=g[q];return p.apply(this,arguments)}}else{if(q!=k){i[k].prototype[q]=p}}})}n.each(f["static"],function(p,q){i[k][q]=p});if(this.onCreate){this.onCreate(o[2],o[3],i[k].prototype)}},walk:function(i,h,j,g){g=g||this;if(i){if(j){i=i[j]}b.each(i,function(k,f){if(h.call(g,k,f,j)===false){return false}b.walk(k,h,j,g)})}},createNS:function(j,h){var g,f;h=h||d;j=j.split(".");for(g=0;g<j.length;g++){f=j[g];if(!h[f]){h[f]={}}h=h[f]}return h},resolve:function(j,h){var g,f;h=h||d;j=j.split(".");for(g=0,f=j.length;g<f;g++){h=h[j[g]];if(!h){break}}return h},addUnload:function(j,i){var h=this;j={func:j,scope:i||this};if(!h.unloads){function g(){var f=h.unloads,l,m;if(f){for(m in f){l=f[m];if(l&&l.func){l.func.call(l.scope,1)}}if(d.detachEvent){d.detachEvent("onbeforeunload",k);d.detachEvent("onunload",g)}else{if(d.removeEventListener){d.removeEventListener("unload",g,false)}}h.unloads=l=f=w=g=0;if(d.CollectGarbage){CollectGarbage()}}}function k(){var l=document;if(l.readyState=="interactive"){function f(){l.detachEvent("onstop",f);if(g){g()}l=0}if(l){l.attachEvent("onstop",f)}d.setTimeout(function(){if(l){l.detachEvent("onstop",f)}},0)}}if(d.attachEvent){d.attachEvent("onunload",g);d.attachEvent("onbeforeunload",k)}else{if(d.addEventListener){d.addEventListener("unload",g,false)}}h.unloads=[j]}else{h.unloads.push(j)}return j},removeUnload:function(i){var g=this.unloads,h=null;b.each(g,function(j,f){if(j&&j.func==i){g.splice(f,1);h=i;return false}});return h},explode:function(f,g){return f?b.map(f.split(g||","),b.trim):f},_addVer:function(g){var f;if(!this.query){return g}f=(g.indexOf("?")==-1?"?":"&")+this.query;if(g.indexOf("#")==-1){return g+f}return g.replace("#",f+"#")},_replace:function(h,f,g){if(c){return g.replace(h,function(){var l=f,j=arguments,k;for(k=0;k<j.length-2;k++){if(j[k]===e){l=l.replace(new RegExp("\\$"+k,"g"),"")}else{l=l.replace(new RegExp("\\$"+k,"g"),j[k])}}return l})}return g.replace(h,f)}};b._init();d.tinymce=d.tinyMCE=b})(window);tinymce.create("tinymce.util.Dispatcher",{scope:null,listeners:null,Dispatcher:function(a){this.scope=a||this;this.listeners=[]},add:function(a,b){this.listeners.push({cb:a,scope:b||this.scope});return a},addToTop:function(a,b){this.listeners.unshift({cb:a,scope:b||this.scope});return a},remove:function(a){var b=this.listeners,c=null;tinymce.each(b,function(e,d){if(a==e.cb){c=a;b.splice(d,1);return false}});return c},dispatch:function(){var f,d=arguments,e,b=this.listeners,g;for(e=0;e<b.length;e++){g=b[e];f=g.cb.apply(g.scope,d);if(f===false){break}}return f}});(function(){var a=tinymce.each;tinymce.create("tinymce.util.URI",{URI:function(e,g){var f=this,i,d,c,h;e=tinymce.trim(e);g=f.settings=g||{};if(/^([\w\-]+):([^\/]{2})/i.test(e)||/^\s*#/.test(e)){f.source=e;return}if(e.indexOf("/")===0&&e.indexOf("//")!==0){e=(g.base_uri?g.base_uri.protocol||"http":"http")+"://mce_host"+e}if(!/^[\w-]*:?\/\//.test(e)){h=g.base_uri?g.base_uri.path:new tinymce.util.URI(location.href).directory;e=((g.base_uri&&g.base_uri.protocol)||"http")+"://mce_host"+f.toAbsPath(h,e)}e=e.replace(/@@/g,"(mce_at)");e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e);a(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],function(b,j){var k=e[j];if(k){k=k.replace(/\(mce_at\)/g,"@@")}f[b]=k});if(c=g.base_uri){if(!f.protocol){f.protocol=c.protocol}if(!f.userInfo){f.userInfo=c.userInfo}if(!f.port&&f.host=="mce_host"){f.port=c.port}if(!f.host||f.host=="mce_host"){f.host=c.host}f.source=""}},setPath:function(c){var b=this;c=/^(.*?)\/?(\w+)?$/.exec(c);b.path=c[0];b.directory=c[1];b.file=c[2];b.source="";b.getURI()},toRelative:function(b){var c=this,d;if(b==="./"){return b}b=new tinymce.util.URI(b,{base_uri:c});if((b.host!="mce_host"&&c.host!=b.host&&b.host)||c.port!=b.port||c.protocol!=b.protocol){return b.getURI()}d=c.toRelPath(c.path,b.path);if(b.query){d+="?"+b.query}if(b.anchor){d+="#"+b.anchor}return d},toAbsolute:function(b,c){var b=new tinymce.util.URI(b,{base_uri:this});return b.getURI(this.host==b.host&&this.protocol==b.protocol?c:0)},toRelPath:function(g,h){var c,f=0,d="",e,b;g=g.substring(0,g.lastIndexOf("/"));g=g.split("/");c=h.split("/");if(g.length>=c.length){for(e=0,b=g.length;e<b;e++){if(e>=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length<c.length){for(e=0,b=c.length;e<b;e++){if(e>=g.length||g[e]!=c[e]){f=e+1;break}}}if(f==1){return h}for(e=0,b=g.length-(f-1);e<b;e++){d+="../"}for(e=f-1,b=c.length;e<b;e++){if(e!=f-1){d+="/"+c[e]}else{d+=c[e]}}return d},toAbsPath:function(e,f){var c,b=0,h=[],d,g;d=/\/$/.test(f)?"/":"";e=e.split("/");f=f.split("/");a(e,function(i){if(i){h.push(i)}});e=h;for(c=f.length-1,h=[];c>=0;c--){if(f[c].length==0||f[c]=="."){continue}if(f[c]==".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!=0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(e,b){var c=new Date();c.setTime(c.getTime()-1000);this.set(e,"",c,b,c)}})})();(function(){function serialize(o,quote){var i,v,t;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&o instanceof Array){for(i=0,v="[";i<o.length;i++){v+=(i>0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(i in o){if(o.hasOwnProperty(i)){v+=typeof o[i]!="function"?(v.length>1?","+quote:quote)+i+quote+":"+serialize(o[i],quote):""}}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={DELETE:46,BACKSPACE:8,ENTER:13,TAB:9,SPACEBAR:32,UP:38,DOWN:40}})(tinymce);(function(k){var i=k.VK,j=i.BACKSPACE,h=i.DELETE;function c(m){var o=m.dom,n=m.selection;m.onKeyDown.add(function(q,u){var p,v,s,t,r;r=u.keyCode==h;if(r||u.keyCode==j){u.preventDefault();p=n.getRng();v=o.getParent(p.startContainer,o.isBlock);if(r){v=o.getNext(v,o.isBlock)}if(v){s=v.firstChild;while(s&&s.nodeType==3&&s.nodeValue.length==0){s=s.nextSibling}if(s&&s.nodeName==="SPAN"){t=s.cloneNode(false)}}q.getDoc().execCommand(r?"ForwardDelete":"Delete",false,null);v=o.getParent(p.startContainer,o.isBlock);k.each(o.select("span.Apple-style-span,font.Apple-style-span",v),function(x){var y=n.getBookmark();if(t){o.replace(t.cloneNode(false),x,true)}else{o.remove(x,true)}n.moveToBookmark(y)})}})}function d(m){m.onKeyUp.add(function(n,p){var o=p.keyCode;if(o==h||o==j){if(n.dom.isEmpty(n.getBody())){n.setContent("",{format:"raw"});n.nodeChanged();return}}})}function b(m){m.dom.bind(m.getDoc(),"focusin",function(){m.selection.setRng(m.selection.getRng())})}function e(m){m.onKeyDown.add(function(n,q){if(q.keyCode===j){if(n.selection.isCollapsed()&&n.selection.getRng(true).startOffset===0){var p=n.selection.getNode();var o=p.previousSibling;if(o&&o.nodeName&&o.nodeName.toLowerCase()==="hr"){n.dom.remove(o);k.dom.Event.cancel(q)}}}})}function g(m){if(!Range.prototype.getClientRects){m.onMouseDown.add(function(o,p){if(p.target.nodeName==="HTML"){var n=o.getBody();n.blur();setTimeout(function(){n.focus()},0)}})}}function f(m){m.onClick.add(function(n,o){o=o.target;if(/^(IMG|HR)$/.test(o.nodeName)){n.selection.getSel().setBaseAndExtent(o,0,o,1)}if(o.nodeName=="A"&&n.dom.hasClass(o,"mceItemAnchor")){n.selection.select(o)}n.nodeChanged()})}function l(m){var o,n;m.dom.bind(m.getDoc(),"selectionchange",function(){if(n){clearTimeout(n);n=0}n=window.setTimeout(function(){var p=m.selection.getRng();if(!o||!k.dom.RangeUtils.compareRanges(p,o)){m.nodeChanged();o=p}},50)})}function a(m){document.body.setAttribute("role","application")}k.create("tinymce.util.Quirks",{Quirks:function(m){if(k.isWebKit){c(m);d(m);b(m);f(m);if(k.isIDevice){l(m)}}if(k.isIE){e(m);d(m);a(m)}if(k.isGecko){e(m);g(m)}}})})(tinymce);(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={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"};g={'"':"&quot;","'":"&#39;","<":"&lt;",">":"&gt;","&":"&amp;"};d={"&lt;":"<","&gt;":">","&amp;":"&","&quot;":'"',"&apos;":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n<m.length;n+=2){o=String.fromCharCode(parseInt(m[n],p));if(!g[o]){l="&"+m[n+1]+";";q[o]=l;q[l]=o}}return q}}a=e("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);j.html=j.html||{};j.html.Entities={encodeRaw:function(m,l){return m.replace(l?k:b,function(n){return g[n]||n})},encodeAllRaw:function(l){return(""+l).replace(f,function(m){return g[m]||m})},encodeNumeric:function(m,l){return m.replace(l?k:b,function(n){if(n.length>1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g<j.length;g++){a[j[g]]="\uFEFF"+g;a["\uFEFF"+g]=j[g]}function c(n,q,p,i){function o(r){r=parseInt(r).toString(16);return r.length>1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(r){var y={},p,n,v,q,u=d.url_converter,x=d.url_converter_scope||this;function o(C,F){var E,B,A,D;E=y[C+"-top"+F];if(!E){return}B=y[C+"-right"+F];if(E!=B){return}A=y[C+"-bottom"+F];if(B!=A){return}D=y[C+"-left"+F];if(A!=D){return}y[C+F]=D;delete y[C+"-top"+F];delete y[C+"-right"+F];delete y[C+"-bottom"+F];delete y[C+"-left"+F]}function t(B){var C=y[B],A;if(!C||C.indexOf(" ")<0){return}C=C.split(" ");A=C.length;while(A--){if(C[A]!==C[0]){return false}}y[B]=C[0];return true}function z(C,B,A,D){if(!t(B)){return}if(!t(A)){return}if(!t(D)){return}y[C]=y[B]+" "+y[A]+" "+y[D];delete y[B];delete y[A];delete y[D]}function s(A){q=true;return a[A]}function i(B,A){if(q){B=B.replace(/\uFEFF[0-9]/g,function(C){return a[C]})}if(!A){B=B.replace(/\\([\'\";:])/g,"$1")}return B}if(r){r=r.replace(/\\[\"\';:\uFEFF]/g,s).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(A){return A.replace(/[;:]/g,s)});while(p=b.exec(r)){n=p[1].replace(l,"").toLowerCase();v=p[2].replace(l,"");if(n&&v.length>0){if(n==="font-weight"&&v==="700"){v="bold"}else{if(n==="color"||n==="background-color"){v=v.toLowerCase()}}v=v.replace(k,c);v=v.replace(h,function(B,A,E,D,F,C){F=F||C;if(F){F=i(F);return"'"+F.replace(/\'/g,"\\'")+"'"}A=i(A||E||D);if(u){A=u.call(x,A,"style")}return"url('"+A.replace(/\'/g,"\\'")+"')"});y[n]=q?i(v,true):v}b.lastIndex=p.index+p[0].length}o("border","");o("border","-width");o("border","-color");o("border","-style");o("padding","");o("margin","");z("border","border-width","border-style","border-color");if(y.border==="medium none"){delete y.border}}return y},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u<s;u++){t=x[u];v=p[t];if(v!==e&&v.length>0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(m){var h={},j,l,g,f,c={},b,e,d=m.makeMap,k=m.each;function i(o,n){return o.split(n||",")}function a(r,q){var o,p={};function n(s){return s.replace(/[A-Z]+/g,function(t){return n(r[t])})}for(o in r){if(r.hasOwnProperty(o)){r[o]=n(r[o])}}n(q).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(v,t,s,u){s=i(s,"|");p[t]={attributes:d(s),attributesOrder:s,children:d(u,"|",{"#comment":{}})}});return p}l="h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,noscript,menu,isindex,samp,header,footer,article,section,hgroup";l=d(l,",",d(l.toUpperCase()));h=a({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]");j=d("checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls");g=d("area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source");f=m.extend(d("td,th,iframe,video,audio,object"),g);b=d("pre,script,style,textarea");e=d("colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr");m.html.Schema=function(r){var A=this,n={},o={},y=[],q,p;r=r||{};if(r.verify_html===false){r.valid_elements="*[*]"}if(r.valid_styles){q={};k(r.valid_styles,function(C,B){q[B]=m.explode(C)})}p=r.whitespace_elements?d(r.whitespace_elements):b;function z(B){return new RegExp("^"+B.replace(/([?+*])/g,".$1")+"$")}function t(I){var H,D,W,S,X,C,F,R,U,N,V,Z,L,G,T,B,P,E,Y,aa,M,Q,K=/^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,O=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,J=/[*?+]/;if(I){I=i(I);if(n["@"]){P=n["@"].attributes;E=n["@"].attributesOrder}for(H=0,D=I.length;H<D;H++){C=K.exec(I[H]);if(C){T=C[1];N=C[2];B=C[3];U=C[4];L={};G=[];F={attributes:L,attributesOrder:G};if(T==="#"){F.paddEmpty=true}if(T==="-"){F.removeEmpty=true}if(P){for(aa in P){L[aa]=P[aa]}G.push.apply(G,E)}if(U){U=i(U,"|");for(W=0,S=U.length;W<S;W++){C=O.exec(U[W]);if(C){R={};Z=C[1];V=C[2].replace(/::/g,":");T=C[3];Q=C[4];if(Z==="!"){F.attributesRequired=F.attributesRequired||[];F.attributesRequired.push(V);R.required=true}if(Z==="-"){delete L[V];G.splice(m.inArray(G,V),1);continue}if(T){if(T==="="){F.attributesDefault=F.attributesDefault||[];F.attributesDefault.push({name:V,value:Q});R.defaultValue=Q}if(T===":"){F.attributesForced=F.attributesForced||[];F.attributesForced.push({name:V,value:Q});R.forcedValue=Q}if(T==="<"){R.validValues=d(Q,"?")}}if(J.test(V)){F.attributePatterns=F.attributePatterns||[];R.pattern=z(V);F.attributePatterns.push(R)}else{if(!L[V]){G.push(V)}L[V]=R}}}}if(!P&&N=="@"){P=L;E=G}if(B){F.outputName=N;n[B]=F}if(J.test(N)){F.pattern=z(N);y.push(F)}else{n[N]=F}}}}}function v(B){n={};y=[];t(B);k(h,function(D,C){o[C]=D.children})}function s(C){var B=/^(~)?(.+)$/;if(C){k(i(C),function(G){var E=B.exec(G),F=E[1]==="~",H=F?"span":"div",D=E[2];o[D]=o[H];c[D]=H;if(!F){l[D]={}}k(o,function(I,J){if(I[H]){I[D]=I[H]}})})}}function u(C){var B=/^([+\-]?)(\w+)\[([^\]]+)\]$/;if(C){k(i(C),function(G){var F=B.exec(G),D,E;if(F){E=F[1];if(E){D=o[F[2]]}else{D=o[F[2]]={"#comment":{}}}D=o[F[2]];k(i(F[3],"|"),function(H){if(E==="-"){delete D[H]}else{D[H]={}}})}})}}function x(B){var D=n[B],C;if(D){return D}C=y.length;while(C--){D=y[C];if(D.pattern.test(B)){return D}}}if(!r.valid_elements){k(h,function(C,B){n[B]={attributes:C.attributes,attributesOrder:C.attributesOrder};o[B]=C.children});k(i("strong/b,em/i"),function(B){B=i(B,"/");n[B[1]].outputName=B[0]});n.img.attributesDefault=[{name:"alt",value:""}];k(i("ol,ul,sub,sup,blockquote,span,font,a,table,tbody,tr"),function(B){n[B].removeEmpty=true});k(i("p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption"),function(B){n[B].paddEmpty=true})}else{v(r.valid_elements)}s(r.custom_elements);u(r.valid_children);t(r.extended_valid_elements);u("+ol[ul|ol],+ul[ul|ol]");if(!x("span")){t("span[!data-mce-type|*]")}if(r.invalid_elements){m.each(m.explode(r.invalid_elements),function(B){if(n[B]){delete n[B]}})}A.children=o;A.styles=q;A.getBoolAttrs=function(){return j};A.getBlockElements=function(){return l};A.getShortEndedElements=function(){return g};A.getSelfClosingElements=function(){return e};A.getNonEmptyElements=function(){return f};A.getWhiteSpaceElements=function(){return p};A.isValidChild=function(B,D){var C=o[B];return !!(C&&C[D])};A.getElementRule=x;A.getCustomElements=function(){return c};A.addValidElements=t;A.setValidElements=v;A.addCustomElements=s;A.addValidChildren=u};m.html.Schema.boolAttrMap=j;m.html.Schema.blockElementsMap=l})(tinymce);(function(a){a.html.SaxParser=function(c,e){var b=this,d=function(){};c=c||{};b.schema=e=e||new a.html.Schema();if(c.fix_self_closing!==false){c.fix_self_closing=true}a.each("comment cdata text start end pi doctype".split(" "),function(f){if(f){b[f]=c[f]||d}});b.parse=function(D){var n=this,g,F=0,H,A,z=[],M,P,B,q,y,r,L,G,N,u,m,k,s,Q,o,O,E,R,K,f,I,l,C,J,h,v=0,j=a.html.Entities.decode,x,p;function t(S){var U,T;U=z.length;while(U--){if(z[U].name===S){break}}if(U>=0){for(T=z.length-1;T>=U;T--){S=z[T];if(S.valid){n.end(S.name)}}z.length=U}}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([^\\s\\/<>]+)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/)>))","g");C=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;J={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};L=e.getShortEndedElements();I=e.getSelfClosingElements();G=e.getBoolAttrs();u=c.validate;r=c.remove_internals;x=c.fix_self_closing;p=a.isIE;o=/^:/;while(g=l.exec(D)){if(F<g.index){n.text(j(D.substr(F,g.index-F)))}if(H=g[6]){H=H.toLowerCase();if(p&&o.test(H)){H=H.substr(1)}t(H)}else{if(H=g[7]){H=H.toLowerCase();if(p&&o.test(H)){H=H.substr(1)}N=H in L;if(x&&I[H]&&z.length>0&&z[z.length-1].name===H){t(H)}if(!u||(m=e.getElementRule(H))){k=true;if(u){O=m.attributes;E=m.attributePatterns}if(Q=g[8]){y=Q.indexOf("data-mce-type")!==-1;if(y&&r){k=false}M=[];M.map={};Q.replace(C,function(T,S,X,W,V){var Y,U;S=S.toLowerCase();X=S in G?S:j(X||W||V||"");if(u&&!y&&S.indexOf("data-")!==0){Y=O[S];if(!Y&&E){U=E.length;while(U--){Y=E[U];if(Y.pattern.test(S)){break}}if(U===-1){Y=null}}if(!Y){return}if(Y.validValues&&!(X in Y.validValues)){return}}M.map[S]=X;M.push({name:S,value:X})})}else{M=[];M.map={}}if(u&&!y){R=m.attributesRequired;K=m.attributesDefault;f=m.attributesForced;if(f){P=f.length;while(P--){s=f[P];q=s.name;h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}if(K){P=K.length;while(P--){s=K[P];q=s.name;if(!(q in M.map)){h=s.value;if(h==="{$uid}"){h="mce_"+v++}M.map[q]=h;M.push({name:q,value:h})}}}if(R){P=R.length;while(P--){if(R[P] in M.map){break}}if(P===-1){k=false}}if(M.map["data-mce-bogus"]){k=false}}if(k){n.start(H,M,N)}}else{k=false}if(A=J[H]){A.lastIndex=F=g.index+g[0].length;if(g=A.exec(D)){if(k){B=D.substr(F,g.index-F)}F=g.index+g[0].length}else{B=D.substr(F);F=D.length}if(k&&B.length>0){n.text(B,true)}if(k){n.end(H)}l.lastIndex=F;continue}if(!N){if(!Q||Q.indexOf("/")!=Q.length-1){z.push({name:H,valid:k})}else{if(k){n.end(H)}}}}else{if(H=g[1]){n.comment(H)}else{if(H=g[2]){n.cdata(H)}else{if(H=g[3]){n.doctype(H)}else{if(H=g[4]){n.pi(H,g[5])}}}}}}F=g.index+g[0].length}if(F<D.length){n.text(j(D.substr(F)))}for(P=z.length-1;P>=0;P--){H=z[P];if(H.valid){n.end(H.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h<f;h++){j=m[h];if(j.name!=="id"){k[k.length]={name:j.name,value:j.value};k.map[j.name]=j.value}}n.attributes=k}n.value=g.value;n.shortEnded=g.shortEnded;return n},wrap:function(g){var f=this;f.parent.insert(g,f);g.append(f);return f},unwrap:function(){var f=this,h,g;for(h=f.firstChild;h;){g=h.next;f.insert(h,f,true);h=g}f.remove()},remove:function(){var f=this,h=f.parent,g=f.next,i=f.prev;if(h){if(h.firstChild===f){h.firstChild=g;if(g){g.prev=null}}else{i.next=g}if(h.lastChild===f){h.lastChild=i;if(i){i.next=null}}else{g.prev=i}f.parent=f.next=f.prev=null}return f},append:function(h){var f=this,g;if(h.parent){h.remove()}g=f.lastChild;if(g){g.next=h;h.prev=g;f.lastChild=h}else{f.lastChild=f.firstChild=h}h.parent=f;return h},insert:function(h,f,i){var g;if(h.parent){h.remove()}g=f.parent||this;if(i){if(f===g.firstChild){g.firstChild=h}else{f.prev.next=h}h.prev=f.prev;h.next=f;f.prev=h}else{if(f===g.lastChild){g.lastChild=h}else{f.next.prev=h}h.next=f.next;h.prev=f;f.next=h}h.parent=g;return h},getAll:function(g){var f=this,h,i=[];for(h=f.firstChild;h;h=a(h,f)){if(h.name===g){i.push(h)}}return i},empty:function(){var g=this,f,h,j;if(g.firstChild){f=[];for(j=g.firstChild;j;j=a(j,g)){f.push(j)}h=f.length;while(h--){j=f[h];j.parent=j.firstChild=j.lastChild=j.next=j.prev=null}}g.firstChild=g.lastChild=null;return g},isEmpty:function(k){var f=this,j=f.firstChild,h,g;if(j){do{if(j.type===1){if(j.attributes.map["data-mce-bogus"]){continue}if(k[j.name]){return false}h=j.attributes.length;while(h--){g=j.attributes[h].name;if(g==="name"||g.indexOf("data-")===0){return false}}}if((j.type===3&&!c.test(j.value))){return false}}while(j=a(j,f))}return true},walk:function(f){return a(this,null,f)}});d.extend(b,{create:function(g,f){var i,h;i=new b(g,e[g]||1);if(f){for(h in f){i.attr(h,f[h])}}return i}});d.html.Node=b})(tinymce);(function(b){var a=b.html.Node;b.html.DomParser=function(g,h){var f=this,e={},d=[],i={},c={};g=g||{};g.validate="validate" in g?g.validate:true;g.root_name=g.root_name||"body";f.schema=h=h||new b.html.Schema();function j(m){var o,p,x,v,z,n,q,l,t,u,k,s,y,r;s=b.makeMap("tr,td,th,tbody,thead,tfoot,table");k=h.getNonEmptyElements();for(o=0;o<m.length;o++){p=m[o];if(!p.parent){continue}v=[p];for(x=p.parent;x&&!h.isValidChild(x.name,p.name)&&!s[x.name];x=x.parent){v.push(x)}if(x&&v.length>1){v.reverse();z=n=f.filterNode(v[0].clone());for(t=0;t<v.length-1;t++){if(h.isValidChild(n.name,v[t].name)){q=f.filterNode(v[t].clone());n.append(q)}else{q=n}for(l=v[t].firstChild;l&&l!=v[t+1];){r=l.next;q.append(l);l=r}n=q}if(!z.isEmpty(k)){x.insert(z,v[0],true);x.insert(p,z)}else{x.insert(p,v[0],true)}x=v[0];if(x.isEmpty(k)||x.firstChild===x.lastChild&&x.firstChild.name==="br"){x.empty().remove()}}else{if(p.parent){if(p.name==="li"){y=p.prev;if(y&&(y.name==="ul"||y.name==="ul")){y.append(p);continue}y=p.next;if(y&&(y.name==="ul"||y.name==="ul")){y.insert(p,y.firstChild,true);continue}p.wrap(f.filterNode(new a("ul",1)));continue}if(h.isValidChild(p.parent.name,"div")&&h.isValidChild("div",p.name)){p.wrap(f.filterNode(new a("div",1)))}else{if(p.name==="style"||p.name==="script"){p.empty().remove()}else{p.unwrap()}}}}}}f.filterNode=function(m){var l,k,n;if(k in e){n=i[k];if(n){n.push(m)}else{i[k]=[m]}}l=d.length;while(l--){k=d[l].name;if(k in m.attributes.map){n=c[k];if(n){n.push(m)}else{c[k]=[m]}}}return m};f.addNodeFilter=function(k,l){b.each(b.explode(k),function(m){var n=e[m];if(!n){e[m]=n=[]}n.push(l)})};f.addAttributeFilter=function(k,l){b.each(b.explode(k),function(m){var n;for(n=0;n<d.length;n++){if(d[n].name===m){d[n].callbacks.push(l);return}}d.push({name:m,callbacks:[l]})})};f.parse=function(v,m){var n,H,A,z,C,B,x,r,E,K,y,o,D,J=[],t,k,s,p,u,q;m=m||{};i={};c={};o=b.extend(b.makeMap("script,style,head,html,body,title,meta,param"),h.getBlockElements());u=h.getNonEmptyElements();p=h.children;y=g.validate;q="forced_root_block" in m?m.forced_root_block:g.forced_root_block;s=h.getWhiteSpaceElements();D=/^[ \t\r\n]+/;t=/[ \t\r\n]+$/;k=/[ \t\r\n]+/g;function F(){var L=H.firstChild,l,M;while(L){l=L.next;if(L.type==3||(L.type==1&&L.name!=="p"&&!o[L.name]&&!L.attr("data-mce-type"))){if(!M){M=I(q,1);H.insert(M,L);M.append(L)}else{M.append(L)}}else{M=null}L=l}}function I(l,L){var M=new a(l,L),N;if(l in e){N=i[l];if(N){N.push(M)}else{i[l]=[M]}}return M}function G(M){var N,l,L;for(N=M.prev;N&&N.type===3;){l=N.value.replace(t,"");if(l.length>0){N.value=l;N=N.prev}else{L=N.prev;N.remove();N=L}}}n=new b.html.SaxParser({validate:y,fix_self_closing:!y,cdata:function(l){A.append(I("#cdata",4)).value=l},text:function(M,l){var L;if(!s[A.name]){M=M.replace(k," ");if(A.lastChild&&o[A.lastChild.name]){M=M.replace(D,"")}}if(M.length!==0){L=I("#text",3);L.raw=!!l;A.append(L).value=M}},comment:function(l){A.append(I("#comment",8)).value=l},pi:function(l,L){A.append(I(l,7)).value=L;G(A)},doctype:function(L){var l;l=A.append(I("#doctype",10));l.value=L;G(A)},start:function(l,T,M){var R,O,N,L,P,U,S,Q;N=y?h.getElementRule(l):{};if(N){R=I(N.outputName||l,1);R.attributes=T;R.shortEnded=M;A.append(R);Q=p[A.name];if(Q&&p[R.name]&&!Q[R.name]){J.push(R)}O=d.length;while(O--){P=d[O].name;if(P in T.map){E=c[P];if(E){E.push(R)}else{c[P]=[R]}}}if(o[l]){G(R)}if(!M){A=R}}},end:function(l){var P,M,O,L,N;M=y?h.getElementRule(l):{};if(M){if(o[l]){if(!s[A.name]){for(P=A.firstChild;P&&P.type===3;){O=P.value.replace(D,"");if(O.length>0){P.value=O;P=P.next}else{L=P.next;P.remove();P=L}}for(P=A.lastChild;P&&P.type===3;){O=P.value.replace(t,"");if(O.length>0){P.value=O;P=P.prev}else{L=P.prev;P.remove();P=L}}}P=A.prev;if(P&&P.type===3){O=P.value.replace(D,"");if(O.length>0){P.value=O}else{P.remove()}}}if(M.removeEmpty||M.paddEmpty){if(A.isEmpty(u)){if(M.paddEmpty){A.empty().append(new a("#text","3")).value="\u00a0"}else{if(!A.attributes.map.name){N=A.parent;A.empty().remove();A=N;return}}}}A=A.parent}}},h);H=A=new a(m.context||g.root_name,11);n.parse(v);if(y&&J.length){if(!m.context){j(J)}else{m.invalid=true}}if(q&&H.name=="body"){F()}if(!m.invalid){for(K in i){E=e[K];z=i[K];x=z.length;while(x--){if(!z[x].parent){z.splice(x,1)}}for(C=0,B=E.length;C<B;C++){E[C](z,K,m)}}for(C=0,B=d.length;C<B;C++){E=d[C];if(E.name in c){z=c[E.name];x=z.length;while(x--){if(!z[x].parent){z.splice(x,1)}}for(x=0,r=E.callbacks.length;x<r;x++){E.callbacks[x](z,E.name,m)}}}}return H};if(g.remove_trailing_brs){f.addNodeFilter("br",function(n,m){var r,q=n.length,o,u=h.getBlockElements(),k=h.getNonEmptyElements(),s,p,t;u.body=1;for(r=0;r<q;r++){o=n[r];s=o.parent;if(u[o.parent.name]&&o===s.lastChild){p=o.prev;while(p){t=p.name;if(t!=="span"||p.attr("data-mce-type")!=="bookmark"){if(t!=="br"){break}if(t==="br"){o=null;break}}p=p.prev}if(o){o.remove();if(s.isEmpty(k)){elementRule=h.getElementRule(s.name);if(elementRule){if(elementRule.removeEmpty){s.remove()}else{if(elementRule.paddEmpty){s.empty().append(new b.html.Node("#text",3)).value="\u00a0"}}}}}}}})}}})(tinymce);tinymce.html.Writer=function(e){var c=[],a,b,d,f,g;e=e||{};a=e.indent;b=tinymce.makeMap(e.indent_before||"");d=tinymce.makeMap(e.indent_after||"");f=tinymce.html.Entities.getEncodeFunc(e.entity_encoding||"raw",e.entities);g=e.element_format=="html";return{start:function(m,k,p){var n,j,h,o;if(a&&b[m]&&c.length>0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n<j;n++){h=k[n];c.push(" ",h.name,'="',f(h.value,true),'"')}}if(!p||g){c[c.length]=">"}else{c[c.length]=" />"}if(p&&a&&d[m]&&c.length>0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("</",h,">");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("<![CDATA[",h,"]]>")},comment:function(h){c.push("<!--",h,"-->")},pi:function(h,i){if(i){c.push("<?",h," ",i,"?>")}else{c.push("<?",h,"?>")}if(a){c.push("\n")}},doctype:function(h){c.push("<!DOCTYPE",h,">",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n<m;n++){r=q.attributesOrder[n];if(r in s.map){p=s.map[r];u.map[r]=p;u.push({name:r,value:p})}}for(n=0,m=s.length;n<m;n++){r=s[n].name;if(!(r in u.map)){p=s.map[r];u.map[r]=p;u.push({name:r,value:p})}}s=u}e.start(k.name,s,o);if(!o){if((k=k.firstChild)){do{f(k)}while(k=k.next)}e.end(j)}}else{t(k)}}if(h.type==1&&!c.inner){f(h)}else{g[11](h)}return e.getContent()}}})(tinymce);(function(h){var f=h.each,e=h.is,d=h.isWebKit,b=h.isIE,c=h.html.Entities,a=/^([a-z0-9],?)+$/i,g=h.html.Schema.blockElementsMap,i=/^[ \t\r\n]*$/;h.create("tinymce.dom.DOMUtils",{doc:null,root:null,files:null,pixelStyles:/^(top|left|bottom|right|width|height|borderWidth)$/,props:{"for":"htmlFor","class":"className",className:"className",checked:"checked",disabled:"disabled",maxlength:"maxLength",readonly:"readOnly",selected:"selected",value:"value",id:"id",name:"name",type:"type"},DOMUtils:function(o,m){var l=this,j,k;l.doc=o;l.win=window;l.files={};l.cssFlicker=false;l.counter=0;l.stdMode=!h.isIE||o.documentMode>=8;l.boxModel=!h.isIE||o.compatMode=="CSS1Compat"||l.stdMode;l.hasOuterHTML="outerHTML" in o.createElement("a");l.settings=m=h.extend({keep_values:false,hex_colors:1},m);l.schema=m.schema;l.styles=new h.html.Styles({url_converter:m.url_converter,url_converter_scope:m.url_converter_scope},m.schema);if(h.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(n){l.cssFlicker=true}}if(b&&m.schema){("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(p){o.createElement(p)});for(k in m.schema.getCustomElements()){o.createElement(k)}}h.addUnload(l.destroy,l)},getRoot:function(){var j=this,k=j.settings;return(k&&j.get(k.root_element))||j.doc.body},getViewPort:function(k){var l,j;k=!k?this.win:k;l=k.document;j=this.boxModel?l.documentElement:l.body;return{x:k.pageXOffset||j.scrollLeft,y:k.pageYOffset||j.scrollTop,w:k.innerWidth||j.clientWidth,h:k.innerHeight||j.clientHeight}},getRect:function(m){var l,j=this,k;m=j.get(m);l=j.getPos(m);k=j.getSize(m);return{x:l.x,y:l.y,w:k.w,h:k.h}},getSize:function(m){var k=this,j,l;m=k.get(m);j=k.getStyle(m,"width");l=k.getStyle(m,"height");if(j.indexOf("px")===-1){j=0}if(l.indexOf("px")===-1){l=0}return{w:parseInt(j)||m.offsetWidth||m.clientWidth,h:parseInt(l)||m.offsetHeight||m.clientHeight}},getParent:function(l,k,j){return this.getParents(l,k,j,false)},getParents:function(u,p,l,s){var k=this,j,m=k.settings,q=[];u=k.get(u);s=s===undefined;if(m.strict_root){l=l||k.getRoot()}if(e(p,"string")){j=p;if(p==="*"){p=function(o){return o.nodeType==1}}else{p=function(o){return k.is(o,j)}}}while(u){if(u==l||!u.nodeType||u.nodeType===9){break}if(!p||p(u)){if(s){q.push(u)}else{return u}}u=u.parentNode}return s?q:null},get:function(j){var k;if(j&&this.doc&&typeof(j)=="string"){k=j;j=this.doc.getElementById(j);if(j&&j.id!==k){return this.doc.getElementsByName(k)[1]}}return j},getNext:function(k,j){return this._findSib(k,j,"nextSibling")},getPrev:function(k,j){return this._findSib(k,j,"previousSibling")},select:function(l,k){var j=this;return h.dom.Sizzle(l,j.get(k)||j.get(j.settings.root_element)||j.doc,[])},is:function(l,j){var k;if(l.length===undefined){if(j==="*"){return l.nodeType==1}if(a.test(j)){j=j.toLowerCase().split(/,/);l=l.nodeName.toLowerCase();for(k=j.length-1;k>=0;k--){if(j[k]==l){return true}}return false}}return h.dom.Sizzle.matches(j,l.nodeType?[l]:l).length>0},add:function(m,q,j,l,o){var k=this;return this.run(m,function(s){var r,n;r=e(q,"string")?k.doc.createElement(q):q;k.setAttribs(r,j);if(l){if(l.nodeType){r.appendChild(l)}else{k.setHTML(r,l)}}return !o?s.appendChild(r):r})},create:function(l,j,k){return this.add(this.doc.createElement(l),l,j,k,1)},createHTML:function(r,j,p){var q="",m=this,l;q+="<"+r;for(l in j){if(j.hasOwnProperty(l)){q+=" "+l+'="'+m.encode(j[l])+'"'}}if(typeof(p)!="undefined"){return q+">"+p+"</"+r+">"}return q+" />"},remove:function(j,k){return this.run(j,function(m){var n,l=m.parentNode;if(!l){return null}if(k){while(n=m.firstChild){if(!h.isIE||n.nodeType!==3||n.nodeValue){l.insertBefore(n,m)}else{m.removeChild(n)}}}return l.removeChild(m)})},setStyle:function(m,j,k){var l=this;return l.run(m,function(p){var o,n;o=p.style;j=j.replace(/-(\D)/g,function(r,q){return q.toUpperCase()});if(l.pixelStyles.test(j)&&(h.is(k,"number")||/^[\-0-9\.]+$/.test(k))){k+="px"}switch(j){case"opacity":if(b){o.filter=k===""?"":"alpha(opacity="+(k*100)+")";if(!m.currentStyle||!m.currentStyle.hasLayout){o.display="inline-block"}}o[j]=o["-moz-opacity"]=o["-khtml-opacity"]=k||"";break;case"float":b?o.styleFloat=k:o.cssFloat=k;break;default:o[j]=k||""}if(l.settings.update_styles){l.setAttrib(p,"data-mce-style")}})},getStyle:function(m,j,l){m=this.get(m);if(!m){return}if(this.doc.defaultView&&l){j=j.replace(/[A-Z]/g,function(n){return"-"+n});try{return this.doc.defaultView.getComputedStyle(m,null).getPropertyValue(j)}catch(k){return null}}j=j.replace(/-(\D)/g,function(o,n){return n.toUpperCase()});if(j=="float"){j=b?"styleFloat":"cssFloat"}if(m.currentStyle&&l){return m.currentStyle[j]}return m.style?m.style[j]:undefined},setStyles:function(m,n){var k=this,l=k.settings,j;j=l.update_styles;l.update_styles=0;f(n,function(o,p){k.setStyle(m,p,o)});l.update_styles=j;if(l.update_styles){k.setAttrib(m,l.cssText)}},removeAllAttribs:function(j){return this.run(j,function(m){var l,k=m.attributes;for(l=k.length-1;l>=0;l--){m.removeAttributeNode(k.item(l))}})},setAttrib:function(l,m,j){var k=this;if(!l||!m){return}if(k.settings.strict){m=m.toLowerCase()}return this.run(l,function(o){var n=k.settings;if(j!==null){switch(m){case"style":if(!e(j,"string")){f(j,function(p,q){k.setStyle(o,q,p)});return}if(n.keep_values){if(j&&!k._isRes(j)){o.setAttribute("data-mce-style",j,2)}else{o.removeAttribute("data-mce-style",2)}}o.style.cssText=j;break;case"class":o.className=j||"";break;case"src":case"href":if(n.keep_values){if(n.url_converter){j=n.url_converter.call(n.url_converter_scope||k,j,m,o)}k.setAttrib(o,"data-mce-"+m,j,2)}break;case"shape":o.setAttribute("data-mce-style",j);break}}if(e(j)&&j!==null&&j.length!==0){o.setAttribute(m,""+j,2)}else{o.removeAttribute(m,2)}})},setAttribs:function(k,l){var j=this;return this.run(k,function(m){f(l,function(o,p){j.setAttrib(m,p,o)})})},getAttrib:function(o,p,l){var j,k=this,m;o=k.get(o);if(!o||o.nodeType!==1){return l===m?false:l}if(!e(l)){l=""}if(/^(src|href|style|coords|shape)$/.test(p)){j=o.getAttribute("data-mce-"+p);if(j){return j}}if(b&&k.props[p]){j=o[k.props[p]];j=j&&j.nodeValue?j.nodeValue:j}if(!j){j=o.getAttribute(p,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(p)){if(o[k.props[p]]===true&&j===""){return p}return j?p:""}if(o.nodeName==="FORM"&&o.getAttributeNode(p)){return o.getAttributeNode(p).nodeValue}if(p==="style"){j=j||o.style.cssText;if(j){j=k.serializeStyle(k.parseStyle(j),o.nodeName);if(k.settings.keep_values&&!k._isRes(j)){o.setAttribute("data-mce-style",j)}}}if(d&&p==="class"&&j){j=j.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(p){case"rowspan":case"colspan":if(j===1){j=""}break;case"size":if(j==="+0"||j===20||j===0){j=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(j===0){j=""}break;case"hspace":if(j===-1){j=""}break;case"maxlength":case"tabindex":if(j===32768||j===2147483647||j==="32768"){j=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(j===65535){return p}return l;case"shape":j=j.toLowerCase();break;default:if(p.indexOf("on")===0&&j){j=h._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+j)}}}return(j!==m&&j!==null&&j!=="")?""+j:l},getPos:function(s,m){var k=this,j=0,q=0,o,p=k.doc,l;s=k.get(s);m=m||p.body;if(s){if(s.getBoundingClientRect){s=s.getBoundingClientRect();o=k.boxModel?p.documentElement:p.body;j=s.left+(p.documentElement.scrollLeft||p.body.scrollLeft)-o.clientTop;q=s.top+(p.documentElement.scrollTop||p.body.scrollTop)-o.clientLeft;return{x:j,y:q}}l=s;while(l&&l!=m&&l.nodeType){j+=l.offsetLeft||0;q+=l.offsetTop||0;l=l.offsetParent}l=s.parentNode;while(l&&l!=m&&l.nodeType){j-=l.scrollLeft||0;q-=l.scrollTop||0;l=l.parentNode}}return{x:j,y:q}},parseStyle:function(j){return this.styles.parse(j)},serializeStyle:function(k,j){return this.styles.serialize(k,j)},loadCSS:function(j){var l=this,m=l.doc,k;if(!j){j=""}k=l.select("head")[0];f(j.split(","),function(n){var o;if(l.files[n]){return}l.files[n]=true;o=l.create("link",{rel:"stylesheet",href:h._addVer(n)});if(b&&m.documentMode&&m.recalc){o.onload=function(){if(m.recalc){m.recalc()}o.onload=null}}k.appendChild(o)})},addClass:function(j,k){return this.run(j,function(l){var m;if(!k){return 0}if(this.hasClass(l,k)){return l.className}m=this.removeClass(l,k);return l.className=(m!=""?(m+" "):"")+k})},removeClass:function(l,m){var j=this,k;return j.run(l,function(o){var n;if(j.hasClass(o,m)){if(!k){k=new RegExp("(^|\\s+)"+m+"(\\s+|$)","g")}n=o.className.replace(k," ");n=h.trim(n!=" "?n:"");o.className=n;if(!n){o.removeAttribute("class");o.removeAttribute("className")}return n}return o.className})},hasClass:function(k,j){k=this.get(k);if(!k||!j){return false}return(" "+k.className+" ").indexOf(" "+j+" ")!==-1},show:function(j){return this.setStyle(j,"display","block")},hide:function(j){return this.setStyle(j,"display","none")},isHidden:function(j){j=this.get(j);return !j||j.style.display=="none"||this.getStyle(j,"display")=="none"},uniqueId:function(j){return(!j?"mce_":j)+(this.counter++)},setHTML:function(l,k){var j=this;return j.run(l,function(n){if(b){while(n.firstChild){n.removeChild(n.firstChild)}try{n.innerHTML="<br />"+k;n.removeChild(n.firstChild)}catch(m){n=j.create("div");n.innerHTML="<br />"+k;f(n.childNodes,function(p,o){if(o){n.appendChild(p)}})}}else{n.innerHTML=k}return k})},getOuterHTML:function(l){var k,j=this;l=j.get(l);if(!l){return null}if(l.nodeType===1&&j.hasOuterHTML){return l.outerHTML}k=(l.ownerDocument||j.doc).createElement("body");k.appendChild(l.cloneNode(true));return k.innerHTML},setOuterHTML:function(m,k,n){var j=this;function l(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){j.insertAfter(s.cloneNode(true),p);s=s.previousSibling}j.remove(p)}return this.run(m,function(p){p=j.get(p);if(p.nodeType==1){n=n||p.ownerDocument||j.doc;if(b){try{if(b&&p.nodeType==1){p.outerHTML=k}else{l(p,k,n)}}catch(o){l(p,k,n)}}else{l(p,k,n)}}})},decode:c.decode,encode:c.encodeAllRaw,insertAfter:function(j,k){k=this.get(k);return this.run(j,function(m){var l,n;l=k.parentNode;n=k.nextSibling;if(n){l.insertBefore(m,n)}else{l.appendChild(m)}return m})},isBlock:function(k){var j=k.nodeType;if(j){return !!(j===1&&g[k.nodeName])}return !!g[k]},replace:function(p,m,j){var l=this;if(e(m,"array")){p=p.cloneNode(true)}return l.run(m,function(k){if(j){f(h.grep(k.childNodes),function(n){p.appendChild(n)})}return k.parentNode.replaceChild(p,k)})},rename:function(m,j){var l=this,k;if(m.nodeName!=j.toUpperCase()){k=l.create(j);f(l.getAttribs(m),function(n){l.setAttrib(k,n.nodeName,l.getAttrib(m,n.nodeName))});l.replace(k,m,1)}return k||m},findCommonAncestor:function(l,j){var m=l,k;while(m){k=j;while(k&&m!=k){k=k.parentNode}if(m==k){break}m=m.parentNode}if(!m&&l.ownerDocument){return l.ownerDocument.documentElement}return m},toHex:function(j){var l=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(j);function k(m){m=parseInt(m).toString(16);return m.length>1?m:"0"+m}if(l){j="#"+k(l[1])+k(l[2])+k(l[3]);return j}return j},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(r){f(r.imports,function(s){q(s)});f(r.cssRules||r.rules,function(s){switch(s.type||1){case 1:if(s.selectorText){f(s.selectorText.split(","),function(t){t=t.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(t)||!/\.[\w\-]+$/.test(t)){return}l=t;t=h._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",t);if(p&&!(t=p(t,l))){return}if(!o[t]){j.push({"class":t});o[t]=1}})}break;case 3:q(s.styleSheet);break}})}try{f(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(m,l,k){var j=this,n;if(j.doc&&typeof(m)==="string"){m=j.get(m)}if(!m){return false}k=k||this;if(!m.nodeType&&(m.length||m.length===0)){n=[];f(m,function(p,o){if(p){if(typeof(p)=="string"){p=j.doc.getElementById(p)}n.push(l.call(k,p,o))}});return n}return l.call(k,m)},getAttribs:function(k){var j;k=this.get(k);if(!k){return[]}if(b){j=[];if(k.nodeName=="OBJECT"){return k.attributes}if(k.nodeName==="OPTION"&&this.getAttrib(k,"selected")){j.push({specified:1,nodeName:"selected"})}k.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(l){j.push({specified:1,nodeName:l})});return j}return k.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p;m=m.firstChild;if(m){j=new h.dom.TreeWalker(m);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){p=m.parentNode;if(l==="br"&&r.isBlock(p)&&p.firstChild===m&&p.lastChild===m){continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if((q===3&&!i.test(m.nodeValue))){return false}}while(m=j.next())}return true},destroy:function(k){var j=this;if(j.events){j.events.destroy()}j.win=j.doc=j.root=j.events=null;if(!k){h.removeUnload(j.destroy)}},createRng:function(){var j=this.doc;return j.createRange?j.createRange():new h.dom.Range(this)},nodeIndex:function(n,o){var j=0,l,m,k;if(n){for(l=n.nodeType,n=n.previousSibling,m=n;n;n=n.previousSibling){k=n.nodeType;if(o&&k==3){if(k==l||!n.nodeValue.length){continue}}j++;l=k}}return j},split:function(n,m,q){var s=this,j=s.createRng(),o,l,p;function k(v){var t,r=v.childNodes,u=v.nodeType;if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=r.length-1;t>=0;t--){k(r[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){if(!s.isBlock(v.parentNode)||h.trim(v.nodeValue).length>0){return}}else{if(u==1){r=v.childNodes;if(r.length==1&&r[0]&&r[0].nodeType==1&&r[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(r[0],v)}if(r.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}s.remove(v)}return v}if(n&&m){j.setStart(n.parentNode,s.nodeIndex(n));j.setEnd(m.parentNode,s.nodeIndex(m));o=j.extractContents();j=s.createRng();j.setStart(m.parentNode,s.nodeIndex(m)+1);j.setEnd(n.parentNode,s.nodeIndex(n)+1);l=j.extractContents();p=n.parentNode;p.insertBefore(k(o),n);if(q){p.replaceChild(q,m)}else{p.insertBefore(m,n)}p.insertBefore(k(l),n);s.remove(n);return q||m}},bind:function(n,j,m,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.add(n,j,m,l||this)},unbind:function(m,j,l){var k=this;if(!k.events){k.events=new h.dom.EventUtils()}return k.events.remove(m,j,l)},_findSib:function(m,j,k){var l=this,n=j;if(m){if(e(n,"string")){n=function(o){return l.is(o,j)}}for(m=m[k];m;m=m[k]){if(n(m)){return m}}}return null},_isRes:function(j){return/^(top|left|bottom|right|width|height)/i.test(j)||/;\s*(top|left|bottom|right|width|height)/i.test(j)}});h.DOM=new h.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var N=this,e=c.doc,S=0,E=1,j=2,D=true,R=false,U="startOffset",h="startContainer",P="endContainer",z="endOffset",k=tinymce.extend,n=c.nodeIndex;k(N,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:D,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:I,setEndBefore:J,setEndAfter:u,collapse:A,selectNode:x,selectNodeContents:F,compareBoundaryPoints:v,deleteContents:p,extractContents:H,cloneContents:d,insertNode:C,surroundContents:M,cloneRange:K});function q(V,t){B(D,V,t)}function s(V,t){B(R,V,t)}function g(t){q(t.parentNode,n(t))}function I(t){q(t.parentNode,n(t)+1)}function J(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function A(t){if(t){N[P]=N[h];N[z]=N[U]}else{N[h]=N[P];N[U]=N[z]}N.collapsed=D}function x(t){g(t);u(t)}function F(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(Y,t){var ab=N[h],W=N[U],aa=N[P],V=N[z],Z=t.startContainer,ad=t.startOffset,X=t.endContainer,ac=t.endOffset;if(Y===0){return G(ab,W,Z,ad)}if(Y===1){return G(aa,V,Z,ad)}if(Y===2){return G(aa,V,X,ac)}if(Y===3){return G(ab,W,X,ac)}}function p(){m(j)}function H(){return m(S)}function d(){return m(E)}function C(Y){var V=this[h],t=this[U],X,W;if((V.nodeType===3||V.nodeType===4)&&V.nodeValue){if(!t){V.parentNode.insertBefore(Y,V)}else{if(t>=V.nodeValue.length){c.insertAfter(Y,V)}else{X=V.splitText(t);V.parentNode.insertBefore(Y,X)}}}else{if(V.childNodes.length>0){W=V.childNodes[t]}if(W){V.insertBefore(Y,W)}else{V.appendChild(Y)}}}function M(V){var t=N.extractContents();N.insertNode(V);V.appendChild(t);N.selectNode(V)}function K(){return k(new b(c),{startContainer:N[h],startOffset:N[U],endContainer:N[P],endOffset:N[z],collapsed:N.collapsed,commonAncestorContainer:N.commonAncestorContainer})}function O(t,V){var W;if(t.nodeType==3){return t}if(V<0){return t}W=t.firstChild;while(W&&V>0){--V;W=W.nextSibling}if(W){return W}return t}function l(){return(N[h]==N[P]&&N[U]==N[z])}function G(X,Z,V,Y){var aa,W,t,ab,ad,ac;if(X==V){if(Z==Y){return 0}if(Z<Y){return -1}return 1}aa=V;while(aa&&aa.parentNode!=X){aa=aa.parentNode}if(aa){W=0;t=X.firstChild;while(t!=aa&&W<Z){W++;t=t.nextSibling}if(Z<=W){return -1}return 1}aa=X;while(aa&&aa.parentNode!=V){aa=aa.parentNode}if(aa){W=0;t=V.firstChild;while(t!=aa&&W<Y){W++;t=t.nextSibling}if(W<Y){return -1}return 1}ab=c.findCommonAncestor(X,V);ad=X;while(ad&&ad.parentNode!=ab){ad=ad.parentNode}if(!ad){ad=ab}ac=V;while(ac&&ac.parentNode!=ab){ac=ac.parentNode}if(!ac){ac=ab}if(ad==ac){return 0}t=ab.firstChild;while(t){if(t==ad){return -1}if(t==ac){return 1}t=t.nextSibling}}function B(V,Y,X){var t,W;if(V){N[h]=Y;N[U]=X}else{N[P]=Y;N[z]=X}t=N[P];while(t.parentNode){t=t.parentNode}W=N[h];while(W.parentNode){W=W.parentNode}if(W==t){if(G(N[h],N[U],N[P],N[z])>0){N.collapse(V)}}else{N.collapse(V)}N.collapsed=l();N.commonAncestorContainer=c.findCommonAncestor(N[h],N[P])}function m(ab){var aa,X=0,ad=0,V,Z,W,Y,t,ac;if(N[h]==N[P]){return f(ab)}for(aa=N[P],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[h]){return r(aa,ab)}++X}for(aa=N[h],V=aa.parentNode;V;aa=V,V=V.parentNode){if(V==N[P]){return T(aa,ab)}++ad}Z=ad-X;W=N[h];while(Z>0){W=W.parentNode;Z--}Y=N[P];while(Z<0){Y=Y.parentNode;Z++}for(t=W.parentNode,ac=Y.parentNode;t!=ac;t=t.parentNode,ac=ac.parentNode){W=t;Y=ac}return o(W,Y,ab)}function f(Z){var ab,Y,X,aa,t,W,V;if(Z!=j){ab=e.createDocumentFragment()}if(N[U]==N[z]){return ab}if(N[h].nodeType==3){Y=N[h].nodeValue;X=Y.substring(N[U],N[z]);if(Z!=E){N[h].deleteData(N[U],N[z]-N[U]);N.collapse(D)}if(Z==j){return}ab.appendChild(e.createTextNode(X));return ab}aa=O(N[h],N[U]);t=N[z]-N[U];while(t>0){W=aa.nextSibling;V=y(aa,Z);if(ab){ab.appendChild(V)}--t;aa=W}if(Z!=E){N.collapse(D)}return ab}function r(ab,Y){var aa,Z,V,t,X,W;if(Y!=j){aa=e.createDocumentFragment()}Z=i(ab,Y);if(aa){aa.appendChild(Z)}V=n(ab);t=V-N[U];if(t<=0){if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}Z=ab.previousSibling;while(t>0){X=Z.previousSibling;W=y(Z,Y);if(aa){aa.insertBefore(W,aa.firstChild)}--t;Z=X}if(Y!=E){N.setEndBefore(ab);N.collapse(R)}return aa}function T(Z,Y){var ab,V,aa,t,X,W;if(Y!=j){ab=e.createDocumentFragment()}aa=Q(Z,Y);if(ab){ab.appendChild(aa)}V=n(Z);++V;t=N[z]-V;aa=Z.nextSibling;while(t>0){X=aa.nextSibling;W=y(aa,Y);if(ab){ab.appendChild(W)}--t;aa=X}if(Y!=E){N.setStartAfter(Z);N.collapse(D)}return ab}function o(Z,t,ac){var W,ae,Y,aa,ab,V,ad,X;if(ac!=j){ae=e.createDocumentFragment()}W=Q(Z,ac);if(ae){ae.appendChild(W)}Y=Z.parentNode;aa=n(Z);ab=n(t);++aa;V=ab-aa;ad=Z.nextSibling;while(V>0){X=ad.nextSibling;W=y(ad,ac);if(ae){ae.appendChild(W)}ad=X;--V}W=i(t,ac);if(ae){ae.appendChild(W)}if(ac!=E){N.setStartAfter(Z);N.collapse(D)}return ae}function i(aa,ab){var W=O(N[P],N[z]-1),ac,Z,Y,t,V,X=W!=N[P];if(W==aa){return L(W,X,R,ab)}ac=W.parentNode;Z=L(ac,R,R,ab);while(ac){while(W){Y=W.previousSibling;t=L(W,X,R,ab);if(ab!=j){Z.insertBefore(t,Z.firstChild)}X=D;W=Y}if(ac==aa){return Z}W=ac.previousSibling;ac=ac.parentNode;V=L(ac,R,R,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function Q(aa,ab){var X=O(N[h],N[U]),Y=X!=N[h],ac,Z,W,t,V;if(X==aa){return L(X,Y,D,ab)}ac=X.parentNode;Z=L(ac,R,D,ab);while(ac){while(X){W=X.nextSibling;t=L(X,Y,D,ab);if(ab!=j){Z.appendChild(t)}Y=D;X=W}if(ac==aa){return Z}X=ac.nextSibling;ac=ac.parentNode;V=L(ac,R,D,ab);if(ab!=j){V.appendChild(Z)}Z=V}}function L(t,Y,ab,ac){var X,W,Z,V,aa;if(Y){return y(t,ac)}if(t.nodeType==3){X=t.nodeValue;if(ab){V=N[U];W=X.substring(V);Z=X.substring(0,V)}else{V=N[z];W=X.substring(0,V);Z=X.substring(V)}if(ac!=E){t.nodeValue=Z}if(ac==j){return}aa=t.cloneNode(R);aa.nodeValue=W;return aa}if(ac==j){return}return t.cloneNode(R)}function y(V,t){if(t!=j){return t==E?V.cloneNode(D):V}V.parentNode.removeChild(V)}}a.Range=b})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}k.setEndPoint(j?"EndToStart":"EndToEnd",i);if(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)>0){k=i.duplicate();k.collapse(j);o=-1;while(s==k.parentElement()){if(k.move("character",-1)==0){break}o++}}o=o||k.text.replace("\r\n"," ").length}else{k.collapse(true);k.setEndPoint(j?"StartToStart":"StartToEnd",i);o=k.text.replace("\r\n"," ").length}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var u,t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p&&t.nodeType!==9){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,s,q,r=d.dom.doc,m=r.body;function j(z){var u,y,t,x,v;t=h.create("a");u=z?k:s;y=z?p:q;x=n.duplicate();if(u==r||u==r.documentElement){u=m;y=0}if(u.nodeType==3){u.parentNode.insertBefore(t,u);x.moveToElementText(t);x.moveStart("character",y);h.remove(t);n.setEndPoint(z?"StartToStart":"EndToEnd",x)}else{v=u.childNodes;if(v.length){if(y>=v.length){h.insertAfter(t,v[v.length-1])}else{u.insertBefore(t,v[y])}x.moveToElementText(t)}else{t=r.createTextNode("\uFEFF");u.appendChild(t);x.moveToElementText(t.parentNode);x.collapse(c)}n.setEndPoint(z?"StartToStart":"EndToEnd",x);h.remove(t)}}k=i.startContainer;p=i.startOffset;s=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==s&&k.nodeType==1&&p==q-1){if(p==q-1){try{l=m.createControlRange();l.addElement(k.childNodes[p]);l.select();return}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(){var p=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,j=0,d=Object.prototype.toString,o=false,i=true;[0,0].sort(function(){i=false;return 0});var b=function(v,e,z,A){z=z||[];e=e||document;var C=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!v||typeof v!=="string"){return z}var x=[],s,E,H,r,u=true,t=b.isXML(e),B=v,D,G,F,y;do{p.exec("");s=p.exec(B);if(s){B=s[3];x.push(s[1]);if(s[2]){r=s[3];break}}}while(s);if(x.length>1&&k.exec(v)){if(x.length===2&&f.relative[x[0]]){E=h(x[0]+x[1],e)}else{E=f.relative[x[0]]?[e]:b(x.shift(),e);while(x.length){v=x.shift();if(f.relative[v]){v+=x.shift()}E=h(v,E)}}}else{if(!A&&x.length>1&&e.nodeType===9&&!t&&f.match.ID.test(x[0])&&!f.match.ID.test(x[x.length-1])){D=b.find(x.shift(),e,t);e=D.expr?b.filter(D.expr,D.set)[0]:D.set[0]}if(e){D=A?{expr:x.pop(),set:a(A)}:b.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&e.parentNode?e.parentNode:e,t);E=D.expr?b.filter(D.expr,D.set):D.set;if(x.length>0){H=a(E)}else{u=false}while(x.length){G=x.pop();F=G;if(!f.relative[G]){G=""}else{F=x.pop()}if(F==null){F=e}f.relative[G](H,F,t)}}else{H=x=[]}}if(!H){H=E}if(!H){b.error(G||v)}if(d.call(H)==="[object Array]"){if(!u){z.push.apply(z,H)}else{if(e&&e.nodeType===1){for(y=0;H[y]!=null;y++){if(H[y]&&(H[y]===true||H[y].nodeType===1&&b.contains(e,H[y]))){z.push(E[y])}}}else{for(y=0;H[y]!=null;y++){if(H[y]&&H[y].nodeType===1){z.push(E[y])}}}}}else{a(H,z)}if(r){b(r,C,z,A);b.uniqueSort(z)}return z};b.uniqueSort=function(r){if(c){o=i;r.sort(c);if(o){for(var e=1;e<r.length;e++){if(r[e]===r[e-1]){r.splice(e--,1)}}}}return r};b.matches=function(e,r){return b(e,null,null,r)};b.find=function(y,e,z){var x;if(!y){return[]}for(var t=0,s=f.order.length;t<s;t++){var v=f.order[t],u;if((u=f.leftMatch[v].exec(y))){var r=u[1];u.splice(1,1);if(r.substr(r.length-1)!=="\\"){u[1]=(u[1]||"").replace(/\\/g,"");x=f.find[v](u,e,z);if(x!=null){y=y.replace(f.match[v],"");break}}}}if(!x){x=e.getElementsByTagName("*")}return{set:x,expr:y}};b.filter=function(C,B,F,u){var s=C,H=[],z=B,x,e,y=B&&B[0]&&b.isXML(B[0]);while(C&&B.length){for(var A in f.filter){if((x=f.leftMatch[A].exec(C))!=null&&x[2]){var r=f.filter[A],G,E,t=x[1];e=false;x.splice(1,1);if(t.substr(t.length-1)==="\\"){continue}if(z===H){H=[]}if(f.preFilter[A]){x=f.preFilter[A](x,z,F,H,u,y);if(!x){e=G=true}else{if(x===true){continue}}}if(x){for(var v=0;(E=z[v])!=null;v++){if(E){G=r(E,x,v,z);var D=u^!!G;if(F&&G!=null){if(D){e=true}else{z[v]=false}}else{if(D){H.push(E);e=true}}}}}if(G!==undefined){if(!F){z=H}C=C.replace(f.match[A],"");if(!e){return[]}break}}}if(C===s){if(e==null){b.error(C)}else{break}}s=C}return z};b.error=function(e){throw"Syntax error, unrecognized expression: "+e};var f=b.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(e){return e.getAttribute("href")}},relative:{"+":function(x,r){var t=typeof r==="string",v=t&&!/\W/.test(r),y=t&&!v;if(v){r=r.toLowerCase()}for(var s=0,e=x.length,u;s<e;s++){if((u=x[s])){while((u=u.previousSibling)&&u.nodeType!==1){}x[s]=y||u&&u.nodeName.toLowerCase()===r?u||false:u===r}}if(y){b.filter(r,x,true)}},">":function(x,r){var u=typeof r==="string",v,s=0,e=x.length;if(u&&!/\W/.test(r)){r=r.toLowerCase();for(;s<e;s++){v=x[s];if(v){var t=v.parentNode;x[s]=t.nodeName.toLowerCase()===r?t:false}}}else{for(;s<e;s++){v=x[s];if(v){x[s]=u?v.parentNode:v.parentNode===r}}if(u){b.filter(r,x,true)}}},"":function(t,r,v){var s=j++,e=q,u;if(typeof r==="string"&&!/\W/.test(r)){r=r.toLowerCase();u=r;e=n}e("parentNode",r,s,t,u,v)},"~":function(t,r,v){var s=j++,e=q,u;if(typeof r==="string"&&!/\W/.test(r)){r=r.toLowerCase();u=r;e=n}e("previousSibling",r,s,t,u,v)}},find:{ID:function(r,s,t){if(typeof s.getElementById!=="undefined"&&!t){var e=s.getElementById(r[1]);return e?[e]:[]}},NAME:function(s,v){if(typeof v.getElementsByName!=="undefined"){var r=[],u=v.getElementsByName(s[1]);for(var t=0,e=u.length;t<e;t++){if(u[t].getAttribute("name")===s[1]){r.push(u[t])}}return r.length===0?null:r}},TAG:function(e,r){return r.getElementsByTagName(e[1])}},preFilter:{CLASS:function(t,r,s,e,x,y){t=" "+t[1].replace(/\\/g,"")+" ";if(y){return t}for(var u=0,v;(v=r[u])!=null;u++){if(v){if(x^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(t)>=0)){if(!s){e.push(v)}}else{if(s){r[u]=false}}}}return false},ID:function(e){return e[1].replace(/\\/g,"")},TAG:function(r,e){return r[1].toLowerCase()},CHILD:function(e){if(e[1]==="nth"){var r=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(r[1]+(r[2]||1))-0;e[3]=r[3]-0}e[0]=j++;return e},ATTR:function(u,r,s,e,v,x){var t=u[1].replace(/\\/g,"");if(!x&&f.attrMap[t]){u[1]=f.attrMap[t]}if(u[2]==="~="){u[4]=" "+u[4]+" "}return u},PSEUDO:function(u,r,s,e,v){if(u[1]==="not"){if((p.exec(u[3])||"").length>1||/^\w/.test(u[3])){u[3]=b(u[3],null,null,r)}else{var t=b.filter(u[3],r,s,true^v);if(!s){e.push.apply(e,t)}return false}}else{if(f.match.POS.test(u[0])||f.match.CHILD.test(u[0])){return true}}return u},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){e.parentNode.selectedIndex;return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(s,r,e){return !!b(e[3],s).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(e){return"text"===e.type},radio:function(e){return"radio"===e.type},checkbox:function(e){return"checkbox"===e.type},file:function(e){return"file"===e.type},password:function(e){return"password"===e.type},submit:function(e){return"submit"===e.type},image:function(e){return"image"===e.type},reset:function(e){return"reset"===e.type},button:function(e){return"button"===e.type||e.nodeName.toLowerCase()==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)}},setFilters:{first:function(r,e){return e===0},last:function(s,r,e,t){return r===t.length-1},even:function(r,e){return e%2===0},odd:function(r,e){return e%2===1},lt:function(s,r,e){return r<e[3]-0},gt:function(s,r,e){return r>e[3]-0},nth:function(s,r,e){return e[3]-0===r},eq:function(s,r,e){return e[3]-0===r}},filter:{PSEUDO:function(s,y,x,z){var e=y[1],r=f.filters[e];if(r){return r(s,x,y,z)}else{if(e==="contains"){return(s.textContent||s.innerText||b.getText([s])||"").indexOf(y[3])>=0}else{if(e==="not"){var t=y[3];for(var v=0,u=t.length;v<u;v++){if(t[v]===s){return false}}return true}else{b.error("Syntax error, unrecognized expression: "+e)}}}},CHILD:function(e,t){var x=t[1],r=e;switch(x){case"only":case"first":while((r=r.previousSibling)){if(r.nodeType===1){return false}}if(x==="first"){return true}r=e;case"last":while((r=r.nextSibling)){if(r.nodeType===1){return false}}return true;case"nth":var s=t[2],A=t[3];if(s===1&&A===0){return true}var v=t[0],z=e.parentNode;if(z&&(z.sizcache!==v||!e.nodeIndex)){var u=0;for(r=z.firstChild;r;r=r.nextSibling){if(r.nodeType===1){r.nodeIndex=++u}}z.sizcache=v}var y=e.nodeIndex-A;if(s===0){return y===0}else{return(y%s===0&&y/s>=0)}}},ID:function(r,e){return r.nodeType===1&&r.getAttribute("id")===e},TAG:function(r,e){return(e==="*"&&r.nodeType===1)||r.nodeName.toLowerCase()===e},CLASS:function(r,e){return(" "+(r.className||r.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(v,t){var s=t[1],e=f.attrHandle[s]?f.attrHandle[s](v):v[s]!=null?v[s]:v.getAttribute(s),x=e+"",u=t[2],r=t[4];return e==null?u==="!=":u==="="?x===r:u==="*="?x.indexOf(r)>=0:u==="~="?(" "+x+" ").indexOf(r)>=0:!r?x&&e!==false:u==="!="?x!==r:u==="^="?x.indexOf(r)===0:u==="$="?x.substr(x.length-r.length)===r:u==="|="?x===r||x.substr(0,r.length+1)===r+"-":false},POS:function(u,r,s,v){var e=r[2],t=f.setFilters[e];if(t){return t(u,s,r,v)}}}};var k=f.match.POS,g=function(r,e){return"\\"+(e-0+1)};for(var m in f.match){f.match[m]=new RegExp(f.match[m].source+(/(?![^\[]*\])(?![^\(]*\))/.source));f.leftMatch[m]=new RegExp(/(^(?:.|\r|\n)*?)/.source+f.match[m].source.replace(/\\(\d+)/g,g))}var a=function(r,e){r=Array.prototype.slice.call(r,0);if(e){e.push.apply(e,r);return e}return r};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType}catch(l){a=function(u,t){var r=t||[],s=0;if(d.call(u)==="[object Array]"){Array.prototype.push.apply(r,u)}else{if(typeof u.length==="number"){for(var e=u.length;s<e;s++){r.push(u[s])}}else{for(;u[s];s++){r.push(u[s])}}}return r}}var c;if(document.documentElement.compareDocumentPosition){c=function(r,e){if(!r.compareDocumentPosition||!e.compareDocumentPosition){if(r==e){o=true}return r.compareDocumentPosition?-1:1}var s=r.compareDocumentPosition(e)&4?-1:r===e?0:1;if(s===0){o=true}return s}}else{if("sourceIndex" in document.documentElement){c=function(r,e){if(!r.sourceIndex||!e.sourceIndex){if(r==e){o=true}return r.sourceIndex?-1:1}var s=r.sourceIndex-e.sourceIndex;if(s===0){o=true}return s}}else{if(document.createRange){c=function(t,r){if(!t.ownerDocument||!r.ownerDocument){if(t==r){o=true}return t.ownerDocument?-1:1}var s=t.ownerDocument.createRange(),e=r.ownerDocument.createRange();s.setStart(t,0);s.setEnd(t,0);e.setStart(r,0);e.setEnd(r,0);var u=s.compareBoundaryPoints(Range.START_TO_END,e);if(u===0){o=true}return u}}}}b.getText=function(e){var r="",t;for(var s=0;e[s];s++){t=e[s];if(t.nodeType===3||t.nodeType===4){r+=t.nodeValue}else{if(t.nodeType!==8){r+=b.getText(t.childNodes)}}}return r};(function(){var r=document.createElement("div"),s="script"+(new Date()).getTime();r.innerHTML="<a name='"+s+"'/>";var e=document.documentElement;e.insertBefore(r,e.firstChild);if(document.getElementById(s)){f.find.ID=function(u,v,x){if(typeof v.getElementById!=="undefined"&&!x){var t=v.getElementById(u[1]);return t?t.id===u[1]||typeof t.getAttributeNode!=="undefined"&&t.getAttributeNode("id").nodeValue===u[1]?[t]:undefined:[]}};f.filter.ID=function(v,t){var u=typeof v.getAttributeNode!=="undefined"&&v.getAttributeNode("id");return v.nodeType===1&&u&&u.nodeValue===t}}e.removeChild(r);e=r=null})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){f.find.TAG=function(r,v){var u=v.getElementsByTagName(r[1]);if(r[1]==="*"){var t=[];for(var s=0;u[s];s++){if(u[s].nodeType===1){t.push(u[s])}}u=t}return u}}e.innerHTML="<a href='#'></a>";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){f.attrHandle.href=function(r){return r.getAttribute("href",2)}}e=null})();if(document.querySelectorAll){(function(){var e=b,s=document.createElement("div");s.innerHTML="<p class='TEST'></p>";if(s.querySelectorAll&&s.querySelectorAll(".TEST").length===0){return}b=function(x,v,t,u){v=v||document;if(!u&&v.nodeType===9&&!b.isXML(v)){try{return a(v.querySelectorAll(x),t)}catch(y){}}return e(x,v,t,u)};for(var r in e){b[r]=e[r]}s=null})()}(function(){var e=document.createElement("div");e.innerHTML="<div class='test e'></div><div class='test'></div>";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}f.order.splice(1,0,"CLASS");f.find.CLASS=function(r,s,t){if(typeof s.getElementsByClassName!=="undefined"&&!t){return s.getElementsByClassName(r[1])}};e=null})();function n(r,x,v,A,y,z){for(var t=0,s=A.length;t<s;t++){var e=A[t];if(e){e=e[r];var u=false;while(e){if(e.sizcache===v){u=A[e.sizset];break}if(e.nodeType===1&&!z){e.sizcache=v;e.sizset=t}if(e.nodeName.toLowerCase()===x){u=e;break}e=e[r]}A[t]=u}}}function q(r,x,v,A,y,z){for(var t=0,s=A.length;t<s;t++){var e=A[t];if(e){e=e[r];var u=false;while(e){if(e.sizcache===v){u=A[e.sizset];break}if(e.nodeType===1){if(!z){e.sizcache=v;e.sizset=t}if(typeof x!=="string"){if(e===x){u=true;break}}else{if(b.filter(x,[e]).length>0){u=e;break}}}e=e[r]}A[t]=u}}}b.contains=document.compareDocumentPosition?function(r,e){return !!(r.compareDocumentPosition(e)&16)}:function(r,e){return r!==e&&(r.contains?r.contains(e):true)};b.isXML=function(e){var r=(e?e.ownerDocument||e:0).documentElement;return r?r.nodeName!=="HTML":false};var h=function(e,y){var t=[],u="",v,s=y.nodeType?[y]:y;while((v=f.match.PSEUDO.exec(e))){u+=v[0];e=e.replace(f.match.PSEUDO,"")}e=f.relative[e]?e+"*":e;for(var x=0,r=s.length;x<r;x++){b(e,s[x],t)}return b.filter(u,t)};window.tinymce.dom.Sizzle=b})();(function(d){var f=d.each,c=d.DOM,b=d.isIE,e=d.isWebKit,a;d.create("tinymce.dom.EventUtils",{EventUtils:function(){this.inits=[];this.events=[]},add:function(m,p,l,j){var g,h=this,i=h.events,k;if(p instanceof Array){k=[];f(p,function(o){k.push(h.add(m,o,l,j))});return k}if(m&&m.hasOwnProperty&&m instanceof Array){k=[];f(m,function(n){n=c.get(n);k.push(h.add(n,p,l,j))});return k}m=c.get(m);if(!m){return}g=function(n){if(h.disabled){return}n=n||window.event;if(n&&b){if(!n.target){n.target=n.srcElement}d.extend(n,h._stoppers)}if(!j){return l(n)}return l.call(j,n)};if(p=="unload"){d.unloads.unshift({func:g});return g}if(p=="init"){if(h.domLoaded){g()}else{h.inits.push(g)}return g}i.push({obj:m,name:p,func:l,cfunc:g,scope:j});h._add(m,p,g);return l},remove:function(l,m,k){var h=this,g=h.events,i=false,j;if(l&&l.hasOwnProperty&&l instanceof Array){j=[];f(l,function(n){n=c.get(n);j.push(h.remove(n,m,k))});return j}l=c.get(l);f(g,function(o,n){if(o.obj==l&&o.name==m&&(!k||(o.func==k||o.cfunc==k))){g.splice(n,1);h._remove(l,m,o.cfunc);i=true;return false}});return i},clear:function(l){var j=this,g=j.events,h,k;if(l){l=c.get(l);for(h=g.length-1;h>=0;h--){k=g[h];if(k.obj===l){j._remove(k.obj,k.name,k.cfunc);k.obj=k.cfunc=null;g.splice(h,1)}}}},cancel:function(g){if(!g){return false}this.stop(g);return this.prevent(g)},stop:function(g){if(g.stopPropagation){g.stopPropagation()}else{g.cancelBubble=true}return false},prevent:function(g){if(g.preventDefault){g.preventDefault()}else{g.returnValue=false}return false},destroy:function(){var g=this;f(g.events,function(j,h){g._remove(j.obj,j.name,j.cfunc);j.obj=j.cfunc=null});g.events=[];g=null},_add:function(h,i,g){if(h.attachEvent){h.attachEvent("on"+i,g)}else{if(h.addEventListener){h.addEventListener(i,g,false)}else{h["on"+i]=g}}},_remove:function(i,j,h){if(i){try{if(i.detachEvent){i.detachEvent("on"+j,h)}else{if(i.removeEventListener){i.removeEventListener(j,h,false)}else{i["on"+j]=null}}}catch(g){}}},_pageInit:function(h){var g=this;if(g.domLoaded){return}g.domLoaded=true;f(g.inits,function(i){i()});g.inits=[]},_wait:function(i){var g=this,h=i.document;if(i.tinyMCE_GZ&&tinyMCE_GZ.loaded){g.domLoaded=1;return}if(h.attachEvent){h.attachEvent("onreadystatechange",function(){if(h.readyState==="complete"){h.detachEvent("onreadystatechange",arguments.callee);g._pageInit(i)}});if(h.documentElement.doScroll&&i==i.top){(function(){if(g.domLoaded){return}try{h.documentElement.doScroll("left")}catch(j){setTimeout(arguments.callee,0);return}g._pageInit(i)})()}}else{if(h.addEventListener){g._add(i,"DOMContentLoaded",function(){g._pageInit(i)})}}g._add(i,"load",function(){g._pageInit(i)})},_stoppers:{preventDefault:function(){this.returnValue=false},stopPropagation:function(){this.cancelBubble=true}}});a=d.dom.Event=new d.dom.EventUtils();a._wait(window);d.addUnload(function(){a.destroy()})})(tinymce);(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j<arguments.length;j++){h.push(arguments[j])}h=e[g].apply(e,h);b.update(g);return h}});a.extend(b,{on:function(i,h,g){return a.dom.Event.add(b.id,i,h,g)},getXY:function(){return{x:parseInt(b.getStyle("left")),y:parseInt(b.getStyle("top"))}},getSize:function(){var g=e.get(b.id);return{w:parseInt(b.getStyle("width")||g.clientWidth),h:parseInt(b.getStyle("height")||g.clientHeight)}},moveTo:function(g,h){b.setStyles({left:g,top:h})},moveBy:function(g,i){var h=b.getXY();b.moveTo(h.x+g,h.y+i)},resizeTo:function(g,i){b.setStyles({width:g,height:i})},resizeBy:function(g,j){var i=b.getSize();b.resizeTo(i.w+g,i.h+j)},update:function(h){var g;if(a.isIE6&&d.blocker){h=h||"";if(h.indexOf("get")===0||h.indexOf("has")===0||h.indexOf("is")===0){return}if(h=="remove"){e.remove(b.blocker);return}if(!b.blocker){b.blocker=e.uniqueId();g=e.add(d.container||e.getRoot(),"iframe",{id:b.blocker,style:"position:absolute;",frameBorder:0,src:'javascript:""'});e.setStyle(g,"opacity",0)}else{g=e.get(b.blocker)}e.setStyles(g,{left:b.getStyle("left",1),top:b.getStyle("top",1),width:b.getStyle("width",1),height:b.getStyle("height",1),display:b.getStyle("display",1),zIndex:parseInt(b.getStyle("zIndex",1)||0)-1})}}})}})(tinymce);(function(c){function e(f){return f.replace(/[\n\r]+/g,"")}var b=c.is,a=c.isIE,d=c.each;c.create("tinymce.dom.Selection",{Selection:function(i,h,g){var f=this;f.dom=i;f.win=h;f.serializer=g;d(["onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent"],function(j){f[j]=new c.util.Dispatcher(f)});if(!f.win.getSelection){f.tridentSel=new c.dom.TridentSelection(f)}if(c.isIE&&i.boxModel){this._fixIESelection()}c.addUnload(f.destroy,f)},setCursorLocation:function(h,i){var f=this;var g=f.dom.createRng();g.setStart(h,i);g.setEnd(h,i);f.setRng(g);f.collapse(false)},getContent:function(g){var f=this,h=f.getRng(),l=f.dom.create("body"),j=f.getSel(),i,k,m;g=g||{};i=k="";g.get=true;g.format=g.format||"html";g.forced_root_block="";f.onBeforeGetContent.dispatch(f,g);if(g.format=="text"){return f.isCollapsed()?"":(h.text||(j.toString?j.toString():""))}if(h.cloneContents){m=h.cloneContents();if(m){l.appendChild(m)}}else{if(b(h.item)||b(h.htmlText)){l.innerHTML="<br>"+(h.item?h.item(0).outerHTML:h.htmlText);l.removeChild(l.firstChild)}else{l.innerHTML=h.toString()}}if(/^\s/.test(l.innerHTML)){i=" "}if(/\s+$/.test(l.innerHTML)){k=" "}g.getInner=true;g.content=f.isCollapsed()?"":i+f.serializer.serialize(l,g)+k;f.onGetContent.dispatch(f,g);return g.content},setContent:function(g,i){var n=this,f=n.getRng(),j,k=n.win.document,m,l;i=i||{format:"html"};i.set=true;g=i.content=g;if(!i.no_events){n.onBeforeSetContent.dispatch(n,i)}g=i.content;if(f.insertNode){g+='<span id="__caret">_</span>';if(f.startContainer==k&&f.endContainer==k){k.body.innerHTML=g}else{f.deleteContents();if(k.body.childNodes.length==0){k.body.innerHTML=g}else{if(f.createContextualFragment){f.insertNode(f.createContextualFragment(g))}else{m=k.createDocumentFragment();l=k.createElement("div");m.appendChild(l);l.outerHTML=g;f.insertNode(m)}}}j=n.dom.get("__caret");f=k.createRange();f.setStartBefore(j);f.setEndBefore(j);n.setRng(f);n.dom.remove("__caret");try{n.setRng(f)}catch(h){}}else{if(f.item){k.execCommand("Delete",false,null);f=n.getRng()}if(/^\s+/.test(g)){f.pasteHTML('<span id="__mce_tmp">_</span>'+g);n.dom.remove("__mce_tmp")}else{f.pasteHTML(g)}}if(!i.no_events){n.onSetContent.dispatch(n,i)}},getStart:function(){var g=this.getRng(),h,f,j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}j=g.duplicate();j.collapse(1);h=j.parentElement();f=i=g.parentElement();while(i=i.parentNode){if(i==h){h=f;break}}return h}else{h=g.startContainer;if(h.nodeType==1&&h.hasChildNodes()){h=h.childNodes[Math.min(h.childNodes.length-1,g.startOffset)]}if(h&&h.nodeType==3){return h.parentNode}return h}},getEnd:function(){var g=this,h=g.getRng(),i,f;if(h.duplicate||h.item){if(h.item){return h.item(0)}h=h.duplicate();h.collapse(0);i=h.parentElement();if(i&&i.nodeName=="BODY"){return i.lastChild||i}return i}else{i=h.endContainer;f=h.endOffset;if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[f>0?f-1:f]}if(i&&i.nodeType==3){return i.parentNode}return i}},getBookmark:function(r,s){var v=this,m=v.dom,g,j,i,n,h,o,p,l="\uFEFF",u;function f(x,y){var t=0;d(m.select(x),function(A,z){if(A==y){t=z}});return t}if(r==2){function k(){var x=v.getRng(true),t=m.getRoot(),y={};function z(C,H){var B=C[H?"startContainer":"endContainer"],G=C[H?"startOffset":"endOffset"],A=[],D,F,E=0;if(B.nodeType==3){if(s){for(D=B.previousSibling;D&&D.nodeType==3;D=D.previousSibling){G+=D.nodeValue.length}}A.push(G)}else{F=B.childNodes;if(G>=F.length&&F.length){E=1;G=Math.max(0,F.length-1)}A.push(v.dom.nodeIndex(F[G],s)+E)}for(;B&&B!=t;B=B.parentNode){A.push(v.dom.nodeIndex(B,s))}return A}y.start=z(x,true);if(!v.isCollapsed()){y.end=z(x)}return y}if(v.tridentSel){return v.tridentSel.getBookmark(r)}return k()}if(r){return{rng:v.getRng()}}g=v.getRng();i=m.uniqueId();n=tinyMCE.activeEditor.selection.isCollapsed();u="overflow:hidden;line-height:0px";if(g.duplicate||g.item){if(!g.item){j=g.duplicate();try{g.collapse();g.pasteHTML('<span data-mce-type="bookmark" id="'+i+'_start" style="'+u+'">'+l+"</span>");if(!n){j.collapse(false);g.moveToElementText(j.parentElement());if(g.compareEndPoints("StartToEnd",j)==0){j.move("character",-1)}j.pasteHTML('<span data-mce-type="bookmark" id="'+i+'_end" style="'+u+'">'+l+"</span>")}}catch(q){return null}}else{o=g.item(0);h=o.nodeName;return{name:h,index:f(h,o)}}}else{o=v.getNode();h=o.nodeName;if(h=="IMG"){return{name:h,index:f(h,o)}}j=g.cloneRange();if(!n){j.collapse(false);j.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_end",style:u},l))}g.collapse(true);g.insertNode(m.create("span",{"data-mce-type":"bookmark",id:i+"_start",style:u},l))}v.moveToBookmark({id:i,keep:1});return{id:i}},moveToBookmark:function(n){var r=this,l=r.dom,i,h,f,q,j,s,o,p;if(n){if(n.start){f=l.createRng();q=l.getRoot();function g(z){var t=n[z?"start":"end"],v,x,y,u;if(t){y=t[0];for(x=q,v=t.length-1;v>=1;v--){u=x.childNodes;if(t[v]>u.length-1){return}x=u[t[v]]}if(x.nodeType===3){y=Math.min(t[0],x.nodeValue.length)}if(x.nodeType===1){y=Math.min(t[0],x.childNodes.length)}if(z){f.setStart(x,y)}else{f.setEnd(x,y)}}return true}if(r.tridentSel){return r.tridentSel.moveToBookmark(n)}if(g(true)&&g()){r.setRng(f)}}else{if(n.id){function k(A){var u=l.get(n.id+"_"+A),z,t,x,y,v=n.keep;if(u){z=u.parentNode;if(A=="start"){if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}j=s=z;o=p=t}else{if(!v){t=l.nodeIndex(u)}else{z=u.firstChild;t=1}s=z;p=t}if(!v){y=u.previousSibling;x=u.nextSibling;d(c.grep(u.childNodes),function(B){if(B.nodeType==3){B.nodeValue=B.nodeValue.replace(/\uFEFF/g,"")}});while(u=l.get(n.id+"_"+A)){l.remove(u,1)}if(y&&x&&y.nodeType==x.nodeType&&y.nodeType==3&&!c.isOpera){t=y.nodeValue.length;y.appendData(x.nodeValue);l.remove(x);if(A=="start"){j=s=y;o=p=t}else{s=y;p=t}}}}}function m(t){if(l.isBlock(t)&&!t.innerHTML){t.innerHTML=!a?'<br data-mce-bogus="1" />':" "}return t}k("start");k("end");if(j){f=l.createRng();f.setStart(m(j),o);f.setEnd(m(s),p);r.setRng(f)}}else{if(n.name){r.select(l.select(n.name)[n.index])}else{if(n.rng){r.setRng(n.rng)}}}}}},select:function(k,j){var i=this,l=i.dom,g=l.createRng(),f;if(k){f=l.nodeIndex(k);g.setStart(k.parentNode,f);g.setEnd(k.parentNode,f+1);if(j){function h(m,o){var n=new c.dom.TreeWalker(m,m);do{if(m.nodeType==3&&c.trim(m.nodeValue).length!=0){if(o){g.setStart(m,0)}else{g.setEnd(m,m.nodeValue.length)}return}if(m.nodeName=="BR"){if(o){g.setStartBefore(m)}else{g.setEndBefore(m)}return}}while(m=(o?n.next():n.prev()))}h(k,1);h(k)}i.setRng(g)}return k},isCollapsed:function(){var f=this,h=f.getRng(),g=f.getSel();if(!h||h.item){return false}if(h.compareEndPoints){return h.compareEndPoints("StartToEnd",h)===0}return !g||h.collapsed},collapse:function(f){var h=this,g=h.getRng(),i;if(g.item){i=g.item(0);g=h.win.document.body.createTextRange();g.moveToElementText(i)}g.collapse(!!f);h.setRng(g)},getSel:function(){var g=this,f=this.win;return f.getSelection?f.getSelection():f.document.selection},getRng:function(l){var g=this,h,i,k,j=g.win.document;if(l&&g.tridentSel){return g.tridentSel.getRangeAt(0)}try{if(h=g.getSel()){i=h.rangeCount>0?h.getRangeAt(0):(h.createRange?h.createRange():j.createRange())}}catch(f){}if(c.isIE&&i&&i.setStart&&j.selection.createRange().item){k=j.selection.createRange().item(0);i=j.createRange();i.setStartBefore(k);i.setEndAfter(k)}if(!i){i=j.createRange?j.createRange():j.body.createTextRange()}if(g.selectedRange&&g.explicitRange){if(i.compareBoundaryPoints(i.START_TO_START,g.selectedRange)===0&&i.compareBoundaryPoints(i.END_TO_END,g.selectedRange)===0){i=g.explicitRange}else{g.selectedRange=null;g.explicitRange=null}}return i},setRng:function(i){var h,g=this;if(!g.tridentSel){h=g.getSel();if(h){g.explicitRange=i;try{h.removeAllRanges()}catch(f){}h.addRange(i);g.selectedRange=h.getRangeAt(0)}}else{if(i.cloneRange){g.tridentSel.addRange(i);return}try{i.select()}catch(f){}}},setNode:function(g){var f=this;f.setContent(f.dom.getOuterHTML(g));return g},getNode:function(){var h=this,g=h.getRng(),i=h.getSel(),l,k=g.startContainer,f=g.endContainer;if(!g){return h.dom.getRoot()}if(g.setStart){l=g.commonAncestorContainer;if(!g.collapsed){if(g.startContainer==g.endContainer){if(g.endOffset-g.startOffset<2){if(g.startContainer.hasChildNodes()){l=g.startContainer.childNodes[g.startOffset]}}}if(k.nodeType===3&&f.nodeType===3){function j(p,m){var o=p;while(p&&p.nodeType===3&&p.length===0){p=m?p.nextSibling:p.previousSibling}return p||o}if(k.length===g.startOffset){k=j(k.nextSibling,true)}else{k=k.parentNode}if(g.endOffset===0){f=j(f.previousSibling,false)}else{f=f.parentNode}if(k&&k===f){return k}}}if(l&&l.nodeType==3){return l.parentNode}return l}return g.item?g.item(0):g.parentElement()},getSelectedBlocks:function(o,g){var m=this,j=m.dom,l,k,h,i=[];l=j.getParent(o||m.getStart(),j.isBlock);k=j.getParent(g||m.getEnd(),j.isBlock);if(l){i.push(l)}if(l&&k&&l!=k){h=l;var f=new c.dom.TreeWalker(l,j.getRoot());while((h=f.next())&&h!=k){if(j.isBlock(h)){i.push(h)}}}if(k&&l!=k){i.push(k)}return i},normalize:function(){var g=this,f,i;if(c.isIE){return}function h(p){var k,o,n,m=g.dom,j=m.getRoot(),l;k=f[(p?"start":"end")+"Container"];o=f[(p?"start":"end")+"Offset"];if(k.nodeType===9){k=k.body;o=0}if(k===j){if(k.hasChildNodes()){k=k.childNodes[Math.min(!p&&o>0?o-1:o,k.childNodes.length-1)];o=0;if(k.hasChildNodes()){l=k;n=new c.dom.TreeWalker(k,j);do{if(l.nodeType===3){o=p?0:l.nodeValue.length-1;k=l;break}if(l.nodeName==="BR"){o=m.nodeIndex(l);k=l.parentNode;break}}while(l=(p?n.next():n.prev()));i=true}}}if(i){f["set"+(p?"Start":"End")](k,o)}}f=g.getRng();h(true);if(f.collapsed){h()}if(i){g.setRng(f)}},destroy:function(g){var f=this;f.win=null;if(!g){c.removeUnload(f.destroy)}},_fixIESelection:function(){var g=this.dom,m=g.doc,h=m.body,j,n,f;m.documentElement.unselectable=true;function i(o,r){var p=h.createTextRange();try{p.moveToPoint(o,r)}catch(q){p=null}return p}function l(p){var o;if(p.button){o=i(p.x,p.y);if(o){if(o.compareEndPoints("StartToStart",n)>0){o.setEndPoint("StartToStart",n)}else{o.setEndPoint("EndToEnd",n)}o.select()}}else{k()}}function k(){var o=m.selection.createRange();if(n&&!o.item&&o.compareEndPoints("StartToEnd",o)===0){n.select()}g.unbind(m,"mouseup",k);g.unbind(m,"mousemove",l);n=j=0}g.bind(m,["mousedown","contextmenu"],function(o){if(o.target.nodeName==="HTML"){if(j){k()}f=m.documentElement;if(f.scrollHeight>f.clientHeight){return}j=1;n=i(o.x,o.y);if(n){g.bind(m,"mouseup",k);g.bind(m,"mousemove",l);g.win.focus();n.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}e.remove_trailing_brs=true;i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/\s*mce(Item\w+|Selected)\s*/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g,"").replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// <![CDATA[\n"+j(o)+"\n// ]]>"}}else{if(o.length>0){n.firstChild.value="<!--\n"+j(o)+"\n-->"}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(m.getInner?o.innerHTML:a.trim(i.getOuterHTML(o),m),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF|\u200B/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],f={},d=[],g=0,e;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=q.create("script",{id:n,type:"text/javascript",src:a._addVer(m)});if(!a.isIE){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==e){j.push(m);l[m]=c}if(q){if(!f[m]){f[m]=[]}f[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(f[r],function(s){s.func.call(s.scope)});f[r]=e}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,s){var i=d.startContainer,l=d.startOffset,t=d.endContainer,m=d.endOffset,j,g,o,h,r,q,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(u){s([u])});return}function f(u){var v;v=u[0];if(v.nodeType===3&&v===i&&l>=v.nodeValue.length){u.splice(0,1)}v=u[u.length-1];if(m===0&&u.length>0&&v===t&&v.nodeType===3){u.splice(u.length-1,1)}return u}function p(x,v,u){var y=[];for(;x&&x!=u;x=x[v]){y.push(x)}return y}function n(v,u){do{if(v.parentNode==u){return v}v=v.parentNode}while(v)}function k(x,v,y){var u=y?"nextSibling":"previousSibling";for(h=x,r=h.parentNode;h&&h!=v;h=r){r=h.parentNode;q=p(h==x?h:h[u],u);if(q.length){if(!y){q.reverse()}s(f(q))}}}if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[l]}if(t.nodeType==1&&t.hasChildNodes()){t=t.childNodes[Math.min(m-1,t.childNodes.length-1)]}if(i==t){return s(f([i]))}j=c.findCommonAncestor(i,t);for(h=i;h;h=h.parentNode){if(h===t){return k(i,j,true)}if(h===j){break}}for(h=t;h;h=h.parentNode){if(h===i){return k(t,j)}if(h===j){break}}g=n(i,j)||i;o=n(t,j)||t;k(i,g,true);q=p(g==i?g:g.nextSibling,"nextSibling",o==t?o.nextSibling:o);if(q.length){s(f(q))}k(t,o)};this.split=function(e){var h=e.startContainer,d=e.startOffset,i=e.endContainer,g=e.endOffset;function f(j,k){return j.splitText(k)}if(h==i&&h.nodeType==3){if(d>0&&d<h.nodeValue.length){i=f(h,d);h=i.previousSibling;if(g>d){g=g-d;h=i=f(i,g).previousSibling;g=i.nodeValue.length;d=0}else{g=0}}}else{if(h.nodeType==3&&d>0&&d<h.nodeValue.length){h=f(h,d);d=0}if(i.nodeType==3&&g>0&&g<i.nodeValue.length){i=f(i,g).previousSibling;g=i.nodeValue.length}}return{startContainer:h,startOffset:d,endContainer:i,endOffset:g}}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var p=this,m=e.root,l=e.items,n=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,k=e.excludeFromTabOrder,j,h,o,d,g;f=f||b.DOM;j=function(q){g=q.target.id};h=function(q){f.setAttrib(q.target.id,"tabindex","-1")};d=function(q){var r=f.get(g);f.setAttrib(r,"tabindex","0");r.focus()};p.focus=function(){f.get(g).focus()};p.destroy=function(){c(l,function(q){f.unbind(f.get(q.id),"focus",j);f.unbind(f.get(q.id),"blur",h)});f.unbind(f.get(m),"focus",d);f.unbind(f.get(m),"keydown",o);l=f=m=p.focus=j=h=o=d=null;p.destroy=function(){}};p.moveFocus=function(u,r){var q=-1,t=p.controls,s;if(!g){return}c(l,function(x,v){if(x.id===g){q=v;return false}});q+=u;if(q<0){q=l.length-1}else{if(q>=l.length){q=0}}s=l[q];f.setAttrib(g,"tabindex","-1");f.setAttrib(s.id,"tabindex","0");f.get(s.id).focus();if(e.actOnFocus){e.onAction(s.id)}if(r){a.cancel(r)}};o=function(y){var u=37,t=39,x=38,z=40,q=27,s=14,r=13,v=32;switch(y.keyCode){case u:if(i){p.moveFocus(-1)}break;case t:if(i){p.moveFocus(1)}break;case x:if(n){p.moveFocus(-1)}break;case z:if(n){p.moveFocus(1)}break;case q:if(e.onCancel){e.onCancel();a.cancel(y)}break;case s:case r:case v:if(e.onAction){e.onAction(g);a.cancel(y)}break}};c(l,function(s,q){var r;if(!s.id){s.id=f.uniqueId("_mce_item_")}if(k){f.bind(s.id,"blur",h);r="-1"}else{r=(q===0?"0":"-1")}f.setAttrib(s.id,"tabindex",r);f.bind(f.get(s.id),"focus",j)});if(l[0]){g=l[0].id}f.setAttrib(m,"tabindex","-1");f.bind(f.get(m),"focus",d);f.bind(f.get(m),"keydown",o)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.clientWidth,j.max_width):g.clientWidth;k=j.max_height?Math.min(g.clientHeight,j.max_height):g.clientHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeight<j.max_height){c.setStyle(l,"overflow","hidden")}}},showMenu:function(p,n,r){var z=this,A=z.settings,o,g=c.getViewPort(),u,l,v,q,i=2,k,j,m=z.classPrefix;z.collapse(1);if(z.isMenuVisible){return}if(!z.rendered){o=c.add(z.settings.container,z.renderNode());f(z.items,function(h){h.postRender()});z.element=new b("menu_"+z.id,{blocker:1,container:A.container})}else{o=c.get("menu_"+z.id)}if(!e.isOpera){c.setStyles(o,{left:-65535,top:-65535})}c.show(o);z.update();p+=A.offset_x||0;n+=A.offset_y||0;g.w-=4;g.h-=4;if(A.constrain){u=o.clientWidth-i;l=o.clientHeight-i;v=g.x+g.w;q=g.y+g.h;if((p+A.vp_offset_x+u)>v){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return a.cancel(s)}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.select("#menu_"+g.id)[0];h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='<a role="button" id="'+this.id+'" href="javascript:;" class="'+f+" "+f+"Enabled "+e["class"]+(c?" "+f+"Labeled":"")+'" onmousedown="return false;" onclick="return false;" aria-labelledby="'+this.id+'_voice" title="'+a.encode(e.title)+'">';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+='<img class="mceIcon" src="'+e.image+'" alt="'+a.encode(e.title)+'" />'+c}else{d+='<span class="mceIcon '+e["class"]+'"></span>'+(c?'<span class="'+f+'Label">'+c+"</span>":"")}d+='<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="'+this.id+'_voice">'+e.title+"</span>";d+="</a>";return d},postRender:function(){var c=this,d=c.settings;b.dom.Event.add(c.id,"click",function(f){if(!c.isDisabled()){return d.onclick.call(d.scope,f)}})}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(i,h,f){var g=this;g.parent(i,h,f);g.items=[];g.onChange=new a(g);g.onPostRender=new a(g);g.onAdd=new a(g);g.onRenderMenu=new d.util.Dispatcher(this);g.classPrefix="mceListBox"},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){var h=this,i,j,g;if(f!=h.selectedIndex){i=c.get(h.id+"_text");g=c.get(h.id+"_voiceDesc");j=h.items[f];if(j){h.selectedValue=j.value;h.selectedIndex=f;c.setHTML(i,c.encode(j.title));c.setHTML(g,h.settings.title+" - "+j.title);c.removeClass(i,"mceTitle");c.setAttrib(h.id,"aria-valuenow",j.title)}else{c.setHTML(i,c.encode(h.settings.title));c.setHTML(g,c.encode(h.settings.title));c.addClass(i,"mceTitle");h.selectedValue=h.selectedIndex=null;c.setAttrib(h.id,"aria-valuenow",h.settings.title)}i=0}},add:function(i,f,h){var g=this;h=h||{};h=d.extend(h,{title:i,value:f});g.items.push(h);g.onAdd.dispatch(g,h)},getLength:function(){return this.items.length},renderHTML:function(){var i="",f=this,g=f.settings,j=f.classPrefix;i='<span role="listbox" aria-haspopup="true" aria-labelledby="'+f.id+'_voiceDesc" aria-describedby="'+f.id+'_voiceDesc"><table role="presentation" tabindex="0" id="'+f.id+'" cellpadding="0" cellspacing="0" class="'+j+" "+j+"Enabled"+(g["class"]?(" "+g["class"]):"")+'"><tbody><tr>';i+="<td>"+c.createHTML("span",{id:f.id+"_voiceDesc","class":"voiceLabel",style:"display:none;"},f.settings.title);i+=c.createHTML("a",{id:f.id+"_text",tabindex:-1,href:"javascript:;","class":"mceText",onclick:"return false;",onmousedown:"return false;"},c.encode(f.settings.title))+"</td>";i+="<td>"+c.createHTML("a",{id:f.id+"_open",tabindex:-1,href:"javascript:;","class":"mceOpen",onclick:"return false;",onmousedown:"return false;"},'<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>')+"</td>";i+="</tr></tbody></table></span>";return i},showMenu:function(){var g=this,i,h=c.get(this.id),f;if(g.isDisabled()||g.items.length==0){return}if(g.menu&&g.menu.isMenuVisible){return g.hideMenu()}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}i=c.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.keyboard_focus=!d.isOpera;if(g.oldID){f.items[g.oldID].setSelected(0)}e(g.items,function(j){if(j.value===g.selectedValue){f.items[j.id].setSelected(1);g.oldID=j.id}});f.showMenu(0,h.clientHeight);b.add(c.doc,"mousedown",g.hideMenu,g);c.addClass(g.id,g.classPrefix+"Selected")},hideMenu:function(g){var f=this;if(f.menu&&f.menu.isMenuVisible){c.removeClass(f.id,f.classPrefix+"Selected");if(g&&g.type=="mousedown"&&(g.target.id==f.id+"_text"||g.target.id==f.id+"_open")){return}if(!g||!c.getParent(g.target,".mceMenu")){c.removeClass(f.id,f.classPrefix+"Selected");b.remove(c.doc,"mousedown",f.hideMenu,f);f.menu.hideMenu()}}},renderMenu:function(){var g=this,f;f=g.settings.control_manager.createDropMenu(g.id+"_menu",{menu_line:1,"class":g.classPrefix+"Menu mceNoIcons",max_width:150,max_height:150});f.onHideMenu.add(function(){g.hideMenu();g.focus()});f.add({title:g.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}});e(g.items,function(h){if(h.value===undefined){f.add({title:h.title,role:"option","class":"mceMenuItemTitle",onclick:function(){if(g.settings.onselect("")!==false){g.select("")}}})}else{h.id=c.uniqueId();h.role="option";h.onclick=function(){if(g.settings.onselect(h.value)!==false){g.select(h.value)}};f.add(h)}});g.onRenderMenu.dispatch(g,f);g.menu=f},postRender:function(){var f=this,g=f.classPrefix;b.add(f.id,"click",f.showMenu,f);b.add(f.id,"keydown",function(h){if(h.keyCode==32){f.showMenu(h);b.cancel(h)}});b.add(f.id,"focus",function(){if(!f._focused){f.keyDownHandler=b.add(f.id,"keydown",function(h){if(h.keyCode==40){f.showMenu();b.cancel(h)}});f.keyPressHandler=b.add(f.id,"keypress",function(i){var h;if(i.keyCode==13){h=f.selectedValue;f.selectedValue=null;b.cancel(i);f.settings.onselect(h)}})}f._focused=1});b.add(f.id,"blur",function(){b.remove(f.id,"keydown",f.keyDownHandler);b.remove(f.id,"keypress",f.keyPressHandler);f._focused=0});if(d.isIE6||!c.boxModel){b.add(f.id,"mouseover",function(){if(!c.hasClass(f.id,g+"Disabled")){c.addClass(f.id,g+"Hover")}});b.add(f.id,"mouseout",function(){if(!c.hasClass(f.id,g+"Disabled")){c.removeClass(f.id,g+"Hover")}})}f.onPostRender.dispatch(f,c.get(f.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(d){var c=d.DOM,b=d.dom.Event,e=d.each,a=d.util.Dispatcher;d.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(g,f){this.parent(g,f);this.classPrefix="mceNativeListBox"},setDisabled:function(f){c.get(this.id).disabled=f;this.setAriaProperty("disabled",f)},isDisabled:function(){return c.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==undefined){return g.selectByIndex(-1)}if(h&&h.call){i=h}else{i=function(f){return f==h}}if(h!=g.selectedValue){e(g.items,function(k,f){if(i(k.value)){j=1;g.selectByIndex(f);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(f){c.get(this.id).selectedIndex=f+1;this.selectedValue=this.items[f]?this.items[f].value:null},add:function(j,g,f){var i,h=this;f=f||{};f.value=g;if(h.isRendered()){c.add(c.get(this.id),"option",f,j)}i={title:j,value:g,attribs:f};h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var g,f=this;g=c.createHTML("option",{value:""},"-- "+f.settings.title+" --");e(f.items,function(h){g+=c.createHTML("option",{value:h.value},h.title)});g=c.createHTML("select",{id:f.id,"class":"mceNativeListBox","aria-labelledby":f.id+"_aria"},g);g+=c.createHTML("span",{id:f.id+"_aria",style:"display: none"},f.settings.title);return g},postRender:function(){var g=this,h,i=true;g.rendered=true;function f(k){var j=g.items[k.target.selectedIndex-1];if(j&&(j=j.value)){g.onChange.dispatch(g,j);if(g.settings.onselect){g.settings.onselect(j)}}}b.add(g.id,"change",f);b.add(g.id,"keydown",function(k){var j;b.remove(g.id,"change",h);i=false;j=b.add(g.id,"blur",function(){if(i){return}i=true;b.add(g.id,"change",f);b.remove(g.id,"blur",j)});if(d.isWebKit&&(k.keyCode==37||k.keyCode==39)){return b.prevent(k)}if(k.keyCode==13||k.keyCode==32){f(k);return b.cancel(k)}});g.onPostRender.dispatch(g,c.get(g.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="<tbody><tr>";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+="<td >"+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');i+="<td >"+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";i+="</tr></tbody>";i=b.createHTML("table",{role:"presentation","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("div",{id:f.id,role:"button",tabindex:"0","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(i){i=i.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");g=c.add(g,"a",{role:"option",href:"javascript:;",style:{backgroundColor:"#"+i},title:p.editor.getLang("colors."+i,i),"data-mce-color":"#"+i});if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+i;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");new d.ui.KeyboardNavigation({root:p.id+"_menu",items:c.select("a",p.id+"_menu"),onCancel:function(){p.hideMenu();p.focus()}});a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return a.cancel(i)});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){this.parent();a.clear(this.id+"_menu");a.clear(this.id+"_more");c.remove(this.id+"_menu")}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('<div id="'+f.id+'" role="group" aria-labelledby="'+f.id+'_voice">');i.push("<span role='application'>");i.push('<span id="'+f.id+'_voice" class="mceVoiceLabel" style="display:none;">'+d.encode(g.name)+"</span>");j(e,function(h){i.push(h.renderHTML())});i.push("</span>");i.push("</div>");return i.join("")},focus:function(){var e=this;d.get(e.id).focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){if(b.isWebKit){d.get(f.editor.id+"_ifr").focus()}f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e<l.length;e++){k=l[e];d=l[e-1];g=l[e+1];if(e===0){j="mceToolbarStart";if(k.Button){j+=" mceToolbarStartButton"}else{if(k.SplitButton){j+=" mceToolbarStartSplitButton"}else{if(k.ListBox){j+=" mceToolbarStartListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,"<!-- IE -->"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,"<!-- IE -->"))}}if(c.stdMode){f+='<td style="position: relative">'+k.renderHTML()+"</td>"}else{f+="<td>"+k.renderHTML()+"</td>"}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,"<!-- IE -->"))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,"<!-- IE -->"));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},"<tbody><tr>"+f+"</tr></tbody>")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!=0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(q){var n=this,p,l=j.ScriptLoader,u,o=[],m;function r(x,y,t){var v=x[y];if(!v){return}if(j.is(v,"string")){t=v.replace(/\.\w+$/,"");t=t?j.resolve(t):0;v=j.resolve(v)}return v.apply(t||this,Array.prototype.slice.call(arguments,2))}q=d({theme:"simple",language:"en"},q);n.settings=q;i.add(document,"init",function(){var s,v;r(q,"onpageload");switch(q.mode){case"exact":s=q.elements||"";if(s.length>0){g(e(s),function(x){if(k.get(x)){m=new j.Editor(x,q);o.push(m);m.render(1)}else{g(document.forms,function(y){g(y.elements,function(z){if(z.name===x){x="mce_editor_"+c++;k.setAttrib(z,"id",x);m=new j.Editor(x,q);o.push(m);m.render(1)}})})}})}break;case"textareas":case"specific_textareas":function t(y,x){return x.constructor===RegExp?x.test(y.className):k.hasClass(y,x)}g(k.select("textarea"),function(x){if(q.editor_deselector&&t(x,q.editor_deselector)){return}if(!q.editor_selector||t(x,q.editor_selector)){u=k.get(x.name);if(!x.id&&!u){x.id=x.name}if(!x.id||n.get(x.id)){x.id=k.uniqueId()}m=new j.Editor(x.id,q);o.push(m);m.render(1)}});break}if(q.oninit){s=v=0;g(o,function(x){v++;if(!x.initialized){x.onInit.add(function(){s++;if(s==v){r(q,"oninit")}})}else{s++}if(s==v){r(q,"oninit")}})}})},get:function(l){if(l===a){return this.editors}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l<o.length;l++){if(o[l]==n){o.splice(l,1);break}}if(m.activeEditor==n){m._setActive(o[0])}n.destroy();m.onRemoveEditor.dispatch(m,n);return n},execCommand:function(r,p,o){var q=this,n=q.get(o),l;switch(r){case"mceFocus":n.focus();return true;case"mceAddEditor":case"mceAddControl":if(!q.get(o)){new j.Editor(o,q.settings).render()}return true;case"mceAddFrameControl":l=o.window;l.tinyMCE=tinyMCE;l.tinymce=j;j.DOM.doc=l.document;j.DOM.win=l;n=new j.Editor(o.element_id,o);n.render();if(j.isIE){function m(){n.destroy();l.detachEvent("onunload",m);l=l.tinyMCE=l.tinymce=null}l.attachEvent("onunload",m)}o.page_window=null;return true;case"mceRemoveEditor":case"mceRemoveControl":if(n){n.remove()}return true;case"mceToggleEditor":if(!n){q.execCommand("mceAddControl",0,o);return true}if(n.isHidden()){n.show()}else{n.hide()}return true}if(q.activeEditor){return q.activeEditor.execCommand(r,p,o)}return false},execInstanceCommand:function(p,o,n,m){var l=this.get(p);if(l){return l.execCommand(o,n,m)}return false},triggerSave:function(){g(this.editors,function(l){l.save()})},addI18n:function(n,q){var l,m=this.i18n;if(!j.is(n,"string")){g(n,function(r,p){g(r,function(t,s){g(t,function(v,u){if(s==="common"){m[p+"."+u]=v}else{m[p+"."+s+"."+u]=v}})})})}else{g(q,function(r,p){m[n+"."+p]=r})}},_setActive:function(l){this.selectedInstance=this.activeEditor=l}})})(tinymce);(function(m){var n=m.DOM,j=m.dom.Event,f=m.extend,k=m.util.Dispatcher,i=m.each,a=m.isGecko,b=m.isIE,e=m.isWebKit,d=m.is,h=m.ThemeManager,c=m.PluginManager,o=m.inArray,l=m.grep,g=m.explode;m.create("tinymce.Editor",{Editor:function(r,q){var p=this;p.id=p.editorId=r;p.execCommands={};p.queryStateCommands={};p.queryValueCommands={};p.isNotDirty=false;p.plugins={};i(["onPreInit","onBeforeRenderUI","onPostRender","onInit","onRemove","onActivate","onDeactivate","onClick","onEvent","onMouseUp","onMouseDown","onDblClick","onKeyDown","onKeyUp","onKeyPress","onContextMenu","onSubmit","onReset","onPaste","onPreProcess","onPostProcess","onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent","onLoadContent","onSaveContent","onNodeChange","onChange","onBeforeExecCommand","onExecCommand","onUndo","onRedo","onVisualAid","onSetProgressState"],function(s){p[s]=new k(p)});p.settings=q=f({id:r,language:"en",docs_language:"en",theme:"simple",skin:"default",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:m.documentBaseURL,add_form_submit_trigger:1,submit_patch:1,add_unload_trigger:1,convert_urls:1,relative_urls:1,remove_script_host:1,table_inline_editing:0,object_resizing:1,cleanup:1,accessibility_focus:1,custom_shortcuts:1,custom_undo_redo_keyboard_shortcuts:1,custom_undo_redo_restore_selection:1,custom_undo_redo:1,doctype:m.isIE6?'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">':"<!DOCTYPE>",visual_table_class:"mceItemTable",visual:1,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",apply_source_formatting:1,directionality:"ltr",forced_root_block:"p",hidden_input:1,padd_empty_editor:1,render_ui:1,init_theme:1,force_p_newlines:1,indentation:"30px",keep_styles:1,fix_table_elements:1,inline_styles:1,convert_fonts_to_spans:true,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr",validate:true,entity_encoding:"named",url_converter:p.convertURL,url_converter_scope:p,ie7_compat:true},q);p.documentBaseURI=new m.util.URI(q.document_base_url||m.documentBaseURL,{base_uri:tinyMCE.baseURI});p.baseURI=m.baseURI;p.contentCSS=[];p.execCallback("setup",p)},render:function(r){var u=this,v=u.settings,x=u.id,p=m.ScriptLoader;if(!j.domLoaded){j.add(document,"init",function(){u.render()});return}tinyMCE.settings=v;if(!u.getElement()){return}if(m.isIDevice&&!m.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(u.getElement().nodeName)&&v.hidden_input&&n.getParent(x,"form")){n.insertAfter(n.create("input",{type:"hidden",name:x}),x)}if(m.WindowManager){u.windowManager=new m.WindowManager(u)}if(v.encoding=="xml"){u.onGetContent.add(function(s,t){if(t.save){t.content=n.encode(t.content)}})}if(v.add_form_submit_trigger){u.onSubmit.addToTop(function(){if(u.initialized){u.save();u.isNotDirty=1}})}if(v.add_unload_trigger){u._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(u.initialized&&!u.destroyed&&!u.isHidden()){u.save({format:"raw",no_events:true})}})}m.addUnload(u.destroy,u);if(v.submit_patch){u.onBeforeRenderUI.add(function(){var s=u.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){u.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){m.triggerSave();u.isNotDirty=1;return u.formElement._mceOldSubmit(u.formElement)}}s=null})}function q(){if(v.language&&v.language_load!==false){p.add(m.baseURL+"/langs/"+v.language+".js")}if(v.theme&&v.theme.charAt(0)!="-"&&!h.urls[v.theme]){h.load(v.theme,"themes/"+v.theme+"/editor_template"+m.suffix+".js")}i(g(v.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(z){var y={prefix:"plugins/",resource:z,suffix:"/editor_plugin"+m.suffix+".js"};var z=c.createUrl(y,z);c.load(z.resource,z)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+m.suffix+".js"})}}});p.loadQueue(function(){if(!u.removed){u.init()}})}q()},init:function(){var r,H=this,I=H.settings,E,A,D=H.getElement(),q,p,F,y,C,G,z,v=[];m.add(H);I.aria_label=I.aria_label||n.getAttrib(D,"aria-label",H.getLang("aria.rich_text_area"));if(I.theme){I.theme=I.theme.replace(/-/,"");q=h.get(I.theme);H.theme=new q();if(H.theme.init&&I.init_theme){H.theme.init(H,h.urls[I.theme]||m.documentBaseURL.replace(/\/$/,""))}}function B(J){var K=c.get(J),t=c.urls[J]||m.documentBaseURL.replace(/\/$/,""),s;if(K&&m.inArray(v,J)===-1){i(c.dependencies(J),function(u){B(u)});s=new K(H,t);H.plugins[J]=s;if(s.init){s.init(H,t);v.push(J)}}}i(g(I.plugins.replace(/\-/g,"")),B);if(I.popup_css!==false){if(I.popup_css){I.popup_css=H.documentBaseURI.toAbsolute(I.popup_css)}else{I.popup_css=H.baseURI.toAbsolute("themes/"+I.theme+"/skins/"+I.skin+"/dialog.css")}}if(I.popup_css_add){I.popup_css+=","+H.documentBaseURI.toAbsolute(I.popup_css_add)}H.controlManager=new m.ControlManager(H);if(I.custom_undo_redo){H.onBeforeExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.beforeChange()}});H.onExecCommand.add(function(t,J,u,K,s){if(J!="Undo"&&J!="Redo"&&J!="mceRepaint"&&(!s||!s.skip_undo)){H.undoManager.add()}})}H.onExecCommand.add(function(s,t){if(!/^(FontName|FontSize)$/.test(t)){H.nodeChanged()}});if(a){function x(s,t){if(!t||!t.initial){H.execCommand("mceRepaint")}}H.onUndo.add(x);H.onRedo.add(x);H.onSetContent.add(x)}H.onBeforeRenderUI.dispatch(H,H.controlManager);if(I.render_ui){E=I.width||D.style.width||D.offsetWidth;A=I.height||D.style.height||D.offsetHeight;H.orgDisplay=D.style.display;G=/^[0-9\.]+(|px)$/i;if(G.test(""+E)){E=Math.max(parseInt(E)+(q.deltaWidth||0),100)}if(G.test(""+A)){A=Math.max(parseInt(A)+(q.deltaHeight||0),100)}q=H.theme.renderUI({targetNode:D,width:E,height:A,deltaWidth:I.delta_width,deltaHeight:I.delta_height});H.editorContainer=q.editorContainer}if(document.domain&&location.hostname!=document.domain){m.relaxedDomain=document.domain}n.setStyles(q.sizeContainer||q.editorContainer,{width:E,height:A});if(I.content_css){m.each(g(I.content_css),function(s){H.contentCSS.push(H.documentBaseURI.toAbsolute(s))})}A=(q.iframeHeight||A)+(typeof(A)=="number"?(q.deltaHeight||0):"");if(A<100){A=100}H.iframeHTML=I.doctype+'<html><head xmlns="http://www.w3.org/1999/xhtml">';if(I.document_base_url!=m.documentBaseURL){H.iframeHTML+='<base href="'+H.documentBaseURI.getURI()+'" />'}if(I.ie7_compat){H.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=7" />'}else{H.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=edge" />'}H.iframeHTML+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';for(z=0;z<H.contentCSS.length;z++){H.iframeHTML+='<link type="text/css" rel="stylesheet" href="'+H.contentCSS[z]+'" />'}y=I.body_id||"tinymce";if(y.indexOf("=")!=-1){y=H.getParam("body_id","","hash");y=y[H.id]||y}C=I.body_class||"";if(C.indexOf("=")!=-1){C=H.getParam("body_class","","hash");C=C[H.id]||""}H.iframeHTML+='</head><body id="'+y+'" class="mceContentBody '+C+'"><br></body></html>';if(m.relaxedDomain&&(b||(m.isOpera&&parseFloat(opera.version())<11))){F='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+H.id+'");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()'}r=n.add(q.iframeContainer,"iframe",{id:H.id+"_ifr",src:F||'javascript:""',frameBorder:"0",allowTransparency:"true",title:I.aria_label,style:{width:"100%",height:A,display:"block"}});H.contentAreaContainer=q.iframeContainer;n.get(q.editorContainer).style.display=H.orgDisplay;n.get(H.id).style.display="none";n.setAttrib(H.id,"aria-hidden",true);if(!m.relaxedDomain||!F){H.setupIframe()}D=r=q=null},setupIframe:function(){var q=this,v=q.settings,x=n.get(q.id),y=q.getDoc(),u,p;if(!b||!m.relaxedDomain){y.open();y.write(q.iframeHTML);y.close();if(m.relaxedDomain){y.domain=m.relaxedDomain}}p=q.getBody();p.disabled=true;if(!v.readonly){p.contentEditable=true}p.disabled=false;q.schema=new m.html.Schema(v);q.dom=new m.dom.DOMUtils(q.getDoc(),{keep_values:true,url_converter:q.convertURL,url_converter_scope:q,hex_colors:v.force_hex_style_colors,class_filter:v.class_filter,update_styles:1,fix_ie_paragraphs:1,schema:q.schema});q.parser=new m.html.DomParser(v,q.schema);if(!q.settings.allow_html_in_named_anchor){q.parser.addAttributeFilter("name",function(s,t){var A=s.length,C,z,B,D;while(A--){D=s[A];if(D.name==="a"&&D.firstChild){B=D.parent;C=D.lastChild;do{z=C.prev;B.insert(C,D);C=z}while(C)}}})}q.parser.addAttributeFilter("src,href,style",function(s,t){var z=s.length,B,D=q.dom,C,A;while(z--){B=s[z];C=B.attr(t);A="data-mce-"+t;if(!B.attributes.map[A]){if(t==="style"){B.attr(A,D.serializeStyle(D.parseStyle(C),B.name))}else{B.attr(A,q.convertURL(C,t,B.name))}}}});q.parser.addNodeFilter("script",function(s,t){var z=s.length,A;while(z--){A=s[z];A.attr("type","mce-"+(A.attr("type")||"text/javascript"))}});q.parser.addNodeFilter("#cdata",function(s,t){var z=s.length,A;while(z--){A=s[z];A.type=8;A.name="#comment";A.value="[CDATA["+A.value+"]]"}});q.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(t,z){var A=t.length,B,s=q.schema.getNonEmptyElements();while(A--){B=t[A];if(B.isEmpty(s)){B.empty().append(new m.html.Node("br",1)).shortEnded=true}}});q.serializer=new m.dom.Serializer(v,q.dom,q.schema);q.selection=new m.dom.Selection(q.dom,q.getWin(),q.serializer);q.formatter=new m.Formatter(this);q.formatter.register({alignleft:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"}},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"}},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"}},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"}}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(s){return true},onformat:function(z,s,t){i(t,function(B,A){q.dom.setAttrib(z,A,B)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});i("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(s){q.formatter.register(s,{block:s,remove:"all"})});q.formatter.register(q.settings.formats);q.undoManager=new m.UndoManager(q);q.undoManager.onAdd.add(function(t,s){if(t.hasUndo()){return q.onChange.dispatch(q,s,t)}});q.undoManager.onUndo.add(function(t,s){return q.onUndo.dispatch(q,s,t)});q.undoManager.onRedo.add(function(t,s){return q.onRedo.dispatch(q,s,t)});q.forceBlocks=new m.ForceBlocks(q,{forced_root_block:v.forced_root_block});q.editorCommands=new m.EditorCommands(q);q.serializer.onPreProcess.add(function(s,t){return q.onPreProcess.dispatch(q,t,s)});q.serializer.onPostProcess.add(function(s,t){return q.onPostProcess.dispatch(q,t,s)});q.onPreInit.dispatch(q);if(!v.gecko_spellcheck){q.getBody().spellcheck=0}if(!v.readonly){q._addEvents()}q.controlManager.onPostRender.dispatch(q,q.controlManager);q.onPostRender.dispatch(q);q.quirks=new m.util.Quirks(this);if(v.directionality){q.getBody().dir=v.directionality}if(v.nowrap){q.getBody().style.whiteSpace="nowrap"}if(v.handle_node_change_callback){q.onNodeChange.add(function(t,s,z){q.execCallback("handle_node_change_callback",q.id,z,-1,-1,true,q.selection.isCollapsed())})}if(v.save_callback){q.onSaveContent.add(function(s,z){var t=q.execCallback("save_callback",q.id,z.content,q.getBody());if(t){z.content=t}})}if(v.onchange_callback){q.onChange.add(function(t,s){q.execCallback("onchange_callback",q,s)})}if(v.protect){q.onBeforeSetContent.add(function(s,t){if(v.protect){i(v.protect,function(z){t.content=t.content.replace(z,function(A){return"<!--mce:protected "+escape(A)+"-->"})})}})}if(v.convert_newlines_to_brs){q.onBeforeSetContent.add(function(s,t){if(t.initial){t.content=t.content.replace(/\r?\n/g,"<br />")}})}if(v.preformatted){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^\s*<pre.*?>/,"");t.content=t.content.replace(/<\/pre>\s*$/,"");if(t.set){t.content='<pre class="mceItemHidden">'+t.content+"</pre>"}})}if(v.verify_css_classes){q.serializer.attribValueFilter=function(B,z){var A,t;if(B=="class"){if(!q.classesRE){t=q.dom.getClasses();if(t.length>0){A="";i(t,function(s){A+=(A?"|":"")+s["class"]});q.classesRE=new RegExp("("+A+")","gi")}}return !q.classesRE||/(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(z)||q.classesRE.test(z)?z:""}return z}}if(v.cleanup_callback){q.onBeforeSetContent.add(function(s,t){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)});q.onPreProcess.add(function(s,t){if(t.set){q.execCallback("cleanup_callback","insert_to_editor_dom",t.node,t)}if(t.get){q.execCallback("cleanup_callback","get_from_editor_dom",t.node,t)}});q.onPostProcess.add(function(s,t){if(t.set){t.content=q.execCallback("cleanup_callback","insert_to_editor",t.content,t)}if(t.get){t.content=q.execCallback("cleanup_callback","get_from_editor",t.content,t)}})}if(v.save_callback){q.onGetContent.add(function(s,t){if(t.save){t.content=q.execCallback("save_callback",q.id,t.content,q.getBody())}})}if(v.handle_event_callback){q.onEvent.add(function(s,t,z){if(q.execCallback("handle_event_callback",t,s,z)===false){j.cancel(t)}})}q.onSetContent.add(function(){q.addVisual(q.getBody())});if(v.padd_empty_editor){q.onPostProcess.add(function(s,t){t.content=t.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/,"")})}if(a){function r(s,t){i(s.dom.select("a"),function(A){var z=A.parentNode;if(s.dom.isBlock(z)&&z.lastChild===A){s.dom.add(z,"br",{"data-mce-bogus":1})}})}q.onExecCommand.add(function(s,t){if(t==="CreateLink"){r(s)}});q.onSetContent.add(q.selection.onSetContent.add(r))}q.load({initial:true,format:"html"});q.startContent=q.getContent({format:"raw"});q.undoManager.add();q.initialized=true;q.onInit.dispatch(q);q.execCallback("setupcontent_callback",q.id,q.getBody(),q.getDoc());q.execCallback("init_instance_callback",q);q.focus(true);q.nodeChanged({initial:1});i(q.contentCSS,function(s){q.dom.loadCSS(s)});if(v.auto_focus){setTimeout(function(){var s=m.get(v.auto_focus);s.selection.select(s.getBody(),1);s.selection.collapse(1);s.getBody().focus();s.getWin().focus()},100)}x=null},focus:function(u){var y,q=this,s=q.selection,x=q.settings.content_editable,r,p,v=q.getDoc();if(!u){r=s.getRng();if(r.item){p=r.item(0)}q._refreshContentEditable();s.normalize();if(!x){q.getWin().focus()}if(m.isGecko){q.getBody().focus()}if(p&&p.ownerDocument==v){r=v.body.createControlRange();r.addElement(p);r.select()}}if(m.activeEditor!=q){if((y=m.activeEditor)!=null){y.onDeactivate.dispatch(y,q)}q.onActivate.dispatch(q,y)}m._setActive(q)},execCallback:function(u){var p=this,r=p.settings[u],q;if(!r){return}if(p.callbackLookup&&(q=p.callbackLookup[u])){r=q.func;q=q.scope}if(d(r,"string")){q=r.replace(/\.\w+$/,"");q=q?m.resolve(q):0;r=m.resolve(r);p.callbackLookup=p.callbackLookup||{};p.callbackLookup[u]={func:r,scope:q}}return r.apply(q||p,Array.prototype.slice.call(arguments,1))},translate:function(p){var r=this.settings.language||"en",q=m.i18n;if(!p){return""}return q[r+"."+p]||p.replace(/{\#([^}]+)\}/g,function(t,s){return q[r+"."+s]||"{#"+s+"}"})},getLang:function(q,p){return m.i18n[(this.settings.language||"en")+"."+q]||(d(p)?p:"{#"+q+"}")},getParam:function(u,r,p){var s=m.trim,q=d(this.settings[u])?this.settings[u]:r,t;if(p==="hash"){t={};if(d(q,"string")){i(q.indexOf("=")>0?q.split(/[;,](?![^=;,]*(?:[;,]|$))/):q.split(","),function(x){x=x.split("=");if(x.length>1){t[s(x[0])]=s(x[1])}else{t[s(x[0])]=s(x)}})}else{t=q}return t}return q},nodeChanged:function(r){var p=this,q=p.selection,u=q.getStart()||p.getBody();if(p.initialized){r=r||{};u=b&&u.ownerDocument!=p.getDoc()?p.getBody():u;r.parents=[];p.dom.getParent(u,function(s){if(s.nodeName=="BODY"){return true}r.parents.push(s)});p.onNodeChange.dispatch(p,r?r.controlManager||p.controlManager:p.controlManager,u,q.isCollapsed(),r)}},addButton:function(r,q){var p=this;p.buttons=p.buttons||{};p.buttons[r]=q},addCommand:function(p,r,q){this.execCommands[p]={func:r,scope:q||this}},addQueryStateHandler:function(p,r,q){this.queryStateCommands[p]={func:r,scope:q||this}},addQueryValueHandler:function(p,r,q){this.queryValueCommands[p]={func:r,scope:q||this}},addShortcut:function(r,u,p,s){var q=this,v;if(!q.settings.custom_shortcuts){return false}q.shortcuts=q.shortcuts||{};if(d(p,"string")){v=p;p=function(){q.execCommand(v,false,null)}}if(d(p,"object")){v=p;p=function(){q.execCommand(v[0],v[1],v[2])}}i(g(r),function(t){var x={func:p,scope:s||this,desc:u,alt:false,ctrl:false,shift:false};i(g(t,"+"),function(y){switch(y){case"alt":case"ctrl":case"shift":x[y]=true;break;default:x.charCode=y.charCodeAt(0);x.keyCode=y.toUpperCase().charCodeAt(0)}});q.shortcuts[(x.ctrl?"ctrl":"")+","+(x.alt?"alt":"")+","+(x.shift?"shift":"")+","+x.keyCode]=x});return true},execCommand:function(x,v,z,p){var r=this,u=0,y,q;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(x)&&(!p||!p.skip_focus)){r.focus()}y={};r.onBeforeExecCommand.dispatch(r,x,v,z,y);if(y.terminate){return false}if(r.execCallback("execcommand_callback",r.id,r.selection.getNode(),x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(y=r.execCommands[x]){q=y.func.call(y.scope,v,z);if(q!==true){r.onExecCommand.dispatch(r,x,v,z,p);return q}}i(r.plugins,function(s){if(s.execCommand&&s.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);u=1;return false}});if(u){return true}if(r.theme&&r.theme.execCommand&&r.theme.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}if(r.editorCommands.execCommand(x,v,z)){r.onExecCommand.dispatch(r,x,v,z,p);return true}r.getDoc().execCommand(x,v,z);r.onExecCommand.dispatch(r,x,v,z,p)},queryCommandState:function(u){var q=this,v,r;if(q._isHidden()){return}if(v=q.queryStateCommands[u]){r=v.func.call(v.scope);if(r!==true){return r}}v=q.editorCommands.queryCommandState(u);if(v!==-1){return v}try{return this.getDoc().queryCommandState(u)}catch(p){}},queryCommandValue:function(v){var q=this,u,r;if(q._isHidden()){return}if(u=q.queryValueCommands[v]){r=u.func.call(u.scope);if(r!==true){return r}}u=q.editorCommands.queryCommandValue(v);if(d(u)){return u}try{return this.getDoc().queryCommandValue(v)}catch(p){}},show:function(){var p=this;n.show(p.getContainer());n.hide(p.id);p.load()},hide:function(){var p=this,q=p.getDoc();if(b&&q){q.execCommand("SelectAll")}p.save();n.hide(p.getContainer());n.setStyle(p.id,"display",p.orgDisplay)},isHidden:function(){return !n.isHidden(this.id)},setProgressState:function(p,q,r){this.onSetProgressState.dispatch(this,p,q,r);return p},load:function(s){var p=this,r=p.getElement(),q;if(r){s=s||{};s.load=true;q=p.setContent(d(r.value)?r.value:r.innerHTML,s);s.element=r;if(!s.no_events){p.onLoadContent.dispatch(p,s)}s.element=r=null;return q}},save:function(u){var p=this,s=p.getElement(),q,r;if(!s||!p.initialized){return}u=u||{};u.save=true;if(!u.no_events){p.undoManager.typing=false;p.undoManager.add()}u.element=s;q=u.content=p.getContent(u);if(!u.no_events){p.onSaveContent.dispatch(p,u)}q=u.content;if(!/TEXTAREA|INPUT/i.test(s.nodeName)){s.innerHTML=q;if(r=n.getParent(p.id,"form")){i(r.elements,function(t){if(t.name==p.id){t.value=q;return false}})}}else{s.value=q}u.element=s=null;return q},setContent:function(u,s){var r=this,q,p=r.getBody(),t;s=s||{};s.format=s.format||"html";s.set=true;s.content=u;if(!s.no_events){r.onBeforeSetContent.dispatch(r,s)}u=s.content;if(!m.isIE&&(u.length===0||/^\s+$/.test(u))){t=r.settings.forced_root_block;if(t){u="<"+t+'><br data-mce-bogus="1"></'+t+">"}else{u='<br data-mce-bogus="1">'}p.innerHTML=u;r.selection.select(p,true);r.selection.collapse(true);return}if(s.format!=="raw"){u=new m.html.Serializer({},r.schema).serialize(r.parser.parse(u))}s.content=m.trim(u);r.dom.setHTML(p,s.content);if(!s.no_events){r.onSetContent.dispatch(r,s)}r.selection.normalize();return s.content},getContent:function(q){var p=this,r;q=q||{};q.format=q.format||"html";q.get=true;if(!q.no_events){p.onBeforeGetContent.dispatch(p,q)}if(q.format=="raw"){r=p.getBody().innerHTML}else{r=p.serializer.serialize(p.getBody(),q)}q.content=m.trim(r);if(!q.no_events){p.onGetContent.dispatch(p,q)}return q.content},isDirty:function(){var p=this;return m.trim(p.startContent)!=m.trim(p.getContent({format:"raw",no_events:1}))&&!p.isNotDirty},getContainer:function(){var p=this;if(!p.container){p.container=n.get(p.editorContainer||p.id+"_parent")}return p.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return n.get(this.settings.content_element||this.id)},getWin:function(){var p=this,q;if(!p.contentWindow){q=n.get(p.id+"_ifr");if(q){p.contentWindow=q.contentWindow}}return p.contentWindow},getDoc:function(){var q=this,p;if(!q.contentDocument){p=q.getWin();if(p){q.contentDocument=p.document}}return q.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(p,x,v){var q=this,r=q.settings;if(r.urlconverter_callback){return q.execCallback("urlconverter_callback",p,v,true,x)}if(!r.convert_urls||(v&&v.nodeName=="LINK")||p.indexOf("file:")===0){return p}if(r.relative_urls){return q.documentBaseURI.toRelative(p)}p=q.documentBaseURI.toAbsolute(p,r.remove_script_host);return p},addVisual:function(r){var p=this,q=p.settings;r=r||p.getBody();if(!d(p.hasVisual)){p.hasVisual=q.visual}i(p.dom.select("table,a",r),function(t){var s;switch(t.nodeName){case"TABLE":s=p.dom.getAttrib(t,"border");if(!s||s=="0"){if(p.hasVisual){p.dom.addClass(t,q.visual_table_class)}else{p.dom.removeClass(t,q.visual_table_class)}}return;case"A":s=p.dom.getAttrib(t,"name");if(s){if(p.hasVisual){p.dom.addClass(t,"mceItemAnchor")}else{p.dom.removeClass(t,"mceItemAnchor")}}return}});p.onVisualAid.dispatch(p,r,p.hasVisual)},remove:function(){var p=this,q=p.getContainer();p.removed=1;p.hide();p.execCallback("remove_instance_callback",p);p.onRemove.dispatch(p);p.onExecCommand.listeners=[];m.remove(p);n.remove(q)},destroy:function(q){var p=this;if(p.destroyed){return}if(!q){m.removeUnload(p.destroy);tinyMCE.onBeforeUnload.remove(p._beforeUnload);if(p.theme&&p.theme.destroy){p.theme.destroy()}p.controlManager.destroy();p.selection.destroy();p.dom.destroy();if(!p.settings.content_editable){j.clear(p.getWin());j.clear(p.getDoc())}j.clear(p.getBody());j.clear(p.formElement)}if(p.formElement){p.formElement.submit=p.formElement._mceOldSubmit;p.formElement._mceOldSubmit=null}p.contentAreaContainer=p.formElement=p.container=p.settings.content_element=p.bodyElement=p.contentDocument=p.contentWindow=null;if(p.selection){p.selection=p.selection.win=p.selection.dom=p.selection.dom.doc=null}p.destroyed=1},_addEvents:function(){var B=this,r,C=B.settings,q=B.dom,x={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function p(t,D){var s=t.type;if(B.removed){return}if(B.onEvent.dispatch(B,t,D)!==false){B[x[t.fakeType||t.type]].dispatch(B,t,D)}}i(x,function(t,s){switch(s){case"contextmenu":q.bind(B.getDoc(),s,p);break;case"paste":q.bind(B.getBody(),s,function(D){p(D)});break;case"submit":case"reset":q.bind(B.getElement().form||n.getParent(B.id,"form"),s,p);break;default:q.bind(C.content_editable?B.getBody():B.getDoc(),s,p)}});q.bind(C.content_editable?B.getBody():(a?B.getDoc():B.getWin()),"focus",function(s){B.focus(true)});if(m.isGecko){q.bind(B.getDoc(),"DOMNodeInserted",function(t){var s;t=t.target;if(t.nodeType===1&&t.nodeName==="IMG"&&(s=t.getAttribute("data-mce-src"))){t.src=B.documentBaseURI.toAbsolute(s)}})}if(a){function u(){var E=this,G=E.getDoc(),F=E.settings;if(a&&!F.readonly){E._refreshContentEditable();try{G.execCommand("styleWithCSS",0,false)}catch(D){if(!E._isHidden()){try{G.execCommand("useCSS",0,true)}catch(D){}}}if(!F.table_inline_editing){try{G.execCommand("enableInlineTableEditing",false,false)}catch(D){}}if(!F.object_resizing){try{G.execCommand("enableObjectResizing",false,false)}catch(D){}}}}B.onBeforeExecCommand.add(u);B.onMouseDown.add(u)}B.onMouseUp.add(B.nodeChanged);B.onKeyUp.add(function(s,t){var D=t.keyCode;if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45||D==46||D==8||(m.isMac&&(D==91||D==93))||t.ctrlKey){B.nodeChanged()}});B.onKeyDown.add(function(t,D){if(D.keyCode!=8){return}var F=t.selection.getRng().startContainer;var E=t.selection.getRng().startOffset;while(F&&F.nodeType&&F.nodeType!=1&&F.parentNode){F=F.parentNode}if(F&&F.parentNode&&F.parentNode.tagName==="BLOCKQUOTE"&&F.parentNode.firstChild==F&&E==0){t.formatter.toggle("blockquote",null,F.parentNode);var s=t.selection.getRng();s.setStart(F,0);s.setEnd(F,0);t.selection.setRng(s);t.selection.collapse(false)}});B.onReset.add(function(){B.setContent(B.startContent,{format:"raw"})});if(C.custom_shortcuts){if(C.custom_undo_redo_keyboard_shortcuts){B.addShortcut("ctrl+z",B.getLang("undo_desc"),"Undo");B.addShortcut("ctrl+y",B.getLang("redo_desc"),"Redo")}B.addShortcut("ctrl+b",B.getLang("bold_desc"),"Bold");B.addShortcut("ctrl+i",B.getLang("italic_desc"),"Italic");B.addShortcut("ctrl+u",B.getLang("underline_desc"),"Underline");for(r=1;r<=6;r++){B.addShortcut("ctrl+"+r,"",["FormatBlock",false,"h"+r])}B.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);B.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);B.addShortcut("ctrl+9","",["FormatBlock",false,"address"]);function v(t){var s=null;if(!t.altKey&&!t.ctrlKey&&!t.metaKey){return s}i(B.shortcuts,function(D){if(m.isMac&&D.ctrl!=t.metaKey){return}else{if(!m.isMac&&D.ctrl!=t.ctrlKey){return}}if(D.alt!=t.altKey){return}if(D.shift!=t.shiftKey){return}if(t.keyCode==D.keyCode||(t.charCode&&t.charCode==D.charCode)){s=D;return false}});return s}B.onKeyUp.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyPress.add(function(s,t){var D=v(t);if(D){return j.cancel(t)}});B.onKeyDown.add(function(s,t){var D=v(t);if(D){D.func.call(D.scope);return j.cancel(t)}})}if(m.isIE){q.bind(B.getDoc(),"controlselect",function(D){var t=B.resizeInfo,s;D=D.target;if(D.nodeName!=="IMG"){return}if(t){q.unbind(t.node,t.ev,t.cb)}if(!q.hasClass(D,"mceItemNoResize")){ev="resizeend";s=q.bind(D,ev,function(F){var E;F=F.target;if(E=q.getStyle(F,"width")){q.setAttrib(F,"width",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"width","")}if(E=q.getStyle(F,"height")){q.setAttrib(F,"height",E.replace(/[^0-9%]+/g,""));q.setStyle(F,"height","")}})}else{ev="resizestart";s=q.bind(D,"resizestart",j.cancel,j)}t=B.resizeInfo={node:D,ev:ev,cb:s}})}if(m.isOpera){B.onClick.add(function(s,t){j.prevent(t)})}if(C.custom_undo_redo){function y(){B.undoManager.typing=false;B.undoManager.add()}q.bind(B.getDoc(),"focusout",function(s){if(!B.removed&&B.undoManager.typing){y()}});B.dom.bind(B.dom.getRoot(),"dragend",function(s){y()});B.onKeyUp.add(function(s,D){var t=D.keyCode;if((t>=33&&t<=36)||(t>=37&&t<=40)||t==13||t==45||D.ctrlKey){y()}});B.onKeyDown.add(function(s,E){var D=E.keyCode,t;if(D==8){t=B.getDoc().selection;if(t&&t.createRange&&t.createRange().item){B.undoManager.beforeChange();s.dom.remove(t.createRange().item(0));y();return j.cancel(E)}}if((D>=33&&D<=36)||(D>=37&&D<=40)||D==13||D==45){if(m.isIE&&D==13){B.undoManager.beforeChange()}if(B.undoManager.typing){y()}return}if((D<16||D>20)&&D!=224&&D!=91&&!B.undoManager.typing){B.undoManager.beforeChange();B.undoManager.typing=true;B.undoManager.add()}});B.onMouseDown.add(function(){if(B.undoManager.typing){y()}})}if(m.isGecko){function A(){var s=B.dom.getAttribs(B.selection.getStart().cloneNode(false));return function(){var t=B.selection.getStart();if(t!==B.getBody()){B.dom.setAttrib(t,"style",null);i(s,function(D){t.setAttributeNode(D.cloneNode(true))})}}}function z(){var t=B.selection;return !t.isCollapsed()&&t.getStart()!=t.getEnd()}B.onKeyPress.add(function(s,D){var t;if((D.keyCode==8||D.keyCode==46)&&z()){t=A();B.getDoc().execCommand("delete",false,null);t();return j.cancel(D)}});B.dom.bind(B.getDoc(),"cut",function(t){var s;if(z()){s=A();B.onKeyUp.addToTop(j.cancel,j);setTimeout(function(){s();B.onKeyUp.remove(j.cancel,j)},0)}})}},_refreshContentEditable:function(){var q=this,p,r;if(q._isHidden()){p=q.getBody();r=p.parentNode;r.removeChild(p);r.appendChild(p);p.focus()}},_isHidden:function(){var p;if(!a){return 0}p=this.selection.getSel();return(!p||!p.rangeCount||p.rangeCount==0)}})})(tinymce);(function(c){var d=c.each,e,a=true,b=false;c.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return b}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return b}function u(v,x){x=x||"exec";d(v,function(z,y){d(y.toLowerCase().split(","),function(A){j[x][A]=z})})}c.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===e){x=b}if(v===e){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:e)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(c.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(b)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);d("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=c.explode(k.font_size_style_values);v=c.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return b}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new c.html.Serializer({},n.schema);v='<span id="mce_marker" data-mce-type="bookmark">\uFEFF</span>';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=n.selection.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.y<L.y)||(C.x>L.x+L.w||C.x<L.x)){H=c.isIE?n.getDoc().documentElement:n.getBody();H.scrollLeft=C.x;H.scrollTop=C.y-L.h+25}x=m.createRng();A=D.previousSibling;if(A&&A.nodeType==3){x.setStart(A,A.nodeValue.length)}else{x.setStartBefore(D);x.setEndBefore(D)}m.remove(D);p.setRng(x);p.onSetContent.dispatch(p,G);n.addVisual()},mceInsertRawHTML:function(y,x,v){p.setContent("tiny_mce_marker");n.setContent(n.getContent().replace(/tiny_mce_marker/g,function(){return v}))},mceSetContent:function(y,x,v){n.setContent(v)},"Indent,Outdent":function(z){var x,v,y;x=k.indentation;v=/[a-z%]+$/i.exec(x);x=parseInt(x);if(!l("InsertUnorderedList")&&!l("InsertOrderedList")){d(p.getSelectedBlocks(),function(A){if(z=="outdent"){y=Math.max(0,parseInt(A.style.paddingLeft||0)-x);m.setStyle(A,"paddingLeft",y?y+v:"")}else{m.setStyle(A,"paddingLeft",(parseInt(A.style.paddingLeft||0)+x)+v)}})}else{f(z)}},mceRepaint:function(){var x;if(c.isGecko){try{i(a);if(p.getSel()){p.getSel().selectAllChildren(n.getBody())}p.collapse(a);g()}catch(v){}}},mceToggleFormat:function(y,x,v){q.toggle(v)},InsertHorizontalRule:function(){n.execCommand("mceInsertContent",false,"<hr />")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();v.setStart(x,0);v.setEnd(x,x.childNodes.length);n.selection.setRng(v)}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){return t("align"+v.substring(7))},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(v){return m.getParent(p.getNode(),v=="insertunorderedlist"?"UL":"OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");if(k.custom_undo_redo){u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(f){var d,e=0,h=[],c;function g(){return b.trim(f.getContent({format:"raw",no_events:1}))}return d={typing:false,onAdd:new a(d),onUndo:new a(d),onRedo:new a(d),beforeChange:function(){c=f.selection.getBookmark(2,true)},add:function(m){var j,k=f.settings,l;m=m||{};m.content=g();l=h[e];if(l&&l.content==m.content){return null}if(h[e]){h[e].beforeBookmark=c}if(k.custom_undo_redo_levels){if(h.length>k.custom_undo_redo_levels){for(j=0;j<h.length-1;j++){h[j]=h[j+1]}h.length--;e=h.length}}m.bookmark=f.selection.getBookmark(2,true);if(e<h.length-1){h.length=e+1}h.push(m);e=h.length-1;d.onAdd.dispatch(d,m);f.isNotDirty=0;return m},undo:function(){var k,j;if(d.typing){d.add();d.typing=false}if(e>0){k=h[--e];f.setContent(k.content,{format:"raw"});f.selection.moveToBookmark(k.beforeBookmark);d.onUndo.dispatch(d,k)}return k},redo:function(){var i;if(e<h.length-1){i=h[++e];f.setContent(i.content,{format:"raw"});f.selection.moveToBookmark(i.bookmark);d.onRedo.dispatch(d,i)}return i},clear:function(){h=[];e=0;d.typing=false},hasUndo:function(){return e>0||this.typing},hasRedo:function(){return e<h.length-1&&!this.typing}}}})(tinymce);(function(l){var j=l.dom.Event,c=l.isIE,a=l.isGecko,b=l.isOpera,i=l.each,h=l.extend,d=true,g=false;function k(o){var p,n,m;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(o.nodeName)){if(p){n=o.cloneNode(false);n.appendChild(p);p=n}else{p=m=o.cloneNode(false)}p.removeAttribute("id")}}while(o=o.parentNode);if(p){return{wrapper:p,inner:m}}}function f(n,o){var m=o.ownerDocument.createRange();m.setStart(n.endContainer,n.endOffset);m.setEndAfter(o);return m.cloneContents().textContent.length==0}function e(o,q,m){var n,p;if(q.isEmpty(m)){n=q.getParent(m,"ul,ol");if(!q.getParent(n.parentNode,"ul,ol")){q.split(n,m);p=q.create("p",0,'<br data-mce-bogus="1" />');q.replace(p,m);o.select(p,1)}return g}return d}l.create("tinymce.ForceBlocks",{ForceBlocks:function(m){var n=this,o=m.settings,p;n.editor=m;n.dom=m.dom;p=(o.forced_root_block||"p").toLowerCase();o.element=p.toUpperCase();m.onPreInit.add(n.setup,n)},setup:function(){var n=this,m=n.editor,p=m.settings,u=m.dom,o=m.selection,q=m.schema.getBlockElements();if(p.forced_root_block){function v(){var y=o.getStart(),t=m.getBody(),s,z,D,F,E,x,A,B=-16777215;if(!y||y.nodeType!==1){return}while(y!=t){if(q[y.nodeName]){return}y=y.parentNode}s=o.getRng();if(s.setStart){z=s.startContainer;D=s.startOffset;F=s.endContainer;E=s.endOffset}else{if(s.item){s=m.getDoc().body.createTextRange();s.moveToElementText(s.item(0))}tmpRng=s.duplicate();tmpRng.collapse(true);D=tmpRng.move("character",B)*-1;if(!tmpRng.collapsed){tmpRng=s.duplicate();tmpRng.collapse(false);E=(tmpRng.move("character",B)*-1)-D}}for(y=t.firstChild;y;y){if(y.nodeType===3||(y.nodeType==1&&!q[y.nodeName])){if(!x){x=u.create(p.forced_root_block);y.parentNode.insertBefore(x,y)}A=y;y=y.nextSibling;x.appendChild(A)}else{x=null;y=y.nextSibling}}if(s.setStart){s.setStart(z,D);s.setEnd(F,E);o.setRng(s)}else{try{s=m.getDoc().body.createTextRange();s.moveToElementText(t);s.collapse(true);s.moveStart("character",D);if(E>0){s.moveEnd("character",E)}s.select()}catch(C){}}m.nodeChanged()}m.onKeyUp.add(v);m.onClick.add(v)}if(p.force_br_newlines){if(c){m.onKeyPress.add(function(s,t){var x;if(t.keyCode==13&&o.getNode().nodeName!="LI"){o.setContent('<br id="__" /> ',{format:"raw"});x=u.get("__");x.removeAttribute("id");o.select(x);o.collapse();return j.cancel(t)}})}}if(p.force_p_newlines){if(!c){m.onKeyPress.add(function(s,t){if(t.keyCode==13&&!t.shiftKey&&!n.insertPara(t)){j.cancel(t)}})}else{l.addUnload(function(){n._previousFormats=0});m.onKeyPress.add(function(s,t){n._previousFormats=0;if(t.keyCode==13&&!t.shiftKey&&s.selection.isCollapsed()&&p.keep_styles){n._previousFormats=k(s.selection.getStart())}});m.onKeyUp.add(function(t,y){if(y.keyCode==13&&!y.shiftKey){var x=t.selection.getStart(),s=n._previousFormats;if(!x.hasChildNodes()&&s){x=u.getParent(x,u.isBlock);if(x&&x.nodeName!="LI"){x.innerHTML="";if(n._previousFormats){x.appendChild(s.wrapper);s.inner.innerHTML="\uFEFF"}else{x.innerHTML="\uFEFF"}o.select(x,1);o.collapse(true);t.getDoc().execCommand("Delete",false,null);n._previousFormats=0}}}})}if(a){m.onKeyDown.add(function(s,t){if((t.keyCode==8||t.keyCode==46)&&!t.shiftKey){n.backspaceDelete(t,t.keyCode==8)}})}}if(l.isWebKit){function r(t){var s=o.getRng(),x,A=u.create("div",null," "),z,y=u.getViewPort(t.getWin()).h;s.insertNode(x=u.create("br"));s.setStartAfter(x);s.setEndAfter(x);o.setRng(s);if(o.getSel().focusNode==x.previousSibling){o.select(u.insertAfter(u.doc.createTextNode("\u00a0"),x));o.collapse(d)}u.insertAfter(A,x);z=u.getPos(A).y;u.remove(A);if(z>y){t.getWin().scrollTo(0,z)}}m.onKeyPress.add(function(s,t){if(t.keyCode==13&&(t.shiftKey||(p.force_br_newlines&&!u.getParent(o.getNode(),"h1,h2,h3,h4,h5,h6,ol,ul")))){r(s);j.cancel(t)}})}if(c){if(p.element!="P"){m.onKeyPress.add(function(s,t){n.lastElm=o.getNode().nodeName});m.onKeyUp.add(function(t,x){var z,y=o.getNode(),s=t.getBody();if(s.childNodes.length===1&&y.nodeName=="P"){y=u.rename(y,p.element);o.select(y);o.collapse();t.nodeChanged()}else{if(x.keyCode==13&&!x.shiftKey&&n.lastElm!="P"){z=u.getParent(y,"p");if(z){u.rename(z,p.element);t.nodeChanged()}}}})}}},getParentBlock:function(o){var m=this.dom;return m.getParent(o,m.isBlock)},insertPara:function(Q){var E=this,v=E.editor,M=v.dom,R=v.getDoc(),V=v.settings,F=v.selection.getSel(),G=F.getRangeAt(0),U=R.body;var J,K,H,O,N,q,o,u,z,m,C,T,p,x,I,L=M.getViewPort(v.getWin()),B,D,A;v.undoManager.beforeChange();J=R.createRange();J.setStart(F.anchorNode,F.anchorOffset);J.collapse(d);K=R.createRange();K.setStart(F.focusNode,F.focusOffset);K.collapse(d);H=J.compareBoundaryPoints(J.START_TO_END,K)<0;O=H?F.anchorNode:F.focusNode;N=H?F.anchorOffset:F.focusOffset;q=H?F.focusNode:F.anchorNode;o=H?F.focusOffset:F.anchorOffset;if(O===q&&/^(TD|TH)$/.test(O.nodeName)){if(O.firstChild.nodeName=="BR"){M.remove(O.firstChild)}if(O.childNodes.length==0){v.dom.add(O,V.element,null,"<br />");T=v.dom.add(O,V.element,null,"<br />")}else{I=O.innerHTML;O.innerHTML="";v.dom.add(O,V.element,null,I);T=v.dom.add(O,V.element,null,"<br />")}G=R.createRange();G.selectNodeContents(T);G.collapse(1);v.selection.setRng(G);return g}if(O==U&&q==U&&U.firstChild&&v.dom.isBlock(U.firstChild)){O=q=O.firstChild;N=o=0;J=R.createRange();J.setStart(O,0);K=R.createRange();K.setStart(q,0)}if(!R.body.hasChildNodes()){R.body.appendChild(M.create("br"))}O=O.nodeName=="HTML"?R.body:O;O=O.nodeName=="BODY"?O.firstChild:O;q=q.nodeName=="HTML"?R.body:q;q=q.nodeName=="BODY"?q.firstChild:q;u=E.getParentBlock(O);z=E.getParentBlock(q);m=u?u.nodeName:V.element;if(I=E.dom.getParent(u,"li,pre")){if(I.nodeName=="LI"){return e(v.selection,E.dom,I)}return d}if(u&&(u.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;u=null}if(z&&(z.nodeName=="CAPTION"||/absolute|relative|fixed/gi.test(M.getStyle(u,"position",1)))){m=V.element;z=null}if(/(TD|TABLE|TH|CAPTION)/.test(m)||(u&&m=="DIV"&&/left|right/gi.test(M.getStyle(u,"float",1)))){m=V.element;u=z=null}C=(u&&u.nodeName==m)?u.cloneNode(0):v.dom.create(m);T=(z&&z.nodeName==m)?z.cloneNode(0):v.dom.create(m);T.removeAttribute("id");if(/^(H[1-6])$/.test(m)&&f(G,u)){T=v.dom.create(V.element)}I=p=O;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}p=I}while((I=I.previousSibling?I.previousSibling:I.parentNode));I=x=q;do{if(I==U||I.nodeType==9||E.dom.isBlock(I)||/(TD|TABLE|TH|CAPTION)/.test(I.nodeName)){break}x=I}while((I=I.nextSibling?I.nextSibling:I.parentNode));if(p.nodeName==m){J.setStart(p,0)}else{J.setStartBefore(p)}J.setEnd(O,N);C.appendChild(J.cloneContents()||R.createTextNode(""));try{K.setEndAfter(x)}catch(P){}K.setStart(q,o);T.appendChild(K.cloneContents()||R.createTextNode(""));G=R.createRange();if(!p.previousSibling&&p.parentNode.nodeName==m){G.setStartBefore(p.parentNode)}else{if(J.startContainer.nodeName==m&&J.startOffset==0){G.setStartBefore(J.startContainer)}else{G.setStart(J.startContainer,J.startOffset)}}if(!x.nextSibling&&x.parentNode.nodeName==m){G.setEndAfter(x.parentNode)}else{G.setEnd(K.endContainer,K.endOffset)}G.deleteContents();if(b){v.getWin().scrollTo(0,L.y)}if(C.firstChild&&C.firstChild.nodeName==m){C.innerHTML=C.firstChild.innerHTML}if(T.firstChild&&T.firstChild.nodeName==m){T.innerHTML=T.firstChild.innerHTML}function S(y,s){var r=[],X,W,t;y.innerHTML="";if(V.keep_styles){W=s;do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(W.nodeName)){X=W.cloneNode(g);M.setAttrib(X,"id","");r.push(X)}}while(W=W.parentNode)}if(r.length>0){for(t=r.length-1,X=y;t>=0;t--){X=X.appendChild(r[t])}r[0].innerHTML=b?"\u00a0":"<br />";return r[0]}else{y.innerHTML=b?"\u00a0":"<br />"}}if(M.isEmpty(C)){S(C,O)}if(M.isEmpty(T)){A=S(T,q)}if(b&&parseFloat(opera.version())<9.5){G.insertNode(C);G.insertNode(T)}else{G.insertNode(T);G.insertNode(C)}T.normalize();C.normalize();v.selection.select(T,true);v.selection.collapse(true);B=v.dom.getPos(T).y;if(B<L.y||B+25>L.y+L.h){v.getWin().scrollTo(0,B<L.y?B:B-L.h+25)}v.undoManager.add();return g},backspaceDelete:function(u,B){var C=this,s=C.editor,y=s.getBody(),q=s.dom,p,v=s.selection,o=v.getRng(),x=o.startContainer,p,z,A,m;if(!B&&o.collapsed&&x.nodeType==1&&o.startOffset==x.childNodes.length){m=new l.dom.TreeWalker(x.lastChild,x);for(p=x.lastChild;p;p=m.prev()){if(p.nodeType==3){o.setStart(p,p.nodeValue.length);o.collapse(true);v.setRng(o);return}}}if(x&&s.dom.isBlock(x)&&!/^(TD|TH)$/.test(x.nodeName)&&B){if(x.childNodes.length==0||(x.childNodes.length==1&&x.firstChild.nodeName=="BR")){p=x;while((p=p.previousSibling)&&!s.dom.isBlock(p)){}if(p){if(x!=y.firstChild){z=s.dom.doc.createTreeWalker(p,NodeFilter.SHOW_TEXT,null,g);while(A=z.nextNode()){p=A}o=s.getDoc().createRange();o.setStart(p,p.nodeValue?p.nodeValue.length:0);o.setEnd(p,p.nodeValue?p.nodeValue.length:0);v.setRng(o);s.dom.remove(x)}return j.cancel(u)}}}}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each,e=c.extend;c.create("tinymce.ControlManager",{ControlManager:function(f,j){var h=this,g;j=j||{};h.editor=f;h.controls={};h.onAdd=new c.util.Dispatcher(h);h.onPostRender=new c.util.Dispatcher(h);h.prefix=j.prefix||f.id+"_";h._cls={};h.onPostRender.add(function(){d(h.controls,function(i){i.postRender()})})},get:function(f){return this.controls[this.prefix+f]||this.controls[f]},setActive:function(h,f){var g=null;if(g=this.get(h)){g.setActive(f)}return g},setDisabled:function(h,f){var g=null;if(g=this.get(h)){g.setDisabled(f)}return g},add:function(g){var f=this;if(g){f.controls[g.id]=g;f.onAdd.dispatch(g,f)}return g},createControl:function(i){var h,g=this,f=g.editor;d(f.plugins,function(j){if(j.createControl){h=j.createControl(i,g);if(h){return false}}});switch(i){case"|":case"separator":return g.createSeparator()}if(!h&&f.buttons&&(h=f.buttons[i])){return g.createButton(i,h)}return g.add(h)},createDropMenu:function(f,n,h){var m=this,i=m.editor,j,g,k,l;n=e({"class":"mceDropDown",constrain:i.settings.constrain_menus},n);n["class"]=n["class"]+" "+i.getParam("skin")+"Skin";if(k=i.getParam("skin_variant")){n["class"]+=" "+i.getParam("skin")+"Skin"+k.substring(0,1).toUpperCase()+k.substring(1)}f=m.prefix+f;l=h||m._cls.dropmenu||c.ui.DropMenu;j=m.controls[f]=new l(f,n);j.onAddItem.add(function(r,q){var p=q.settings;p.title=i.getLang(p.title,p.title);if(!p.onclick){p.onclick=function(o){if(p.cmd){i.execCommand(p.cmd,p.ui||false,p.value)}}}});i.onRemove.add(function(){j.destroy()});if(c.isIE){j.onShowMenu.add(function(){i.focus();g=i.selection.getBookmark(1)});j.onHideMenu.add(function(){if(g){i.selection.moveToBookmark(g);g=0}})}return m.add(j)},createListBox:function(f,n,h){var l=this,j=l.editor,i,k,m;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,scope:n.scope,control_manager:l},n);f=l.prefix+f;function g(o){return o.settings.use_accessible_selects&&!c.isGecko}if(j.settings.use_native_selects||g(j)){k=new c.ui.NativeListBox(f,n)}else{m=h||l._cls.listbox||c.ui.ListBox;k=new m(f,n,j)}l.controls[f]=k;if(c.isWebKit){k.onPostRender.add(function(p,o){a.add(o,"mousedown",function(){j.bookmark=j.selection.getBookmark(1)});a.add(o,"focus",function(){j.selection.moveToBookmark(j.bookmark);j.bookmark=null})})}if(k.hideMenu){j.onMouseDown.add(k.hideMenu,k)}return l.add(k)},createButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.label=g.translate(i.label);i.scope=i.scope||g;if(!i.onclick&&!i.menu_button){i.onclick=function(){g.execCommand(i.cmd,i.ui||false,i.value)}}i=e({title:i.title,"class":"mce_"+m,unavailable_prefix:g.getLang("unavailable",""),scope:i.scope,control_manager:h},i);m=h.prefix+m;if(i.menu_button){f=l||h._cls.menubutton||c.ui.MenuButton;k=new f(m,i,g);g.onMouseDown.add(k.hideMenu,k)}else{f=h._cls.button||c.ui.Button;k=new f(m,i,g)}return h.add(k)},createMenuButton:function(h,f,g){f=f||{};f.menu_button=1;return this.createButton(h,f,g)},createSplitButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.scope=i.scope||g;if(!i.onclick){i.onclick=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}if(!i.onselect){i.onselect=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}i=e({title:i.title,"class":"mce_"+m,scope:i.scope,control_manager:h},i);m=h.prefix+m;f=l||h._cls.splitbutton||c.ui.SplitButton;k=h.add(new f(m,i,g));g.onMouseDown.add(k.hideMenu,k);return k},createColorSplitButton:function(f,n,h){var l=this,j=l.editor,i,k,m,g;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onclick){n.onclick=function(o){if(c.isIE){g=j.selection.getBookmark(1)}j.execCommand(n.cmd,n.ui||false,o||n.value)}}if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,menu_class:j.getParam("skin")+"Skin",scope:n.scope,more_colors_title:j.getLang("more_colors")},n);f=l.prefix+f;m=h||l._cls.colorsplitbutton||c.ui.ColorSplitButton;k=new m(f,n,j);j.onMouseDown.add(k.hideMenu,k);j.onRemove.add(function(){k.destroy()});if(c.isIE){k.onShowMenu.add(function(){j.focus();g=j.selection.getBookmark(1)});k.onHideMenu.add(function(){if(g){j.selection.moveToBookmark(g);g=0}})}return l.add(k)},createToolbar:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||g._cls.toolbar||c.ui.Toolbar;i=new f(k,h,g.editor);if(g.get(k)){return null}return g.add(i)},createToolbarGroup:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||this._cls.toolbarGroup||c.ui.ToolbarGroup;i=new f(k,h,g.editor);if(g.get(k)){return null}return g.add(i)},createSeparator:function(g){var f=g||this._cls.separator||c.ui.Separator;return new f()},setControlType:function(g,f){return this._cls[g.toLowerCase()]=f},destroy:function(){d(this.controls,function(f){f.destroy()});this.controls=null}})})(tinymce);(function(d){var a=d.util.Dispatcher,e=d.each,c=d.isIE,b=d.isOpera;d.create("tinymce.WindowManager",{WindowManager:function(f){var g=this;g.editor=f;g.onOpen=new a(g);g.onClose=new a(g);g.params={};g.features={}},open:function(z,h){var v=this,k="",n,m,i=v.editor.settings.dialog_type=="modal",q,o,j,g=d.DOM.getViewPort(),r;z=z||{};h=h||{};o=b?g.w:screen.width;j=b?g.h:screen.height;z.name=z.name||"mc_"+new Date().getTime();z.width=parseInt(z.width||320);z.height=parseInt(z.height||240);z.resizable=true;z.left=z.left||parseInt(o/2)-(z.width/2);z.top=z.top||parseInt(j/2)-(z.height/2);h.inline=false;h.mce_width=z.width;h.mce_height=z.height;h.mce_auto_focus=z.auto_focus;if(i){if(c){z.center=true;z.help=false;z.dialogWidth=z.width+"px";z.dialogHeight=z.height+"px";z.scroll=z.scrollbars||false}}e(z,function(p,f){if(d.is(p,"boolean")){p=p?"yes":"no"}if(!/^(name|url)$/.test(f)){if(c&&i){k+=(k?";":"")+f+":"+p}else{k+=(k?",":"")+f+"="+p}}});v.features=z;v.params=h;v.onOpen.dispatch(v,z,h);r=z.url||z.file;r=d._addVer(r);try{if(c&&i){q=1;window.showModalDialog(r,window,k)}else{q=window.open(r,z.name,k)}}catch(l){}if(!q){alert(v.editor.getLang("popup_blocked"))}},close:function(f){f.close();this.onClose.dispatch(this)},createInstance:function(i,h,g,m,l,k){var j=d.resolve(i);return new j(h,g,m,l,k)},confirm:function(h,f,i,g){g=g||window;f.call(i||this,g.confirm(this._decode(this.editor.getLang(h,h))))},alert:function(h,f,j,g){var i=this;g=g||window;g.alert(i._decode(i.editor.getLang(h,h)));if(f){f.call(j||i)}},resizeBy:function(f,g,h){h.resizeBy(f,g)},_decode:function(f){return d.DOM.decode(f).replace(/\\n/g,"\n")}})}(tinymce));(function(a){a.Formatter=function(U){var M={},O=a.each,c=U.dom,q=U.selection,t=a.dom.TreeWalker,K=new a.dom.RangeUtils(c),d=U.schema.isValidChild,F=c.isBlock,l=U.settings.forced_root_block,s=c.nodeIndex,E="\uFEFF",e=/^(src|href|style)$/,R=false,B=true,p;function z(V){return V instanceof Array}function m(W,V){return c.getParents(W,V,c.getRoot())}function b(V){return V.nodeType===1&&(V.face==="mceinline"||V.style.fontFamily==="mceinline")}function Q(V){return V?M[V]:M}function k(V,W){if(V){if(typeof(V)!=="string"){O(V,function(Y,X){k(X,Y)})}else{W=W.length?W:[W];O(W,function(X){if(X.deep===p){X.deep=!X.selector}if(X.split===p){X.split=!X.selector||X.inline}if(X.remove===p&&X.selector&&!X.inline){X.remove="none"}if(X.selector&&X.inline){X.mixed=true;X.block_expand=true}if(typeof(X.classes)==="string"){X.classes=X.classes.split(/\s+/)}});M[V]=W}}}var i=function(W){var V;U.dom.getParent(W,function(X){V=U.dom.getStyle(X,"text-decoration");return V&&V!=="none"});return V};var I=function(V){var W;if(V.nodeType===1&&V.parentNode&&V.parentNode.nodeType===1){W=i(V.parentNode);if(U.dom.getStyle(V,"color")&&W){U.dom.setStyle(V,"text-decoration",W)}else{if(U.dom.getStyle(V,"textdecoration")===W){U.dom.setStyle(V,"text-decoration",null)}}}};function S(Y,ag,ab){var ac=Q(Y),ah=ac[0],af,W,ae,ad=q.isCollapsed();function Z(al){var ak=al.startContainer,ao=al.startOffset,an,am;if(ak.nodeType==1||ak.nodeValue===""){ak=ak.nodeType==1?ak.childNodes[ao]:ak;if(ak){an=new t(ak,ak.parentNode);for(am=an.current();am;am=an.next()){if(am.nodeType==3&&!f(am)){al.setStart(am,0);break}}}}return al}function V(al,ak){ak=ak||ah;if(al){if(ak.onformat){ak.onformat(al,ak,ag,ab)}O(ak.styles,function(an,am){c.setStyle(al,am,r(an,ag))});O(ak.attributes,function(an,am){c.setAttrib(al,am,r(an,ag))});O(ak.classes,function(am){am=r(am,ag);if(!c.hasClass(al,am)){c.addClass(al,am)}})}}function aa(){function am(at,aq){var ar=new t(aq);for(ab=ar.current();ab;ab=ar.prev()){if(ab.childNodes.length>1||ab==at){return ab}}}var al=U.selection.getRng();var ap=al.startContainer;var ak=al.endContainer;if(ap!=ak&&al.endOffset==0){var ao=am(ap,ak);var an=ao.nodeType==3?ao.length:ao.childNodes.length;al.setEnd(ao,an)}return al}function X(an,at,aq,ap,al){var ak=[],am=-1,ar,av=-1,ao=-1,au;O(an.childNodes,function(ax,aw){if(ax.nodeName==="UL"||ax.nodeName==="OL"){am=aw;ar=ax;return false}});O(an.childNodes,function(ax,aw){if(ax.nodeName==="SPAN"&&c.getAttrib(ax,"data-mce-type")=="bookmark"){if(ax.id==at.id+"_start"){av=aw}else{if(ax.id==at.id+"_end"){ao=aw}}}});if(am<=0||(av<am&&ao>am)){O(a.grep(an.childNodes),al);return 0}else{au=aq.cloneNode(R);O(a.grep(an.childNodes),function(ax,aw){if((av<am&&aw<am)||(av>am&&aw>am)){ak.push(ax);ax.parentNode.removeChild(ax)}});if(av<am){an.insertBefore(au,ar)}else{if(av>am){an.insertBefore(au,ar.nextSibling)}}ap.push(au);O(ak,function(aw){au.appendChild(aw)});return au}}function ai(al,an,ap){var ak=[],ao,am;ao=ah.inline||ah.block;am=c.create(ao);V(am);K.walk(al,function(aq){var ar;function at(au){var ax=au.nodeName.toLowerCase(),aw=au.parentNode.nodeName.toLowerCase(),av;if(g(ax,"br")){ar=0;if(ah.block){c.remove(au)}return}if(ah.wrapper&&x(au,Y,ag)){ar=0;return}if(ah.block&&!ah.wrapper&&G(ax)){au=c.rename(au,ao);V(au);ak.push(au);ar=0;return}if(ah.selector){O(ac,function(ay){if("collapsed" in ay&&ay.collapsed!==ad){return}if(c.is(au,ay.selector)&&!b(au)){V(au,ay);av=true}});if(!ah.inline||av){ar=0;return}}if(d(ao,ax)&&d(aw,ao)&&!(!ap&&au.nodeType===3&&au.nodeValue.length===1&&au.nodeValue.charCodeAt(0)===65279)&&au.id!=="_mce_caret"){if(!ar){ar=am.cloneNode(R);au.parentNode.insertBefore(ar,au);ak.push(ar)}ar.appendChild(au)}else{if(ax=="li"&&an){ar=X(au,an,am,ak,at)}else{ar=0;O(a.grep(au.childNodes),at);ar=0}}}O(aq,at)});if(ah.wrap_links===false){O(ak,function(aq){function ar(aw){var av,au,at;if(aw.nodeName==="A"){au=am.cloneNode(R);ak.push(au);at=a.grep(aw.childNodes);for(av=0;av<at.length;av++){au.appendChild(at[av])}aw.appendChild(au)}O(a.grep(aw.childNodes),ar)}ar(aq)})}O(ak,function(at){var aq;function au(aw){var av=0;O(aw.childNodes,function(ax){if(!f(ax)&&!H(ax)){av++}});return av}function ar(av){var ax,aw;O(av.childNodes,function(ay){if(ay.nodeType==1&&!H(ay)&&!b(ay)){ax=ay;return R}});if(ax&&h(ax,ah)){aw=ax.cloneNode(R);V(aw);c.replace(aw,av,B);c.remove(ax,1)}return aw||av}aq=au(at);if((ak.length>1||!F(at))&&aq===0){c.remove(at,1);return}if(ah.inline||ah.wrapper){if(!ah.exact&&aq===1){at=ar(at)}O(ac,function(av){O(c.select(av.inline,at),function(ax){var aw;if(av.wrap_links===false){aw=ax.parentNode;do{if(aw.nodeName==="A"){return}}while(aw=aw.parentNode)}T(av,ag,ax,av.exact?ax:null)})});if(x(at.parentNode,Y,ag)){c.remove(at,1);at=0;return B}if(ah.merge_with_parents){c.getParent(at.parentNode,function(av){if(x(av,Y,ag)){c.remove(at,1);at=0;return B}})}if(at&&ah.merge_siblings!==false){at=u(C(at),at);at=u(at,C(at,B))}}})}if(ah){if(ab){if(ab.nodeType){W=c.createRng();W.setStartBefore(ab);W.setEndAfter(ab);ai(o(W,ac),null,true)}else{ai(ab,null,true)}}else{if(!ad||!ah.inline||c.select("td.mceSelected,th.mceSelected").length){var aj=U.selection.getNode();U.selection.setRng(aa());af=q.getBookmark();ai(o(q.getRng(B),ac),af);if(ah.styles&&(ah.styles.color||ah.styles.textDecoration)){a.walk(aj,I,"childNodes");I(aj)}q.moveToBookmark(af);q.setRng(Z(q.getRng(B)));U.nodeChanged()}else{P("apply",Y,ag)}}}}function A(X,ag,aa){var ab=Q(X),ai=ab[0],af,ae,W;function Z(al){var ak=al.startContainer,aq=al.startOffset,ap,ao,am,an;if(ak.nodeType==3&&aq>=ak.nodeValue.length-1){ak=ak.parentNode;aq=s(ak)+1}if(ak.nodeType==1){am=ak.childNodes;ak=am[Math.min(aq,am.length-1)];ap=new t(ak);if(aq>am.length-1){ap.next()}for(ao=ap.current();ao;ao=ap.next()){if(ao.nodeType==3&&!f(ao)){an=c.create("a",null,E);ao.parentNode.insertBefore(an,ao);al.setStart(ao,0);q.setRng(al);c.remove(an);return}}}}function Y(an){var am,al,ak;am=a.grep(an.childNodes);for(al=0,ak=ab.length;al<ak;al++){if(T(ab[al],ag,an,an)){break}}if(ai.deep){for(al=0,ak=am.length;al<ak;al++){Y(am[al])}}}function ac(ak){var al;O(m(ak.parentNode).reverse(),function(am){var an;if(!al&&am.id!="_start"&&am.id!="_end"){an=x(am,X,ag);if(an&&an.split!==false){al=am}}});return al}function V(an,ak,ap,at){var au,ar,aq,am,ao,al;if(an){al=an.parentNode;for(au=ak.parentNode;au&&au!=al;au=au.parentNode){ar=au.cloneNode(R);for(ao=0;ao<ab.length;ao++){if(T(ab[ao],ag,ar,ar)){ar=0;break}}if(ar){if(aq){ar.appendChild(aq)}if(!am){am=ar}aq=ar}}if(at&&(!ai.mixed||!F(an))){ak=c.split(an,ak)}if(aq){ap.parentNode.insertBefore(aq,ap);am.appendChild(ap)}}return ak}function ah(ak){return V(ac(ak),ak,ak,true)}function ad(am){var al=c.get(am?"_start":"_end"),ak=al[am?"firstChild":"lastChild"];if(H(ak)){ak=ak[am?"firstChild":"lastChild"]}c.remove(al,true);return ak}function aj(ak){var al,am;ak=o(ak,ab,B);if(ai.split){al=J(ak,B);am=J(ak);if(al!=am){al=N(al,"span",{id:"_start","data-mce-type":"bookmark"});am=N(am,"span",{id:"_end","data-mce-type":"bookmark"});ah(al);ah(am);al=ad(B);am=ad()}else{al=am=ah(al)}ak.startContainer=al.parentNode;ak.startOffset=s(al);ak.endContainer=am.parentNode;ak.endOffset=s(am)+1}K.walk(ak,function(an){O(an,function(ao){Y(ao);if(ao.nodeType===1&&U.dom.getStyle(ao,"text-decoration")==="underline"&&ao.parentNode&&i(ao.parentNode)==="underline"){T({deep:false,exact:true,inline:"span",styles:{textDecoration:"underline"}},null,ao)}})})}if(aa){if(aa.nodeType){W=c.createRng();W.setStartBefore(aa);W.setEndAfter(aa);aj(W)}else{aj(aa)}return}if(!q.isCollapsed()||!ai.inline||c.select("td.mceSelected,th.mceSelected").length){af=q.getBookmark();aj(q.getRng(B));q.moveToBookmark(af);if(ai.inline&&j(X,ag,q.getStart())){Z(q.getRng(true))}U.nodeChanged()}else{P("remove",X,ag)}if(a.isWebKit){U.execCommand("mceCleanup")}}function D(W,Y,X){var V=Q(W);if(j(W,Y,X)&&(!("toggle" in V[0])||V[0]["toggle"])){A(W,Y,X)}else{S(W,Y,X)}}function x(W,V,ab,Z){var X=Q(V),ac,aa,Y;function ad(ah,aj,ak){var ag,ai,ae=aj[ak],af;if(aj.onmatch){return aj.onmatch(ah,aj,ak)}if(ae){if(ae.length===p){for(ag in ae){if(ae.hasOwnProperty(ag)){if(ak==="attributes"){ai=c.getAttrib(ah,ag)}else{ai=L(ah,ag)}if(Z&&!ai&&!aj.exact){return}if((!Z||aj.exact)&&!g(ai,r(ae[ag],ab))){return}}}}else{for(af=0;af<ae.length;af++){if(ak==="attributes"?c.getAttrib(ah,ae[af]):L(ah,ae[af])){return aj}}}}return aj}if(X&&W){for(aa=0;aa<X.length;aa++){ac=X[aa];if(h(W,ac)&&ad(W,ac,"attributes")&&ad(W,ac,"styles")){if(Y=ac.classes){for(aa=0;aa<Y.length;aa++){if(!c.hasClass(W,Y[aa])){return}}}return ac}}}}function j(X,Z,Y){var W;function V(aa){aa=c.getParent(aa,function(ab){return !!x(ab,X,Z,true)});return x(aa,X,Z)}if(Y){return V(Y)}Y=q.getNode();if(V(Y)){return B}W=q.getStart();if(W!=Y){if(V(W)){return B}}return R}function v(ac,ab){var Z,aa=[],Y={},X,W,V;Z=q.getStart();c.getParent(Z,function(af){var ae,ad;for(ae=0;ae<ac.length;ae++){ad=ac[ae];if(!Y[ad]&&x(af,ad,ab)){Y[ad]=true;aa.push(ad)}}});return aa}function y(Z){var ab=Q(Z),Y,X,aa,W,V;if(ab){Y=q.getStart();X=m(Y);for(W=ab.length-1;W>=0;W--){V=ab[W].selector;if(!V){return B}for(aa=X.length-1;aa>=0;aa--){if(c.is(X[aa],V)){return B}}}}return R}a.extend(this,{get:Q,register:k,apply:S,remove:A,toggle:D,match:j,matchAll:v,matchNode:x,canApply:y});function h(V,W){if(g(V,W.inline)){return B}if(g(V,W.block)){return B}if(W.selector){return c.is(V,W.selector)}}function g(W,V){W=W||"";V=V||"";W=""+(W.nodeName||W);V=""+(V.nodeName||V);return W.toLowerCase()==V.toLowerCase()}function L(W,V){var X=c.getStyle(W,V);if(V=="color"||V=="backgroundColor"){X=c.toHex(X)}if(V=="fontWeight"&&X==700){X="bold"}return""+X}function r(V,W){if(typeof(V)!="string"){V=V(W)}else{if(W){V=V.replace(/%(\w+)/g,function(Y,X){return W[X]||Y})}}return V}function f(V){return V&&V.nodeType===3&&/^([\t \r\n]+|)$/.test(V.nodeValue)}function N(X,W,V){var Y=c.create(W,V);X.parentNode.insertBefore(Y,X);Y.appendChild(X);return Y}function o(V,ah,Y){var X=V.startContainer,ac=V.startOffset,ak=V.endContainer,ae=V.endOffset,aj,ag,ab,af;function ai(aq){var al,ao,ap,an,am;al=ao=aq?X:ak;am=aq?"previousSibling":"nextSibling";root=c.getRoot();if(al.nodeType==3&&!f(al)){if(aq?ac>0:ae<al.nodeValue.length){return al}}for(;;){if(ao==root||(!ah[0].block_expand&&F(ao))){return ao}for(an=ao[am];an;an=an[am]){if(!H(an)&&!f(an)){return ao}}ao=ao.parentNode}return al}function aa(al,am){if(am===p){am=al.nodeType===3?al.length:al.childNodes.length}while(al&&al.hasChildNodes()){al=al.childNodes[am];if(al){am=al.nodeType===3?al.length:al.childNodes.length}}return{node:al,offset:am}}if(X.nodeType==1&&X.hasChildNodes()){ag=X.childNodes.length-1;X=X.childNodes[ac>ag?ag:ac];if(X.nodeType==3){ac=0}}if(ak.nodeType==1&&ak.hasChildNodes()){ag=ak.childNodes.length-1;ak=ak.childNodes[ae>ag?ag:ae-1];if(ak.nodeType==3){ae=ak.nodeValue.length}}if(H(X.parentNode)||H(X)){X=H(X)?X:X.parentNode;X=X.nextSibling||X;if(X.nodeType==3){ac=0}}if(H(ak.parentNode)||H(ak)){ak=H(ak)?ak:ak.parentNode;ak=ak.previousSibling||ak;if(ak.nodeType==3){ae=ak.length}}if(ah[0].inline){if(V.collapsed){function ad(am,aq,at){var ap,an,ar,al;function ao(av,ax){var ay,au,aw=av.nodeValue;if(typeof(ax)=="undefined"){ax=at?aw.length:0}if(at){ay=aw.lastIndexOf(" ",ax);au=aw.lastIndexOf("\u00a0",ax);ay=ay>au?ay:au;if(ay!==-1&&!Y){ay++}}else{ay=aw.indexOf(" ",ax);au=aw.indexOf("\u00a0",ax);ay=ay!==-1&&(au===-1||ay<au)?ay:au}return ay}if(am.nodeType===3){ar=ao(am,aq);if(ar!==-1){return{container:am,offset:ar}}al=am}ap=new t(am,c.getParent(am,F)||U.getBody());while(an=ap[at?"prev":"next"]()){if(an.nodeType===3){al=an;ar=ao(an);if(ar!==-1){return{container:an,offset:ar}}}else{if(F(an)){break}}}if(al){if(at){aq=0}else{aq=al.length}return{container:al,offset:aq}}}af=ad(X,ac,true);if(af){X=af.container;ac=af.offset}af=ad(ak,ae);if(af){ak=af.container;ae=af.offset}}ab=aa(ak,ae);if(ab.node){while(ab.node&&ab.offset===0&&ab.node.previousSibling){ab=aa(ab.node.previousSibling)}if(ab.node&&ab.offset>0&&ab.node.nodeType===3&&ab.node.nodeValue.charAt(ab.offset-1)===" "){if(ab.offset>1){ak=ab.node;ak.splitText(ab.offset-1)}else{if(ab.node.previousSibling){}}}}}if(ah[0].inline||ah[0].block_expand){if(!ah[0].inline||(X.nodeType!=3||ac===0)){X=ai(true)}if(!ah[0].inline||(ak.nodeType!=3||ae===ak.nodeValue.length)){ak=ai()}}if(ah[0].selector&&ah[0].expand!==R&&!ah[0].inline){function Z(am,al){var an,ao,aq,ap;if(am.nodeType==3&&am.nodeValue.length==0&&am[al]){am=am[al]}an=m(am);for(ao=0;ao<an.length;ao++){for(aq=0;aq<ah.length;aq++){ap=ah[aq];if("collapsed" in ap&&ap.collapsed!==V.collapsed){continue}if(c.is(an[ao],ap.selector)){return an[ao]}}}return am}X=Z(X,"previousSibling");ak=Z(ak,"nextSibling")}if(ah[0].block||ah[0].selector){function W(am,al,ao){var an;if(!ah[0].wrapper){an=c.getParent(am,ah[0].block)}if(!an){an=c.getParent(am.nodeType==3?am.parentNode:am,F)}if(an&&ah[0].wrapper){an=m(an,"ul,ol").reverse()[0]||an}if(!an){an=am;while(an[al]&&!F(an[al])){an=an[al];if(g(an,"br")){break}}}return an||am}X=W(X,"previousSibling");ak=W(ak,"nextSibling");if(ah[0].block){if(!F(X)){X=ai(true)}if(!F(ak)){ak=ai()}}}if(X.nodeType==1){ac=s(X);X=X.parentNode}if(ak.nodeType==1){ae=s(ak)+1;ak=ak.parentNode}return{startContainer:X,startOffset:ac,endContainer:ak,endOffset:ae}}function T(ab,aa,Y,V){var X,W,Z;if(!h(Y,ab)){return R}if(ab.remove!="all"){O(ab.styles,function(ad,ac){ad=r(ad,aa);if(typeof(ac)==="number"){ac=ad;V=0}if(!V||g(L(V,ac),ad)){c.setStyle(Y,ac,"")}Z=1});if(Z&&c.getAttrib(Y,"style")==""){Y.removeAttribute("style");Y.removeAttribute("data-mce-style")}O(ab.attributes,function(ae,ac){var ad;ae=r(ae,aa);if(typeof(ac)==="number"){ac=ae;V=0}if(!V||g(c.getAttrib(V,ac),ae)){if(ac=="class"){ae=c.getAttrib(Y,ac);if(ae){ad="";O(ae.split(/\s+/),function(af){if(/mce\w+/.test(af)){ad+=(ad?" ":"")+af}});if(ad){c.setAttrib(Y,ac,ad);return}}}if(ac=="class"){Y.removeAttribute("className")}if(e.test(ac)){Y.removeAttribute("data-mce-"+ac)}Y.removeAttribute(ac)}});O(ab.classes,function(ac){ac=r(ac,aa);if(!V||c.hasClass(V,ac)){c.removeClass(Y,ac)}});W=c.getAttribs(Y);for(X=0;X<W.length;X++){if(W[X].nodeName.indexOf("_")!==0){return R}}}if(ab.remove!="none"){n(Y,ab);return B}}function n(X,Y){var V=X.parentNode,W;if(Y.block){if(!l){function Z(ab,aa,ac){ab=C(ab,aa,ac);return !ab||(ab.nodeName=="BR"||F(ab))}if(F(X)&&!F(V)){if(!Z(X,R)&&!Z(X.firstChild,B,1)){X.insertBefore(c.create("br"),X.firstChild)}if(!Z(X,B)&&!Z(X.lastChild,R,1)){X.appendChild(c.create("br"))}}}else{if(V==c.getRoot()){if(!Y.list_block||!g(X,Y.list_block)){O(a.grep(X.childNodes),function(aa){if(d(l,aa.nodeName.toLowerCase())){if(!W){W=N(aa,l)}else{W.appendChild(aa)}}else{W=0}})}}}}if(Y.selector&&Y.inline&&!g(Y.inline,X)){return}c.remove(X,1)}function C(W,V,X){if(W){V=V?"nextSibling":"previousSibling";for(W=X?W:W[V];W;W=W[V]){if(W.nodeType==1||!f(W)){return W}}}}function H(V){return V&&V.nodeType==1&&V.getAttribute("data-mce-type")=="bookmark"}function u(Z,Y){var V,X,W;function ab(ae,ad){if(ae.nodeName!=ad.nodeName){return R}function ac(ag){var ah={};O(c.getAttribs(ag),function(ai){var aj=ai.nodeName.toLowerCase();if(aj.indexOf("_")!==0&&aj!=="style"){ah[aj]=c.getAttrib(ag,aj)}});return ah}function af(aj,ai){var ah,ag;for(ag in aj){if(aj.hasOwnProperty(ag)){ah=ai[ag];if(ah===p){return R}if(aj[ag]!=ah){return R}delete ai[ag]}}for(ag in ai){if(ai.hasOwnProperty(ag)){return R}}return B}if(!af(ac(ae),ac(ad))){return R}if(!af(c.parseStyle(c.getAttrib(ae,"style")),c.parseStyle(c.getAttrib(ad,"style")))){return R}return B}if(Z&&Y){function aa(ad,ac){for(X=ad;X;X=X[ac]){if(X.nodeType==3&&X.nodeValue.length!==0){return ad}if(X.nodeType==1&&!H(X)){return X}}return ad}Z=aa(Z,"previousSibling");Y=aa(Y,"nextSibling");if(ab(Z,Y)){for(X=Z.nextSibling;X&&X!=Y;){W=X;X=X.nextSibling;Z.appendChild(W)}c.remove(Y);O(a.grep(Y.childNodes),function(ac){Z.appendChild(ac)});return Z}}return Y}function G(V){return/^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(V)}function J(W,aa){var V,Z,X,Y;V=W[aa?"startContainer":"endContainer"];Z=W[aa?"startOffset":"endOffset"];if(V.nodeType==1){X=V.childNodes.length-1;if(!aa&&Z){Z--}V=V.childNodes[Z>X?X:Z]}if(V.nodeType===3&&aa&&Z>=V.nodeValue.length){V=new t(V,U.getBody()).next()||V}if(V.nodeType===3&&!aa&&Z==0){V=new t(V,U.getBody()).prev()||V}return V}function P(ae,V,ac){var ah,af="_mce_caret",W=U.settings.caret_debug;ah=a.isGecko?"\u200B":E;function X(aj){var ai=c.create("span",{id:af,"data-mce-bogus":true,style:W?"color:red":""});if(aj){ai.appendChild(U.getDoc().createTextNode(ah))}return ai}function ad(aj,ai){while(aj){if((aj.nodeType===3&&aj.nodeValue!==ah)||aj.childNodes.length>1){return false}if(ai&&aj.nodeType===1){ai.push(aj)}aj=aj.firstChild}return true}function aa(ai){while(ai){if(ai.id===af){return ai}ai=ai.parentNode}}function Z(ai){var aj;if(ai){aj=new t(ai,ai);for(ai=aj.current();ai;ai=aj.next()){if(ai.nodeType===3){return ai}}}}function Y(ak,aj){var al,ai;if(!ak){ak=aa(q.getStart());if(!ak){while(ak=c.get(af)){Y(ak,false)}}}else{ai=q.getRng(true);if(ad(ak)){if(aj!==false){ai.setStartBefore(ak);ai.setEndBefore(ak)}c.remove(ak)}else{al=Z(ak);al=al.deleteData(0,1);c.remove(ak,1)}q.setRng(ai)}}function ab(){var ak,ai,ao,an,al,aj,am;ak=q.getRng(true);an=ak.startOffset;aj=ak.startContainer;am=aj.nodeValue;ai=aa(q.getStart());if(ai){ao=Z(ai)}if(am&&an>0&&an<am.length&&/\w/.test(am.charAt(an))&&/\w/.test(am.charAt(an-1))){al=q.getBookmark();ak.collapse(true);ak=o(ak,Q(V));ak=K.split(ak);S(V,ac,ak);q.moveToBookmark(al)}else{if(!ai||ao.nodeValue!==ah){ai=X(true);ao=ai.firstChild;ak.insertNode(ai);an=1;S(V,ac,ai)}else{S(V,ac,ai)}q.setCursorLocation(ao,an)}}function ag(){var ai=q.getRng(true),aj,al,ao,an,ak,ar,aq=[],am,ap;aj=ai.startContainer;al=ai.startOffset;ak=aj;if(aj.nodeType==3){if(al!=aj.nodeValue.length||aj.nodeValue===ah){an=true}ak=ak.parentNode}while(ak){if(x(ak,V,ac)){ar=ak;break}if(ak.nextSibling){an=true}aq.push(ak);ak=ak.parentNode}if(!ar){return}if(an){ao=q.getBookmark();ai.collapse(true);ai=o(ai,Q(V),true);ai=K.split(ai);A(V,ac,ai);q.moveToBookmark(ao)}else{ap=X();ak=ap;for(am=aq.length-1;am>=0;am--){ak.appendChild(aq[am].cloneNode(false));ak=ak.firstChild}ak.appendChild(c.doc.createTextNode(ah));ak=ak.firstChild;c.insertAfter(ap,ar);q.setCursorLocation(ak,1)}}U.onBeforeGetContent.addToTop(function(){var ai=[],aj;if(ad(aa(q.getStart()),ai)){aj=ai.length;while(aj--){c.setAttrib(ai[aj],"data-mce-bogus","1")}}});a.each("onMouseUp onKeyUp".split(" "),function(ai){U[ai].addToTop(function(){Y()})});U.onKeyDown.addToTop(function(ai,ak){var aj=ak.keyCode;if(aj==8||aj==37||aj==39){Y(aa(q.getStart()))}});if(ae=="apply"){ab()}else{ag()}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;if(c.inline_styles){h=e.explode(c.font_size_legacy_values);function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}}); \ No newline at end of file
+(function(e){var a=/^\s*|\s*$/g,b,d="B".replace(/A(.)|B/,"$1")==="$1";var c={majorVersion:"3",minorVersion:"5.8",releaseDate:"2012-11-20",_init:function(){var s=this,q=document,o=navigator,g=o.userAgent,m,f,l,k,j,r;s.isOpera=e.opera&&opera.buildNumber;s.isWebKit=/WebKit/.test(g);s.isIE=!s.isWebKit&&!s.isOpera&&(/MSIE/gi).test(g)&&(/Explorer/gi).test(o.appName);s.isIE6=s.isIE&&/MSIE [56]/.test(g);s.isIE7=s.isIE&&/MSIE [7]/.test(g);s.isIE8=s.isIE&&/MSIE [8]/.test(g);s.isIE9=s.isIE&&/MSIE [9]/.test(g);s.isGecko=!s.isWebKit&&/Gecko/.test(g);s.isMac=g.indexOf("Mac")!=-1;s.isAir=/adobeair/i.test(g);s.isIDevice=/(iPad|iPhone)/.test(g);s.isIOS5=s.isIDevice&&g.match(/AppleWebKit\/(\d*)/)[1]>=534;if(e.tinyMCEPreInit){s.suffix=tinyMCEPreInit.suffix;s.baseURL=tinyMCEPreInit.base;s.query=tinyMCEPreInit.query;return}s.suffix="";f=q.getElementsByTagName("base");for(m=0;m<f.length;m++){r=f[m].href;if(r){if(/^https?:\/\/[^\/]+$/.test(r)){r+="/"}k=r?r.match(/.*\//)[0]:""}}function h(i){if(i.src&&/tiny_mce(|_gzip|_jquery|_prototype|_full)(_dev|_src)?.js/.test(i.src)){if(/_(src|dev)\.js/g.test(i.src)){s.suffix="_src"}if((j=i.src.indexOf("?"))!=-1){s.query=i.src.substring(j+1)}s.baseURL=i.src.substring(0,i.src.lastIndexOf("/"));if(k&&s.baseURL.indexOf("://")==-1&&s.baseURL.indexOf("/")!==0){s.baseURL=k+s.baseURL}return s.baseURL}return null}f=q.getElementsByTagName("script");for(m=0;m<f.length;m++){if(h(f[m])){return}}l=q.getElementsByTagName("head")[0];if(l){f=l.getElementsByTagName("script");for(m=0;m<f.length;m++){if(h(f[m])){return}}}return},is:function(g,f){if(!f){return g!==b}if(f=="array"&&c.isArray(g)){return true}return typeof(g)==f},isArray:Array.isArray||function(f){return Object.prototype.toString.call(f)==="[object Array]"},makeMap:function(f,j,h){var g;f=f||[];j=j||",";if(typeof(f)=="string"){f=f.split(j)}h=h||{};g=f.length;while(g--){h[f[g]]={}}return h},each:function(i,f,h){var j,g;if(!i){return 0}h=h||i;if(i.length!==b){for(j=0,g=i.length;j<g;j++){if(f.call(h,i[j],j,i)===false){return 0}}}else{for(j in i){if(i.hasOwnProperty(j)){if(f.call(h,i[j],j,i)===false){return 0}}}}return 1},map:function(g,h){var i=[];c.each(g,function(f){i.push(h(f))});return i},grep:function(g,h){var i=[];c.each(g,function(f){if(!h||h(f)){i.push(f)}});return i},inArray:function(g,h){var j,f;if(g){for(j=0,f=g.length;j<f;j++){if(g[j]===h){return j}}}return -1},extend:function(n,k){var j,f,h,g=arguments,m;for(j=1,f=g.length;j<f;j++){k=g[j];for(h in k){if(k.hasOwnProperty(h)){m=k[h];if(m!==b){n[h]=m}}}}return n},trim:function(f){return(f?""+f:"").replace(a,"")},create:function(o,f,j){var n=this,g,i,k,l,h,m=0;o=/^((static) )?([\w.]+)(:([\w.]+))?/.exec(o);k=o[3].match(/(^|\.)(\w+)$/i)[2];i=n.createNS(o[3].replace(/\.\w+$/,""),j);if(i[k]){return}if(o[2]=="static"){i[k]=f;if(this.onCreate){this.onCreate(o[2],o[3],i[k])}return}if(!f[k]){f[k]=function(){};m=1}i[k]=f[k];n.extend(i[k].prototype,f);if(o[5]){g=n.resolve(o[5]).prototype;l=o[5].match(/\.(\w+)$/i)[1];h=i[k];if(m){i[k]=function(){return g[l].apply(this,arguments)}}else{i[k]=function(){this.parent=g[l];return h.apply(this,arguments)}}i[k].prototype[k]=i[k];n.each(g,function(p,q){i[k].prototype[q]=g[q]});n.each(f,function(p,q){if(g[q]){i[k].prototype[q]=function(){this.parent=g[q];return p.apply(this,arguments)}}else{if(q!=k){i[k].prototype[q]=p}}})}n.each(f["static"],function(p,q){i[k][q]=p});if(this.onCreate){this.onCreate(o[2],o[3],i[k].prototype)}},walk:function(i,h,j,g){g=g||this;if(i){if(j){i=i[j]}c.each(i,function(k,f){if(h.call(g,k,f,j)===false){return false}c.walk(k,h,j,g)})}},createNS:function(j,h){var g,f;h=h||e;j=j.split(".");for(g=0;g<j.length;g++){f=j[g];if(!h[f]){h[f]={}}h=h[f]}return h},resolve:function(j,h){var g,f;h=h||e;j=j.split(".");for(g=0,f=j.length;g<f;g++){h=h[j[g]];if(!h){break}}return h},addUnload:function(j,i){var h=this,g;g=function(){var f=h.unloads,l,m;if(f){for(m in f){l=f[m];if(l&&l.func){l.func.call(l.scope,1)}}if(e.detachEvent){e.detachEvent("onbeforeunload",k);e.detachEvent("onunload",g)}else{if(e.removeEventListener){e.removeEventListener("unload",g,false)}}h.unloads=l=f=w=g=0;if(e.CollectGarbage){CollectGarbage()}}};function k(){var l=document;function f(){l.detachEvent("onstop",f);if(g){g()}l=0}if(l.readyState=="interactive"){if(l){l.attachEvent("onstop",f)}e.setTimeout(function(){if(l){l.detachEvent("onstop",f)}},0)}}j={func:j,scope:i||this};if(!h.unloads){if(e.attachEvent){e.attachEvent("onunload",g);e.attachEvent("onbeforeunload",k)}else{if(e.addEventListener){e.addEventListener("unload",g,false)}}h.unloads=[j]}else{h.unloads.push(j)}return j},removeUnload:function(i){var g=this.unloads,h=null;c.each(g,function(j,f){if(j&&j.func==i){g.splice(f,1);h=i;return false}});return h},explode:function(f,g){if(!f||c.is(f,"array")){return f}return c.map(f.split(g||","),c.trim)},_addVer:function(g){var f;if(!this.query){return g}f=(g.indexOf("?")==-1?"?":"&")+this.query;if(g.indexOf("#")==-1){return g+f}return g.replace("#",f+"#")},_replace:function(h,f,g){if(d){return g.replace(h,function(){var l=f,j=arguments,k;for(k=0;k<j.length-2;k++){if(j[k]===b){l=l.replace(new RegExp("\\$"+k,"g"),"")}else{l=l.replace(new RegExp("\\$"+k,"g"),j[k])}}return l})}return g.replace(h,f)}};c._init();e.tinymce=e.tinyMCE=c})(window);tinymce.create("tinymce.util.Dispatcher",{scope:null,listeners:null,inDispatch:false,Dispatcher:function(a){this.scope=a||this;this.listeners=[]},add:function(b,a){this.listeners.push({cb:b,scope:a||this.scope});return b},addToTop:function(d,b){var a=this,c={cb:d,scope:b||a.scope};if(a.inDispatch){a.listeners=[c].concat(a.listeners)}else{a.listeners.unshift(c)}return d},remove:function(c){var b=this.listeners,a=null;tinymce.each(b,function(e,d){if(c==e.cb){a=e;b.splice(d,1);return false}});return a},dispatch:function(){var a=this,e,b=arguments,c,d=a.listeners,f;a.inDispatch=true;for(c=0;c<d.length;c++){f=d[c];e=f.cb.apply(f.scope,b.length>0?b:[f.scope]);if(e===false){break}}a.inDispatch=false;return e}});(function(){var a=tinymce.each;tinymce.create("tinymce.util.URI",{URI:function(e,g){var f=this,i,d,c,h;e=tinymce.trim(e);g=f.settings=g||{};if(/^([\w\-]+):([^\/]{2})/i.test(e)||/^\s*#/.test(e)){f.source=e;return}if(e.indexOf("/")===0&&e.indexOf("//")!==0){e=(g.base_uri?g.base_uri.protocol||"http":"http")+"://mce_host"+e}if(!/^[\w\-]*:?\/\//.test(e)){h=g.base_uri?g.base_uri.path:new tinymce.util.URI(location.href).directory;e=((g.base_uri&&g.base_uri.protocol)||"http")+"://mce_host"+f.toAbsPath(h,e)}e=e.replace(/@@/g,"(mce_at)");e=/^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(e);a(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"],function(b,j){var k=e[j];if(k){k=k.replace(/\(mce_at\)/g,"@@")}f[b]=k});c=g.base_uri;if(c){if(!f.protocol){f.protocol=c.protocol}if(!f.userInfo){f.userInfo=c.userInfo}if(!f.port&&f.host==="mce_host"){f.port=c.port}if(!f.host||f.host==="mce_host"){f.host=c.host}f.source=""}},setPath:function(c){var b=this;c=/^(.*?)\/?(\w+)?$/.exec(c);b.path=c[0];b.directory=c[1];b.file=c[2];b.source="";b.getURI()},toRelative:function(b){var d=this,f;if(b==="./"){return b}b=new tinymce.util.URI(b,{base_uri:d});if((b.host!="mce_host"&&d.host!=b.host&&b.host)||d.port!=b.port||d.protocol!=b.protocol){return b.getURI()}var c=d.getURI(),e=b.getURI();if(c==e||(c.charAt(c.length-1)=="/"&&c.substr(0,c.length-1)==e)){return c}f=d.toRelPath(d.path,b.path);if(b.query){f+="?"+b.query}if(b.anchor){f+="#"+b.anchor}return f},toAbsolute:function(b,c){b=new tinymce.util.URI(b,{base_uri:this});return b.getURI(this.host==b.host&&this.protocol==b.protocol?c:0)},toRelPath:function(g,h){var c,f=0,d="",e,b;g=g.substring(0,g.lastIndexOf("/"));g=g.split("/");c=h.split("/");if(g.length>=c.length){for(e=0,b=g.length;e<b;e++){if(e>=c.length||g[e]!=c[e]){f=e+1;break}}}if(g.length<c.length){for(e=0,b=c.length;e<b;e++){if(e>=g.length||g[e]!=c[e]){f=e+1;break}}}if(f===1){return h}for(e=0,b=g.length-(f-1);e<b;e++){d+="../"}for(e=f-1,b=c.length;e<b;e++){if(e!=f-1){d+="/"+c[e]}else{d+=c[e]}}return d},toAbsPath:function(e,f){var c,b=0,h=[],d,g;d=/\/$/.test(f)?"/":"";e=e.split("/");f=f.split("/");a(e,function(i){if(i){h.push(i)}});e=h;for(c=f.length-1,h=[];c>=0;c--){if(f[c].length===0||f[c]==="."){continue}if(f[c]===".."){b++;continue}if(b>0){b--;continue}h.push(f[c])}c=e.length-b;if(c<=0){g=h.reverse().join("/")}else{g=e.slice(0,c).join("/")+"/"+h.reverse().join("/")}if(g.indexOf("/")!==0){g="/"+g}if(d&&g.lastIndexOf("/")!==g.length-1){g+=d}return g},getURI:function(d){var c,b=this;if(!b.source||d){c="";if(!d){if(b.protocol){c+=b.protocol+"://"}if(b.userInfo){c+=b.userInfo+"@"}if(b.host){c+=b.host}if(b.port){c+=":"+b.port}}if(b.path){c+=b.path}if(b.query){c+="?"+b.query}if(b.anchor){c+="#"+b.anchor}b.source=c}return b.source}})})();(function(){var a=tinymce.each;tinymce.create("static tinymce.util.Cookie",{getHash:function(d){var b=this.get(d),c;if(b){a(b.split("&"),function(e){e=e.split("=");c=c||{};c[unescape(e[0])]=unescape(e[1])})}return c},setHash:function(j,b,g,f,i,c){var h="";a(b,function(e,d){h+=(!h?"":"&")+escape(d)+"="+escape(e)});this.set(j,h,g,f,i,c)},get:function(i){var h=document.cookie,g,f=i+"=",d;if(!h){return}d=h.indexOf("; "+f);if(d==-1){d=h.indexOf(f);if(d!==0){return null}}else{d+=2}g=h.indexOf(";",d);if(g==-1){g=h.length}return unescape(h.substring(d+f.length,g))},set:function(i,b,g,f,h,c){document.cookie=i+"="+escape(b)+((g)?"; expires="+g.toGMTString():"")+((f)?"; path="+escape(f):"")+((h)?"; domain="+h:"")+((c)?"; secure":"")},remove:function(c,e,d){var b=new Date();b.setTime(b.getTime()-1000);this.set(c,"",b,e,d)}})})();(function(){function serialize(o,quote){var i,v,t,name;quote=quote||'"';if(o==null){return"null"}t=typeof o;if(t=="string"){v="\bb\tt\nn\ff\rr\"\"''\\\\";return quote+o.replace(/([\u0080-\uFFFF\x00-\x1f\"\'\\])/g,function(a,b){if(quote==='"'&&a==="'"){return a}i=v.indexOf(b);if(i+1){return"\\"+v.charAt(i+1)}a=b.charCodeAt().toString(16);return"\\u"+"0000".substring(a.length)+a})+quote}if(t=="object"){if(o.hasOwnProperty&&Object.prototype.toString.call(o)==="[object Array]"){for(i=0,v="[";i<o.length;i++){v+=(i>0?",":"")+serialize(o[i],quote)}return v+"]"}v="{";for(name in o){if(o.hasOwnProperty(name)){v+=typeof o[name]!="function"?(v.length>1?","+quote:quote)+name+quote+":"+serialize(o[name],quote):""}}return v+"}"}return""+o}tinymce.util.JSON={serialize:serialize,parse:function(s){try{return eval("("+s+")")}catch(ex){}}}})();tinymce.create("static tinymce.util.XHR",{send:function(g){var a,e,b=window,h=0;function f(){if(!g.async||a.readyState==4||h++>10000){if(g.success&&h<10000&&a.status==200){g.success.call(g.success_scope,""+a.responseText,a,g)}else{if(g.error){g.error.call(g.error_scope,h>10000?"TIMED_OUT":"GENERAL",a,g)}}a=null}else{b.setTimeout(f,10)}}g.scope=g.scope||this;g.success_scope=g.success_scope||g.scope;g.error_scope=g.error_scope||g.scope;g.async=g.async===false?false:true;g.data=g.data||"";function d(i){a=0;try{a=new ActiveXObject(i)}catch(c){}return a}a=b.XMLHttpRequest?new XMLHttpRequest():d("Microsoft.XMLHTTP")||d("Msxml2.XMLHTTP");if(a){if(a.overrideMimeType){a.overrideMimeType(g.content_type)}a.open(g.type||(g.data?"POST":"GET"),g.url,g.async);if(g.content_type){a.setRequestHeader("Content-Type",g.content_type)}a.setRequestHeader("X-Requested-With","XMLHttpRequest");a.send(g.data);if(!g.async){return f()}e=b.setTimeout(f,10)}}});(function(){var c=tinymce.extend,b=tinymce.util.JSON,a=tinymce.util.XHR;tinymce.create("tinymce.util.JSONRequest",{JSONRequest:function(d){this.settings=c({},d);this.count=0},send:function(f){var e=f.error,d=f.success;f=c(this.settings,f);f.success=function(h,g){h=b.parse(h);if(typeof(h)=="undefined"){h={error:"JSON Parse error."}}if(h.error){e.call(f.error_scope||f.scope,h.error,g)}else{d.call(f.success_scope||f.scope,h.result)}};f.error=function(h,g){if(e){e.call(f.error_scope||f.scope,h,g)}};f.data=b.serialize({id:f.id||"c"+(this.count++),method:f.method,params:f.params});f.content_type="application/json";a.send(f)},"static":{sendRPC:function(d){return new tinymce.util.JSONRequest().send(d)}}})}());(function(a){a.VK={BACKSPACE:8,DELETE:46,DOWN:40,ENTER:13,LEFT:37,RIGHT:39,SPACEBAR:32,TAB:9,UP:38,modifierPressed:function(b){return b.shiftKey||b.ctrlKey||b.altKey},metaKeyPressed:function(b){return a.isMac?b.metaKey:b.ctrlKey&&!b.altKey}}})(tinymce);tinymce.util.Quirks=function(a){var j=tinymce.VK,f=j.BACKSPACE,k=j.DELETE,e=a.dom,l=a.selection,H=a.settings,v=a.parser,o=a.serializer,E=tinymce.each;function A(N,M){try{a.getDoc().execCommand(N,false,M)}catch(L){}}function n(){var L=a.getDoc().documentMode;return L?L:6}function z(L){return L.isDefaultPrevented()}function J(){function L(O){var M,Q,N,P;M=l.getRng();Q=e.getParent(M.startContainer,e.isBlock);if(O){Q=e.getNext(Q,e.isBlock)}if(Q){N=Q.firstChild;while(N&&N.nodeType==3&&N.nodeValue.length===0){N=N.nextSibling}if(N&&N.nodeName==="SPAN"){P=N.cloneNode(false)}}E(e.select("span",Q),function(R){R.setAttribute("data-mce-mark","1")});a.getDoc().execCommand(O?"ForwardDelete":"Delete",false,null);Q=e.getParent(M.startContainer,e.isBlock);E(e.select("span",Q),function(R){var S=l.getBookmark();if(P){e.replace(P.cloneNode(false),R,true)}else{if(!R.getAttribute("data-mce-mark")){e.remove(R,true)}else{R.removeAttribute("data-mce-mark")}}l.moveToBookmark(S)})}a.onKeyDown.add(function(M,O){var N;N=O.keyCode==k;if(!z(O)&&(N||O.keyCode==f)&&!j.modifierPressed(O)){O.preventDefault();L(N)}});a.addCommand("Delete",function(){L()})}function q(){function L(O){var N=e.create("body");var P=O.cloneContents();N.appendChild(P);return l.serializer.serialize(N,{format:"html"})}function M(N){var P=L(N);var Q=e.createRng();Q.selectNode(a.getBody());var O=L(Q);return P===O}a.onKeyDown.add(function(O,Q){var P=Q.keyCode,N;if(!z(Q)&&(P==k||P==f)){N=O.selection.isCollapsed();if(N&&!e.isEmpty(O.getBody())){return}if(tinymce.isIE&&!N){return}if(!N&&!M(O.selection.getRng())){return}O.setContent("");O.selection.setCursorLocation(O.getBody(),0);O.nodeChanged()}})}function I(){a.onKeyDown.add(function(L,M){if(!z(M)&&M.keyCode==65&&j.metaKeyPressed(M)){M.preventDefault();L.execCommand("SelectAll")}})}function K(){if(!a.settings.content_editable){e.bind(a.getDoc(),"focusin",function(L){l.setRng(l.getRng())});e.bind(a.getDoc(),"mousedown",function(L){if(L.target==a.getDoc().documentElement){a.getWin().focus();l.setRng(l.getRng())}})}}function B(){a.onKeyDown.add(function(L,O){if(!z(O)&&O.keyCode===f){if(l.isCollapsed()&&l.getRng(true).startOffset===0){var N=l.getNode();var M=N.previousSibling;if(M&&M.nodeName&&M.nodeName.toLowerCase()==="hr"){e.remove(M);tinymce.dom.Event.cancel(O)}}}})}function y(){if(!Range.prototype.getClientRects){a.onMouseDown.add(function(M,N){if(!z(N)&&N.target.nodeName==="HTML"){var L=M.getBody();L.blur();setTimeout(function(){L.focus()},0)}})}}function h(){a.onClick.add(function(L,M){M=M.target;if(/^(IMG|HR)$/.test(M.nodeName)){l.getSel().setBaseAndExtent(M,0,M,1)}if(M.nodeName=="A"&&e.hasClass(M,"mceItemAnchor")){l.select(M)}L.nodeChanged()})}function c(){function M(){var O=e.getAttribs(l.getStart().cloneNode(false));return function(){var P=l.getStart();if(P!==a.getBody()){e.setAttrib(P,"style",null);E(O,function(Q){P.setAttributeNode(Q.cloneNode(true))})}}}function L(){return !l.isCollapsed()&&e.getParent(l.getStart(),e.isBlock)!=e.getParent(l.getEnd(),e.isBlock)}function N(O,P){P.preventDefault();return false}a.onKeyPress.add(function(O,Q){var P;if(!z(Q)&&(Q.keyCode==8||Q.keyCode==46)&&L()){P=M();O.getDoc().execCommand("delete",false,null);P();Q.preventDefault();return false}});e.bind(a.getDoc(),"cut",function(P){var O;if(!z(P)&&L()){O=M();a.onKeyUp.addToTop(N);setTimeout(function(){O();a.onKeyUp.remove(N)},0)}})}function b(){var M,L;e.bind(a.getDoc(),"selectionchange",function(){if(L){clearTimeout(L);L=0}L=window.setTimeout(function(){var N=l.getRng();if(!M||!tinymce.dom.RangeUtils.compareRanges(N,M)){a.nodeChanged();M=N}},50)})}function x(){document.body.setAttribute("role","application")}function t(){a.onKeyDown.add(function(L,N){if(!z(N)&&N.keyCode===f){if(l.isCollapsed()&&l.getRng(true).startOffset===0){var M=l.getNode().previousSibling;if(M&&M.nodeName&&M.nodeName.toLowerCase()==="table"){return tinymce.dom.Event.cancel(N)}}}})}function C(){if(n()>7){return}A("RespectVisibilityInDesign",true);a.contentStyles.push(".mceHideBrInPre pre br {display: none}");e.addClass(a.getBody(),"mceHideBrInPre");v.addNodeFilter("pre",function(L,N){var O=L.length,Q,M,R,P;while(O--){Q=L[O].getAll("br");M=Q.length;while(M--){R=Q[M];P=R.prev;if(P&&P.type===3&&P.value.charAt(P.value-1)!="\n"){P.value+="\n"}else{R.parent.insert(new tinymce.html.Node("#text",3),R,true).value="\n"}}}});o.addNodeFilter("pre",function(L,N){var O=L.length,Q,M,R,P;while(O--){Q=L[O].getAll("br");M=Q.length;while(M--){R=Q[M];P=R.prev;if(P&&P.type==3){P.value=P.value.replace(/\r?\n$/,"")}}}})}function g(){e.bind(a.getBody(),"mouseup",function(N){var M,L=l.getNode();if(L.nodeName=="IMG"){if(M=e.getStyle(L,"width")){e.setAttrib(L,"width",M.replace(/[^0-9%]+/g,""));e.setStyle(L,"width","")}if(M=e.getStyle(L,"height")){e.setAttrib(L,"height",M.replace(/[^0-9%]+/g,""));e.setStyle(L,"height","")}}})}function d(){a.onKeyDown.add(function(R,S){var Q,L,M,O,P,T,N;Q=S.keyCode==k;if(!z(S)&&(Q||S.keyCode==f)&&!j.modifierPressed(S)){L=l.getRng();M=L.startContainer;O=L.startOffset;N=L.collapsed;if(M.nodeType==3&&M.nodeValue.length>0&&((O===0&&!N)||(N&&O===(Q?0:1)))){nonEmptyElements=R.schema.getNonEmptyElements();S.preventDefault();P=e.create("br",{id:"__tmp"});M.parentNode.insertBefore(P,M);R.getDoc().execCommand(Q?"ForwardDelete":"Delete",false,null);M=l.getRng().startContainer;T=M.previousSibling;if(T&&T.nodeType==1&&!e.isBlock(T)&&e.isEmpty(T)&&!nonEmptyElements[T.nodeName.toLowerCase()]){e.remove(T)}e.remove("__tmp")}}})}function G(){a.onKeyDown.add(function(P,Q){var N,M,R,L,O;if(z(Q)||Q.keyCode!=j.BACKSPACE){return}N=l.getRng();M=N.startContainer;R=N.startOffset;L=e.getRoot();O=M;if(!N.collapsed||R!==0){return}while(O&&O.parentNode&&O.parentNode.firstChild==O&&O.parentNode!=L){O=O.parentNode}if(O.tagName==="BLOCKQUOTE"){P.formatter.toggle("blockquote",null,O);N=e.createRng();N.setStart(M,0);N.setEnd(M,0);l.setRng(N)}})}function F(){function L(){a._refreshContentEditable();A("StyleWithCSS",false);A("enableInlineTableEditing",false);if(!H.object_resizing){A("enableObjectResizing",false)}}if(!H.readonly){a.onBeforeExecCommand.add(L);a.onMouseDown.add(L)}}function s(){function L(M,N){E(e.select("a"),function(Q){var O=Q.parentNode,P=e.getRoot();if(O.lastChild===Q){while(O&&!e.isBlock(O)){if(O.parentNode.lastChild!==O||O===P){return}O=O.parentNode}e.add(O,"br",{"data-mce-bogus":1})}})}a.onExecCommand.add(function(M,N){if(N==="CreateLink"){L(M)}});a.onSetContent.add(l.onSetContent.add(L))}function m(){if(H.forced_root_block){a.onInit.add(function(){A("DefaultParagraphSeparator",H.forced_root_block)})}}function p(){function L(N,M){if(!N||!M.initial){a.execCommand("mceRepaint")}}a.onUndo.add(L);a.onRedo.add(L);a.onSetContent.add(L)}function i(){a.onKeyDown.add(function(M,N){var L;if(!z(N)&&N.keyCode==f){L=M.getDoc().selection.createRange();if(L&&L.item){N.preventDefault();M.undoManager.beforeChange();e.remove(L.item(0));M.undoManager.add()}}})}function r(){var L;if(n()>=10){L="";E("p div h1 h2 h3 h4 h5 h6".split(" "),function(M,N){L+=(N>0?",":"")+M+":empty"});a.contentStyles.push(L+"{padding-right: 1px !important}")}}function u(){var N,M,ad,L,Y,ab,Z,ac,O,P,aa,W,V,X=document,T=a.getDoc();if(!H.object_resizing||H.webkit_fake_resize===false){return}A("enableObjectResizing",false);aa={n:[0.5,0,0,-1],e:[1,0.5,1,0],s:[0.5,1,0,1],w:[0,0.5,-1,0],nw:[0,0,-1,-1],ne:[1,0,1,-1],se:[1,1,1,1],sw:[0,1,-1,1]};function R(ah){var ag,af;ag=ah.screenX-ab;af=ah.screenY-Z;W=ag*Y[2]+ac;V=af*Y[3]+O;W=W<5?5:W;V=V<5?5:V;if(j.modifierPressed(ah)||(ad.nodeName=="IMG"&&Y[2]*Y[3]!==0)){W=Math.round(V/P);V=Math.round(W*P)}e.setStyles(L,{width:W,height:V});if(Y[2]<0&&L.clientWidth<=W){e.setStyle(L,"left",N+(ac-W))}if(Y[3]<0&&L.clientHeight<=V){e.setStyle(L,"top",M+(O-V))}}function ae(){function af(ag,ah){if(ah){if(ad.style[ag]||!a.schema.isValid(ad.nodeName.toLowerCase(),ag)){e.setStyle(ad,ag,ah)}else{e.setAttrib(ad,ag,ah)}}}af("width",W);af("height",V);e.unbind(T,"mousemove",R);e.unbind(T,"mouseup",ae);if(X!=T){e.unbind(X,"mousemove",R);e.unbind(X,"mouseup",ae)}e.remove(L);Q(ad)}function Q(ai){var ag,ah,af;S();ag=e.getPos(ai);N=ag.x;M=ag.y;ah=ai.offsetWidth;af=ai.offsetHeight;if(ad!=ai){ad=ai;W=V=0}E(aa,function(al,aj){var ak;ak=e.get("mceResizeHandle"+aj);if(!ak){ak=e.add(T.documentElement,"div",{id:"mceResizeHandle"+aj,"class":"mceResizeHandle",style:"cursor:"+aj+"-resize; margin:0; padding:0"});e.bind(ak,"mousedown",function(am){am.preventDefault();ae();ab=am.screenX;Z=am.screenY;ac=ad.clientWidth;O=ad.clientHeight;P=O/ac;Y=al;L=ad.cloneNode(true);e.addClass(L,"mceClonedResizable");e.setStyles(L,{left:N,top:M,margin:0});T.documentElement.appendChild(L);e.bind(T,"mousemove",R);e.bind(T,"mouseup",ae);if(X!=T){e.bind(X,"mousemove",R);e.bind(X,"mouseup",ae)}})}else{e.show(ak)}e.setStyles(ak,{left:(ah*al[0]+N)-(ak.offsetWidth/2),top:(af*al[1]+M)-(ak.offsetHeight/2)})});if(!tinymce.isOpera&&ad.nodeName=="IMG"){ad.setAttribute("data-mce-selected","1")}}function S(){if(ad){ad.removeAttribute("data-mce-selected")}for(var af in aa){e.hide("mceResizeHandle"+af)}}a.contentStyles.push(".mceResizeHandle {position: absolute;border: 1px solid black;background: #FFF;width: 5px;height: 5px;z-index: 10000}.mceResizeHandle:hover {background: #000}img[data-mce-selected] {outline: 1px solid black}img.mceClonedResizable, table.mceClonedResizable {position: absolute;outline: 1px dashed black;opacity: .5;z-index: 10000}");function U(){var af=e.getParent(l.getNode(),"table,img");E(e.select("img[data-mce-selected]"),function(ag){ag.removeAttribute("data-mce-selected")});if(af){Q(af)}else{S()}}a.onNodeChange.add(U);e.bind(T,"selectionchange",U);a.serializer.addAttributeFilter("data-mce-selected",function(af,ag){var ah=af.length;while(ah--){af[ah].attr(ag,null)}})}function D(){if(n()<9){v.addNodeFilter("noscript",function(L){var M=L.length,N,O;while(M--){N=L[M];O=N.firstChild;if(O){N.attr("data-mce-innertext",O.value)}}});o.addNodeFilter("noscript",function(L){var M=L.length,N,P,O;while(M--){N=L[M];P=L[M].firstChild;if(P){P.value=tinymce.html.Entities.decode(P.value)}else{O=N.attributes.map["data-mce-innertext"];if(O){N.attr("data-mce-innertext",null);P=new tinymce.html.Node("#text",3);P.value=O;P.raw=true;N.append(P)}}}})}}t();G();q();if(tinymce.isWebKit){d();J();K();h();m();if(tinymce.isIDevice){b()}else{u();I()}}if(tinymce.isIE){B();x();C();g();i();r();D()}if(tinymce.isGecko){B();y();c();F();s();p()}if(tinymce.isOpera){u()}};(function(j){var a,g,d,k=/[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,b=/[<>&\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,f=/[<>&\"\']/g,c=/&(#x|#)?([\w]+);/g,i={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"};g={'"':"&quot;","'":"&#39;","<":"&lt;",">":"&gt;","&":"&amp;"};d={"&lt;":"<","&gt;":">","&amp;":"&","&quot;":'"',"&apos;":"'"};function h(l){var m;m=document.createElement("div");m.innerHTML=l;return m.textContent||m.innerText||l}function e(m,p){var n,o,l,q={};if(m){m=m.split(",");p=p||10;for(n=0;n<m.length;n+=2){o=String.fromCharCode(parseInt(m[n],p));if(!g[o]){l="&"+m[n+1]+";";q[o]=l;q[l]=o}}return q}}a=e("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);j.html=j.html||{};j.html.Entities={encodeRaw:function(m,l){return m.replace(l?k:b,function(n){return g[n]||n})},encodeAllRaw:function(l){return(""+l).replace(f,function(m){return g[m]||m})},encodeNumeric:function(m,l){return m.replace(l?k:b,function(n){if(n.length>1){return"&#"+(((n.charCodeAt(0)-55296)*1024)+(n.charCodeAt(1)-56320)+65536)+";"}return g[n]||"&#"+n.charCodeAt(0)+";"})},encodeNamed:function(n,l,m){m=m||a;return n.replace(l?k:b,function(o){return g[o]||m[o]||o})},getEncodeFunc:function(l,o){var p=j.html.Entities;o=e(o)||a;function m(r,q){return r.replace(q?k:b,function(s){return g[s]||o[s]||"&#"+s.charCodeAt(0)+";"||s})}function n(r,q){return p.encodeNamed(r,q,o)}l=j.makeMap(l.replace(/\+/g,","));if(l.named&&l.numeric){return m}if(l.named){if(o){return n}return p.encodeNamed}if(l.numeric){return p.encodeNumeric}return p.encodeRaw},decode:function(l){return l.replace(c,function(n,m,o){if(m){o=parseInt(o,m.length===2?16:10);if(o>65535){o-=65536;return String.fromCharCode(55296+(o>>10),56320+(o&1023))}else{return i[o]||String.fromCharCode(o)}}return d[n]||a[n]||h(n)})}}})(tinymce);tinymce.html.Styles=function(d,f){var k=/rgb\s*\(\s*([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\s*\)/gi,h=/(?:url(?:(?:\(\s*\"([^\"]+)\"\s*\))|(?:\(\s*\'([^\']+)\'\s*\))|(?:\(\s*([^)\s]+)\s*\))))|(?:\'([^\']+)\')|(?:\"([^\"]+)\")/gi,b=/\s*([^:]+):\s*([^;]+);?/g,l=/\s+$/,m=/rgb/,e,g,a={},j;d=d||{};j="\\\" \\' \\; \\: ; : \uFEFF".split(" ");for(g=0;g<j.length;g++){a[j[g]]="\uFEFF"+g;a["\uFEFF"+g]=j[g]}function c(n,q,p,i){function o(r){r=parseInt(r).toString(16);return r.length>1?r:"0"+r}return"#"+o(q)+o(p)+o(i)}return{toHex:function(i){return i.replace(k,c)},parse:function(s){var z={},q,n,x,r,v=d.url_converter,y=d.url_converter_scope||this;function p(D,G){var F,C,B,E;F=z[D+"-top"+G];if(!F){return}C=z[D+"-right"+G];if(F!=C){return}B=z[D+"-bottom"+G];if(C!=B){return}E=z[D+"-left"+G];if(B!=E){return}z[D+G]=E;delete z[D+"-top"+G];delete z[D+"-right"+G];delete z[D+"-bottom"+G];delete z[D+"-left"+G]}function u(C){var D=z[C],B;if(!D||D.indexOf(" ")<0){return}D=D.split(" ");B=D.length;while(B--){if(D[B]!==D[0]){return false}}z[C]=D[0];return true}function A(D,C,B,E){if(!u(C)){return}if(!u(B)){return}if(!u(E)){return}z[D]=z[C]+" "+z[B]+" "+z[E];delete z[C];delete z[B];delete z[E]}function t(B){r=true;return a[B]}function i(C,B){if(r){C=C.replace(/\uFEFF[0-9]/g,function(D){return a[D]})}if(!B){C=C.replace(/\\([\'\";:])/g,"$1")}return C}function o(C,B,F,E,G,D){G=G||D;if(G){G=i(G);return"'"+G.replace(/\'/g,"\\'")+"'"}B=i(B||F||E);if(v){B=v.call(y,B,"style")}return"url('"+B.replace(/\'/g,"\\'")+"')"}if(s){s=s.replace(/\\[\"\';:\uFEFF]/g,t).replace(/\"[^\"]+\"|\'[^\']+\'/g,function(B){return B.replace(/[;:]/g,t)});while(q=b.exec(s)){n=q[1].replace(l,"").toLowerCase();x=q[2].replace(l,"");if(n&&x.length>0){if(n==="font-weight"&&x==="700"){x="bold"}else{if(n==="color"||n==="background-color"){x=x.toLowerCase()}}x=x.replace(k,c);x=x.replace(h,o);z[n]=r?i(x,true):x}b.lastIndex=q.index+q[0].length}p("border","");p("border","-width");p("border","-color");p("border","-style");p("padding","");p("margin","");A("border","border-width","border-style","border-color");if(z.border==="medium none"){delete z.border}}return z},serialize:function(p,r){var o="",n,q;function i(t){var x,u,s,v;x=f.styles[t];if(x){for(u=0,s=x.length;u<s;u++){t=x[u];v=p[t];if(v!==e&&v.length>0){o+=(o.length>0?" ":"")+t+": "+v+";"}}}}if(r&&f&&f.styles){i("*");i(r)}else{for(n in p){q=p[n];if(q!==e&&q.length>0){o+=(o.length>0?" ":"")+n+": "+q+";"}}}return o}}};(function(f){var a={},e=f.makeMap,g=f.each;function d(j,i){return j.split(i||",")}function h(m,l){var j,k={};function i(n){return n.replace(/[A-Z]+/g,function(o){return i(m[o])})}for(j in m){if(m.hasOwnProperty(j)){m[j]=i(m[j])}}i(l).replace(/#/g,"#text").replace(/(\w+)\[([^\]]+)\]\[([^\]]*)\]/g,function(q,o,n,p){n=d(n,"|");k[o]={attributes:e(n),attributesOrder:n,children:e(p,"|",{"#comment":{}})}});return k}function b(){var i=a.html5;if(!i){i=a.html5=h({A:"id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video|wbr",C:"#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video"},"html[A|manifest][body|head]head[A][base|command|link|meta|noscript|script|style|title]title[A][#]base[A|href|target][]link[A|href|rel|media|type|sizes][]meta[A|http-equiv|name|content|charset][]style[A|type|media|scoped][#]script[A|charset|type|src|defer|async][#]noscript[A][C]body[A][C]section[A][C]nav[A][C]article[A][C]aside[A][C]h1[A][B]h2[A][B]h3[A][B]h4[A][B]h5[A][B]h6[A][B]hgroup[A][h1|h2|h3|h4|h5|h6]header[A][C]footer[A][C]address[A][C]p[A][B]br[A][]pre[A][B]dialog[A][dd|dt]blockquote[A|cite][C]ol[A|start|reversed][li]ul[A][li]li[A|value][C]dl[A][dd|dt]dt[A][B]dd[A][C]a[A|href|target|ping|rel|media|type][B]em[A][B]strong[A][B]small[A][B]cite[A][B]q[A|cite][B]dfn[A][B]abbr[A][B]code[A][B]var[A][B]samp[A][B]kbd[A][B]sub[A][B]sup[A][B]i[A][B]b[A][B]mark[A][B]progress[A|value|max][B]meter[A|value|min|max|low|high|optimum][B]time[A|datetime][B]ruby[A][B|rt|rp]rt[A][B]rp[A][B]bdo[A][B]span[A][B]ins[A|cite|datetime][B]del[A|cite|datetime][B]figure[A][C|legend|figcaption]figcaption[A][C]img[A|alt|src|height|width|usemap|ismap][]iframe[A|name|src|height|width|sandbox|seamless][]embed[A|src|height|width|type][]object[A|data|type|height|width|usemap|name|form|classid][param]param[A|name|value][]details[A|open][C|legend]command[A|type|label|icon|disabled|checked|radiogroup][]menu[A|type|label][C|li]legend[A][C|B]div[A][C]source[A|src|type|media][]audio[A|src|autobuffer|autoplay|loop|controls][source]video[A|src|autobuffer|autoplay|loop|controls|width|height|poster][source]hr[A][]form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]fieldset[A|disabled|form|name][C|legend]label[A|form|for][B]input[A|type|accept|alt|autocomplete|autofocus|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value|name][]button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]datalist[A][B|option]optgroup[A|disabled|label][option]option[A|disabled|selected|label|value][]textarea[A|autofocus|disabled|form|maxlength|name|placeholder|readonly|required|rows|cols|wrap][]keygen[A|autofocus|challenge|disabled|form|keytype|name][]output[A|for|form|name][B]canvas[A|width|height][]map[A|name][B|C]area[A|shape|coords|href|alt|target|media|rel|ping|type][]mathml[A][]svg[A][]table[A|border][caption|colgroup|thead|tfoot|tbody|tr]caption[A][C]colgroup[A|span][col]col[A|span][]thead[A][tr]tfoot[A][tr]tbody[A][tr]tr[A][th|td]th[A|headers|rowspan|colspan|scope][B]td[A|headers|rowspan|colspan][C]wbr[A][]")}return i}function c(){var i=a.html4;if(!i){i=a.html4=h({Z:"H|K|N|O|P",Y:"X|form|R|Q",ZG:"E|span|width|align|char|charoff|valign",X:"p|T|div|U|W|isindex|fieldset|table",ZF:"E|align|char|charoff|valign",W:"pre|hr|blockquote|address|center|noframes",ZE:"abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height",ZD:"[E][S]",U:"ul|ol|dl|menu|dir",ZC:"p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q",T:"h1|h2|h3|h4|h5|h6",ZB:"X|S|Q",S:"R|P",ZA:"a|G|J|M|O|P",R:"a|H|K|N|O",Q:"noscript|P",P:"ins|del|script",O:"input|select|textarea|label|button",N:"M|L",M:"em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym",L:"sub|sup",K:"J|I",J:"tt|i|b|u|s|strike",I:"big|small|font|basefont",H:"G|F",G:"br|span|bdo",F:"object|applet|img|map|iframe",E:"A|B|C",D:"accesskey|tabindex|onfocus|onblur",C:"onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup",B:"lang|xml:lang|dir",A:"id|class|style|title"},"script[id|charset|type|language|src|defer|xml:space][]style[B|id|type|media|title|xml:space][]object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]param[id|name|value|valuetype|type][]p[E|align][#|S]a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]br[A|clear][]span[E][#|S]bdo[A|C|B][#|S]applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]h1[E|align][#|S]img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]map[B|C|A|name][X|form|Q|area]h2[E|align][#|S]iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]h3[E|align][#|S]tt[E][#|S]i[E][#|S]b[E][#|S]u[E][#|S]s[E][#|S]strike[E][#|S]big[E][#|S]small[E][#|S]font[A|B|size|color|face][#|S]basefont[id|size|color|face][]em[E][#|S]strong[E][#|S]dfn[E][#|S]code[E][#|S]q[E|cite][#|S]samp[E][#|S]kbd[E][#|S]var[E][#|S]cite[E][#|S]abbr[E][#|S]acronym[E][#|S]sub[E][#|S]sup[E][#|S]input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]optgroup[E|disabled|label][option]option[E|selected|disabled|label|value][]textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]label[E|for|accesskey|onfocus|onblur][#|S]button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]h4[E|align][#|S]ins[E|cite|datetime][#|Y]h5[E|align][#|S]del[E|cite|datetime][#|Y]h6[E|align][#|S]div[E|align][#|Y]ul[E|type|compact][li]li[E|type|value][#|Y]ol[E|type|compact|start][li]dl[E|compact][dt|dd]dt[E][#|S]dd[E][#|Y]menu[E|compact][li]dir[E|compact][li]pre[E|width|xml:space][#|ZA]hr[E|align|noshade|size|width][]blockquote[E|cite][#|Y]address[E][#|S|p]center[E][#|Y]noframes[E][#|Y]isindex[A|B|prompt][]fieldset[E][#|legend|Y]legend[E|accesskey|align][#|S]table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]caption[E|align][#|S]col[ZG][]colgroup[ZG][col]thead[ZF][tr]tr[ZF|bgcolor][th|td]th[E|ZE][#|Y]form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]noscript[E][#|Y]td[E|ZE][#|Y]tfoot[ZF][tr]tbody[ZF][tr]area[E|D|shape|coords|href|nohref|alt|target][]base[id|href|target][]body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]")}return i}f.html.Schema=function(A){var u=this,s={},k={},j=[],D,y;var o,q,z,r,v,n,p={};function m(F,E,H){var G=A[F];if(!G){G=a[F];if(!G){G=e(E," ",e(E.toUpperCase()," "));G=f.extend(G,H);a[F]=G}}else{G=e(G,",",e(G.toUpperCase()," "))}return G}A=A||{};y=A.schema=="html5"?b():c();if(A.verify_html===false){A.valid_elements="*[*]"}if(A.valid_styles){D={};g(A.valid_styles,function(F,E){D[E]=f.explode(F)})}o=m("whitespace_elements","pre script noscript style textarea");q=m("self_closing_elements","colgroup dd dt li option p td tfoot th thead tr");z=m("short_ended_elements","area base basefont br col frame hr img input isindex link meta param embed source wbr");r=m("boolean_attributes","checked compact declare defer disabled ismap multiple nohref noresize noshade nowrap readonly selected autoplay loop controls");n=m("non_empty_elements","td th iframe video audio object",z);textBlockElementsMap=m("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");v=m("block_elements","hr table tbody thead tfoot th tr td li ol ul caption dl dt dd noscript menu isindex samp option datalist select optgroup",textBlockElementsMap);function i(E){return new RegExp("^"+E.replace(/([?+*])/g,".$1")+"$")}function C(L){var K,G,Z,V,aa,F,I,U,X,Q,Y,ac,O,J,W,E,S,H,ab,ad,P,T,N=/^([#+\-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,R=/^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,M=/[*?+]/;if(L){L=d(L);if(s["@"]){S=s["@"].attributes;H=s["@"].attributesOrder}for(K=0,G=L.length;K<G;K++){F=N.exec(L[K]);if(F){W=F[1];Q=F[2];E=F[3];X=F[4];O={};J=[];I={attributes:O,attributesOrder:J};if(W==="#"){I.paddEmpty=true}if(W==="-"){I.removeEmpty=true}if(S){for(ad in S){O[ad]=S[ad]}J.push.apply(J,H)}if(X){X=d(X,"|");for(Z=0,V=X.length;Z<V;Z++){F=R.exec(X[Z]);if(F){U={};ac=F[1];Y=F[2].replace(/::/g,":");W=F[3];T=F[4];if(ac==="!"){I.attributesRequired=I.attributesRequired||[];I.attributesRequired.push(Y);U.required=true}if(ac==="-"){delete O[Y];J.splice(f.inArray(J,Y),1);continue}if(W){if(W==="="){I.attributesDefault=I.attributesDefault||[];I.attributesDefault.push({name:Y,value:T});U.defaultValue=T}if(W===":"){I.attributesForced=I.attributesForced||[];I.attributesForced.push({name:Y,value:T});U.forcedValue=T}if(W==="<"){U.validValues=e(T,"?")}}if(M.test(Y)){I.attributePatterns=I.attributePatterns||[];U.pattern=i(Y);I.attributePatterns.push(U)}else{if(!O[Y]){J.push(Y)}O[Y]=U}}}}if(!S&&Q=="@"){S=O;H=J}if(E){I.outputName=Q;s[E]=I}if(M.test(Q)){I.pattern=i(Q);j.push(I)}else{s[Q]=I}}}}}function t(E){s={};j=[];C(E);g(y,function(G,F){k[F]=G.children})}function l(F){var E=/^(~)?(.+)$/;if(F){g(d(F),function(J){var H=E.exec(J),I=H[1]==="~",K=I?"span":"div",G=H[2];k[G]=k[K];p[G]=K;if(!I){v[G.toUpperCase()]={};v[G]={}}if(!s[G]){s[G]=s[K]}g(k,function(L,M){if(L[K]){L[G]=L[K]}})})}}function x(F){var E=/^([+\-]?)(\w+)\[([^\]]+)\]$/;if(F){g(d(F),function(J){var I=E.exec(J),G,H;if(I){H=I[1];if(H){G=k[I[2]]}else{G=k[I[2]]={"#comment":{}}}G=k[I[2]];g(d(I[3],"|"),function(K){if(H==="-"){delete G[K]}else{G[K]={}}})}})}}function B(E){var G=s[E],F;if(G){return G}F=j.length;while(F--){G=j[F];if(G.pattern.test(E)){return G}}}if(!A.valid_elements){g(y,function(F,E){s[E]={attributes:F.attributes,attributesOrder:F.attributesOrder};k[E]=F.children});if(A.schema!="html5"){g(d("strong/b,em/i"),function(E){E=d(E,"/");s[E[1]].outputName=E[0]})}s.img.attributesDefault=[{name:"alt",value:""}];g(d("ol,ul,sub,sup,blockquote,span,font,a,table,tbody,tr,strong,em,b,i"),function(E){if(s[E]){s[E].removeEmpty=true}});g(d("p,h1,h2,h3,h4,h5,h6,th,td,pre,div,address,caption"),function(E){s[E].paddEmpty=true})}else{t(A.valid_elements)}l(A.custom_elements);x(A.valid_children);C(A.extended_valid_elements);x("+ol[ul|ol],+ul[ul|ol]");if(A.invalid_elements){f.each(f.explode(A.invalid_elements),function(E){if(s[E]){delete s[E]}})}if(!B("span")){C("span[!data-mce-type|*]")}u.children=k;u.styles=D;u.getBoolAttrs=function(){return r};u.getBlockElements=function(){return v};u.getTextBlockElements=function(){return textBlockElementsMap};u.getShortEndedElements=function(){return z};u.getSelfClosingElements=function(){return q};u.getNonEmptyElements=function(){return n};u.getWhiteSpaceElements=function(){return o};u.isValidChild=function(E,G){var F=k[E];return !!(F&&F[G])};u.isValid=function(F,E){var H,G,I=B(F);if(I){if(E){if(I.attributes[E]){return true}H=I.attributePatterns;if(H){G=H.length;while(G--){if(H[G].pattern.test(F)){return true}}}}else{return true}}return false};u.getElementRule=B;u.getCustomElements=function(){return p};u.addValidElements=C;u.setValidElements=t;u.addCustomElements=l;u.addValidChildren=x;u.elements=s}})(tinymce);(function(a){a.html.SaxParser=function(c,e){var b=this,d=function(){};c=c||{};b.schema=e=e||new a.html.Schema();if(c.fix_self_closing!==false){c.fix_self_closing=true}a.each("comment cdata text start end pi doctype".split(" "),function(f){if(f){b[f]=c[f]||d}});b.parse=function(E){var n=this,g,G=0,I,B,A=[],N,Q,C,r,z,s,M,H,O,v,m,k,t,R,o,P,F,S,L,f,J,l,D,K,h,x=0,j=a.html.Entities.decode,y,q;function u(T){var V,U;V=A.length;while(V--){if(A[V].name===T){break}}if(V>=0){for(U=A.length-1;U>=V;U--){T=A[U];if(T.valid){n.end(T.name)}}A.length=V}}function p(U,T,Y,X,W){var Z,V;T=T.toLowerCase();Y=T in H?T:j(Y||X||W||"");if(v&&!z&&T.indexOf("data-")!==0){Z=P[T];if(!Z&&F){V=F.length;while(V--){Z=F[V];if(Z.pattern.test(T)){break}}if(V===-1){Z=null}}if(!Z){return}if(Z.validValues&&!(Y in Z.validValues)){return}}N.map[T]=Y;N.push({name:T,value:Y})}l=new RegExp("<(?:(?:!--([\\w\\W]*?)-->)|(?:!\\[CDATA\\[([\\w\\W]*?)\\]\\]>)|(?:!DOCTYPE([\\w\\W]*?)>)|(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|(?:\\/([^>]+)>)|(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^\"'>]+(?:(?:\"[^\"]*\")|(?:'[^']*')|[^>]*))*|\\/|\\s+)>))","g");D=/([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;K={script:/<\/script[^>]*>/gi,style:/<\/style[^>]*>/gi,noscript:/<\/noscript[^>]*>/gi};M=e.getShortEndedElements();J=c.self_closing_elements||e.getSelfClosingElements();H=e.getBoolAttrs();v=c.validate;s=c.remove_internals;y=c.fix_self_closing;q=a.isIE;o=/^:/;while(g=l.exec(E)){if(G<g.index){n.text(j(E.substr(G,g.index-G)))}if(I=g[6]){I=I.toLowerCase();if(q&&o.test(I)){I=I.substr(1)}u(I)}else{if(I=g[7]){I=I.toLowerCase();if(q&&o.test(I)){I=I.substr(1)}O=I in M;if(y&&J[I]&&A.length>0&&A[A.length-1].name===I){u(I)}if(!v||(m=e.getElementRule(I))){k=true;if(v){P=m.attributes;F=m.attributePatterns}if(R=g[8]){z=R.indexOf("data-mce-type")!==-1;if(z&&s){k=false}N=[];N.map={};R.replace(D,p)}else{N=[];N.map={}}if(v&&!z){S=m.attributesRequired;L=m.attributesDefault;f=m.attributesForced;if(f){Q=f.length;while(Q--){t=f[Q];r=t.name;h=t.value;if(h==="{$uid}"){h="mce_"+x++}N.map[r]=h;N.push({name:r,value:h})}}if(L){Q=L.length;while(Q--){t=L[Q];r=t.name;if(!(r in N.map)){h=t.value;if(h==="{$uid}"){h="mce_"+x++}N.map[r]=h;N.push({name:r,value:h})}}}if(S){Q=S.length;while(Q--){if(S[Q] in N.map){break}}if(Q===-1){k=false}}if(N.map["data-mce-bogus"]){k=false}}if(k){n.start(I,N,O)}}else{k=false}if(B=K[I]){B.lastIndex=G=g.index+g[0].length;if(g=B.exec(E)){if(k){C=E.substr(G,g.index-G)}G=g.index+g[0].length}else{C=E.substr(G);G=E.length}if(k&&C.length>0){n.text(C,true)}if(k){n.end(I)}l.lastIndex=G;continue}if(!O){if(!R||R.indexOf("/")!=R.length-1){A.push({name:I,valid:k})}else{if(k){n.end(I)}}}}else{if(I=g[1]){n.comment(I)}else{if(I=g[2]){n.cdata(I)}else{if(I=g[3]){n.doctype(I)}else{if(I=g[4]){n.pi(I,g[5])}}}}}}G=g.index+g[0].length}if(G<E.length){n.text(j(E.substr(G)))}for(Q=A.length-1;Q>=0;Q--){I=A[Q];if(I.valid){n.end(I.name)}}}}})(tinymce);(function(d){var c=/^[ \t\r\n]*$/,e={"#text":3,"#comment":8,"#cdata":4,"#pi":7,"#doctype":10,"#document-fragment":11};function a(k,l,j){var i,h,f=j?"lastChild":"firstChild",g=j?"prev":"next";if(k[f]){return k[f]}if(k!==l){i=k[g];if(i){return i}for(h=k.parent;h&&h!==l;h=h.parent){i=h[g];if(i){return i}}}}function b(f,g){this.name=f;this.type=g;if(g===1){this.attributes=[];this.attributes.map={}}}d.extend(b.prototype,{replace:function(g){var f=this;if(g.parent){g.remove()}f.insert(g,f);f.remove();return f},attr:function(h,l){var f=this,g,j,k;if(typeof h!=="string"){for(j in h){f.attr(j,h[j])}return f}if(g=f.attributes){if(l!==k){if(l===null){if(h in g.map){delete g.map[h];j=g.length;while(j--){if(g[j].name===h){g=g.splice(j,1);return f}}}return f}if(h in g.map){j=g.length;while(j--){if(g[j].name===h){g[j].value=l;break}}}else{g.push({name:h,value:l})}g.map[h]=l;return f}else{return g.map[h]}}},clone:function(){var g=this,n=new b(g.name,g.type),h,f,m,j,k;if(m=g.attributes){k=[];k.map={};for(h=0,f=m.length;h<f;h++){j=m[h];if(j.name!=="id"){k[k.length]={name:j.name,value:j.value};k.map[j.name]=j.value}}n.attributes=k}n.value=g.value;n.shortEnded=g.shortEnded;return n},wrap:function(g){var f=this;f.parent.insert(g,f);g.append(f);return f},unwrap:function(){var f=this,h,g;for(h=f.firstChild;h;){g=h.next;f.insert(h,f,true);h=g}f.remove()},remove:function(){var f=this,h=f.parent,g=f.next,i=f.prev;if(h){if(h.firstChild===f){h.firstChild=g;if(g){g.prev=null}}else{i.next=g}if(h.lastChild===f){h.lastChild=i;if(i){i.next=null}}else{g.prev=i}f.parent=f.next=f.prev=null}return f},append:function(h){var f=this,g;if(h.parent){h.remove()}g=f.lastChild;if(g){g.next=h;h.prev=g;f.lastChild=h}else{f.lastChild=f.firstChild=h}h.parent=f;return h},insert:function(h,f,i){var g;if(h.parent){h.remove()}g=f.parent||this;if(i){if(f===g.firstChild){g.firstChild=h}else{f.prev.next=h}h.prev=f.prev;h.next=f;f.prev=h}else{if(f===g.lastChild){g.lastChild=h}else{f.next.prev=h}h.next=f.next;h.prev=f;f.next=h}h.parent=g;return h},getAll:function(g){var f=this,h,i=[];for(h=f.firstChild;h;h=a(h,f)){if(h.name===g){i.push(h)}}return i},empty:function(){var g=this,f,h,j;if(g.firstChild){f=[];for(j=g.firstChild;j;j=a(j,g)){f.push(j)}h=f.length;while(h--){j=f[h];j.parent=j.firstChild=j.lastChild=j.next=j.prev=null}}g.firstChild=g.lastChild=null;return g},isEmpty:function(k){var f=this,j=f.firstChild,h,g;if(j){do{if(j.type===1){if(j.attributes.map["data-mce-bogus"]){continue}if(k[j.name]){return false}h=j.attributes.length;while(h--){g=j.attributes[h].name;if(g==="name"||g.indexOf("data-mce-")===0){return false}}}if(j.type===8){return false}if((j.type===3&&!c.test(j.value))){return false}}while(j=a(j,f))}return true},walk:function(f){return a(this,null,f)}});d.extend(b,{create:function(g,f){var i,h;i=new b(g,e[g]||1);if(f){for(h in f){i.attr(h,f[h])}}return i}});d.html.Node=b})(tinymce);(function(b){var a=b.html.Node;b.html.DomParser=function(g,h){var f=this,e={},d=[],i={},c={};g=g||{};g.validate="validate" in g?g.validate:true;g.root_name=g.root_name||"body";f.schema=h=h||new b.html.Schema();function j(n){var p,q,y,x,A,o,r,l,u,v,k,t,m,z,s;t=b.makeMap("tr,td,th,tbody,thead,tfoot,table");k=h.getNonEmptyElements();m=h.getTextBlockElements();for(p=0;p<n.length;p++){q=n[p];if(!q.parent||q.fixed){continue}if(m[q.name]&&q.parent.name=="li"){z=q.next;while(z){if(m[z.name]){z.name="li";z.fixed=true;q.parent.insert(z,q.parent)}else{break}z=z.next}q.unwrap(q);continue}x=[q];for(y=q.parent;y&&!h.isValidChild(y.name,q.name)&&!t[y.name];y=y.parent){x.push(y)}if(y&&x.length>1){x.reverse();A=o=f.filterNode(x[0].clone());for(u=0;u<x.length-1;u++){if(h.isValidChild(o.name,x[u].name)){r=f.filterNode(x[u].clone());o.append(r)}else{r=o}for(l=x[u].firstChild;l&&l!=x[u+1];){s=l.next;r.append(l);l=s}o=r}if(!A.isEmpty(k)){y.insert(A,x[0],true);y.insert(q,A)}else{y.insert(q,x[0],true)}y=x[0];if(y.isEmpty(k)||y.firstChild===y.lastChild&&y.firstChild.name==="br"){y.empty().remove()}}else{if(q.parent){if(q.name==="li"){z=q.prev;if(z&&(z.name==="ul"||z.name==="ul")){z.append(q);continue}z=q.next;if(z&&(z.name==="ul"||z.name==="ul")){z.insert(q,z.firstChild,true);continue}q.wrap(f.filterNode(new a("ul",1)));continue}if(h.isValidChild(q.parent.name,"div")&&h.isValidChild("div",q.name)){q.wrap(f.filterNode(new a("div",1)))}else{if(q.name==="style"||q.name==="script"){q.empty().remove()}else{q.unwrap()}}}}}}f.filterNode=function(m){var l,k,n;if(k in e){n=i[k];if(n){n.push(m)}else{i[k]=[m]}}l=d.length;while(l--){k=d[l].name;if(k in m.attributes.map){n=c[k];if(n){n.push(m)}else{c[k]=[m]}}}return m};f.addNodeFilter=function(k,l){b.each(b.explode(k),function(m){var n=e[m];if(!n){e[m]=n=[]}n.push(l)})};f.addAttributeFilter=function(k,l){b.each(b.explode(k),function(m){var n;for(n=0;n<d.length;n++){if(d[n].name===m){d[n].callbacks.push(l);return}}d.push({name:m,callbacks:[l]})})};f.parse=function(v,m){var n,J,B,A,D,C,x,r,F,N,z,o,E,M=[],L,t,k,y,s,p,u,q;m=m||{};i={};c={};o=b.extend(b.makeMap("script,style,head,html,body,title,meta,param"),h.getBlockElements());u=h.getNonEmptyElements();p=h.children;z=g.validate;q="forced_root_block" in m?m.forced_root_block:g.forced_root_block;s=h.getWhiteSpaceElements();E=/^[ \t\r\n]+/;t=/[ \t\r\n]+$/;k=/[ \t\r\n]+/g;y=/^[ \t\r\n]+$/;function G(){var O=J.firstChild,l,P;while(O){l=O.next;if(O.type==3||(O.type==1&&O.name!=="p"&&!o[O.name]&&!O.attr("data-mce-type"))){if(!P){P=K(q,1);J.insert(P,O);P.append(O)}else{P.append(O)}}else{P=null}O=l}}function K(l,O){var P=new a(l,O),Q;if(l in e){Q=i[l];if(Q){Q.push(P)}else{i[l]=[P]}}return P}function I(P){var Q,l,O;for(Q=P.prev;Q&&Q.type===3;){l=Q.value.replace(t,"");if(l.length>0){Q.value=l;Q=Q.prev}else{O=Q.prev;Q.remove();Q=O}}}function H(O){var P,l={};for(P in O){if(P!=="li"&&P!="p"){l[P]=O[P]}}return l}n=new b.html.SaxParser({validate:z,self_closing_elements:H(h.getSelfClosingElements()),cdata:function(l){B.append(K("#cdata",4)).value=l},text:function(P,l){var O;if(!L){P=P.replace(k," ");if(B.lastChild&&o[B.lastChild.name]){P=P.replace(E,"")}}if(P.length!==0){O=K("#text",3);O.raw=!!l;B.append(O).value=P}},comment:function(l){B.append(K("#comment",8)).value=l},pi:function(l,O){B.append(K(l,7)).value=O;I(B)},doctype:function(O){var l;l=B.append(K("#doctype",10));l.value=O;I(B)},start:function(l,W,P){var U,R,Q,O,S,X,V,T;Q=z?h.getElementRule(l):{};if(Q){U=K(Q.outputName||l,1);U.attributes=W;U.shortEnded=P;B.append(U);T=p[B.name];if(T&&p[U.name]&&!T[U.name]){M.push(U)}R=d.length;while(R--){S=d[R].name;if(S in W.map){F=c[S];if(F){F.push(U)}else{c[S]=[U]}}}if(o[l]){I(U)}if(!P){B=U}if(!L&&s[l]){L=true}}},end:function(l){var S,P,R,O,Q;P=z?h.getElementRule(l):{};if(P){if(o[l]){if(!L){S=B.firstChild;if(S&&S.type===3){R=S.value.replace(E,"");if(R.length>0){S.value=R;S=S.next}else{O=S.next;S.remove();S=O}while(S&&S.type===3){R=S.value;O=S.next;if(R.length===0||y.test(R)){S.remove();S=O}S=O}}S=B.lastChild;if(S&&S.type===3){R=S.value.replace(t,"");if(R.length>0){S.value=R;S=S.prev}else{O=S.prev;S.remove();S=O}while(S&&S.type===3){R=S.value;O=S.prev;if(R.length===0||y.test(R)){S.remove();S=O}S=O}}}}if(L&&s[l]){L=false}if(P.removeEmpty||P.paddEmpty){if(B.isEmpty(u)){if(P.paddEmpty){B.empty().append(new a("#text","3")).value="\u00a0"}else{if(!B.attributes.map.name&&!B.attributes.map.id){Q=B.parent;B.empty().remove();B=Q;return}}}}B=B.parent}}},h);J=B=new a(m.context||g.root_name,11);n.parse(v);if(z&&M.length){if(!m.context){j(M)}else{m.invalid=true}}if(q&&J.name=="body"){G()}if(!m.invalid){for(N in i){F=e[N];A=i[N];x=A.length;while(x--){if(!A[x].parent){A.splice(x,1)}}for(D=0,C=F.length;D<C;D++){F[D](A,N,m)}}for(D=0,C=d.length;D<C;D++){F=d[D];if(F.name in c){A=c[F.name];x=A.length;while(x--){if(!A[x].parent){A.splice(x,1)}}for(x=0,r=F.callbacks.length;x<r;x++){F.callbacks[x](A,F.name,m)}}}}return J};if(g.remove_trailing_brs){f.addNodeFilter("br",function(n,m){var r,q=n.length,o,v=b.extend({},h.getBlockElements()),k=h.getNonEmptyElements(),t,s,p,u;v.body=1;for(r=0;r<q;r++){o=n[r];t=o.parent;if(v[o.parent.name]&&o===t.lastChild){p=o.prev;while(p){u=p.name;if(u!=="span"||p.attr("data-mce-type")!=="bookmark"){if(u!=="br"){break}if(u==="br"){o=null;break}}p=p.prev}if(o){o.remove();if(t.isEmpty(k)){elementRule=h.getElementRule(t.name);if(elementRule){if(elementRule.removeEmpty){t.remove()}else{if(elementRule.paddEmpty){t.empty().append(new b.html.Node("#text",3)).value="\u00a0"}}}}}}else{s=o;while(t.firstChild===s&&t.lastChild===s){s=t;if(v[t.name]){break}t=t.parent}if(s===t){textNode=new b.html.Node("#text",3);textNode.value="\u00a0";o.replace(textNode)}}}})}if(!g.allow_html_in_named_anchor){f.addAttributeFilter("id,name",function(k,l){var n=k.length,p,m,o,q;while(n--){q=k[n];if(q.name==="a"&&q.firstChild&&!q.attr("href")){o=q.parent;p=q.lastChild;do{m=p.prev;o.insert(p,q);p=m}while(p)}}})}}})(tinymce);tinymce.html.Writer=function(e){var c=[],a,b,d,f,g;e=e||{};a=e.indent;b=tinymce.makeMap(e.indent_before||"");d=tinymce.makeMap(e.indent_after||"");f=tinymce.html.Entities.getEncodeFunc(e.entity_encoding||"raw",e.entities);g=e.element_format=="html";return{start:function(m,k,p){var n,j,h,o;if(a&&b[m]&&c.length>0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}c.push("<",m);if(k){for(n=0,j=k.length;n<j;n++){h=k[n];c.push(" ",h.name,'="',f(h.value,true),'"')}}if(!p||g){c[c.length]=">"}else{c[c.length]=" />"}if(p&&a&&d[m]&&c.length>0){o=c[c.length-1];if(o.length>0&&o!=="\n"){c.push("\n")}}},end:function(h){var i;c.push("</",h,">");if(a&&d[h]&&c.length>0){i=c[c.length-1];if(i.length>0&&i!=="\n"){c.push("\n")}}},text:function(i,h){if(i.length>0){c[c.length]=h?i:f(i)}},cdata:function(h){c.push("<![CDATA[",h,"]]>")},comment:function(h){c.push("<!--",h,"-->")},pi:function(h,i){if(i){c.push("<?",h," ",i,"?>")}else{c.push("<?",h,"?>")}if(a){c.push("\n")}},doctype:function(h){c.push("<!DOCTYPE",h,">",a?"\n":"")},reset:function(){c.length=0},getContent:function(){return c.join("").replace(/\n$/,"")}}};(function(a){a.html.Serializer=function(c,d){var b=this,e=new a.html.Writer(c);c=c||{};c.validate="validate" in c?c.validate:true;b.schema=d=d||new a.html.Schema();b.writer=e;b.serialize=function(h){var g,i;i=c.validate;g={3:function(k,j){e.text(k.value,k.raw)},8:function(j){e.comment(j.value)},7:function(j){e.pi(j.name,j.value)},10:function(j){e.doctype(j.value)},4:function(j){e.cdata(j.value)},11:function(j){if((j=j.firstChild)){do{f(j)}while(j=j.next)}}};e.reset();function f(k){var t=g[k.type],j,o,s,r,p,u,n,m,q;if(!t){j=k.name;o=k.shortEnded;s=k.attributes;if(i&&s&&s.length>1){u=[];u.map={};q=d.getElementRule(k.name);for(n=0,m=q.attributesOrder.length;n<m;n++){r=q.attributesOrder[n];if(r in s.map){p=s.map[r];u.map[r]=p;u.push({name:r,value:p})}}for(n=0,m=s.length;n<m;n++){r=s[n].name;if(!(r in u.map)){p=s.map[r];u.map[r]=p;u.push({name:r,value:p})}}s=u}e.start(k.name,s,o);if(!o){if((k=k.firstChild)){do{f(k)}while(k=k.next)}e.end(j)}}else{t(k)}}if(h.type==1&&!c.inner){f(h)}else{g[11](h)}return e.getContent()}}})(tinymce);tinymce.dom={};(function(b,h){var g=!!document.addEventListener;function c(k,j,l,i){if(k.addEventListener){k.addEventListener(j,l,i||false)}else{if(k.attachEvent){k.attachEvent("on"+j,l)}}}function e(k,j,l,i){if(k.removeEventListener){k.removeEventListener(j,l,i||false)}else{if(k.detachEvent){k.detachEvent("on"+j,l)}}}function a(n,l){var i,k=l||{};function j(){return false}function m(){return true}for(i in n){if(i!=="layerX"&&i!=="layerY"){k[i]=n[i]}}if(!k.target){k.target=k.srcElement||document}k.preventDefault=function(){k.isDefaultPrevented=m;if(n){if(n.preventDefault){n.preventDefault()}else{n.returnValue=false}}};k.stopPropagation=function(){k.isPropagationStopped=m;if(n){if(n.stopPropagation){n.stopPropagation()}else{n.cancelBubble=true}}};k.stopImmediatePropagation=function(){k.isImmediatePropagationStopped=m;k.stopPropagation()};if(!k.isDefaultPrevented){k.isDefaultPrevented=j;k.isPropagationStopped=j;k.isImmediatePropagationStopped=j}return k}function d(m,n,l){var k=m.document,j={type:"ready"};function i(){if(!l.domLoaded){l.domLoaded=true;n(j)}}if(k.readyState=="complete"){i();return}if(g){c(m,"DOMContentLoaded",i)}else{c(k,"readystatechange",function(){if(k.readyState==="complete"){e(k,"readystatechange",arguments.callee);i()}});if(k.documentElement.doScroll&&m===m.top){(function(){try{k.documentElement.doScroll("left")}catch(o){setTimeout(arguments.callee,0);return}i()})()}}c(m,"load",i)}function f(k){var q=this,p={},i,o,n,m,l;m="onmouseenter" in document.documentElement;n="onfocusin" in document.documentElement;l={mouseenter:"mouseover",mouseleave:"mouseout"};i=1;q.domLoaded=false;q.events=p;function j(t,x){var s,u,r,v;s=p[x][t.type];if(s){for(u=0,r=s.length;u<r;u++){v=s[u];if(v&&v.func.call(v.scope,t)===false){t.preventDefault()}if(t.isImmediatePropagationStopped()){return}}}}q.bind=function(x,A,D,E){var s,t,u,r,B,z,C,v=window;function y(F){j(a(F||v.event),s)}if(!x||x.nodeType===3||x.nodeType===8){return}if(!x[h]){s=i++;x[h]=s;p[s]={}}else{s=x[h];if(!p[s]){p[s]={}}}E=E||x;A=A.split(" ");u=A.length;while(u--){r=A[u];z=y;B=C=false;if(r==="DOMContentLoaded"){r="ready"}if((q.domLoaded||x.readyState=="complete")&&r==="ready"){q.domLoaded=true;D.call(E,a({type:r}));continue}if(!m){B=l[r];if(B){z=function(F){var H,G;H=F.currentTarget;G=F.relatedTarget;if(G&&H.contains){G=H.contains(G)}else{while(G&&G!==H){G=G.parentNode}}if(!G){F=a(F||v.event);F.type=F.type==="mouseout"?"mouseleave":"mouseenter";F.target=H;j(F,s)}}}}if(!n&&(r==="focusin"||r==="focusout")){C=true;B=r==="focusin"?"focus":"blur";z=function(F){F=a(F||v.event);F.type=F.type==="focus"?"focusin":"focusout";j(F,s)}}t=p[s][r];if(!t){p[s][r]=t=[{func:D,scope:E}];t.fakeName=B;t.capture=C;t.nativeHandler=z;if(!g){t.proxyHandler=k(s)}if(r==="ready"){d(x,z,q)}else{c(x,B||r,g?z:t.proxyHandler,C)}}else{t.push({func:D,scope:E})}}x=t=0;return D};q.unbind=function(x,z,A){var s,u,v,B,r,t;if(!x||x.nodeType===3||x.nodeType===8){return q}s=x[h];if(s){t=p[s];if(z){z=z.split(" ");v=z.length;while(v--){r=z[v];u=t[r];if(u){if(A){B=u.length;while(B--){if(u[B].func===A){u.splice(B,1)}}}if(!A||u.length===0){delete t[r];e(x,u.fakeName||r,g?u.nativeHandler:u.proxyHandler,u.capture)}}}}else{for(r in t){u=t[r];e(x,u.fakeName||r,g?u.nativeHandler:u.proxyHandler,u.capture)}t={}}for(r in t){return q}delete p[s];try{delete x[h]}catch(y){x[h]=null}}return q};q.fire=function(u,s,r){var v,t;if(!u||u.nodeType===3||u.nodeType===8){return q}t=a(null,r);t.type=s;do{v=u[h];if(v){j(t,v)}u=u.parentNode||u.ownerDocument||u.defaultView||u.parentWindow}while(u&&!t.isPropagationStopped());return q};q.clean=function(u){var s,r,t=q.unbind;if(!u||u.nodeType===3||u.nodeType===8){return q}if(u[h]){t(u)}if(!u.getElementsByTagName){u=u.document}if(u&&u.getElementsByTagName){t(u);r=u.getElementsByTagName("*");s=r.length;while(s--){u=r[s];if(u[h]){t(u)}}}return q};q.callNativeHandler=function(s,r){if(p){p[s][r.type].nativeHandler(r)}};q.destory=function(){p={}};q.add=function(v,s,u,t){if(typeof(v)==="string"){v=document.getElementById(v)}if(v&&v instanceof Array){var r=v.length;while(r--){q.add(v[r],s,u,t)}return}if(s==="init"){s="ready"}return q.bind(v,s instanceof Array?s.join(" "):s,u,t)};q.remove=function(v,s,u,t){if(!v){return q}if(typeof(v)==="string"){v=document.getElementById(v)}if(v instanceof Array){var r=v.length;while(r--){q.remove(v[r],s,u,t)}return q}return q.unbind(v,s instanceof Array?s.join(" "):s,u)};q.clear=function(r){if(typeof(r)==="string"){r=document.getElementById(r)}return q.clean(r)};q.cancel=function(r){if(r){q.prevent(r);q.stop(r)}return false};q.prevent=function(r){if(!r.preventDefault){r=a(r)}r.preventDefault();return false};q.stop=function(r){if(!r.stopPropagation){r=a(r)}r.stopPropagation();return false}}b.EventUtils=f;b.Event=new f(function(i){return function(j){tinymce.dom.Event.callNativeHandler(i,j)}});b.Event.bind(window,"ready",function(){});b=0})(tinymce.dom,"data-mce-expando");tinymce.dom.TreeWalker=function(a,c){var b=a;function d(i,f,e,j){var h,g;if(i){if(!j&&i[f]){return i[f]}if(i!=c){h=i[e];if(h){return h}for(g=i.parentNode;g&&g!=c;g=g.parentNode){h=g[e];if(h){return h}}}}}this.current=function(){return b};this.next=function(e){return(b=d(b,"firstChild","nextSibling",e))};this.prev=function(e){return(b=d(b,"lastChild","previousSibling",e))}};(function(e){var g=e.each,d=e.is,f=e.isWebKit,b=e.isIE,h=e.html.Entities,c=/^([a-z0-9],?)+$/i,a=/^[ \t\r\n]*$/;e.create("tinymce.dom.DOMUtils",{doc:null,root:null,files:null,pixelStyles:/^(top|left|bottom|right|width|height|borderWidth)$/,props:{"for":"htmlFor","class":"className",className:"className",checked:"checked",disabled:"disabled",maxlength:"maxLength",readonly:"readOnly",selected:"selected",value:"value",id:"id",name:"name",type:"type"},DOMUtils:function(o,l){var k=this,i,j,n;k.doc=o;k.win=window;k.files={};k.cssFlicker=false;k.counter=0;k.stdMode=!e.isIE||o.documentMode>=8;k.boxModel=!e.isIE||o.compatMode=="CSS1Compat"||k.stdMode;k.hasOuterHTML="outerHTML" in o.createElement("a");k.settings=l=e.extend({keep_values:false,hex_colors:1},l);k.schema=l.schema;k.styles=new e.html.Styles({url_converter:l.url_converter,url_converter_scope:l.url_converter_scope},l.schema);if(e.isIE6){try{o.execCommand("BackgroundImageCache",false,true)}catch(m){k.cssFlicker=true}}k.fixDoc(o);k.events=l.ownEvents?new e.dom.EventUtils(l.proxy):e.dom.Event;e.addUnload(k.destroy,k);n=l.schema?l.schema.getBlockElements():{};k.isBlock=function(q){if(!q){return false}var p=q.nodeType;if(p){return !!(p===1&&n[q.nodeName])}return !!n[q]}},fixDoc:function(k){var j=this.settings,i;if(b&&j.schema){("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(l){k.createElement(l)});for(i in j.schema.getCustomElements()){k.createElement(i)}}},clone:function(k,i){var j=this,m,l;if(!b||k.nodeType!==1||i){return k.cloneNode(i)}l=j.doc;if(!i){m=l.createElement(k.nodeName);g(j.getAttribs(k),function(n){j.setAttrib(m,n.nodeName,j.getAttrib(k,n.nodeName))});return m}return m.firstChild},getRoot:function(){var i=this,j=i.settings;return(j&&i.get(j.root_element))||i.doc.body},getViewPort:function(j){var k,i;j=!j?this.win:j;k=j.document;i=this.boxModel?k.documentElement:k.body;return{x:j.pageXOffset||i.scrollLeft,y:j.pageYOffset||i.scrollTop,w:j.innerWidth||i.clientWidth,h:j.innerHeight||i.clientHeight}},getRect:function(l){var k,i=this,j;l=i.get(l);k=i.getPos(l);j=i.getSize(l);return{x:k.x,y:k.y,w:j.w,h:j.h}},getSize:function(l){var j=this,i,k;l=j.get(l);i=j.getStyle(l,"width");k=j.getStyle(l,"height");if(i.indexOf("px")===-1){i=0}if(k.indexOf("px")===-1){k=0}return{w:parseInt(i,10)||l.offsetWidth||l.clientWidth,h:parseInt(k,10)||l.offsetHeight||l.clientHeight}},getParent:function(k,j,i){return this.getParents(k,j,i,false)},getParents:function(s,m,k,q){var j=this,i,l=j.settings,p=[];s=j.get(s);q=q===undefined;if(l.strict_root){k=k||j.getRoot()}if(d(m,"string")){i=m;if(m==="*"){m=function(o){return o.nodeType==1}}else{m=function(o){return j.is(o,i)}}}while(s){if(s==k||!s.nodeType||s.nodeType===9){break}if(!m||m(s)){if(q){p.push(s)}else{return s}}s=s.parentNode}return q?p:null},get:function(i){var j;if(i&&this.doc&&typeof(i)=="string"){j=i;i=this.doc.getElementById(i);if(i&&i.id!==j){return this.doc.getElementsByName(j)[1]}}return i},getNext:function(j,i){return this._findSib(j,i,"nextSibling")},getPrev:function(j,i){return this._findSib(j,i,"previousSibling")},select:function(k,j){var i=this;return e.dom.Sizzle(k,i.get(j)||i.get(i.settings.root_element)||i.doc,[])},is:function(l,j){var k;if(l.length===undefined){if(j==="*"){return l.nodeType==1}if(c.test(j)){j=j.toLowerCase().split(/,/);l=l.nodeName.toLowerCase();for(k=j.length-1;k>=0;k--){if(j[k]==l){return true}}return false}}return e.dom.Sizzle.matches(j,l.nodeType?[l]:l).length>0},add:function(l,o,i,k,m){var j=this;return this.run(l,function(r){var q,n;q=d(o,"string")?j.doc.createElement(o):o;j.setAttribs(q,i);if(k){if(k.nodeType){q.appendChild(k)}else{j.setHTML(q,k)}}return !m?r.appendChild(q):q})},create:function(k,i,j){return this.add(this.doc.createElement(k),k,i,j,1)},createHTML:function(q,i,m){var p="",l=this,j;p+="<"+q;for(j in i){if(i.hasOwnProperty(j)){p+=" "+j+'="'+l.encode(i[j])+'"'}}if(typeof(m)!="undefined"){return p+">"+m+"</"+q+">"}return p+" />"},remove:function(i,j){return this.run(i,function(l){var m,k=l.parentNode;if(!k){return null}if(j){while(m=l.firstChild){if(!e.isIE||m.nodeType!==3||m.nodeValue){k.insertBefore(m,l)}else{l.removeChild(m)}}}return k.removeChild(l)})},setStyle:function(l,i,j){var k=this;return k.run(l,function(o){var n,m;n=o.style;i=i.replace(/-(\D)/g,function(q,p){return p.toUpperCase()});if(k.pixelStyles.test(i)&&(e.is(j,"number")||/^[\-0-9\.]+$/.test(j))){j+="px"}switch(i){case"opacity":if(b){n.filter=j===""?"":"alpha(opacity="+(j*100)+")";if(!l.currentStyle||!l.currentStyle.hasLayout){n.display="inline-block"}}n[i]=n["-moz-opacity"]=n["-khtml-opacity"]=j||"";break;case"float":b?n.styleFloat=j:n.cssFloat=j;break;default:n[i]=j||""}if(k.settings.update_styles){k.setAttrib(o,"data-mce-style")}})},getStyle:function(l,i,k){l=this.get(l);if(!l){return}if(this.doc.defaultView&&k){i=i.replace(/[A-Z]/g,function(m){return"-"+m});try{return this.doc.defaultView.getComputedStyle(l,null).getPropertyValue(i)}catch(j){return null}}i=i.replace(/-(\D)/g,function(n,m){return m.toUpperCase()});if(i=="float"){i=b?"styleFloat":"cssFloat"}if(l.currentStyle&&k){return l.currentStyle[i]}return l.style?l.style[i]:undefined},setStyles:function(l,m){var j=this,k=j.settings,i;i=k.update_styles;k.update_styles=0;g(m,function(o,p){j.setStyle(l,p,o)});k.update_styles=i;if(k.update_styles){j.setAttrib(l,k.cssText)}},removeAllAttribs:function(i){return this.run(i,function(l){var k,j=l.attributes;for(k=j.length-1;k>=0;k--){l.removeAttributeNode(j.item(k))}})},setAttrib:function(k,l,i){var j=this;if(!k||!l){return}if(j.settings.strict){l=l.toLowerCase()}return this.run(k,function(p){var o=j.settings;var m=p.getAttribute(l);if(i!==null){switch(l){case"style":if(!d(i,"string")){g(i,function(q,r){j.setStyle(p,r,q)});return}if(o.keep_values){if(i&&!j._isRes(i)){p.setAttribute("data-mce-style",i,2)}else{p.removeAttribute("data-mce-style",2)}}p.style.cssText=i;break;case"class":p.className=i||"";break;case"src":case"href":if(o.keep_values){if(o.url_converter){i=o.url_converter.call(o.url_converter_scope||j,i,l,p)}j.setAttrib(p,"data-mce-"+l,i,2)}break;case"shape":p.setAttribute("data-mce-style",i);break}}if(d(i)&&i!==null&&i.length!==0){p.setAttribute(l,""+i,2)}else{p.removeAttribute(l,2)}if(tinyMCE.activeEditor&&m!=i){var n=tinyMCE.activeEditor;n.onSetAttrib.dispatch(n,p,l,i)}})},setAttribs:function(j,k){var i=this;return this.run(j,function(l){g(k,function(m,o){i.setAttrib(l,o,m)})})},getAttrib:function(m,o,k){var i,j=this,l;m=j.get(m);if(!m||m.nodeType!==1){return k===l?false:k}if(!d(k)){k=""}if(/^(src|href|style|coords|shape)$/.test(o)){i=m.getAttribute("data-mce-"+o);if(i){return i}}if(b&&j.props[o]){i=m[j.props[o]];i=i&&i.nodeValue?i.nodeValue:i}if(!i){i=m.getAttribute(o,2)}if(/^(checked|compact|declare|defer|disabled|ismap|multiple|nohref|noshade|nowrap|readonly|selected)$/.test(o)){if(m[j.props[o]]===true&&i===""){return o}return i?o:""}if(m.nodeName==="FORM"&&m.getAttributeNode(o)){return m.getAttributeNode(o).nodeValue}if(o==="style"){i=i||m.style.cssText;if(i){i=j.serializeStyle(j.parseStyle(i),m.nodeName);if(j.settings.keep_values&&!j._isRes(i)){m.setAttribute("data-mce-style",i)}}}if(f&&o==="class"&&i){i=i.replace(/(apple|webkit)\-[a-z\-]+/gi,"")}if(b){switch(o){case"rowspan":case"colspan":if(i===1){i=""}break;case"size":if(i==="+0"||i===20||i===0){i=""}break;case"width":case"height":case"vspace":case"checked":case"disabled":case"readonly":if(i===0){i=""}break;case"hspace":if(i===-1){i=""}break;case"maxlength":case"tabindex":if(i===32768||i===2147483647||i==="32768"){i=""}break;case"multiple":case"compact":case"noshade":case"nowrap":if(i===65535){return o}return k;case"shape":i=i.toLowerCase();break;default:if(o.indexOf("on")===0&&i){i=e._replace(/^function\s+\w+\(\)\s+\{\s+(.*)\s+\}$/,"$1",""+i)}}}return(i!==l&&i!==null&&i!=="")?""+i:k},getPos:function(q,l){var j=this,i=0,p=0,m,o=j.doc,k;q=j.get(q);l=l||o.body;if(q){if(q.getBoundingClientRect){q=q.getBoundingClientRect();m=j.boxModel?o.documentElement:o.body;i=q.left+(o.documentElement.scrollLeft||o.body.scrollLeft)-m.clientTop;p=q.top+(o.documentElement.scrollTop||o.body.scrollTop)-m.clientLeft;return{x:i,y:p}}k=q;while(k&&k!=l&&k.nodeType){i+=k.offsetLeft||0;p+=k.offsetTop||0;k=k.offsetParent}k=q.parentNode;while(k&&k!=l&&k.nodeType){i-=k.scrollLeft||0;p-=k.scrollTop||0;k=k.parentNode}}return{x:i,y:p}},parseStyle:function(i){return this.styles.parse(i)},serializeStyle:function(j,i){return this.styles.serialize(j,i)},addStyle:function(j){var k=this.doc,i;styleElm=k.getElementById("mceDefaultStyles");if(!styleElm){styleElm=k.createElement("style"),styleElm.id="mceDefaultStyles";styleElm.type="text/css";i=k.getElementsByTagName("head")[0];if(i.firstChild){i.insertBefore(styleElm,i.firstChild)}else{i.appendChild(styleElm)}}if(styleElm.styleSheet){styleElm.styleSheet.cssText+=j}else{styleElm.appendChild(k.createTextNode(j))}},loadCSS:function(i){var k=this,l=k.doc,j;if(!i){i=""}j=l.getElementsByTagName("head")[0];g(i.split(","),function(m){var n;if(k.files[m]){return}k.files[m]=true;n=k.create("link",{rel:"stylesheet",href:e._addVer(m)});if(b&&l.documentMode&&l.recalc){n.onload=function(){if(l.recalc){l.recalc()}n.onload=null}}j.appendChild(n)})},addClass:function(i,j){return this.run(i,function(k){var l;if(!j){return 0}if(this.hasClass(k,j)){return k.className}l=this.removeClass(k,j);return k.className=(l!=""?(l+" "):"")+j})},removeClass:function(k,l){var i=this,j;return i.run(k,function(n){var m;if(i.hasClass(n,l)){if(!j){j=new RegExp("(^|\\s+)"+l+"(\\s+|$)","g")}m=n.className.replace(j," ");m=e.trim(m!=" "?m:"");n.className=m;if(!m){n.removeAttribute("class");n.removeAttribute("className")}return m}return n.className})},hasClass:function(j,i){j=this.get(j);if(!j||!i){return false}return(" "+j.className+" ").indexOf(" "+i+" ")!==-1},show:function(i){return this.setStyle(i,"display","block")},hide:function(i){return this.setStyle(i,"display","none")},isHidden:function(i){i=this.get(i);return !i||i.style.display=="none"||this.getStyle(i,"display")=="none"},uniqueId:function(i){return(!i?"mce_":i)+(this.counter++)},setHTML:function(k,j){var i=this;return i.run(k,function(m){if(b){while(m.firstChild){m.removeChild(m.firstChild)}try{m.innerHTML="<br />"+j;m.removeChild(m.firstChild)}catch(l){var n=i.create("div");n.innerHTML="<br />"+j;g(e.grep(n.childNodes),function(p,o){if(o&&m.canHaveHTML){m.appendChild(p)}})}}else{m.innerHTML=j}return j})},getOuterHTML:function(k){var j,i=this;k=i.get(k);if(!k){return null}if(k.nodeType===1&&i.hasOuterHTML){return k.outerHTML}j=(k.ownerDocument||i.doc).createElement("body");j.appendChild(k.cloneNode(true));return j.innerHTML},setOuterHTML:function(l,j,m){var i=this;function k(p,o,r){var s,q;q=r.createElement("body");q.innerHTML=o;s=q.lastChild;while(s){i.insertAfter(s.cloneNode(true),p);s=s.previousSibling}i.remove(p)}return this.run(l,function(o){o=i.get(o);if(o.nodeType==1){m=m||o.ownerDocument||i.doc;if(b){try{if(b&&o.nodeType==1){o.outerHTML=j}else{k(o,j,m)}}catch(n){k(o,j,m)}}else{k(o,j,m)}}})},decode:h.decode,encode:h.encodeAllRaw,insertAfter:function(i,j){j=this.get(j);return this.run(i,function(l){var k,m;k=j.parentNode;m=j.nextSibling;if(m){k.insertBefore(l,m)}else{k.appendChild(l)}return l})},replace:function(m,l,i){var j=this;if(d(l,"array")){m=m.cloneNode(true)}return j.run(l,function(k){if(i){g(e.grep(k.childNodes),function(n){m.appendChild(n)})}return k.parentNode.replaceChild(m,k)})},rename:function(l,i){var k=this,j;if(l.nodeName!=i.toUpperCase()){j=k.create(i);g(k.getAttribs(l),function(m){k.setAttrib(j,m.nodeName,k.getAttrib(l,m.nodeName))});k.replace(j,l,1)}return j||l},findCommonAncestor:function(k,i){var l=k,j;while(l){j=i;while(j&&l!=j){j=j.parentNode}if(l==j){break}l=l.parentNode}if(!l&&k.ownerDocument){return k.ownerDocument.documentElement}return l},toHex:function(i){var k=/^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(i);function j(l){l=parseInt(l,10).toString(16);return l.length>1?l:"0"+l}if(k){i="#"+j(k[1])+j(k[2])+j(k[3]);return i}return i},getClasses:function(){var n=this,j=[],m,o={},p=n.settings.class_filter,l;if(n.classes){return n.classes}function q(i){g(i.imports,function(s){q(s)});g(i.cssRules||i.rules,function(s){switch(s.type||1){case 1:if(s.selectorText){g(s.selectorText.split(","),function(r){r=r.replace(/^\s*|\s*$|^\s\./g,"");if(/\.mce/.test(r)||!/\.[\w\-]+$/.test(r)){return}l=r;r=e._replace(/.*\.([a-z0-9_\-]+).*/i,"$1",r);if(p&&!(r=p(r,l))){return}if(!o[r]){j.push({"class":r});o[r]=1}})}break;case 3:q(s.styleSheet);break}})}try{g(n.doc.styleSheets,q)}catch(k){}if(j.length>0){n.classes=j}return j},run:function(l,k,j){var i=this,m;if(i.doc&&typeof(l)==="string"){l=i.get(l)}if(!l){return false}j=j||this;if(!l.nodeType&&(l.length||l.length===0)){m=[];g(l,function(o,n){if(o){if(typeof(o)=="string"){o=i.doc.getElementById(o)}m.push(k.call(j,o,n))}});return m}return k.call(j,l)},getAttribs:function(j){var i;j=this.get(j);if(!j){return[]}if(b){i=[];if(j.nodeName=="OBJECT"){return j.attributes}if(j.nodeName==="OPTION"&&this.getAttrib(j,"selected")){i.push({specified:1,nodeName:"selected"})}j.cloneNode(false).outerHTML.replace(/<\/?[\w:\-]+ ?|=[\"][^\"]+\"|=\'[^\']+\'|=[\w\-]+|>/gi,"").replace(/[\w:\-]+/gi,function(k){i.push({specified:1,nodeName:k})});return i}return j.attributes},isEmpty:function(m,k){var r=this,o,n,q,j,l,p=0;m=m.firstChild;if(m){j=new e.dom.TreeWalker(m,m.parentNode);k=k||r.schema?r.schema.getNonEmptyElements():null;do{q=m.nodeType;if(q===1){if(m.getAttribute("data-mce-bogus")){continue}l=m.nodeName.toLowerCase();if(k&&k[l]){if(l==="br"){p++;continue}return false}n=r.getAttribs(m);o=m.attributes.length;while(o--){l=m.attributes[o].nodeName;if(l==="name"||l==="data-mce-bookmark"){return false}}}if(q==8){return false}if((q===3&&!a.test(m.nodeValue))){return false}}while(m=j.next())}return p<=1},destroy:function(j){var i=this;i.win=i.doc=i.root=i.events=i.frag=null;if(!j){e.removeUnload(i.destroy)}},createRng:function(){var i=this.doc;return i.createRange?i.createRange():new e.dom.Range(this)},nodeIndex:function(m,n){var i=0,k,l,j;if(m){for(k=m.nodeType,m=m.previousSibling,l=m;m;m=m.previousSibling){j=m.nodeType;if(n&&j==3){if(j==k||!m.nodeValue.length){continue}}i++;k=j}}return i},split:function(m,l,p){var q=this,i=q.createRng(),n,k,o;function j(v){var t,s=v.childNodes,u=v.nodeType;function x(A){var z=A.previousSibling&&A.previousSibling.nodeName=="SPAN";var y=A.nextSibling&&A.nextSibling.nodeName=="SPAN";return z&&y}if(u==1&&v.getAttribute("data-mce-type")=="bookmark"){return}for(t=s.length-1;t>=0;t--){j(s[t])}if(u!=9){if(u==3&&v.nodeValue.length>0){var r=e.trim(v.nodeValue).length;if(!q.isBlock(v.parentNode)||r>0||r===0&&x(v)){return}}else{if(u==1){s=v.childNodes;if(s.length==1&&s[0]&&s[0].nodeType==1&&s[0].getAttribute("data-mce-type")=="bookmark"){v.parentNode.insertBefore(s[0],v)}if(s.length||/^(br|hr|input|img)$/i.test(v.nodeName)){return}}}q.remove(v)}return v}if(m&&l){i.setStart(m.parentNode,q.nodeIndex(m));i.setEnd(l.parentNode,q.nodeIndex(l));n=i.extractContents();i=q.createRng();i.setStart(l.parentNode,q.nodeIndex(l)+1);i.setEnd(m.parentNode,q.nodeIndex(m)+1);k=i.extractContents();o=m.parentNode;o.insertBefore(j(n),m);if(p){o.replaceChild(p,l)}else{o.insertBefore(l,m)}o.insertBefore(j(k),m);q.remove(m);return p||l}},bind:function(l,i,k,j){return this.events.add(l,i,k,j||this)},unbind:function(k,i,j){return this.events.remove(k,i,j)},fire:function(k,j,i){return this.events.fire(k,j,i)},getContentEditable:function(j){var i;if(j.nodeType!=1){return null}i=j.getAttribute("data-mce-contenteditable");if(i&&i!=="inherit"){return i}return j.contentEditable!=="inherit"?j.contentEditable:null},_findSib:function(l,i,j){var k=this,m=i;if(l){if(d(m,"string")){m=function(n){return k.is(n,i)}}for(l=l[j];l;l=l[j]){if(m(l)){return l}}}return null},_isRes:function(i){return/^(top|left|bottom|right|width|height)/i.test(i)||/;\s*(top|left|bottom|right|width|height)/i.test(i)}});e.DOM=new e.dom.DOMUtils(document,{process_html:0})})(tinymce);(function(a){function b(c){var O=this,e=c.doc,U=0,F=1,j=2,E=true,S=false,W="startOffset",h="startContainer",Q="endContainer",A="endOffset",k=tinymce.extend,n=c.nodeIndex;k(O,{startContainer:e,startOffset:0,endContainer:e,endOffset:0,collapsed:E,commonAncestorContainer:e,START_TO_START:0,START_TO_END:1,END_TO_END:2,END_TO_START:3,setStart:q,setEnd:s,setStartBefore:g,setStartAfter:J,setEndBefore:K,setEndAfter:u,collapse:B,selectNode:y,selectNodeContents:G,compareBoundaryPoints:v,deleteContents:p,extractContents:I,cloneContents:d,insertNode:D,surroundContents:N,cloneRange:L,toStringIE:T});function x(){return e.createDocumentFragment()}function q(X,t){C(E,X,t)}function s(X,t){C(S,X,t)}function g(t){q(t.parentNode,n(t))}function J(t){q(t.parentNode,n(t)+1)}function K(t){s(t.parentNode,n(t))}function u(t){s(t.parentNode,n(t)+1)}function B(t){if(t){O[Q]=O[h];O[A]=O[W]}else{O[h]=O[Q];O[W]=O[A]}O.collapsed=E}function y(t){g(t);u(t)}function G(t){q(t,0);s(t,t.nodeType===1?t.childNodes.length:t.nodeValue.length)}function v(aa,t){var ad=O[h],Y=O[W],ac=O[Q],X=O[A],ab=t.startContainer,af=t.startOffset,Z=t.endContainer,ae=t.endOffset;if(aa===0){return H(ad,Y,ab,af)}if(aa===1){return H(ac,X,ab,af)}if(aa===2){return H(ac,X,Z,ae)}if(aa===3){return H(ad,Y,Z,ae)}}function p(){l(j)}function I(){return l(U)}function d(){return l(F)}function D(aa){var X=this[h],t=this[W],Z,Y;if((X.nodeType===3||X.nodeType===4)&&X.nodeValue){if(!t){X.parentNode.insertBefore(aa,X)}else{if(t>=X.nodeValue.length){c.insertAfter(aa,X)}else{Z=X.splitText(t);X.parentNode.insertBefore(aa,Z)}}}else{if(X.childNodes.length>0){Y=X.childNodes[t]}if(Y){X.insertBefore(aa,Y)}else{X.appendChild(aa)}}}function N(X){var t=O.extractContents();O.insertNode(X);X.appendChild(t);O.selectNode(X)}function L(){return k(new b(c),{startContainer:O[h],startOffset:O[W],endContainer:O[Q],endOffset:O[A],collapsed:O.collapsed,commonAncestorContainer:O.commonAncestorContainer})}function P(t,X){var Y;if(t.nodeType==3){return t}if(X<0){return t}Y=t.firstChild;while(Y&&X>0){--X;Y=Y.nextSibling}if(Y){return Y}return t}function m(){return(O[h]==O[Q]&&O[W]==O[A])}function H(Z,ab,X,aa){var ac,Y,t,ad,af,ae;if(Z==X){if(ab==aa){return 0}if(ab<aa){return -1}return 1}ac=X;while(ac&&ac.parentNode!=Z){ac=ac.parentNode}if(ac){Y=0;t=Z.firstChild;while(t!=ac&&Y<ab){Y++;t=t.nextSibling}if(ab<=Y){return -1}return 1}ac=Z;while(ac&&ac.parentNode!=X){ac=ac.parentNode}if(ac){Y=0;t=X.firstChild;while(t!=ac&&Y<aa){Y++;t=t.nextSibling}if(Y<aa){return -1}return 1}ad=c.findCommonAncestor(Z,X);af=Z;while(af&&af.parentNode!=ad){af=af.parentNode}if(!af){af=ad}ae=X;while(ae&&ae.parentNode!=ad){ae=ae.parentNode}if(!ae){ae=ad}if(af==ae){return 0}t=ad.firstChild;while(t){if(t==af){return -1}if(t==ae){return 1}t=t.nextSibling}}function C(X,aa,Z){var t,Y;if(X){O[h]=aa;O[W]=Z}else{O[Q]=aa;O[A]=Z}t=O[Q];while(t.parentNode){t=t.parentNode}Y=O[h];while(Y.parentNode){Y=Y.parentNode}if(Y==t){if(H(O[h],O[W],O[Q],O[A])>0){O.collapse(X)}}else{O.collapse(X)}O.collapsed=m();O.commonAncestorContainer=c.findCommonAncestor(O[h],O[Q])}function l(ad){var ac,Z=0,af=0,X,ab,Y,aa,t,ae;if(O[h]==O[Q]){return f(ad)}for(ac=O[Q],X=ac.parentNode;X;ac=X,X=X.parentNode){if(X==O[h]){return r(ac,ad)}++Z}for(ac=O[h],X=ac.parentNode;X;ac=X,X=X.parentNode){if(X==O[Q]){return V(ac,ad)}++af}ab=af-Z;Y=O[h];while(ab>0){Y=Y.parentNode;ab--}aa=O[Q];while(ab<0){aa=aa.parentNode;ab++}for(t=Y.parentNode,ae=aa.parentNode;t!=ae;t=t.parentNode,ae=ae.parentNode){Y=t;aa=ae}return o(Y,aa,ad)}function f(ac){var ae,af,t,Y,Z,ad,aa,X,ab;if(ac!=j){ae=x()}if(O[W]==O[A]){return ae}if(O[h].nodeType==3){af=O[h].nodeValue;t=af.substring(O[W],O[A]);if(ac!=F){Y=O[h];X=O[W];ab=O[A]-O[W];if(X===0&&ab>=Y.nodeValue.length-1){Y.parentNode.removeChild(Y)}else{Y.deleteData(X,ab)}O.collapse(E)}if(ac==j){return}if(t.length>0){ae.appendChild(e.createTextNode(t))}return ae}Y=P(O[h],O[W]);Z=O[A]-O[W];while(Y&&Z>0){ad=Y.nextSibling;aa=z(Y,ac);if(ae){ae.appendChild(aa)}--Z;Y=ad}if(ac!=F){O.collapse(E)}return ae}function r(ad,aa){var ac,ab,X,t,Z,Y;if(aa!=j){ac=x()}ab=i(ad,aa);if(ac){ac.appendChild(ab)}X=n(ad);t=X-O[W];if(t<=0){if(aa!=F){O.setEndBefore(ad);O.collapse(S)}return ac}ab=ad.previousSibling;while(t>0){Z=ab.previousSibling;Y=z(ab,aa);if(ac){ac.insertBefore(Y,ac.firstChild)}--t;ab=Z}if(aa!=F){O.setEndBefore(ad);O.collapse(S)}return ac}function V(ab,aa){var ad,X,ac,t,Z,Y;if(aa!=j){ad=x()}ac=R(ab,aa);if(ad){ad.appendChild(ac)}X=n(ab);++X;t=O[A]-X;ac=ab.nextSibling;while(ac&&t>0){Z=ac.nextSibling;Y=z(ac,aa);if(ad){ad.appendChild(Y)}--t;ac=Z}if(aa!=F){O.setStartAfter(ab);O.collapse(E)}return ad}function o(ab,t,ae){var Y,ag,aa,ac,ad,X,af,Z;if(ae!=j){ag=x()}Y=R(ab,ae);if(ag){ag.appendChild(Y)}aa=ab.parentNode;ac=n(ab);ad=n(t);++ac;X=ad-ac;af=ab.nextSibling;while(X>0){Z=af.nextSibling;Y=z(af,ae);if(ag){ag.appendChild(Y)}af=Z;--X}Y=i(t,ae);if(ag){ag.appendChild(Y)}if(ae!=F){O.setStartAfter(ab);O.collapse(E)}return ag}function i(ac,ad){var Y=P(O[Q],O[A]-1),ae,ab,aa,t,X,Z=Y!=O[Q];if(Y==ac){return M(Y,Z,S,ad)}ae=Y.parentNode;ab=M(ae,S,S,ad);while(ae){while(Y){aa=Y.previousSibling;t=M(Y,Z,S,ad);if(ad!=j){ab.insertBefore(t,ab.firstChild)}Z=E;Y=aa}if(ae==ac){return ab}Y=ae.previousSibling;ae=ae.parentNode;X=M(ae,S,S,ad);if(ad!=j){X.appendChild(ab)}ab=X}}function R(ac,ad){var Z=P(O[h],O[W]),aa=Z!=O[h],ae,ab,Y,t,X;if(Z==ac){return M(Z,aa,E,ad)}ae=Z.parentNode;ab=M(ae,S,E,ad);while(ae){while(Z){Y=Z.nextSibling;t=M(Z,aa,E,ad);if(ad!=j){ab.appendChild(t)}aa=E;Z=Y}if(ae==ac){return ab}Z=ae.nextSibling;ae=ae.parentNode;X=M(ae,S,E,ad);if(ad!=j){X.appendChild(ab)}ab=X}}function M(t,aa,ad,ae){var Z,Y,ab,X,ac;if(aa){return z(t,ae)}if(t.nodeType==3){Z=t.nodeValue;if(ad){X=O[W];Y=Z.substring(X);ab=Z.substring(0,X)}else{X=O[A];Y=Z.substring(0,X);ab=Z.substring(X)}if(ae!=F){t.nodeValue=ab}if(ae==j){return}ac=c.clone(t,S);ac.nodeValue=Y;return ac}if(ae==j){return}return c.clone(t,S)}function z(X,t){if(t!=j){return t==F?c.clone(X,E):X}X.parentNode.removeChild(X)}function T(){return c.create("body",null,d()).outerText}return O}a.Range=b;b.prototype.toString=function(){return this.toStringIE()}})(tinymce.dom);(function(){function a(d){var b=this,h=d.dom,c=true,f=false;function e(i,j){var k,t=0,q,n,m,l,o,r,p=-1,s;k=i.duplicate();k.collapse(j);s=k.parentElement();if(s.ownerDocument!==d.dom.doc){return}while(s.contentEditable==="false"){s=s.parentNode}if(!s.hasChildNodes()){return{node:s,inside:1}}m=s.children;q=m.length-1;while(t<=q){r=Math.floor((t+q)/2);l=m[r];k.moveToElementText(l);p=k.compareEndPoints(j?"StartToStart":"EndToEnd",i);if(p>0){q=r-1}else{if(p<0){t=r+1}else{return{node:l}}}}if(p<0){if(!l){k.moveToElementText(s);k.collapse(true);l=s;n=true}else{k.collapse(false)}o=0;while(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)!==0){if(k.move("character",1)===0||s!=k.parentElement()){break}o++}}else{k.collapse(true);o=0;while(k.compareEndPoints(j?"StartToStart":"StartToEnd",i)!==0){if(k.move("character",-1)===0||s!=k.parentElement()){break}o++}}return{node:l,position:p,offset:o,inside:n}}function g(){var i=d.getRng(),r=h.createRng(),l,k,p,q,m,j;l=i.item?i.item(0):i.parentElement();if(l.ownerDocument!=h.doc){return r}k=d.isCollapsed();if(i.item){r.setStart(l.parentNode,h.nodeIndex(l));r.setEnd(r.startContainer,r.startOffset+1);return r}function o(A){var u=e(i,A),s,y,z=0,x,v,t;s=u.node;y=u.offset;if(u.inside&&!s.hasChildNodes()){r[A?"setStart":"setEnd"](s,0);return}if(y===v){r[A?"setStartBefore":"setEndAfter"](s);return}if(u.position<0){x=u.inside?s.firstChild:s.nextSibling;if(!x){r[A?"setStartAfter":"setEndAfter"](s);return}if(!y){if(x.nodeType==3){r[A?"setStart":"setEnd"](x,0)}else{r[A?"setStartBefore":"setEndBefore"](x)}return}while(x){t=x.nodeValue;z+=t.length;if(z>=y){s=x;z-=y;z=t.length-z;break}x=x.nextSibling}}else{x=s.previousSibling;if(!x){return r[A?"setStartBefore":"setEndBefore"](s)}if(!y){if(s.nodeType==3){r[A?"setStart":"setEnd"](x,s.nodeValue.length)}else{r[A?"setStartAfter":"setEndAfter"](x)}return}while(x){z+=x.nodeValue.length;if(z>=y){s=x;z-=y;break}x=x.previousSibling}}r[A?"setStart":"setEnd"](s,z)}try{o(true);if(!k){o()}}catch(n){if(n.number==-2147024809){m=b.getBookmark(2);p=i.duplicate();p.collapse(true);l=p.parentElement();if(!k){p=i.duplicate();p.collapse(false);q=p.parentElement();q.innerHTML=q.innerHTML}l.innerHTML=l.innerHTML;b.moveToBookmark(m);i=d.getRng();o(true);if(!k){o()}}else{throw n}}return r}this.getBookmark=function(m){var j=d.getRng(),o,i,l={};function n(u){var t,p,s,r,q=[];t=u.parentNode;p=h.getRoot().parentNode;while(t!=p&&t.nodeType!==9){s=t.children;r=s.length;while(r--){if(u===s[r]){q.push(r);break}}u=t;t=t.parentNode}return q}function k(q){var p;p=e(j,q);if(p){return{position:p.position,offset:p.offset,indexes:n(p.node),inside:p.inside}}}if(m===2){if(!j.item){l.start=k(true);if(!d.isCollapsed()){l.end=k()}}else{l.start={ctrl:true,indexes:n(j.item(0))}}}return l};this.moveToBookmark=function(k){var j,i=h.doc.body;function m(o){var r,q,n,p;r=h.getRoot();for(q=o.length-1;q>=0;q--){p=r.children;n=o[q];if(n<=p.length-1){r=p[n]}}return r}function l(r){var n=k[r?"start":"end"],q,p,o;if(n){q=n.position>0;p=i.createTextRange();p.moveToElementText(m(n.indexes));offset=n.offset;if(offset!==o){p.collapse(n.inside||q);p.moveStart("character",q?-offset:offset)}else{p.collapse(r)}j.setEndPoint(r?"StartToStart":"EndToStart",p);if(r){j.collapse(true)}}}if(k.start){if(k.start.ctrl){j=i.createControlRange();j.addElement(m(k.start.indexes));j.select()}else{j=i.createTextRange();l(true);l();j.select()}}};this.addRange=function(i){var n,l,k,p,v,q,t,s=d.dom.doc,m=s.body,r,u;function j(C){var y,B,x,A,z;x=h.create("a");y=C?k:v;B=C?p:q;A=n.duplicate();if(y==s||y==s.documentElement){y=m;B=0}if(y.nodeType==3){y.parentNode.insertBefore(x,y);A.moveToElementText(x);A.moveStart("character",B);h.remove(x);n.setEndPoint(C?"StartToStart":"EndToEnd",A)}else{z=y.childNodes;if(z.length){if(B>=z.length){h.insertAfter(x,z[z.length-1])}else{y.insertBefore(x,z[B])}A.moveToElementText(x)}else{if(y.canHaveHTML){y.innerHTML="<span>\uFEFF</span>";x=y.firstChild;A.moveToElementText(x);A.collapse(f)}}n.setEndPoint(C?"StartToStart":"EndToEnd",A);h.remove(x)}}k=i.startContainer;p=i.startOffset;v=i.endContainer;q=i.endOffset;n=m.createTextRange();if(k==v&&k.nodeType==1){if(p==q&&!k.hasChildNodes()){if(k.canHaveHTML){t=k.previousSibling;if(t&&!t.hasChildNodes()&&h.isBlock(t)){t.innerHTML="\uFEFF"}else{t=null}k.innerHTML="<span>\uFEFF</span><span>\uFEFF</span>";n.moveToElementText(k.lastChild);n.select();h.doc.selection.clear();k.innerHTML="";if(t){t.innerHTML=""}return}else{p=h.nodeIndex(k);k=k.parentNode}}if(p==q-1){try{u=k.childNodes[p];l=m.createControlRange();l.addElement(u);l.select();r=d.getRng();if(r.item&&u===r.item(0)){return}}catch(o){}}}j(true);j();n.select()};this.getRangeAt=g}tinymce.dom.TridentSelection=a})();(function(){var n=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,i="sizcache",o=0,r=Object.prototype.toString,h=false,g=true,q=/\\/g,u=/\r\n/g,x=/\W/;[0,0].sort(function(){g=false;return 0});var d=function(C,e,F,G){F=F||[];e=e||document;var I=e;if(e.nodeType!==1&&e.nodeType!==9){return[]}if(!C||typeof C!=="string"){return F}var z,K,N,y,J,M,L,E,B=true,A=d.isXML(e),D=[],H=C;do{n.exec("");z=n.exec(H);if(z){H=z[3];D.push(z[1]);if(z[2]){y=z[3];break}}}while(z);if(D.length>1&&j.exec(C)){if(D.length===2&&k.relative[D[0]]){K=s(D[0]+D[1],e,G)}else{K=k.relative[D[0]]?[e]:d(D.shift(),e);while(D.length){C=D.shift();if(k.relative[C]){C+=D.shift()}K=s(C,K,G)}}}else{if(!G&&D.length>1&&e.nodeType===9&&!A&&k.match.ID.test(D[0])&&!k.match.ID.test(D[D.length-1])){J=d.find(D.shift(),e,A);e=J.expr?d.filter(J.expr,J.set)[0]:J.set[0]}if(e){J=G?{expr:D.pop(),set:l(G)}:d.find(D.pop(),D.length===1&&(D[0]==="~"||D[0]==="+")&&e.parentNode?e.parentNode:e,A);K=J.expr?d.filter(J.expr,J.set):J.set;if(D.length>0){N=l(K)}else{B=false}while(D.length){M=D.pop();L=M;if(!k.relative[M]){M=""}else{L=D.pop()}if(L==null){L=e}k.relative[M](N,L,A)}}else{N=D=[]}}if(!N){N=K}if(!N){d.error(M||C)}if(r.call(N)==="[object Array]"){if(!B){F.push.apply(F,N)}else{if(e&&e.nodeType===1){for(E=0;N[E]!=null;E++){if(N[E]&&(N[E]===true||N[E].nodeType===1&&d.contains(e,N[E]))){F.push(K[E])}}}else{for(E=0;N[E]!=null;E++){if(N[E]&&N[E].nodeType===1){F.push(K[E])}}}}}else{l(N,F)}if(y){d(y,I,F,G);d.uniqueSort(F)}return F};d.uniqueSort=function(y){if(p){h=g;y.sort(p);if(h){for(var e=1;e<y.length;e++){if(y[e]===y[e-1]){y.splice(e--,1)}}}}return y};d.matches=function(e,y){return d(e,null,null,y)};d.matchesSelector=function(e,y){return d(y,null,null,[e]).length>0};d.find=function(E,e,F){var D,z,B,A,C,y;if(!E){return[]}for(z=0,B=k.order.length;z<B;z++){C=k.order[z];if((A=k.leftMatch[C].exec(E))){y=A[1];A.splice(1,1);if(y.substr(y.length-1)!=="\\"){A[1]=(A[1]||"").replace(q,"");D=k.find[C](A,e,F);if(D!=null){E=E.replace(k.match[C],"");break}}}}if(!D){D=typeof e.getElementsByTagName!=="undefined"?e.getElementsByTagName("*"):[]}return{set:D,expr:E}};d.filter=function(I,H,L,B){var D,e,G,N,K,y,A,C,J,z=I,M=[],F=H,E=H&&H[0]&&d.isXML(H[0]);while(I&&H.length){for(G in k.filter){if((D=k.leftMatch[G].exec(I))!=null&&D[2]){y=k.filter[G];A=D[1];e=false;D.splice(1,1);if(A.substr(A.length-1)==="\\"){continue}if(F===M){M=[]}if(k.preFilter[G]){D=k.preFilter[G](D,F,L,M,B,E);if(!D){e=N=true}else{if(D===true){continue}}}if(D){for(C=0;(K=F[C])!=null;C++){if(K){N=y(K,D,C,F);J=B^N;if(L&&N!=null){if(J){e=true}else{F[C]=false}}else{if(J){M.push(K);e=true}}}}}if(N!==undefined){if(!L){F=M}I=I.replace(k.match[G],"");if(!e){return[]}break}}}if(I===z){if(e==null){d.error(I)}else{break}}z=I}return F};d.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)};var b=d.getText=function(B){var z,A,e=B.nodeType,y="";if(e){if(e===1||e===9||e===11){if(typeof B.textContent==="string"){return B.textContent}else{if(typeof B.innerText==="string"){return B.innerText.replace(u,"")}else{for(B=B.firstChild;B;B=B.nextSibling){y+=b(B)}}}}else{if(e===3||e===4){return B.nodeValue}}}else{for(z=0;(A=B[z]);z++){if(A.nodeType!==8){y+=b(A)}}}return y};var k=d.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,CLASS:/\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(e){return e.getAttribute("href")},type:function(e){return e.getAttribute("type")}},relative:{"+":function(D,y){var A=typeof y==="string",C=A&&!x.test(y),E=A&&!C;if(C){y=y.toLowerCase()}for(var z=0,e=D.length,B;z<e;z++){if((B=D[z])){while((B=B.previousSibling)&&B.nodeType!==1){}D[z]=E||B&&B.nodeName.toLowerCase()===y?B||false:B===y}}if(E){d.filter(y,D,true)}},">":function(D,y){var C,B=typeof y==="string",z=0,e=D.length;if(B&&!x.test(y)){y=y.toLowerCase();for(;z<e;z++){C=D[z];if(C){var A=C.parentNode;D[z]=A.nodeName.toLowerCase()===y?A:false}}}else{for(;z<e;z++){C=D[z];if(C){D[z]=B?C.parentNode:C.parentNode===y}}if(B){d.filter(y,D,true)}}},"":function(A,y,C){var B,z=o++,e=t;if(typeof y==="string"&&!x.test(y)){y=y.toLowerCase();B=y;e=a}e("parentNode",y,z,A,B,C)},"~":function(A,y,C){var B,z=o++,e=t;if(typeof y==="string"&&!x.test(y)){y=y.toLowerCase();B=y;e=a}e("previousSibling",y,z,A,B,C)}},find:{ID:function(y,z,A){if(typeof z.getElementById!=="undefined"&&!A){var e=z.getElementById(y[1]);return e&&e.parentNode?[e]:[]}},NAME:function(z,C){if(typeof C.getElementsByName!=="undefined"){var y=[],B=C.getElementsByName(z[1]);for(var A=0,e=B.length;A<e;A++){if(B[A].getAttribute("name")===z[1]){y.push(B[A])}}return y.length===0?null:y}},TAG:function(e,y){if(typeof y.getElementsByTagName!=="undefined"){return y.getElementsByTagName(e[1])}}},preFilter:{CLASS:function(A,y,z,e,D,E){A=" "+A[1].replace(q,"")+" ";if(E){return A}for(var B=0,C;(C=y[B])!=null;B++){if(C){if(D^(C.className&&(" "+C.className+" ").replace(/[\t\n\r]/g," ").indexOf(A)>=0)){if(!z){e.push(C)}}else{if(z){y[B]=false}}}}return false},ID:function(e){return e[1].replace(q,"")},TAG:function(y,e){return y[1].replace(q,"").toLowerCase()},CHILD:function(e){if(e[1]==="nth"){if(!e[2]){d.error(e[0])}e[2]=e[2].replace(/^\+|\s*/g,"");var y=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(e[2]==="even"&&"2n"||e[2]==="odd"&&"2n+1"||!/\D/.test(e[2])&&"0n+"+e[2]||e[2]);e[2]=(y[1]+(y[2]||1))-0;e[3]=y[3]-0}else{if(e[2]){d.error(e[0])}}e[0]=o++;return e},ATTR:function(B,y,z,e,C,D){var A=B[1]=B[1].replace(q,"");if(!D&&k.attrMap[A]){B[1]=k.attrMap[A]}B[4]=(B[4]||B[5]||"").replace(q,"");if(B[2]==="~="){B[4]=" "+B[4]+" "}return B},PSEUDO:function(B,y,z,e,C){if(B[1]==="not"){if((n.exec(B[3])||"").length>1||/^\w/.test(B[3])){B[3]=d(B[3],null,null,y)}else{var A=d.filter(B[3],y,z,true^C);if(!z){e.push.apply(e,A)}return false}}else{if(k.match.POS.test(B[0])||k.match.CHILD.test(B[0])){return true}}return B},POS:function(e){e.unshift(true);return e}},filters:{enabled:function(e){return e.disabled===false&&e.type!=="hidden"},disabled:function(e){return e.disabled===true},checked:function(e){return e.checked===true},selected:function(e){if(e.parentNode){e.parentNode.selectedIndex}return e.selected===true},parent:function(e){return !!e.firstChild},empty:function(e){return !e.firstChild},has:function(z,y,e){return !!d(e[3],z).length},header:function(e){return(/h\d/i).test(e.nodeName)},text:function(z){var e=z.getAttribute("type"),y=z.type;return z.nodeName.toLowerCase()==="input"&&"text"===y&&(e===y||e===null)},radio:function(e){return e.nodeName.toLowerCase()==="input"&&"radio"===e.type},checkbox:function(e){return e.nodeName.toLowerCase()==="input"&&"checkbox"===e.type},file:function(e){return e.nodeName.toLowerCase()==="input"&&"file"===e.type},password:function(e){return e.nodeName.toLowerCase()==="input"&&"password"===e.type},submit:function(y){var e=y.nodeName.toLowerCase();return(e==="input"||e==="button")&&"submit"===y.type},image:function(e){return e.nodeName.toLowerCase()==="input"&&"image"===e.type},reset:function(y){var e=y.nodeName.toLowerCase();return(e==="input"||e==="button")&&"reset"===y.type},button:function(y){var e=y.nodeName.toLowerCase();return e==="input"&&"button"===y.type||e==="button"},input:function(e){return(/input|select|textarea|button/i).test(e.nodeName)},focus:function(e){return e===e.ownerDocument.activeElement}},setFilters:{first:function(y,e){return e===0},last:function(z,y,e,A){return y===A.length-1},even:function(y,e){return e%2===0},odd:function(y,e){return e%2===1},lt:function(z,y,e){return y<e[3]-0},gt:function(z,y,e){return y>e[3]-0},nth:function(z,y,e){return e[3]-0===y},eq:function(z,y,e){return e[3]-0===y}},filter:{PSEUDO:function(z,E,D,F){var e=E[1],y=k.filters[e];if(y){return y(z,D,E,F)}else{if(e==="contains"){return(z.textContent||z.innerText||b([z])||"").indexOf(E[3])>=0}else{if(e==="not"){var A=E[3];for(var C=0,B=A.length;C<B;C++){if(A[C]===z){return false}}return true}else{d.error(e)}}}},CHILD:function(z,B){var A,H,D,G,e,C,F,E=B[1],y=z;switch(E){case"only":case"first":while((y=y.previousSibling)){if(y.nodeType===1){return false}}if(E==="first"){return true}y=z;case"last":while((y=y.nextSibling)){if(y.nodeType===1){return false}}return true;case"nth":A=B[2];H=B[3];if(A===1&&H===0){return true}D=B[0];G=z.parentNode;if(G&&(G[i]!==D||!z.nodeIndex)){C=0;for(y=G.firstChild;y;y=y.nextSibling){if(y.nodeType===1){y.nodeIndex=++C}}G[i]=D}F=z.nodeIndex-H;if(A===0){return F===0}else{return(F%A===0&&F/A>=0)}}},ID:function(y,e){return y.nodeType===1&&y.getAttribute("id")===e},TAG:function(y,e){return(e==="*"&&y.nodeType===1)||!!y.nodeName&&y.nodeName.toLowerCase()===e},CLASS:function(y,e){return(" "+(y.className||y.getAttribute("class"))+" ").indexOf(e)>-1},ATTR:function(C,A){var z=A[1],e=d.attr?d.attr(C,z):k.attrHandle[z]?k.attrHandle[z](C):C[z]!=null?C[z]:C.getAttribute(z),D=e+"",B=A[2],y=A[4];return e==null?B==="!=":!B&&d.attr?e!=null:B==="="?D===y:B==="*="?D.indexOf(y)>=0:B==="~="?(" "+D+" ").indexOf(y)>=0:!y?D&&e!==false:B==="!="?D!==y:B==="^="?D.indexOf(y)===0:B==="$="?D.substr(D.length-y.length)===y:B==="|="?D===y||D.substr(0,y.length+1)===y+"-":false},POS:function(B,y,z,C){var e=y[2],A=k.setFilters[e];if(A){return A(B,z,y,C)}}}};var j=k.match.POS,c=function(y,e){return"\\"+(e-0+1)};for(var f in k.match){k.match[f]=new RegExp(k.match[f].source+(/(?![^\[]*\])(?![^\(]*\))/.source));k.leftMatch[f]=new RegExp(/(^(?:.|\r|\n)*?)/.source+k.match[f].source.replace(/\\(\d+)/g,c))}k.match.globalPOS=j;var l=function(y,e){y=Array.prototype.slice.call(y,0);if(e){e.push.apply(e,y);return e}return y};try{Array.prototype.slice.call(document.documentElement.childNodes,0)[0].nodeType}catch(v){l=function(B,A){var z=0,y=A||[];if(r.call(B)==="[object Array]"){Array.prototype.push.apply(y,B)}else{if(typeof B.length==="number"){for(var e=B.length;z<e;z++){y.push(B[z])}}else{for(;B[z];z++){y.push(B[z])}}}return y}}var p,m;if(document.documentElement.compareDocumentPosition){p=function(y,e){if(y===e){h=true;return 0}if(!y.compareDocumentPosition||!e.compareDocumentPosition){return y.compareDocumentPosition?-1:1}return y.compareDocumentPosition(e)&4?-1:1}}else{p=function(F,E){if(F===E){h=true;return 0}else{if(F.sourceIndex&&E.sourceIndex){return F.sourceIndex-E.sourceIndex}}var C,y,z=[],e=[],B=F.parentNode,D=E.parentNode,G=B;if(B===D){return m(F,E)}else{if(!B){return -1}else{if(!D){return 1}}}while(G){z.unshift(G);G=G.parentNode}G=D;while(G){e.unshift(G);G=G.parentNode}C=z.length;y=e.length;for(var A=0;A<C&&A<y;A++){if(z[A]!==e[A]){return m(z[A],e[A])}}return A===C?m(F,e[A],-1):m(z[A],E,1)};m=function(y,e,z){if(y===e){return z}var A=y.nextSibling;while(A){if(A===e){return -1}A=A.nextSibling}return 1}}(function(){var y=document.createElement("div"),z="script"+(new Date()).getTime(),e=document.documentElement;y.innerHTML="<a name='"+z+"'/>";e.insertBefore(y,e.firstChild);if(document.getElementById(z)){k.find.ID=function(B,C,D){if(typeof C.getElementById!=="undefined"&&!D){var A=C.getElementById(B[1]);return A?A.id===B[1]||typeof A.getAttributeNode!=="undefined"&&A.getAttributeNode("id").nodeValue===B[1]?[A]:undefined:[]}};k.filter.ID=function(C,A){var B=typeof C.getAttributeNode!=="undefined"&&C.getAttributeNode("id");return C.nodeType===1&&B&&B.nodeValue===A}}e.removeChild(y);e=y=null})();(function(){var e=document.createElement("div");e.appendChild(document.createComment(""));if(e.getElementsByTagName("*").length>0){k.find.TAG=function(y,C){var B=C.getElementsByTagName(y[1]);if(y[1]==="*"){var A=[];for(var z=0;B[z];z++){if(B[z].nodeType===1){A.push(B[z])}}B=A}return B}}e.innerHTML="<a href='#'></a>";if(e.firstChild&&typeof e.firstChild.getAttribute!=="undefined"&&e.firstChild.getAttribute("href")!=="#"){k.attrHandle.href=function(y){return y.getAttribute("href",2)}}e=null})();if(document.querySelectorAll){(function(){var e=d,A=document.createElement("div"),z="__sizzle__";A.innerHTML="<p class='TEST'></p>";if(A.querySelectorAll&&A.querySelectorAll(".TEST").length===0){return}d=function(L,C,G,K){C=C||document;if(!K&&!d.isXML(C)){var J=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(L);if(J&&(C.nodeType===1||C.nodeType===9)){if(J[1]){return l(C.getElementsByTagName(L),G)}else{if(J[2]&&k.find.CLASS&&C.getElementsByClassName){return l(C.getElementsByClassName(J[2]),G)}}}if(C.nodeType===9){if(L==="body"&&C.body){return l([C.body],G)}else{if(J&&J[3]){var F=C.getElementById(J[3]);if(F&&F.parentNode){if(F.id===J[3]){return l([F],G)}}else{return l([],G)}}}try{return l(C.querySelectorAll(L),G)}catch(H){}}else{if(C.nodeType===1&&C.nodeName.toLowerCase()!=="object"){var D=C,E=C.getAttribute("id"),B=E||z,N=C.parentNode,M=/^\s*[+~]/.test(L);if(!E){C.setAttribute("id",B)}else{B=B.replace(/'/g,"\\$&")}if(M&&N){C=C.parentNode}try{if(!M||N){return l(C.querySelectorAll("[id='"+B+"'] "+L),G)}}catch(I){}finally{if(!E){D.removeAttribute("id")}}}}}return e(L,C,G,K)};for(var y in e){d[y]=e[y]}A=null})()}(function(){var e=document.documentElement,z=e.matchesSelector||e.mozMatchesSelector||e.webkitMatchesSelector||e.msMatchesSelector;if(z){var B=!z.call(document.createElement("div"),"div"),y=false;try{z.call(document.documentElement,"[test!='']:sizzle")}catch(A){y=true}d.matchesSelector=function(D,F){F=F.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!d.isXML(D)){try{if(y||!k.match.PSEUDO.test(F)&&!/!=/.test(F)){var C=z.call(D,F);if(C||!B||D.document&&D.document.nodeType!==11){return C}}}catch(E){}}return d(F,null,null,[D]).length>0}}})();(function(){var e=document.createElement("div");e.innerHTML="<div class='test e'></div><div class='test'></div>";if(!e.getElementsByClassName||e.getElementsByClassName("e").length===0){return}e.lastChild.className="e";if(e.getElementsByClassName("e").length===1){return}k.order.splice(1,0,"CLASS");k.find.CLASS=function(y,z,A){if(typeof z.getElementsByClassName!=="undefined"&&!A){return z.getElementsByClassName(y[1])}};e=null})();function a(y,D,C,G,E,F){for(var A=0,z=G.length;A<z;A++){var e=G[A];if(e){var B=false;e=e[y];while(e){if(e[i]===C){B=G[e.sizset];break}if(e.nodeType===1&&!F){e[i]=C;e.sizset=A}if(e.nodeName.toLowerCase()===D){B=e;break}e=e[y]}G[A]=B}}}function t(y,D,C,G,E,F){for(var A=0,z=G.length;A<z;A++){var e=G[A];if(e){var B=false;e=e[y];while(e){if(e[i]===C){B=G[e.sizset];break}if(e.nodeType===1){if(!F){e[i]=C;e.sizset=A}if(typeof D!=="string"){if(e===D){B=true;break}}else{if(d.filter(D,[e]).length>0){B=e;break}}}e=e[y]}G[A]=B}}}if(document.documentElement.contains){d.contains=function(y,e){return y!==e&&(y.contains?y.contains(e):true)}}else{if(document.documentElement.compareDocumentPosition){d.contains=function(y,e){return !!(y.compareDocumentPosition(e)&16)}}else{d.contains=function(){return false}}}d.isXML=function(e){var y=(e?e.ownerDocument||e:0).documentElement;return y?y.nodeName!=="HTML":false};var s=function(z,e,D){var C,E=[],B="",F=e.nodeType?[e]:e;while((C=k.match.PSEUDO.exec(z))){B+=C[0];z=z.replace(k.match.PSEUDO,"")}z=k.relative[z]?z+"*":z;for(var A=0,y=F.length;A<y;A++){d(z,F[A],E,D)}return d.filter(B,E)};window.tinymce.dom.Sizzle=d})();(function(a){a.dom.Element=function(f,d){var b=this,e,c;b.settings=d=d||{};b.id=f;b.dom=e=d.dom||a.DOM;if(!a.isIE){c=e.get(b.id)}a.each(("getPos,getRect,getParent,add,setStyle,getStyle,setStyles,setAttrib,setAttribs,getAttrib,addClass,removeClass,hasClass,getOuterHTML,setOuterHTML,remove,show,hide,isHidden,setHTML,get").split(/,/),function(g){b[g]=function(){var h=[f],j;for(j=0;j<arguments.length;j++){h.push(arguments[j])}h=e[g].apply(e,h);b.update(g);return h}});a.extend(b,{on:function(i,h,g){return a.dom.Event.add(b.id,i,h,g)},getXY:function(){return{x:parseInt(b.getStyle("left")),y:parseInt(b.getStyle("top"))}},getSize:function(){var g=e.get(b.id);return{w:parseInt(b.getStyle("width")||g.clientWidth),h:parseInt(b.getStyle("height")||g.clientHeight)}},moveTo:function(g,h){b.setStyles({left:g,top:h})},moveBy:function(g,i){var h=b.getXY();b.moveTo(h.x+g,h.y+i)},resizeTo:function(g,i){b.setStyles({width:g,height:i})},resizeBy:function(g,j){var i=b.getSize();b.resizeTo(i.w+g,i.h+j)},update:function(h){var g;if(a.isIE6&&d.blocker){h=h||"";if(h.indexOf("get")===0||h.indexOf("has")===0||h.indexOf("is")===0){return}if(h=="remove"){e.remove(b.blocker);return}if(!b.blocker){b.blocker=e.uniqueId();g=e.add(d.container||e.getRoot(),"iframe",{id:b.blocker,style:"position:absolute;",frameBorder:0,src:'javascript:""'});e.setStyle(g,"opacity",0)}else{g=e.get(b.blocker)}e.setStyles(g,{left:b.getStyle("left",1),top:b.getStyle("top",1),width:b.getStyle("width",1),height:b.getStyle("height",1),display:b.getStyle("display",1),zIndex:parseInt(b.getStyle("zIndex",1)||0)-1})}}})}})(tinymce);(function(d){function f(g){return g.replace(/[\n\r]+/g,"")}var c=d.is,b=d.isIE,e=d.each,a=d.dom.TreeWalker;d.create("tinymce.dom.Selection",{Selection:function(k,j,i,h){var g=this;g.dom=k;g.win=j;g.serializer=i;g.editor=h;e(["onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent"],function(l){g[l]=new d.util.Dispatcher(g)});if(!g.win.getSelection){g.tridentSel=new d.dom.TridentSelection(g)}if(d.isIE&&k.boxModel){this._fixIESelection()}d.addUnload(g.destroy,g)},setCursorLocation:function(i,j){var g=this;var h=g.dom.createRng();h.setStart(i,j);h.setEnd(i,j);g.setRng(h);g.collapse(false)},getContent:function(h){var g=this,i=g.getRng(),m=g.dom.create("body"),k=g.getSel(),j,l,o;h=h||{};j=l="";h.get=true;h.format=h.format||"html";h.forced_root_block="";g.onBeforeGetContent.dispatch(g,h);if(h.format=="text"){return g.isCollapsed()?"":(i.text||(k.toString?k.toString():""))}if(i.cloneContents){o=i.cloneContents();if(o){m.appendChild(o)}}else{if(c(i.item)||c(i.htmlText)){m.innerHTML="<br>"+(i.item?i.item(0).outerHTML:i.htmlText);m.removeChild(m.firstChild)}else{m.innerHTML=i.toString()}}if(/^\s/.test(m.innerHTML)){j=" "}if(/\s+$/.test(m.innerHTML)){l=" "}h.getInner=true;h.content=g.isCollapsed()?"":j+g.serializer.serialize(m,h)+l;g.onGetContent.dispatch(g,h);return h.content},setContent:function(h,j){var o=this,g=o.getRng(),k,l=o.win.document,n,m;j=j||{format:"html"};j.set=true;h=j.content=h;if(!j.no_events){o.onBeforeSetContent.dispatch(o,j)}h=j.content;if(g.insertNode){h+='<span id="__caret">_</span>';if(g.startContainer==l&&g.endContainer==l){l.body.innerHTML=h}else{g.deleteContents();if(l.body.childNodes.length===0){l.body.innerHTML=h}else{if(g.createContextualFragment){g.insertNode(g.createContextualFragment(h))}else{n=l.createDocumentFragment();m=l.createElement("div");n.appendChild(m);m.outerHTML=h;g.insertNode(n)}}}k=o.dom.get("__caret");g=l.createRange();g.setStartBefore(k);g.setEndBefore(k);o.setRng(g);o.dom.remove("__caret");try{o.setRng(g)}catch(i){}}else{if(g.item){l.execCommand("Delete",false,null);g=o.getRng()}if(/^\s+/.test(h)){g.pasteHTML('<span id="__mce_tmp">_</span>'+h);o.dom.remove("__mce_tmp")}else{g.pasteHTML(h)}}if(!j.no_events){o.onSetContent.dispatch(o,j)}},getStart:function(){var i=this,h=i.getRng(),j,g,l,k;if(h.duplicate||h.item){if(h.item){return h.item(0)}l=h.duplicate();l.collapse(1);j=l.parentElement();if(j.ownerDocument!==i.dom.doc){j=i.dom.getRoot()}g=k=h.parentElement();while(k=k.parentNode){if(k==j){j=g;break}}return j}else{j=h.startContainer;if(j.nodeType==1&&j.hasChildNodes()){j=j.childNodes[Math.min(j.childNodes.length-1,h.startOffset)]}if(j&&j.nodeType==3){return j.parentNode}return j}},getEnd:function(){var h=this,g=h.getRng(),j,i;if(g.duplicate||g.item){if(g.item){return g.item(0)}g=g.duplicate();g.collapse(0);j=g.parentElement();if(j.ownerDocument!==h.dom.doc){j=h.dom.getRoot()}if(j&&j.nodeName=="BODY"){return j.lastChild||j}return j}else{j=g.endContainer;i=g.endOffset;if(j.nodeType==1&&j.hasChildNodes()){j=j.childNodes[i>0?i-1:i]}if(j&&j.nodeType==3){return j.parentNode}return j}},getBookmark:function(s,v){var y=this,n=y.dom,h,k,j,o,i,p,q,m="\uFEFF",x;function g(z,A){var t=0;e(n.select(z),function(C,B){if(C==A){t=B}});return t}function u(t){function z(E){var A,D,C,B=E?"start":"end";A=t[B+"Container"];D=t[B+"Offset"];if(A.nodeType==1&&A.nodeName=="TR"){C=A.childNodes;A=C[Math.min(E?D:D-1,C.length-1)];if(A){D=E?0:A.childNodes.length;t["set"+(E?"Start":"End")](A,D)}}}z(true);z();return t}function l(){var z=y.getRng(true),t=n.getRoot(),A={};function B(E,J){var D=E[J?"startContainer":"endContainer"],I=E[J?"startOffset":"endOffset"],C=[],F,H,G=0;if(D.nodeType==3){if(v){for(F=D.previousSibling;F&&F.nodeType==3;F=F.previousSibling){I+=F.nodeValue.length}}C.push(I)}else{H=D.childNodes;if(I>=H.length&&H.length){G=1;I=Math.max(0,H.length-1)}C.push(y.dom.nodeIndex(H[I],v)+G)}for(;D&&D!=t;D=D.parentNode){C.push(y.dom.nodeIndex(D,v))}return C}A.start=B(z,true);if(!y.isCollapsed()){A.end=B(z)}return A}if(s==2){if(y.tridentSel){return y.tridentSel.getBookmark(s)}return l()}if(s){return{rng:y.getRng()}}h=y.getRng();j=n.uniqueId();o=tinyMCE.activeEditor.selection.isCollapsed();x="overflow:hidden;line-height:0px";if(h.duplicate||h.item){if(!h.item){k=h.duplicate();try{h.collapse();h.pasteHTML('<span data-mce-type="bookmark" id="'+j+'_start" style="'+x+'">'+m+"</span>");if(!o){k.collapse(false);h.moveToElementText(k.parentElement());if(h.compareEndPoints("StartToEnd",k)===0){k.move("character",-1)}k.pasteHTML('<span data-mce-type="bookmark" id="'+j+'_end" style="'+x+'">'+m+"</span>")}}catch(r){return null}}else{p=h.item(0);i=p.nodeName;return{name:i,index:g(i,p)}}}else{p=y.getNode();i=p.nodeName;if(i=="IMG"){return{name:i,index:g(i,p)}}k=u(h.cloneRange());if(!o){k.collapse(false);k.insertNode(n.create("span",{"data-mce-type":"bookmark",id:j+"_end",style:x},m))}h=u(h);h.collapse(true);h.insertNode(n.create("span",{"data-mce-type":"bookmark",id:j+"_start",style:x},m))}y.moveToBookmark({id:j,keep:1});return{id:j}},moveToBookmark:function(o){var s=this,m=s.dom,j,i,g,r,k,u,p,q;function h(A){var t=o[A?"start":"end"],x,y,z,v;if(t){z=t[0];for(y=r,x=t.length-1;x>=1;x--){v=y.childNodes;if(t[x]>v.length-1){return}y=v[t[x]]}if(y.nodeType===3){z=Math.min(t[0],y.nodeValue.length)}if(y.nodeType===1){z=Math.min(t[0],y.childNodes.length)}if(A){g.setStart(y,z)}else{g.setEnd(y,z)}}return true}function l(B){var v=m.get(o.id+"_"+B),A,t,y,z,x=o.keep;if(v){A=v.parentNode;if(B=="start"){if(!x){t=m.nodeIndex(v)}else{A=v.firstChild;t=1}k=u=A;p=q=t}else{if(!x){t=m.nodeIndex(v)}else{A=v.firstChild;t=1}u=A;q=t}if(!x){z=v.previousSibling;y=v.nextSibling;e(d.grep(v.childNodes),function(C){if(C.nodeType==3){C.nodeValue=C.nodeValue.replace(/\uFEFF/g,"")}});while(v=m.get(o.id+"_"+B)){m.remove(v,1)}if(z&&y&&z.nodeType==y.nodeType&&z.nodeType==3&&!d.isOpera){t=z.nodeValue.length;z.appendData(y.nodeValue);m.remove(y);if(B=="start"){k=u=z;p=q=t}else{u=z;q=t}}}}}function n(t){if(m.isBlock(t)&&!t.innerHTML&&!b){t.innerHTML='<br data-mce-bogus="1" />'}return t}if(o){if(o.start){g=m.createRng();r=m.getRoot();if(s.tridentSel){return s.tridentSel.moveToBookmark(o)}if(h(true)&&h()){s.setRng(g)}}else{if(o.id){l("start");l("end");if(k){g=m.createRng();g.setStart(n(k),p);g.setEnd(n(u),q);s.setRng(g)}}else{if(o.name){s.select(m.select(o.name)[o.index])}else{if(o.rng){s.setRng(o.rng)}}}}}},select:function(l,k){var j=this,m=j.dom,h=m.createRng(),g;function i(n,p){var o=new a(n,n);do{if(n.nodeType==3&&d.trim(n.nodeValue).length!==0){if(p){h.setStart(n,0)}else{h.setEnd(n,n.nodeValue.length)}return}if(n.nodeName=="BR"){if(p){h.setStartBefore(n)}else{h.setEndBefore(n)}return}}while(n=(p?o.next():o.prev()))}if(l){g=m.nodeIndex(l);h.setStart(l.parentNode,g);h.setEnd(l.parentNode,g+1);if(k){i(l,1);i(l)}j.setRng(h)}return l},isCollapsed:function(){var g=this,i=g.getRng(),h=g.getSel();if(!i||i.item){return false}if(i.compareEndPoints){return i.compareEndPoints("StartToEnd",i)===0}return !h||i.collapsed},collapse:function(g){var i=this,h=i.getRng(),j;if(h.item){j=h.item(0);h=i.win.document.body.createTextRange();h.moveToElementText(j)}h.collapse(!!g);i.setRng(h)},getSel:function(){var h=this,g=this.win;return g.getSelection?g.getSelection():g.document.selection},getRng:function(m){var h=this,j,g,l,k=h.win.document;if(m&&h.tridentSel){return h.tridentSel.getRangeAt(0)}try{if(j=h.getSel()){g=j.rangeCount>0?j.getRangeAt(0):(j.createRange?j.createRange():k.createRange())}}catch(i){}if(d.isIE&&g&&g.setStart&&k.selection.createRange().item){l=k.selection.createRange().item(0);g=k.createRange();g.setStartBefore(l);g.setEndAfter(l)}if(!g){g=k.createRange?k.createRange():k.body.createTextRange()}if(g.setStart&&g.startContainer.nodeType===9&&g.collapsed){l=h.dom.getRoot();g.setStart(l,0);g.setEnd(l,0)}if(h.selectedRange&&h.explicitRange){if(g.compareBoundaryPoints(g.START_TO_START,h.selectedRange)===0&&g.compareBoundaryPoints(g.END_TO_END,h.selectedRange)===0){g=h.explicitRange}else{h.selectedRange=null;h.explicitRange=null}}return g},setRng:function(k,g){var j,i=this;if(!i.tridentSel){j=i.getSel();if(j){i.explicitRange=k;try{j.removeAllRanges()}catch(h){}j.addRange(k);if(g===false&&j.extend){j.collapse(k.endContainer,k.endOffset);j.extend(k.startContainer,k.startOffset)}i.selectedRange=j.rangeCount>0?j.getRangeAt(0):null}}else{if(k.cloneRange){try{i.tridentSel.addRange(k);return}catch(h){}}try{k.select()}catch(h){}}},setNode:function(h){var g=this;g.setContent(g.dom.getOuterHTML(h));return h},getNode:function(){var i=this,h=i.getRng(),j=i.getSel(),m,l=h.startContainer,g=h.endContainer;function k(q,o){var p=q;while(q&&q.nodeType===3&&q.length===0){q=o?q.nextSibling:q.previousSibling}return q||p}if(!h){return i.dom.getRoot()}if(h.setStart){m=h.commonAncestorContainer;if(!h.collapsed){if(h.startContainer==h.endContainer){if(h.endOffset-h.startOffset<2){if(h.startContainer.hasChildNodes()){m=h.startContainer.childNodes[h.startOffset]}}}if(l.nodeType===3&&g.nodeType===3){if(l.length===h.startOffset){l=k(l.nextSibling,true)}else{l=l.parentNode}if(h.endOffset===0){g=k(g.previousSibling,false)}else{g=g.parentNode}if(l&&l===g){return l}}}if(m&&m.nodeType==3){return m.parentNode}return m}return h.item?h.item(0):h.parentElement()},getSelectedBlocks:function(p,h){var o=this,k=o.dom,m,l,i,j=[];m=k.getParent(p||o.getStart(),k.isBlock);l=k.getParent(h||o.getEnd(),k.isBlock);if(m){j.push(m)}if(m&&l&&m!=l){i=m;var g=new a(m,k.getRoot());while((i=g.next())&&i!=l){if(k.isBlock(i)){j.push(i)}}}if(l&&m!=l){j.push(l)}return j},isForward:function(){var i=this.dom,g=this.getSel(),j,h;if(!g||g.anchorNode==null||g.focusNode==null){return true}j=i.createRng();j.setStart(g.anchorNode,g.anchorOffset);j.collapse(true);h=i.createRng();h.setStart(g.focusNode,g.focusOffset);h.collapse(true);return j.compareBoundaryPoints(j.START_TO_START,h)<=0},normalize:function(){var h=this,g,m,l,j,i;function k(p){var o,r,n,s=h.dom,u=s.getRoot(),q,t,v;function y(z,A){var B=new a(z,s.getParent(z.parentNode,s.isBlock)||u);while(z=B[A?"prev":"next"]()){if(z.nodeName==="BR"){return true}}}function x(B,z){var C,A;z=z||o;C=new a(z,s.getParent(z.parentNode,s.isBlock)||u);while(q=C[B?"prev":"next"]()){if(q.nodeType===3&&q.nodeValue.length>0){o=q;r=B?q.nodeValue.length:0;m=true;return}if(s.isBlock(q)||t[q.nodeName.toLowerCase()]){return}A=q}if(l&&A){o=A;m=true;r=0}}o=g[(p?"start":"end")+"Container"];r=g[(p?"start":"end")+"Offset"];t=s.schema.getNonEmptyElements();if(o.nodeType===9){o=s.getRoot();r=0}if(o===u){if(p){q=o.childNodes[r>0?r-1:0];if(q){v=q.nodeName.toLowerCase();if(t[q.nodeName]||q.nodeName=="TABLE"){return}}}if(o.hasChildNodes()){o=o.childNodes[Math.min(!p&&r>0?r-1:r,o.childNodes.length-1)];r=0;if(o.hasChildNodes()&&!/TABLE/.test(o.nodeName)){q=o;n=new a(o,u);do{if(q.nodeType===3&&q.nodeValue.length>0){r=p?0:q.nodeValue.length;o=q;m=true;break}if(t[q.nodeName.toLowerCase()]){r=s.nodeIndex(q);o=q.parentNode;if(q.nodeName=="IMG"&&!p){r++}m=true;break}}while(q=(p?n.next():n.prev()))}}}if(l){if(o.nodeType===3&&r===0){x(true)}if(o.nodeType===1){q=o.childNodes[r];if(q&&q.nodeName==="BR"&&!y(q)&&!y(q,true)){x(true,o.childNodes[r])}}}if(p&&!l&&o.nodeType===3&&r===o.nodeValue.length){x(false)}if(m){g["set"+(p?"Start":"End")](o,r)}}if(d.isIE){return}g=h.getRng();l=g.collapsed;k(true);if(!l){k()}if(m){if(l){g.collapse(true)}h.setRng(g,h.isForward())}},selectorChanged:function(g,j){var h=this,i;if(!h.selectorChangedData){h.selectorChangedData={};i={};h.editor.onNodeChange.addToTop(function(l,k,o){var p=h.dom,m=p.getParents(o,null,p.getRoot()),n={};e(h.selectorChangedData,function(r,q){e(m,function(s){if(p.is(s,q)){if(!i[q]){e(r,function(t){t(true,{node:s,selector:q,parents:m})});i[q]=r}n[q]=r;return false}})});e(i,function(r,q){if(!n[q]){delete i[q];e(r,function(s){s(false,{node:o,selector:q,parents:m})})}})})}if(!h.selectorChangedData[g]){h.selectorChangedData[g]=[]}h.selectorChangedData[g].push(j);return h},scrollIntoView:function(k){var j,h,g=this,i=g.dom;h=i.getViewPort(g.editor.getWin());j=i.getPos(k).y;if(j<h.y||j+25>h.y+h.h){g.editor.getWin().scrollTo(0,j<h.y?j:j-h.h+25)}},destroy:function(h){var g=this;g.win=null;if(!h){d.removeUnload(g.destroy)}},_fixIESelection:function(){var h=this.dom,n=h.doc,i=n.body,k,o,g;function j(p,s){var q=i.createTextRange();try{q.moveToPoint(p,s)}catch(r){q=null}return q}function m(q){var p;if(q.button){p=j(q.x,q.y);if(p){if(p.compareEndPoints("StartToStart",o)>0){p.setEndPoint("StartToStart",o)}else{p.setEndPoint("EndToEnd",o)}p.select()}}else{l()}}function l(){var p=n.selection.createRange();if(o&&!p.item&&p.compareEndPoints("StartToEnd",p)===0){o.select()}h.unbind(n,"mouseup",l);h.unbind(n,"mousemove",m);o=k=0}n.documentElement.unselectable=true;h.bind(n,["mousedown","contextmenu"],function(p){if(p.target.nodeName==="HTML"){if(k){l()}g=n.documentElement;if(g.scrollHeight>g.clientHeight){return}k=1;o=j(p.x,p.y);if(o){h.bind(n,"mouseup",l);h.bind(n,"mousemove",m);h.win.focus();o.select()}}})}})})(tinymce);(function(a){a.dom.Serializer=function(e,i,f){var h,b,d=a.isIE,g=a.each,c;if(!e.apply_source_formatting){e.indent=false}i=i||a.DOM;f=f||new a.html.Schema(e);e.entity_encoding=e.entity_encoding||"named";e.remove_trailing_brs="remove_trailing_brs" in e?e.remove_trailing_brs:true;h=new a.util.Dispatcher(self);b=new a.util.Dispatcher(self);c=new a.html.DomParser(e,f);c.addAttributeFilter("src,href,style",function(k,j){var o=k.length,l,q,n="data-mce-"+j,p=e.url_converter,r=e.url_converter_scope,m;while(o--){l=k[o];q=l.attributes.map[n];if(q!==m){l.attr(j,q.length>0?q:null);l.attr(n,null)}else{q=l.attributes.map[j];if(j==="style"){q=i.serializeStyle(i.parseStyle(q),l.name)}else{if(p){q=p.call(r,q,j,l.name)}}l.attr(j,q.length>0?q:null)}}});c.addAttributeFilter("class",function(j,k){var l=j.length,m,n;while(l--){m=j[l];n=m.attr("class").replace(/(?:^|\s)mce(Item\w+|Selected)(?!\S)/g,"");m.attr("class",n.length>0?n:null)}});c.addAttributeFilter("data-mce-type",function(j,l,k){var m=j.length,n;while(m--){n=j[m];if(n.attributes.map["data-mce-type"]==="bookmark"&&!k.cleanup){n.remove()}}});c.addAttributeFilter("data-mce-expando",function(j,l,k){var m=j.length;while(m--){j[m].attr(l,null)}});c.addNodeFilter("noscript",function(j){var k=j.length,l;while(k--){l=j[k].firstChild;if(l){l.value=a.html.Entities.decode(l.value)}}});c.addNodeFilter("script,style",function(k,l){var m=k.length,n,o;function j(p){return p.replace(/(<!--\[CDATA\[|\]\]-->)/g,"\n").replace(/^[\r\n]*|[\r\n]*$/g,"").replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi,"").replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g,"")}while(m--){n=k[m];o=n.firstChild?n.firstChild.value:"";if(l==="script"){n.attr("type",(n.attr("type")||"text/javascript").replace(/^mce\-/,""));if(o.length>0){n.firstChild.value="// <![CDATA[\n"+j(o)+"\n// ]]>"}}else{if(o.length>0){n.firstChild.value="<!--\n"+j(o)+"\n-->"}}}});c.addNodeFilter("#comment",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.value.indexOf("[CDATA[")===0){m.name="#cdata";m.type=4;m.value=m.value.replace(/^\[CDATA\[|\]\]$/g,"")}else{if(m.value.indexOf("mce:protected ")===0){m.name="#text";m.type=3;m.raw=true;m.value=unescape(m.value).substr(14)}}}});c.addNodeFilter("xml:namespace,input",function(j,k){var l=j.length,m;while(l--){m=j[l];if(m.type===7){m.remove()}else{if(m.type===1){if(k==="input"&&!("type" in m.attributes.map)){m.attr("type","text")}}}}});if(e.fix_list_elements){c.addNodeFilter("ul,ol",function(k,l){var m=k.length,n,j;while(m--){n=k[m];j=n.parent;if(j.name==="ul"||j.name==="ol"){if(n.prev&&n.prev.name==="li"){n.prev.append(n)}}}})}c.addAttributeFilter("data-mce-src,data-mce-href,data-mce-style",function(j,k){var l=j.length;while(l--){j[l].attr(k,null)}});return{schema:f,addNodeFilter:c.addNodeFilter,addAttributeFilter:c.addAttributeFilter,onPreProcess:h,onPostProcess:b,serialize:function(o,m){var l,p,k,j,n;if(d&&i.select("script,style,select,map").length>0){n=o.innerHTML;o=o.cloneNode(false);i.setHTML(o,n)}else{o=o.cloneNode(true)}l=o.ownerDocument.implementation;if(l.createHTMLDocument){p=l.createHTMLDocument("");g(o.nodeName=="BODY"?o.childNodes:[o],function(q){p.body.appendChild(p.importNode(q,true))});if(o.nodeName!="BODY"){o=p.body.firstChild}else{o=p.body}k=i.doc;i.doc=p}m=m||{};m.format=m.format||"html";if(!m.no_events){m.node=o;h.dispatch(self,m)}j=new a.html.Serializer(e,f);m.content=j.serialize(c.parse(a.trim(m.getInner?o.innerHTML:i.getOuterHTML(o)),m));if(!m.cleanup){m.content=m.content.replace(/\uFEFF/g,"")}if(!m.no_events){b.dispatch(self,m)}if(k){i.doc=k}m.node=null;return m.content},addRules:function(j){f.addValidElements(j)},setRules:function(j){f.setValidElements(j)}}}})(tinymce);(function(a){a.dom.ScriptLoader=function(h){var c=0,k=1,i=2,l={},j=[],e={},d=[],g=0,f;function b(m,v){var x=this,q=a.DOM,s,o,r,n;function p(){q.remove(n);if(s){s.onreadystatechange=s.onload=s=null}v()}function u(){if(typeof(console)!=="undefined"&&console.log){console.log("Failed to load: "+m)}}n=q.uniqueId();if(a.isIE6){o=new a.util.URI(m);r=location;if(o.host==r.hostname&&o.port==r.port&&(o.protocol+":")==r.protocol&&o.protocol.toLowerCase()!="file"){a.util.XHR.send({url:a._addVer(o.getURI()),success:function(y){var t=q.create("script",{type:"text/javascript"});t.text=y;document.getElementsByTagName("head")[0].appendChild(t);q.remove(t);p()},error:u});return}}s=document.createElement("script");s.id=n;s.type="text/javascript";s.src=a._addVer(m);if(!a.isIE){s.onload=p}s.onerror=u;if(!a.isOpera){s.onreadystatechange=function(){var t=s.readyState;if(t=="complete"||t=="loaded"){p()}}}(document.getElementsByTagName("head")[0]||document.body).appendChild(s)}this.isDone=function(m){return l[m]==i};this.markDone=function(m){l[m]=i};this.add=this.load=function(m,q,n){var o,p=l[m];if(p==f){j.push(m);l[m]=c}if(q){if(!e[m]){e[m]=[]}e[m].push({func:q,scope:n||this})}};this.loadQueue=function(n,m){this.loadScripts(j,n,m)};this.loadScripts=function(m,q,p){var o;function n(r){a.each(e[r],function(s){s.func.call(s.scope)});e[r]=f}d.push({func:q,scope:p||this});o=function(){var r=a.grep(m);m.length=0;a.each(r,function(s){if(l[s]==i){n(s);return}if(l[s]!=k){l[s]=k;g++;b(s,function(){l[s]=i;g--;n(s);o()})}});if(!g){a.each(d,function(s){s.func.call(s.scope)});d.length=0}};o()}};a.ScriptLoader=new a.dom.ScriptLoader()})(tinymce);(function(a){a.dom.RangeUtils=function(c){var b="\uFEFF";this.walk=function(d,s){var i=d.startContainer,l=d.startOffset,t=d.endContainer,m=d.endOffset,j,g,o,h,r,q,e;e=c.select("td.mceSelected,th.mceSelected");if(e.length>0){a.each(e,function(u){s([u])});return}function f(u){var v;v=u[0];if(v.nodeType===3&&v===i&&l>=v.nodeValue.length){u.splice(0,1)}v=u[u.length-1];if(m===0&&u.length>0&&v===t&&v.nodeType===3){u.splice(u.length-1,1)}return u}function p(x,v,u){var y=[];for(;x&&x!=u;x=x[v]){y.push(x)}return y}function n(v,u){do{if(v.parentNode==u){return v}v=v.parentNode}while(v)}function k(x,v,y){var u=y?"nextSibling":"previousSibling";for(h=x,r=h.parentNode;h&&h!=v;h=r){r=h.parentNode;q=p(h==x?h:h[u],u);if(q.length){if(!y){q.reverse()}s(f(q))}}}if(i.nodeType==1&&i.hasChildNodes()){i=i.childNodes[l]}if(t.nodeType==1&&t.hasChildNodes()){t=t.childNodes[Math.min(m-1,t.childNodes.length-1)]}if(i==t){return s(f([i]))}j=c.findCommonAncestor(i,t);for(h=i;h;h=h.parentNode){if(h===t){return k(i,j,true)}if(h===j){break}}for(h=t;h;h=h.parentNode){if(h===i){return k(t,j)}if(h===j){break}}g=n(i,j)||i;o=n(t,j)||t;k(i,g,true);q=p(g==i?g:g.nextSibling,"nextSibling",o==t?o.nextSibling:o);if(q.length){s(f(q))}k(t,o)};this.split=function(e){var h=e.startContainer,d=e.startOffset,i=e.endContainer,g=e.endOffset;function f(j,k){return j.splitText(k)}if(h==i&&h.nodeType==3){if(d>0&&d<h.nodeValue.length){i=f(h,d);h=i.previousSibling;if(g>d){g=g-d;h=i=f(i,g).previousSibling;g=i.nodeValue.length;d=0}else{g=0}}}else{if(h.nodeType==3&&d>0&&d<h.nodeValue.length){h=f(h,d);d=0}if(i.nodeType==3&&g>0&&g<i.nodeValue.length){i=f(i,g).previousSibling;g=i.nodeValue.length}}return{startContainer:h,startOffset:d,endContainer:i,endOffset:g}}};a.dom.RangeUtils.compareRanges=function(c,b){if(c&&b){if(c.item||c.duplicate){if(c.item&&b.item&&c.item(0)===b.item(0)){return true}if(c.isEqual&&b.isEqual&&b.isEqual(c)){return true}}else{return c.startContainer==b.startContainer&&c.startOffset==b.startOffset}}return false}})(tinymce);(function(b){var a=b.dom.Event,c=b.each;b.create("tinymce.ui.KeyboardNavigation",{KeyboardNavigation:function(e,f){var q=this,n=e.root,m=e.items,o=e.enableUpDown,i=e.enableLeftRight||!e.enableUpDown,l=e.excludeFromTabOrder,k,h,p,d,g;f=f||b.DOM;k=function(r){g=r.target.id};h=function(r){f.setAttrib(r.target.id,"tabindex","-1")};d=function(r){var s=f.get(g);f.setAttrib(s,"tabindex","0");s.focus()};q.focus=function(){f.get(g).focus()};q.destroy=function(){c(m,function(s){var t=f.get(s.id);f.unbind(t,"focus",k);f.unbind(t,"blur",h)});var r=f.get(n);f.unbind(r,"focus",d);f.unbind(r,"keydown",p);m=f=n=q.focus=k=h=p=d=null;q.destroy=function(){}};q.moveFocus=function(v,s){var r=-1,u=q.controls,t;if(!g){return}c(m,function(y,x){if(y.id===g){r=x;return false}});r+=v;if(r<0){r=m.length-1}else{if(r>=m.length){r=0}}t=m[r];f.setAttrib(g,"tabindex","-1");f.setAttrib(t.id,"tabindex","0");f.get(t.id).focus();if(e.actOnFocus){e.onAction(t.id)}if(s){a.cancel(s)}};p=function(z){var v=37,u=39,y=38,A=40,r=27,t=14,s=13,x=32;switch(z.keyCode){case v:if(i){q.moveFocus(-1)}break;case u:if(i){q.moveFocus(1)}break;case y:if(o){q.moveFocus(-1)}break;case A:if(o){q.moveFocus(1)}break;case r:if(e.onCancel){e.onCancel();a.cancel(z)}break;case t:case s:case x:if(e.onAction){e.onAction(g);a.cancel(z)}break}};c(m,function(t,r){var s,u;if(!t.id){t.id=f.uniqueId("_mce_item_")}u=f.get(t.id);if(l){f.bind(u,"blur",h);s="-1"}else{s=(r===0?"0":"-1")}u.setAttribute("tabindex",s);f.bind(u,"focus",k)});if(m[0]){g=m[0].id}f.setAttrib(n,"tabindex","-1");var j=f.get(n);f.bind(j,"focus",d);f.bind(j,"keydown",p)}})})(tinymce);(function(c){var b=c.DOM,a=c.is;c.create("tinymce.ui.Control",{Control:function(f,e,d){this.id=f;this.settings=e=e||{};this.rendered=false;this.onRender=new c.util.Dispatcher(this);this.classPrefix="";this.scope=e.scope||this;this.disabled=0;this.active=0;this.editor=d},setAriaProperty:function(f,e){var d=b.get(this.id+"_aria")||b.get(this.id);if(d){b.setAttrib(d,"aria-"+f,!!e)}},focus:function(){b.get(this.id).focus()},setDisabled:function(d){if(d!=this.disabled){this.setAriaProperty("disabled",d);this.setState("Disabled",d);this.setState("Enabled",!d);this.disabled=d}},isDisabled:function(){return this.disabled},setActive:function(d){if(d!=this.active){this.setState("Active",d);this.active=d;this.setAriaProperty("pressed",d)}},isActive:function(){return this.active},setState:function(f,d){var e=b.get(this.id);f=this.classPrefix+f;if(d){b.addClass(e,f)}else{b.removeClass(e,f)}},isRendered:function(){return this.rendered},renderHTML:function(){},renderTo:function(d){b.setHTML(d,this.renderHTML())},postRender:function(){var e=this,d;if(a(e.disabled)){d=e.disabled;e.disabled=-1;e.setDisabled(d)}if(a(e.active)){d=e.active;e.active=-1;e.setActive(d)}},remove:function(){b.remove(this.id);this.destroy()},destroy:function(){c.dom.Event.clear(this.id)}})})(tinymce);tinymce.create("tinymce.ui.Container:tinymce.ui.Control",{Container:function(c,b,a){this.parent(c,b,a);this.controls=[];this.lookup={}},add:function(a){this.lookup[a.id]=a;this.controls.push(a);return a},get:function(a){return this.lookup[a]}});tinymce.create("tinymce.ui.Separator:tinymce.ui.Control",{Separator:function(b,a){this.parent(b,a);this.classPrefix="mceSeparator";this.setDisabled(true)},renderHTML:function(){return tinymce.DOM.createHTML("span",{"class":this.classPrefix,role:"separator","aria-orientation":"vertical",tabindex:"-1"})}});(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.MenuItem:tinymce.ui.Control",{MenuItem:function(g,f){this.parent(g,f);this.classPrefix="mceMenuItem"},setSelected:function(f){this.setState("Selected",f);this.setAriaProperty("checked",!!f);this.selected=f},isSelected:function(){return this.selected},postRender:function(){var f=this;f.parent();if(c(f.selected)){f.setSelected(f.selected)}}})})(tinymce);(function(d){var c=d.is,b=d.DOM,e=d.each,a=d.walk;d.create("tinymce.ui.Menu:tinymce.ui.MenuItem",{Menu:function(h,g){var f=this;f.parent(h,g);f.items={};f.collapsed=false;f.menuCount=0;f.onAddItem=new d.util.Dispatcher(this)},expand:function(g){var f=this;if(g){a(f,function(h){if(h.expand){h.expand()}},"items",f)}f.collapsed=false},collapse:function(g){var f=this;if(g){a(f,function(h){if(h.collapse){h.collapse()}},"items",f)}f.collapsed=true},isCollapsed:function(){return this.collapsed},add:function(f){if(!f.settings){f=new d.ui.MenuItem(f.id||b.uniqueId(),f)}this.onAddItem.dispatch(this,f);return this.items[f.id]=f},addSeparator:function(){return this.add({separator:true})},addMenu:function(f){if(!f.collapse){f=this.createMenu(f)}this.menuCount++;return this.add(f)},hasMenus:function(){return this.menuCount!==0},remove:function(f){delete this.items[f.id]},removeAll:function(){var f=this;a(f,function(g){if(g.removeAll){g.removeAll()}else{g.remove()}g.destroy()},"items",f);f.items={}},createMenu:function(g){var f=new d.ui.Menu(g.id||b.uniqueId(),g);f.onAddItem.add(this.onAddItem.dispatch,this.onAddItem);return f}})})(tinymce);(function(e){var d=e.is,c=e.DOM,f=e.each,a=e.dom.Event,b=e.dom.Element;e.create("tinymce.ui.DropMenu:tinymce.ui.Menu",{DropMenu:function(h,g){g=g||{};g.container=g.container||c.doc.body;g.offset_x=g.offset_x||0;g.offset_y=g.offset_y||0;g.vp_offset_x=g.vp_offset_x||0;g.vp_offset_y=g.vp_offset_y||0;if(d(g.icons)&&!g.icons){g["class"]+=" mceNoIcons"}this.parent(h,g);this.onShowMenu=new e.util.Dispatcher(this);this.onHideMenu=new e.util.Dispatcher(this);this.classPrefix="mceMenu"},createMenu:function(j){var h=this,i=h.settings,g;j.container=j.container||i.container;j.parent=h;j.constrain=j.constrain||i.constrain;j["class"]=j["class"]||i["class"];j.vp_offset_x=j.vp_offset_x||i.vp_offset_x;j.vp_offset_y=j.vp_offset_y||i.vp_offset_y;j.keyboard_focus=i.keyboard_focus;g=new e.ui.DropMenu(j.id||c.uniqueId(),j);g.onAddItem.add(h.onAddItem.dispatch,h.onAddItem);return g},focus:function(){var g=this;if(g.keyboardNav){g.keyboardNav.focus()}},update:function(){var i=this,j=i.settings,g=c.get("menu_"+i.id+"_tbl"),l=c.get("menu_"+i.id+"_co"),h,k;h=j.max_width?Math.min(g.offsetWidth,j.max_width):g.offsetWidth;k=j.max_height?Math.min(g.offsetHeight,j.max_height):g.offsetHeight;if(!c.boxModel){i.element.setStyles({width:h+2,height:k+2})}else{i.element.setStyles({width:h,height:k})}if(j.max_width){c.setStyle(l,"width",h)}if(j.max_height){c.setStyle(l,"height",k);if(g.clientHeight<j.max_height){c.setStyle(l,"overflow","hidden")}}},showMenu:function(p,n,r){var z=this,A=z.settings,o,g=c.getViewPort(),u,l,v,q,i=2,k,j,m=z.classPrefix;z.collapse(1);if(z.isMenuVisible){return}if(!z.rendered){o=c.add(z.settings.container,z.renderNode());f(z.items,function(h){h.postRender()});z.element=new b("menu_"+z.id,{blocker:1,container:A.container})}else{o=c.get("menu_"+z.id)}if(!e.isOpera){c.setStyles(o,{left:-65535,top:-65535})}c.show(o);z.update();p+=A.offset_x||0;n+=A.offset_y||0;g.w-=4;g.h-=4;if(A.constrain){u=o.clientWidth-i;l=o.clientHeight-i;v=g.x+g.w;q=g.y+g.h;if((p+A.vp_offset_x+u)>v){p=r?r-u:Math.max(0,(v-A.vp_offset_x)-u)}if((n+A.vp_offset_y+l)>q){n=Math.max(0,(q-A.vp_offset_y)-l)}}c.setStyles(o,{left:p,top:n});z.element.update();z.isMenuVisible=1;z.mouseClickFunc=a.add(o,"click",function(s){var h;s=s.target;if(s&&(s=c.getParent(s,"tr"))&&!c.hasClass(s,m+"ItemSub")){h=z.items[s.id];if(h.isDisabled()){return}k=z;while(k){if(k.hideMenu){k.hideMenu()}k=k.settings.parent}if(h.settings.onclick){h.settings.onclick(s)}return false}});if(z.hasMenus()){z.mouseOverFunc=a.add(o,"mouseover",function(x){var h,t,s;x=x.target;if(x&&(x=c.getParent(x,"tr"))){h=z.items[x.id];if(z.lastMenu){z.lastMenu.collapse(1)}if(h.isDisabled()){return}if(x&&c.hasClass(x,m+"ItemSub")){t=c.getRect(x);h.showMenu((t.x+t.w-i),t.y-i,t.x);z.lastMenu=h;c.addClass(c.get(h.id).firstChild,m+"ItemActive")}}})}a.add(o,"keydown",z._keyHandler,z);z.onShowMenu.dispatch(z);if(A.keyboard_focus){z._setupKeyboardNav()}},hideMenu:function(j){var g=this,i=c.get("menu_"+g.id),h;if(!g.isMenuVisible){return}if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(i,"mouseover",g.mouseOverFunc);a.remove(i,"click",g.mouseClickFunc);a.remove(i,"keydown",g._keyHandler);c.hide(i);g.isMenuVisible=0;if(!j){g.collapse(1)}if(g.element){g.element.hide()}if(h=c.get(g.id)){c.removeClass(h.firstChild,g.classPrefix+"ItemActive")}g.onHideMenu.dispatch(g)},add:function(i){var g=this,h;i=g.parent(i);if(g.isRendered&&(h=c.get("menu_"+g.id))){g._add(c.select("tbody",h)[0],i)}return i},collapse:function(g){this.parent(g);this.hideMenu(1)},remove:function(g){c.remove(g.id);this.destroy();return this.parent(g)},destroy:function(){var g=this,h=c.get("menu_"+g.id);if(g.keyboardNav){g.keyboardNav.destroy()}a.remove(h,"mouseover",g.mouseOverFunc);a.remove(c.select("a",h),"focus",g.mouseOverFunc);a.remove(h,"click",g.mouseClickFunc);a.remove(h,"keydown",g._keyHandler);if(g.element){g.element.remove()}c.remove(h)},renderNode:function(){var i=this,j=i.settings,l,h,k,g;g=c.create("div",{role:"listbox",id:"menu_"+i.id,"class":j["class"],style:"position:absolute;left:0;top:0;z-index:200000;outline:0"});if(i.settings.parent){c.setAttrib(g,"aria-parent","menu_"+i.settings.parent.id)}k=c.add(g,"div",{role:"presentation",id:"menu_"+i.id+"_co","class":i.classPrefix+(j["class"]?" "+j["class"]:"")});i.element=new b("menu_"+i.id,{blocker:1,container:j.container});if(j.menu_line){c.add(k,"span",{"class":i.classPrefix+"Line"})}l=c.add(k,"table",{role:"presentation",id:"menu_"+i.id+"_tbl",border:0,cellPadding:0,cellSpacing:0});h=c.add(l,"tbody");f(i.items,function(m){i._add(h,m)});i.rendered=true;return g},_setupKeyboardNav:function(){var i,h,g=this;i=c.get("menu_"+g.id);h=c.select("a[role=option]","menu_"+g.id);h.splice(0,0,i);g.keyboardNav=new e.ui.KeyboardNavigation({root:"menu_"+g.id,items:h,onCancel:function(){g.hideMenu()},enableUpDown:true});i.focus()},_keyHandler:function(g){var h=this,i;switch(g.keyCode){case 37:if(h.settings.parent){h.hideMenu();h.settings.parent.focus();a.cancel(g)}break;case 39:if(h.mouseOverFunc){h.mouseOverFunc(g)}break}},_add:function(j,h){var i,q=h.settings,p,l,k,m=this.classPrefix,g;if(q.separator){l=c.add(j,"tr",{id:h.id,"class":m+"ItemSeparator"});c.add(l,"td",{"class":m+"ItemSeparator"});if(i=l.previousSibling){c.addClass(i,"mceLast")}return}i=l=c.add(j,"tr",{id:h.id,"class":m+"Item "+m+"ItemEnabled"});i=k=c.add(i,q.titleItem?"th":"td");i=p=c.add(i,"a",{id:h.id+"_aria",role:q.titleItem?"presentation":"option",href:"javascript:;",onclick:"return false;",onmousedown:"return false;"});if(q.parent){c.setAttrib(p,"aria-haspopup","true");c.setAttrib(p,"aria-owns","menu_"+h.id)}c.addClass(k,q["class"]);g=c.add(i,"span",{"class":"mceIcon"+(q.icon?" mce_"+q.icon:"")});if(q.icon_src){c.add(g,"img",{src:q.icon_src})}i=c.add(i,q.element||"span",{"class":"mceText",title:h.settings.title},h.settings.title);if(h.settings.style){if(typeof h.settings.style=="function"){h.settings.style=h.settings.style()}c.setAttrib(i,"style",h.settings.style)}if(j.childNodes.length==1){c.addClass(l,"mceFirst")}if((i=l.previousSibling)&&c.hasClass(i,m+"ItemSeparator")){c.addClass(l,"mceFirst")}if(h.collapse){c.addClass(l,m+"ItemSub")}if(i=l.previousSibling){c.removeClass(i,"mceLast")}c.addClass(l,"mceLast")}})})(tinymce);(function(b){var a=b.DOM;b.create("tinymce.ui.Button:tinymce.ui.Control",{Button:function(e,d,c){this.parent(e,d,c);this.classPrefix="mceButton"},renderHTML:function(){var f=this.classPrefix,e=this.settings,d,c;c=a.encode(e.label||"");d='<a role="button" id="'+this.id+'" href="javascript:;" class="'+f+" "+f+"Enabled "+e["class"]+(c?" "+f+"Labeled":"")+'" onmousedown="return false;" onclick="return false;" aria-labelledby="'+this.id+'_voice" title="'+a.encode(e.title)+'">';if(e.image&&!(this.editor&&this.editor.forcedHighContrastMode)){d+='<span class="mceIcon '+e["class"]+'"><img class="mceIcon" src="'+e.image+'" alt="'+a.encode(e.title)+'" /></span>'+(c?'<span class="'+f+'Label">'+c+"</span>":"")}else{d+='<span class="mceIcon '+e["class"]+'"></span>'+(c?'<span class="'+f+'Label">'+c+"</span>":"")}d+='<span class="mceVoiceLabel mceIconOnly" style="display: none;" id="'+this.id+'_voice">'+e.title+"</span>";d+="</a>";return d},postRender:function(){var d=this,e=d.settings,c;if(b.isIE&&d.editor){b.dom.Event.add(d.id,"mousedown",function(f){var g=d.editor.selection.getNode().nodeName;c=g==="IMG"?d.editor.selection.getBookmark():null})}b.dom.Event.add(d.id,"click",function(f){if(!d.isDisabled()){if(b.isIE&&d.editor&&c!==null){d.editor.selection.moveToBookmark(c)}return e.onclick.call(e.scope,f)}});b.dom.Event.add(d.id,"keyup",function(f){if(!d.isDisabled()&&f.keyCode==b.VK.SPACEBAR){return e.onclick.call(e.scope,f)}})}})})(tinymce);(function(e){var d=e.DOM,b=e.dom.Event,f=e.each,a=e.util.Dispatcher,c;e.create("tinymce.ui.ListBox:tinymce.ui.Control",{ListBox:function(j,i,g){var h=this;h.parent(j,i,g);h.items=[];h.onChange=new a(h);h.onPostRender=new a(h);h.onAdd=new a(h);h.onRenderMenu=new e.util.Dispatcher(this);h.classPrefix="mceListBox";h.marked={}},select:function(h){var g=this,j,i;g.marked={};if(h==c){return g.selectByIndex(-1)}if(h&&typeof(h)=="function"){i=h}else{i=function(k){return k==h}}if(h!=g.selectedValue){f(g.items,function(l,k){if(i(l.value)){j=1;g.selectByIndex(k);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(g){var i=this,j,k,h;i.marked={};if(g!=i.selectedIndex){j=d.get(i.id+"_text");h=d.get(i.id+"_voiceDesc");k=i.items[g];if(k){i.selectedValue=k.value;i.selectedIndex=g;d.setHTML(j,d.encode(k.title));d.setHTML(h,i.settings.title+" - "+k.title);d.removeClass(j,"mceTitle");d.setAttrib(i.id,"aria-valuenow",k.title)}else{d.setHTML(j,d.encode(i.settings.title));d.setHTML(h,d.encode(i.settings.title));d.addClass(j,"mceTitle");i.selectedValue=i.selectedIndex=null;d.setAttrib(i.id,"aria-valuenow",i.settings.title)}j=0}},mark:function(g){this.marked[g]=true},add:function(j,g,i){var h=this;i=i||{};i=e.extend(i,{title:j,value:g});h.items.push(i);h.onAdd.dispatch(h,i)},getLength:function(){return this.items.length},renderHTML:function(){var j="",g=this,i=g.settings,k=g.classPrefix;j='<span role="listbox" aria-haspopup="true" aria-labelledby="'+g.id+'_voiceDesc" aria-describedby="'+g.id+'_voiceDesc"><table role="presentation" tabindex="0" id="'+g.id+'" cellpadding="0" cellspacing="0" class="'+k+" "+k+"Enabled"+(i["class"]?(" "+i["class"]):"")+'"><tbody><tr>';j+="<td>"+d.createHTML("span",{id:g.id+"_voiceDesc","class":"voiceLabel",style:"display:none;"},g.settings.title);j+=d.createHTML("a",{id:g.id+"_text",tabindex:-1,href:"javascript:;","class":"mceText",onclick:"return false;",onmousedown:"return false;"},d.encode(g.settings.title))+"</td>";j+="<td>"+d.createHTML("a",{id:g.id+"_open",tabindex:-1,href:"javascript:;","class":"mceOpen",onclick:"return false;",onmousedown:"return false;"},'<span><span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span></span>')+"</td>";j+="</tr></tbody></table></span>";return j},showMenu:function(){var h=this,j,i=d.get(this.id),g;if(h.isDisabled()||h.items.length===0){return}if(h.menu&&h.menu.isMenuVisible){return h.hideMenu()}if(!h.isMenuRendered){h.renderMenu();h.isMenuRendered=true}j=d.getPos(i);g=h.menu;g.settings.offset_x=j.x;g.settings.offset_y=j.y;g.settings.keyboard_focus=!e.isOpera;f(h.items,function(k){if(g.items[k.id]){g.items[k.id].setSelected(0)}});f(h.items,function(k){if(g.items[k.id]&&h.marked[k.value]){g.items[k.id].setSelected(1)}if(k.value===h.selectedValue){g.items[k.id].setSelected(1)}});g.showMenu(0,i.clientHeight);b.add(d.doc,"mousedown",h.hideMenu,h);d.addClass(h.id,h.classPrefix+"Selected")},hideMenu:function(h){var g=this;if(g.menu&&g.menu.isMenuVisible){d.removeClass(g.id,g.classPrefix+"Selected");if(h&&h.type=="mousedown"&&(h.target.id==g.id+"_text"||h.target.id==g.id+"_open")){return}if(!h||!d.getParent(h.target,".mceMenu")){d.removeClass(g.id,g.classPrefix+"Selected");b.remove(d.doc,"mousedown",g.hideMenu,g);g.menu.hideMenu()}}},renderMenu:function(){var h=this,g;g=h.settings.control_manager.createDropMenu(h.id+"_menu",{menu_line:1,"class":h.classPrefix+"Menu mceNoIcons",max_width:250,max_height:150});g.onHideMenu.add(function(){h.hideMenu();h.focus()});g.add({title:h.settings.title,"class":"mceMenuItemTitle",onclick:function(){if(h.settings.onselect("")!==false){h.select("")}}});f(h.items,function(i){if(i.value===c){g.add({title:i.title,role:"option","class":"mceMenuItemTitle",onclick:function(){if(h.settings.onselect("")!==false){h.select("")}}})}else{i.id=d.uniqueId();i.role="option";i.onclick=function(){if(h.settings.onselect(i.value)!==false){h.select(i.value)}};g.add(i)}});h.onRenderMenu.dispatch(h,g);h.menu=g},postRender:function(){var g=this,h=g.classPrefix;b.add(g.id,"click",g.showMenu,g);b.add(g.id,"keydown",function(i){if(i.keyCode==32){g.showMenu(i);b.cancel(i)}});b.add(g.id,"focus",function(){if(!g._focused){g.keyDownHandler=b.add(g.id,"keydown",function(i){if(i.keyCode==40){g.showMenu();b.cancel(i)}});g.keyPressHandler=b.add(g.id,"keypress",function(j){var i;if(j.keyCode==13){i=g.selectedValue;g.selectedValue=null;b.cancel(j);g.settings.onselect(i)}})}g._focused=1});b.add(g.id,"blur",function(){b.remove(g.id,"keydown",g.keyDownHandler);b.remove(g.id,"keypress",g.keyPressHandler);g._focused=0});if(e.isIE6||!d.boxModel){b.add(g.id,"mouseover",function(){if(!d.hasClass(g.id,h+"Disabled")){d.addClass(g.id,h+"Hover")}});b.add(g.id,"mouseout",function(){if(!d.hasClass(g.id,h+"Disabled")){d.removeClass(g.id,h+"Hover")}})}g.onPostRender.dispatch(g,d.get(g.id))},destroy:function(){this.parent();b.clear(this.id+"_text");b.clear(this.id+"_open")}})})(tinymce);(function(e){var d=e.DOM,b=e.dom.Event,f=e.each,a=e.util.Dispatcher,c;e.create("tinymce.ui.NativeListBox:tinymce.ui.ListBox",{NativeListBox:function(h,g){this.parent(h,g);this.classPrefix="mceNativeListBox"},setDisabled:function(g){d.get(this.id).disabled=g;this.setAriaProperty("disabled",g)},isDisabled:function(){return d.get(this.id).disabled},select:function(h){var g=this,j,i;if(h==c){return g.selectByIndex(-1)}if(h&&typeof(h)=="function"){i=h}else{i=function(k){return k==h}}if(h!=g.selectedValue){f(g.items,function(l,k){if(i(l.value)){j=1;g.selectByIndex(k);return false}});if(!j){g.selectByIndex(-1)}}},selectByIndex:function(g){d.get(this.id).selectedIndex=g+1;this.selectedValue=this.items[g]?this.items[g].value:null},add:function(k,h,g){var j,i=this;g=g||{};g.value=h;if(i.isRendered()){d.add(d.get(this.id),"option",g,k)}j={title:k,value:h,attribs:g};i.items.push(j);i.onAdd.dispatch(i,j)},getLength:function(){return this.items.length},renderHTML:function(){var i,g=this;i=d.createHTML("option",{value:""},"-- "+g.settings.title+" --");f(g.items,function(h){i+=d.createHTML("option",{value:h.value},h.title)});i=d.createHTML("select",{id:g.id,"class":"mceNativeListBox","aria-labelledby":g.id+"_aria"},i);i+=d.createHTML("span",{id:g.id+"_aria",style:"display: none"},g.settings.title);return i},postRender:function(){var h=this,i,j=true;h.rendered=true;function g(l){var k=h.items[l.target.selectedIndex-1];if(k&&(k=k.value)){h.onChange.dispatch(h,k);if(h.settings.onselect){h.settings.onselect(k)}}}b.add(h.id,"change",g);b.add(h.id,"keydown",function(l){var k;b.remove(h.id,"change",i);j=false;k=b.add(h.id,"blur",function(){if(j){return}j=true;b.add(h.id,"change",g);b.remove(h.id,"blur",k)});if(e.isWebKit&&(l.keyCode==37||l.keyCode==39)){return b.prevent(l)}if(l.keyCode==13||l.keyCode==32){g(l);return b.cancel(l)}});h.onPostRender.dispatch(h,d.get(h.id))}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.MenuButton:tinymce.ui.Button",{MenuButton:function(g,f,e){this.parent(g,f,e);this.onRenderMenu=new c.util.Dispatcher(this);f.menu_container=f.menu_container||b.doc.body},showMenu:function(){var g=this,j,i,h=b.get(g.id),f;if(g.isDisabled()){return}if(!g.isMenuRendered){g.renderMenu();g.isMenuRendered=true}if(g.isMenuVisible){return g.hideMenu()}j=b.getPos(g.settings.menu_container);i=b.getPos(h);f=g.menu;f.settings.offset_x=i.x;f.settings.offset_y=i.y;f.settings.vp_offset_x=i.x;f.settings.vp_offset_y=i.y;f.settings.keyboard_focus=g._focused;f.showMenu(0,h.firstChild.clientHeight);a.add(b.doc,"mousedown",g.hideMenu,g);g.setState("Selected",1);g.isMenuVisible=1},renderMenu:function(){var f=this,e;e=f.settings.control_manager.createDropMenu(f.id+"_menu",{menu_line:1,"class":this.classPrefix+"Menu",icons:f.settings.icons});e.onHideMenu.add(function(){f.hideMenu();f.focus()});f.onRenderMenu.dispatch(f,e);f.menu=e},hideMenu:function(g){var f=this;if(g&&g.type=="mousedown"&&b.getParent(g.target,function(h){return h.id===f.id||h.id===f.id+"_open"})){return}if(!g||!b.getParent(g.target,".mceMenu")){f.setState("Selected",0);a.remove(b.doc,"mousedown",f.hideMenu,f);if(f.menu){f.menu.hideMenu()}}f.isMenuVisible=0},postRender:function(){var e=this,f=e.settings;a.add(e.id,"click",function(){if(!e.isDisabled()){if(f.onclick){f.onclick(e.value)}e.showMenu()}})}})})(tinymce);(function(c){var b=c.DOM,a=c.dom.Event,d=c.each;c.create("tinymce.ui.SplitButton:tinymce.ui.MenuButton",{SplitButton:function(g,f,e){this.parent(g,f,e);this.classPrefix="mceSplitButton"},renderHTML:function(){var i,f=this,g=f.settings,e;i="<tbody><tr>";if(g.image){e=b.createHTML("img ",{src:g.image,role:"presentation","class":"mceAction "+g["class"]})}else{e=b.createHTML("span",{"class":"mceAction "+g["class"]},"")}e+=b.createHTML("span",{"class":"mceVoiceLabel mceIconOnly",id:f.id+"_voice",style:"display:none;"},g.title);i+="<td >"+b.createHTML("a",{role:"button",id:f.id+"_action",tabindex:"-1",href:"javascript:;","class":"mceAction "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";e=b.createHTML("span",{"class":"mceOpen "+g["class"]},'<span style="display:none;" class="mceIconOnly" aria-hidden="true">\u25BC</span>');i+="<td >"+b.createHTML("a",{role:"button",id:f.id+"_open",tabindex:"-1",href:"javascript:;","class":"mceOpen "+g["class"],onclick:"return false;",onmousedown:"return false;",title:g.title},e)+"</td>";i+="</tr></tbody>";i=b.createHTML("table",{role:"presentation","class":"mceSplitButton mceSplitButtonEnabled "+g["class"],cellpadding:"0",cellspacing:"0",title:g.title},i);return b.createHTML("div",{id:f.id,role:"button",tabindex:"0","aria-labelledby":f.id+"_voice","aria-haspopup":"true"},i)},postRender:function(){var e=this,g=e.settings,f;if(g.onclick){f=function(h){if(!e.isDisabled()){g.onclick(e.value);a.cancel(h)}};a.add(e.id+"_action","click",f);a.add(e.id,["click","keydown"],function(h){var k=32,m=14,i=13,j=38,l=40;if((h.keyCode===32||h.keyCode===13||h.keyCode===14)&&!h.altKey&&!h.ctrlKey&&!h.metaKey){f();a.cancel(h)}else{if(h.type==="click"||h.keyCode===l){e.showMenu();a.cancel(h)}}})}a.add(e.id+"_open","click",function(h){e.showMenu();a.cancel(h)});a.add([e.id,e.id+"_open"],"focus",function(){e._focused=1});a.add([e.id,e.id+"_open"],"blur",function(){e._focused=0});if(c.isIE6||!b.boxModel){a.add(e.id,"mouseover",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.addClass(e.id,"mceSplitButtonHover")}});a.add(e.id,"mouseout",function(){if(!b.hasClass(e.id,"mceSplitButtonDisabled")){b.removeClass(e.id,"mceSplitButtonHover")}})}},destroy:function(){this.parent();a.clear(this.id+"_action");a.clear(this.id+"_open");a.clear(this.id)}})})(tinymce);(function(d){var c=d.DOM,a=d.dom.Event,b=d.is,e=d.each;d.create("tinymce.ui.ColorSplitButton:tinymce.ui.SplitButton",{ColorSplitButton:function(i,h,f){var g=this;g.parent(i,h,f);g.settings=h=d.extend({colors:"000000,993300,333300,003300,003366,000080,333399,333333,800000,FF6600,808000,008000,008080,0000FF,666699,808080,FF0000,FF9900,99CC00,339966,33CCCC,3366FF,800080,999999,FF00FF,FFCC00,FFFF00,00FF00,00FFFF,00CCFF,993366,C0C0C0,FF99CC,FFCC99,FFFF99,CCFFCC,CCFFFF,99CCFF,CC99FF,FFFFFF",grid_width:8,default_color:"#888888"},g.settings);g.onShowMenu=new d.util.Dispatcher(g);g.onHideMenu=new d.util.Dispatcher(g);g.value=h.default_color},showMenu:function(){var f=this,g,j,i,h;if(f.isDisabled()){return}if(!f.isMenuRendered){f.renderMenu();f.isMenuRendered=true}if(f.isMenuVisible){return f.hideMenu()}i=c.get(f.id);c.show(f.id+"_menu");c.addClass(i,"mceSplitButtonSelected");h=c.getPos(i);c.setStyles(f.id+"_menu",{left:h.x,top:h.y+i.firstChild.clientHeight,zIndex:200000});i=0;a.add(c.doc,"mousedown",f.hideMenu,f);f.onShowMenu.dispatch(f);if(f._focused){f._keyHandler=a.add(f.id+"_menu","keydown",function(k){if(k.keyCode==27){f.hideMenu()}});c.select("a",f.id+"_menu")[0].focus()}f.keyboardNav=new d.ui.KeyboardNavigation({root:f.id+"_menu",items:c.select("a",f.id+"_menu"),onCancel:function(){f.hideMenu();f.focus()}});f.keyboardNav.focus();f.isMenuVisible=1},hideMenu:function(g){var f=this;if(f.isMenuVisible){if(g&&g.type=="mousedown"&&c.getParent(g.target,function(h){return h.id===f.id+"_open"})){return}if(!g||!c.getParent(g.target,".mceSplitButtonMenu")){c.removeClass(f.id,"mceSplitButtonSelected");a.remove(c.doc,"mousedown",f.hideMenu,f);a.remove(f.id+"_menu","keydown",f._keyHandler);c.hide(f.id+"_menu")}f.isMenuVisible=0;f.onHideMenu.dispatch();f.keyboardNav.destroy()}},renderMenu:function(){var p=this,h,k=0,q=p.settings,g,j,l,o,f;o=c.add(q.menu_container,"div",{role:"listbox",id:p.id+"_menu","class":q.menu_class+" "+q["class"],style:"position:absolute;left:0;top:-1000px;"});h=c.add(o,"div",{"class":q["class"]+" mceSplitButtonMenu"});c.add(h,"span",{"class":"mceMenuLine"});g=c.add(h,"table",{role:"presentation","class":"mceColorSplitMenu"});j=c.add(g,"tbody");k=0;e(b(q.colors,"array")?q.colors:q.colors.split(","),function(m){m=m.replace(/^#/,"");if(!k--){l=c.add(j,"tr");k=q.grid_width-1}g=c.add(l,"td");var i={href:"javascript:;",style:{backgroundColor:"#"+m},title:p.editor.getLang("colors."+m,m),"data-mce-color":"#"+m};if(!d.isIE){i.role="option"}g=c.add(g,"a",i);if(p.editor.forcedHighContrastMode){g=c.add(g,"canvas",{width:16,height:16,"aria-hidden":"true"});if(g.getContext&&(f=g.getContext("2d"))){f.fillStyle="#"+m;f.fillRect(0,0,16,16)}else{c.remove(g)}}});if(q.more_colors_func){g=c.add(j,"tr");g=c.add(g,"td",{colspan:q.grid_width,"class":"mceMoreColors"});g=c.add(g,"a",{role:"option",id:p.id+"_more",href:"javascript:;",onclick:"return false;","class":"mceMoreColors"},q.more_colors_title);a.add(g,"click",function(i){q.more_colors_func.call(q.more_colors_scope||this);return a.cancel(i)})}c.addClass(h,"mceColorSplitMenu");a.add(p.id+"_menu","mousedown",function(i){return a.cancel(i)});a.add(p.id+"_menu","click",function(i){var m;i=c.getParent(i.target,"a",j);if(i&&i.nodeName.toLowerCase()=="a"&&(m=i.getAttribute("data-mce-color"))){p.setColor(m)}return false});return o},setColor:function(f){this.displayColor(f);this.hideMenu();this.settings.onselect(f)},displayColor:function(g){var f=this;c.setStyle(f.id+"_preview","backgroundColor",g);f.value=g},postRender:function(){var f=this,g=f.id;f.parent();c.add(g+"_action","div",{id:g+"_preview","class":"mceColorPreview"});c.setStyle(f.id+"_preview","backgroundColor",f.value)},destroy:function(){var f=this;f.parent();a.clear(f.id+"_menu");a.clear(f.id+"_more");c.remove(f.id+"_menu");if(f.keyboardNav){f.keyboardNav.destroy()}}})})(tinymce);(function(b){var d=b.DOM,c=b.each,a=b.dom.Event;b.create("tinymce.ui.ToolbarGroup:tinymce.ui.Container",{renderHTML:function(){var f=this,i=[],e=f.controls,j=b.each,g=f.settings;i.push('<div id="'+f.id+'" role="group" aria-labelledby="'+f.id+'_voice">');i.push("<span role='application'>");i.push('<span id="'+f.id+'_voice" class="mceVoiceLabel" style="display:none;">'+d.encode(g.name)+"</span>");j(e,function(h){i.push(h.renderHTML())});i.push("</span>");i.push("</div>");return i.join("")},focus:function(){var e=this;d.get(e.id).focus()},postRender:function(){var f=this,e=[];c(f.controls,function(g){c(g.controls,function(h){if(h.id){e.push(h)}})});f.keyNav=new b.ui.KeyboardNavigation({root:f.id,items:e,onCancel:function(){if(b.isWebKit){d.get(f.editor.id+"_ifr").focus()}f.editor.focus()},excludeFromTabOrder:!f.settings.tab_focus_toolbar})},destroy:function(){var e=this;e.parent();e.keyNav.destroy();a.clear(e.id)}})})(tinymce);(function(a){var c=a.DOM,b=a.each;a.create("tinymce.ui.Toolbar:tinymce.ui.Container",{renderHTML:function(){var m=this,f="",j,k,n=m.settings,e,d,g,l;l=m.controls;for(e=0;e<l.length;e++){k=l[e];d=l[e-1];g=l[e+1];if(e===0){j="mceToolbarStart";if(k.Button){j+=" mceToolbarStartButton"}else{if(k.SplitButton){j+=" mceToolbarStartSplitButton"}else{if(k.ListBox){j+=" mceToolbarStartListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,"<!-- IE -->"))}if(d&&k.ListBox){if(d.Button||d.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarEnd"},c.createHTML("span",null,"<!-- IE -->"))}}if(c.stdMode){f+='<td style="position: relative">'+k.renderHTML()+"</td>"}else{f+="<td>"+k.renderHTML()+"</td>"}if(g&&k.ListBox){if(g.Button||g.SplitButton){f+=c.createHTML("td",{"class":"mceToolbarStart"},c.createHTML("span",null,"<!-- IE -->"))}}}j="mceToolbarEnd";if(k.Button){j+=" mceToolbarEndButton"}else{if(k.SplitButton){j+=" mceToolbarEndSplitButton"}else{if(k.ListBox){j+=" mceToolbarEndListBox"}}}f+=c.createHTML("td",{"class":j},c.createHTML("span",null,"<!-- IE -->"));return c.createHTML("table",{id:m.id,"class":"mceToolbar"+(n["class"]?" "+n["class"]:""),cellpadding:"0",cellspacing:"0",align:m.settings.align||"",role:"presentation",tabindex:"-1"},"<tbody><tr>"+f+"</tr></tbody>")}})})(tinymce);(function(b){var a=b.util.Dispatcher,c=b.each;b.create("tinymce.AddOnManager",{AddOnManager:function(){var d=this;d.items=[];d.urls={};d.lookup={};d.onAdd=new a(d)},get:function(d){if(this.lookup[d]){return this.lookup[d].instance}else{return undefined}},dependencies:function(e){var d;if(this.lookup[e]){d=this.lookup[e].dependencies}return d||[]},requireLangPack:function(e){var d=b.settings;if(d&&d.language&&d.language_load!==false){b.ScriptLoader.add(this.urls[e]+"/langs/"+d.language+".js")}},add:function(f,e,d){this.items.push(e);this.lookup[f]={instance:e,dependencies:d};this.onAdd.dispatch(this,f,e);return e},createUrl:function(d,e){if(typeof e==="object"){return e}else{return{prefix:d.prefix,resource:e,suffix:d.suffix}}},addComponents:function(f,d){var e=this.urls[f];b.each(d,function(g){b.ScriptLoader.add(e+"/"+g)})},load:function(j,f,d,h){var g=this,e=f;function i(){var k=g.dependencies(j);b.each(k,function(m){var l=g.createUrl(f,m);g.load(l.resource,l,undefined,undefined)});if(d){if(h){d.call(h)}else{d.call(b.ScriptLoader)}}}if(g.urls[j]){return}if(typeof f==="object"){e=f.prefix+f.resource+f.suffix}if(e.indexOf("/")!==0&&e.indexOf("://")==-1){e=b.baseURL+"/"+e}g.urls[j]=e.substring(0,e.lastIndexOf("/"));if(g.lookup[j]){i()}else{b.ScriptLoader.add(e,i,h)}}});b.PluginManager=new b.AddOnManager();b.ThemeManager=new b.AddOnManager()}(tinymce));(function(j){var g=j.each,d=j.extend,k=j.DOM,i=j.dom.Event,f=j.ThemeManager,b=j.PluginManager,e=j.explode,h=j.util.Dispatcher,a,c=0;j.documentBaseURL=window.location.href.replace(/[\?#].*$/,"").replace(/[\/\\][^\/]+$/,"");if(!/[\/\\]$/.test(j.documentBaseURL)){j.documentBaseURL+="/"}j.baseURL=new j.util.URI(j.documentBaseURL).toAbsolute(j.baseURL);j.baseURI=new j.util.URI(j.baseURL);j.onBeforeUnload=new h(j);i.add(window,"beforeunload",function(l){j.onBeforeUnload.dispatch(j,l)});j.onAddEditor=new h(j);j.onRemoveEditor=new h(j);j.EditorManager=d(j,{editors:[],i18n:{},activeEditor:null,init:function(x){var v=this,o,n=j.ScriptLoader,u,l=[],r;function q(t){var s=t.id;if(!s){s=t.name;if(s&&!k.get(s)){s=t.name}else{s=k.uniqueId()}t.setAttribute("id",s)}return s}function m(z,A,t){var y=z[A];if(!y){return}if(j.is(y,"string")){t=y.replace(/\.\w+$/,"");t=t?j.resolve(t):0;y=j.resolve(y)}return y.apply(t||this,Array.prototype.slice.call(arguments,2))}function p(t,s){return s.constructor===RegExp?s.test(t.className):k.hasClass(t,s)}v.settings=x;i.bind(window,"ready",function(){var s,t;m(x,"onpageload");switch(x.mode){case"exact":s=x.elements||"";if(s.length>0){g(e(s),function(y){if(k.get(y)){r=new j.Editor(y,x);l.push(r);r.render(1)}else{g(document.forms,function(z){g(z.elements,function(A){if(A.name===y){y="mce_editor_"+c++;k.setAttrib(A,"id",y);r=new j.Editor(y,x);l.push(r);r.render(1)}})})}})}break;case"textareas":case"specific_textareas":g(k.select("textarea"),function(y){if(x.editor_deselector&&p(y,x.editor_deselector)){return}if(!x.editor_selector||p(y,x.editor_selector)){r=new j.Editor(q(y),x);l.push(r);r.render(1)}});break;default:if(x.types){g(x.types,function(y){g(k.select(y.selector),function(A){var z=new j.Editor(q(A),j.extend({},x,y));l.push(z);z.render(1)})})}else{if(x.selector){g(k.select(x.selector),function(z){var y=new j.Editor(q(z),x);l.push(y);y.render(1)})}}}if(x.oninit){s=t=0;g(l,function(y){t++;if(!y.initialized){y.onInit.add(function(){s++;if(s==t){m(x,"oninit")}})}else{s++}if(s==t){m(x,"oninit")}})}})},get:function(l){if(l===a){return this.editors}if(!this.editors.hasOwnProperty(l)){return a}return this.editors[l]},getInstanceById:function(l){return this.get(l)},add:function(m){var l=this,n=l.editors;n[m.id]=m;n.push(m);l._setActive(m);l.onAddEditor.dispatch(l,m);return m},remove:function(n){var m=this,l,o=m.editors;if(!o[n.id]){return null}delete o[n.id];for(l=0;l<o.length;l++){if(o[l]==n){o.splice(l,1);break}}if(m.activeEditor==n){m._setActive(o[0])}n.destroy();m.onRemoveEditor.dispatch(m,n);return n},execCommand:function(r,p,o){var q=this,n=q.get(o),l;function m(){n.destroy();l.detachEvent("onunload",m);l=l.tinyMCE=l.tinymce=null}switch(r){case"mceFocus":n.focus();return true;case"mceAddEditor":case"mceAddControl":if(!q.get(o)){new j.Editor(o,q.settings).render()}return true;case"mceAddFrameControl":l=o.window;l.tinyMCE=tinyMCE;l.tinymce=j;j.DOM.doc=l.document;j.DOM.win=l;n=new j.Editor(o.element_id,o);n.render();if(j.isIE){l.attachEvent("onunload",m)}o.page_window=null;return true;case"mceRemoveEditor":case"mceRemoveControl":if(n){n.remove()}return true;case"mceToggleEditor":if(!n){q.execCommand("mceAddControl",0,o);return true}if(n.isHidden()){n.show()}else{n.hide()}return true}if(q.activeEditor){return q.activeEditor.execCommand(r,p,o)}return false},execInstanceCommand:function(p,o,n,m){var l=this.get(p);if(l){return l.execCommand(o,n,m)}return false},triggerSave:function(){g(this.editors,function(l){l.save()})},addI18n:function(n,q){var l,m=this.i18n;if(!j.is(n,"string")){g(n,function(r,p){g(r,function(t,s){g(t,function(v,u){if(s==="common"){m[p+"."+u]=v}else{m[p+"."+s+"."+u]=v}})})})}else{g(q,function(r,p){m[n+"."+p]=r})}},_setActive:function(l){this.selectedInstance=this.activeEditor=l}})})(tinymce);(function(k){var l=k.DOM,j=k.dom.Event,f=k.extend,i=k.each,a=k.isGecko,b=k.isIE,e=k.isWebKit,d=k.is,h=k.ThemeManager,c=k.PluginManager,g=k.explode;k.create("tinymce.Editor",{Editor:function(p,o){var m=this,n=true;m.settings=o=f({id:p,language:"en",theme:"advanced",skin:"default",delta_width:0,delta_height:0,popup_css:"",plugins:"",document_base_url:k.documentBaseURL,add_form_submit_trigger:n,submit_patch:n,add_unload_trigger:n,convert_urls:n,relative_urls:n,remove_script_host:n,table_inline_editing:false,object_resizing:n,accessibility_focus:n,doctype:k.isIE6?'<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">':"<!DOCTYPE>",visual:n,font_size_style_values:"xx-small,x-small,small,medium,large,x-large,xx-large",font_size_legacy_values:"xx-small,small,medium,large,x-large,xx-large,300%",apply_source_formatting:n,directionality:"ltr",forced_root_block:"p",hidden_input:n,padd_empty_editor:n,render_ui:n,indentation:"30px",fix_table_elements:n,inline_styles:n,convert_fonts_to_spans:n,indent:"simple",indent_before:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",indent_after:"p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist",validate:n,entity_encoding:"named",url_converter:m.convertURL,url_converter_scope:m,ie7_compat:n},o);m.id=m.editorId=p;m.isNotDirty=false;m.plugins={};m.documentBaseURI=new k.util.URI(o.document_base_url||k.documentBaseURL,{base_uri:tinyMCE.baseURI});m.baseURI=k.baseURI;m.contentCSS=[];m.contentStyles=[];m.setupEvents();m.execCommands={};m.queryStateCommands={};m.queryValueCommands={};m.execCallback("setup",m)},render:function(o){var p=this,q=p.settings,r=p.id,m=k.ScriptLoader;if(!j.domLoaded){j.add(window,"ready",function(){p.render()});return}tinyMCE.settings=q;if(!p.getElement()){return}if(k.isIDevice&&!k.isIOS5){return}if(!/TEXTAREA|INPUT/i.test(p.getElement().nodeName)&&q.hidden_input&&l.getParent(r,"form")){l.insertAfter(l.create("input",{type:"hidden",name:r}),r)}if(!q.content_editable){p.orgVisibility=p.getElement().style.visibility;p.getElement().style.visibility="hidden"}if(k.WindowManager){p.windowManager=new k.WindowManager(p)}if(q.encoding=="xml"){p.onGetContent.add(function(s,t){if(t.save){t.content=l.encode(t.content)}})}if(q.add_form_submit_trigger){p.onSubmit.addToTop(function(){if(p.initialized){p.save();p.isNotDirty=1}})}if(q.add_unload_trigger){p._beforeUnload=tinyMCE.onBeforeUnload.add(function(){if(p.initialized&&!p.destroyed&&!p.isHidden()){p.save({format:"raw",no_events:true})}})}k.addUnload(p.destroy,p);if(q.submit_patch){p.onBeforeRenderUI.add(function(){var s=p.getElement().form;if(!s){return}if(s._mceOldSubmit){return}if(!s.submit.nodeType&&!s.submit.length){p.formElement=s;s._mceOldSubmit=s.submit;s.submit=function(){k.triggerSave();p.isNotDirty=1;return p.formElement._mceOldSubmit(p.formElement)}}s=null})}function n(){if(q.language&&q.language_load!==false){m.add(k.baseURL+"/langs/"+q.language+".js")}if(q.theme&&typeof q.theme!="function"&&q.theme.charAt(0)!="-"&&!h.urls[q.theme]){h.load(q.theme,"themes/"+q.theme+"/editor_template"+k.suffix+".js")}i(g(q.plugins),function(t){if(t&&!c.urls[t]){if(t.charAt(0)=="-"){t=t.substr(1,t.length);var s=c.dependencies(t);i(s,function(v){var u={prefix:"plugins/",resource:v,suffix:"/editor_plugin"+k.suffix+".js"};v=c.createUrl(u,v);c.load(v.resource,v)})}else{if(t=="safari"){return}c.load(t,{prefix:"plugins/",resource:t,suffix:"/editor_plugin"+k.suffix+".js"})}}});m.loadQueue(function(){if(!p.removed){p.init()}})}n()},init:function(){var q,G=this,H=G.settings,D,y,z,C=G.getElement(),p,m,E,v,B,F,x,r=[];k.add(G);H.aria_label=H.aria_label||l.getAttrib(C,"aria-label",G.getLang("aria.rich_text_area"));if(H.theme){if(typeof H.theme!="function"){H.theme=H.theme.replace(/-/,"");p=h.get(H.theme);G.theme=new p();if(G.theme.init){G.theme.init(G,h.urls[H.theme]||k.documentBaseURL.replace(/\/$/,""))}}else{G.theme=H.theme}}function A(s){var t=c.get(s),o=c.urls[s]||k.documentBaseURL.replace(/\/$/,""),n;if(t&&k.inArray(r,s)===-1){i(c.dependencies(s),function(u){A(u)});n=new t(G,o);G.plugins[s]=n;if(n.init){n.init(G,o);r.push(s)}}}i(g(H.plugins.replace(/\-/g,"")),A);if(H.popup_css!==false){if(H.popup_css){H.popup_css=G.documentBaseURI.toAbsolute(H.popup_css)}else{H.popup_css=G.baseURI.toAbsolute("themes/"+H.theme+"/skins/"+H.skin+"/dialog.css")}}if(H.popup_css_add){H.popup_css+=","+G.documentBaseURI.toAbsolute(H.popup_css_add)}G.controlManager=new k.ControlManager(G);G.onBeforeRenderUI.dispatch(G,G.controlManager);if(H.render_ui&&G.theme){G.orgDisplay=C.style.display;if(typeof H.theme!="function"){D=H.width||C.style.width||C.offsetWidth;y=H.height||C.style.height||C.offsetHeight;z=H.min_height||100;F=/^[0-9\.]+(|px)$/i;if(F.test(""+D)){D=Math.max(parseInt(D,10)+(p.deltaWidth||0),100)}if(F.test(""+y)){y=Math.max(parseInt(y,10)+(p.deltaHeight||0),z)}p=G.theme.renderUI({targetNode:C,width:D,height:y,deltaWidth:H.delta_width,deltaHeight:H.delta_height});l.setStyles(p.sizeContainer||p.editorContainer,{width:D,height:y});y=(p.iframeHeight||y)+(typeof(y)=="number"?(p.deltaHeight||0):"");if(y<z){y=z}}else{p=H.theme(G,C);if(p.editorContainer.nodeType){p.editorContainer=p.editorContainer.id=p.editorContainer.id||G.id+"_parent"}if(p.iframeContainer.nodeType){p.iframeContainer=p.iframeContainer.id=p.iframeContainer.id||G.id+"_iframecontainer"}y=p.iframeHeight||C.offsetHeight;if(b){G.onInit.add(function(n){n.dom.bind(n.getBody(),"beforedeactivate keydown",function(){n.lastIERng=n.selection.getRng()})})}}G.editorContainer=p.editorContainer}if(H.content_css){i(g(H.content_css),function(n){G.contentCSS.push(G.documentBaseURI.toAbsolute(n))})}if(H.content_style){G.contentStyles.push(H.content_style)}if(H.content_editable){C=q=p=null;return G.initContentBody()}if(document.domain&&location.hostname!=document.domain){k.relaxedDomain=document.domain}G.iframeHTML=H.doctype+'<html><head xmlns="http://www.w3.org/1999/xhtml">';if(H.document_base_url!=k.documentBaseURL){G.iframeHTML+='<base href="'+G.documentBaseURI.getURI()+'" />'}if(k.isIE8){if(H.ie7_compat){G.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=7" />'}else{G.iframeHTML+='<meta http-equiv="X-UA-Compatible" content="IE=edge" />'}}G.iframeHTML+='<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';for(x=0;x<G.contentCSS.length;x++){G.iframeHTML+='<link type="text/css" rel="stylesheet" href="'+G.contentCSS[x]+'" />'}G.contentCSS=[];v=H.body_id||"tinymce";if(v.indexOf("=")!=-1){v=G.getParam("body_id","","hash");v=v[G.id]||v}B=H.body_class||"";if(B.indexOf("=")!=-1){B=G.getParam("body_class","","hash");B=B[G.id]||""}G.iframeHTML+='</head><body id="'+v+'" class="mceContentBody '+B+'" onload="window.parent.tinyMCE.get(\''+G.id+"').onLoad.dispatch();\"><br></body></html>";if(k.relaxedDomain&&(b||(k.isOpera&&parseFloat(opera.version())<11))){E='javascript:(function(){document.open();document.domain="'+document.domain+'";var ed = window.parent.tinyMCE.get("'+G.id+'");document.write(ed.iframeHTML);document.close();ed.initContentBody();})()'}q=l.add(p.iframeContainer,"iframe",{id:G.id+"_ifr",src:E||'javascript:""',frameBorder:"0",allowTransparency:"true",title:H.aria_label,style:{width:"100%",height:y,display:"block"}});G.contentAreaContainer=p.iframeContainer;if(p.editorContainer){l.get(p.editorContainer).style.display=G.orgDisplay}C.style.visibility=G.orgVisibility;l.get(G.id).style.display="none";l.setAttrib(G.id,"aria-hidden",true);if(!k.relaxedDomain||!E){G.initContentBody()}C=q=p=null},initContentBody:function(){var n=this,p=n.settings,q=l.get(n.id),r=n.getDoc(),o,m,s;if((!b||!k.relaxedDomain)&&!p.content_editable){r.open();r.write(n.iframeHTML);r.close();if(k.relaxedDomain){r.domain=k.relaxedDomain}}if(p.content_editable){l.addClass(q,"mceContentBody");n.contentDocument=r=p.content_document||document;n.contentWindow=p.content_window||window;n.bodyElement=q;p.content_document=p.content_window=null}m=n.getBody();m.disabled=true;if(!p.readonly){m.contentEditable=n.getParam("content_editable_state",true)}m.disabled=false;n.schema=new k.html.Schema(p);n.dom=new k.dom.DOMUtils(r,{keep_values:true,url_converter:n.convertURL,url_converter_scope:n,hex_colors:p.force_hex_style_colors,class_filter:p.class_filter,update_styles:true,root_element:p.content_editable?n.id:null,schema:n.schema});n.parser=new k.html.DomParser(p,n.schema);n.parser.addAttributeFilter("src,href,style",function(t,u){var v=t.length,y,A=n.dom,z,x;while(v--){y=t[v];z=y.attr(u);x="data-mce-"+u;if(!y.attributes.map[x]){if(u==="style"){y.attr(x,A.serializeStyle(A.parseStyle(z),y.name))}else{y.attr(x,n.convertURL(z,u,y.name))}}}});n.parser.addNodeFilter("script",function(t,u){var v=t.length,x;while(v--){x=t[v];x.attr("type","mce-"+(x.attr("type")||"text/javascript"))}});n.parser.addNodeFilter("#cdata",function(t,u){var v=t.length,x;while(v--){x=t[v];x.type=8;x.name="#comment";x.value="[CDATA["+x.value+"]]"}});n.parser.addNodeFilter("p,h1,h2,h3,h4,h5,h6,div",function(u,v){var x=u.length,y,t=n.schema.getNonEmptyElements();while(x--){y=u[x];if(y.isEmpty(t)){y.empty().append(new k.html.Node("br",1)).shortEnded=true}}});n.serializer=new k.dom.Serializer(p,n.dom,n.schema);n.selection=new k.dom.Selection(n.dom,n.getWin(),n.serializer,n);n.formatter=new k.Formatter(n);n.undoManager=new k.UndoManager(n);n.forceBlocks=new k.ForceBlocks(n);n.enterKey=new k.EnterKey(n);n.editorCommands=new k.EditorCommands(n);n.onExecCommand.add(function(t,u){if(!/^(FontName|FontSize)$/.test(u)){n.nodeChanged()}});n.serializer.onPreProcess.add(function(t,u){return n.onPreProcess.dispatch(n,u,t)});n.serializer.onPostProcess.add(function(t,u){return n.onPostProcess.dispatch(n,u,t)});n.onPreInit.dispatch(n);if(!p.browser_spellcheck&&!p.gecko_spellcheck){r.body.spellcheck=false}if(!p.readonly){n.bindNativeEvents()}n.controlManager.onPostRender.dispatch(n,n.controlManager);n.onPostRender.dispatch(n);n.quirks=k.util.Quirks(n);if(p.directionality){m.dir=p.directionality}if(p.nowrap){m.style.whiteSpace="nowrap"}if(p.protect){n.onBeforeSetContent.add(function(t,u){i(p.protect,function(v){u.content=u.content.replace(v,function(x){return"<!--mce:protected "+escape(x)+"-->"})})})}n.onSetContent.add(function(){n.addVisual(n.getBody())});if(p.padd_empty_editor){n.onPostProcess.add(function(t,u){u.content=u.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/,"")})}n.load({initial:true,format:"html"});n.startContent=n.getContent({format:"raw"});n.initialized=true;n.onInit.dispatch(n);n.execCallback("setupcontent_callback",n.id,m,r);n.execCallback("init_instance_callback",n);n.focus(true);n.nodeChanged({initial:true});if(n.contentStyles.length>0){s="";i(n.contentStyles,function(t){s+=t+"\r\n"});n.dom.addStyle(s)}i(n.contentCSS,function(t){n.dom.loadCSS(t)});if(p.auto_focus){setTimeout(function(){var t=k.get(p.auto_focus);t.selection.select(t.getBody(),1);t.selection.collapse(1);t.getBody().focus();t.getWin().focus()},100)}q=r=m=null},focus:function(p){var o,u=this,t=u.selection,q=u.settings.content_editable,n,r,s=u.getDoc(),m;if(!p){if(u.lastIERng){t.setRng(u.lastIERng)}n=t.getRng();if(n.item){r=n.item(0)}u._refreshContentEditable();if(!q){u.getWin().focus()}if(k.isGecko||q){m=u.getBody();if(m.setActive){m.setActive()}else{m.focus()}if(q){t.normalize()}}if(r&&r.ownerDocument==s){n=s.body.createControlRange();n.addElement(r);n.select()}}if(k.activeEditor!=u){if((o=k.activeEditor)!=null){o.onDeactivate.dispatch(o,u)}u.onActivate.dispatch(u,o)}k._setActive(u)},execCallback:function(q){var m=this,p=m.settings[q],o;if(!p){return}if(m.callbackLookup&&(o=m.callbackLookup[q])){p=o.func;o=o.scope}if(d(p,"string")){o=p.replace(/\.\w+$/,"");o=o?k.resolve(o):0;p=k.resolve(p);m.callbackLookup=m.callbackLookup||{};m.callbackLookup[q]={func:p,scope:o}}return p.apply(o||m,Array.prototype.slice.call(arguments,1))},translate:function(m){var o=this.settings.language||"en",n=k.i18n;if(!m){return""}return n[o+"."+m]||m.replace(/\{\#([^\}]+)\}/g,function(q,p){return n[o+"."+p]||"{#"+p+"}"})},getLang:function(o,m){return k.i18n[(this.settings.language||"en")+"."+o]||(d(m)?m:"{#"+o+"}")},getParam:function(t,q,m){var r=k.trim,p=d(this.settings[t])?this.settings[t]:q,s;if(m==="hash"){s={};if(d(p,"string")){i(p.indexOf("=")>0?p.split(/[;,](?![^=;,]*(?:[;,]|$))/):p.split(","),function(n){n=n.split("=");if(n.length>1){s[r(n[0])]=r(n[1])}else{s[r(n[0])]=r(n)}})}else{s=p}return s}return p},nodeChanged:function(q){var m=this,n=m.selection,p;if(m.initialized){q=q||{};p=n.getStart()||m.getBody();p=b&&p.ownerDocument!=m.getDoc()?m.getBody():p;q.parents=[];m.dom.getParent(p,function(o){if(o.nodeName=="BODY"){return true}q.parents.push(o)});m.onNodeChange.dispatch(m,q?q.controlManager||m.controlManager:m.controlManager,p,n.isCollapsed(),q)}},addButton:function(n,o){var m=this;m.buttons=m.buttons||{};m.buttons[n]=o},addCommand:function(m,o,n){this.execCommands[m]={func:o,scope:n||this}},addQueryStateHandler:function(m,o,n){this.queryStateCommands[m]={func:o,scope:n||this}},addQueryValueHandler:function(m,o,n){this.queryValueCommands[m]={func:o,scope:n||this}},addShortcut:function(o,q,m,p){var n=this,r;if(n.settings.custom_shortcuts===false){return false}n.shortcuts=n.shortcuts||{};if(d(m,"string")){r=m;m=function(){n.execCommand(r,false,null)}}if(d(m,"object")){r=m;m=function(){n.execCommand(r[0],r[1],r[2])}}i(g(o),function(s){var t={func:m,scope:p||this,desc:n.translate(q),alt:false,ctrl:false,shift:false};i(g(s,"+"),function(u){switch(u){case"alt":case"ctrl":case"shift":t[u]=true;break;default:t.charCode=u.charCodeAt(0);t.keyCode=u.toUpperCase().charCodeAt(0)}});n.shortcuts[(t.ctrl?"ctrl":"")+","+(t.alt?"alt":"")+","+(t.shift?"shift":"")+","+t.keyCode]=t});return true},execCommand:function(u,r,x,m){var p=this,q=0,v,n;if(!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(u)&&(!m||!m.skip_focus)){p.focus()}m=f({},m);p.onBeforeExecCommand.dispatch(p,u,r,x,m);if(m.terminate){return false}if(p.execCallback("execcommand_callback",p.id,p.selection.getNode(),u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}if(v=p.execCommands[u]){n=v.func.call(v.scope,r,x);if(n!==true){p.onExecCommand.dispatch(p,u,r,x,m);return n}}i(p.plugins,function(o){if(o.execCommand&&o.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);q=1;return false}});if(q){return true}if(p.theme&&p.theme.execCommand&&p.theme.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}if(p.editorCommands.execCommand(u,r,x)){p.onExecCommand.dispatch(p,u,r,x,m);return true}p.getDoc().execCommand(u,r,x);p.onExecCommand.dispatch(p,u,r,x,m)},queryCommandState:function(q){var n=this,r,p;if(n._isHidden()){return}if(r=n.queryStateCommands[q]){p=r.func.call(r.scope);if(p!==true){return p}}r=n.editorCommands.queryCommandState(q);if(r!==-1){return r}try{return this.getDoc().queryCommandState(q)}catch(m){}},queryCommandValue:function(r){var n=this,q,p;if(n._isHidden()){return}if(q=n.queryValueCommands[r]){p=q.func.call(q.scope);if(p!==true){return p}}q=n.editorCommands.queryCommandValue(r);if(d(q)){return q}try{return this.getDoc().queryCommandValue(r)}catch(m){}},show:function(){var m=this;l.show(m.getContainer());l.hide(m.id);m.load()},hide:function(){var m=this,n=m.getDoc();if(b&&n){n.execCommand("SelectAll")}m.save();l.hide(m.getContainer());l.setStyle(m.id,"display",m.orgDisplay)},isHidden:function(){return !l.isHidden(this.id)},setProgressState:function(m,n,p){this.onSetProgressState.dispatch(this,m,n,p);return m},load:function(q){var m=this,p=m.getElement(),n;if(p){q=q||{};q.load=true;n=m.setContent(d(p.value)?p.value:p.innerHTML,q);q.element=p;if(!q.no_events){m.onLoadContent.dispatch(m,q)}q.element=p=null;return n}},save:function(r){var m=this,q=m.getElement(),n,p;if(!q||!m.initialized){return}r=r||{};r.save=true;r.element=q;n=r.content=m.getContent(r);if(!r.no_events){m.onSaveContent.dispatch(m,r)}n=r.content;if(!/TEXTAREA|INPUT/i.test(q.nodeName)){q.innerHTML=n;if(p=l.getParent(m.id,"form")){i(p.elements,function(o){if(o.name==m.id){o.value=n;return false}})}}else{q.value=n}r.element=q=null;return n},setContent:function(r,p){var o=this,n,m=o.getBody(),q;p=p||{};p.format=p.format||"html";p.set=true;p.content=r;if(!p.no_events){o.onBeforeSetContent.dispatch(o,p)}r=p.content;if(!k.isIE&&(r.length===0||/^\s+$/.test(r))){q=o.settings.forced_root_block;if(q){r="<"+q+'><br data-mce-bogus="1"></'+q+">"}else{r='<br data-mce-bogus="1">'}m.innerHTML=r;o.selection.select(m,true);o.selection.collapse(true);return}if(p.format!=="raw"){r=new k.html.Serializer({},o.schema).serialize(o.parser.parse(r))}p.content=k.trim(r);o.dom.setHTML(m,p.content);if(!p.no_events){o.onSetContent.dispatch(o,p)}if(!o.settings.content_editable||document.activeElement===o.getBody()){o.selection.normalize()}return p.content},getContent:function(o){var n=this,p,m=n.getBody();o=o||{};o.format=o.format||"html";o.get=true;o.getInner=true;if(!o.no_events){n.onBeforeGetContent.dispatch(n,o)}if(o.format=="raw"){p=m.innerHTML}else{if(o.format=="text"){p=m.innerText||m.textContent}else{p=n.serializer.serialize(m,o)}}if(o.format!="text"){o.content=k.trim(p)}else{o.content=p}if(!o.no_events){n.onGetContent.dispatch(n,o)}return o.content},isDirty:function(){var m=this;return k.trim(m.startContent)!=k.trim(m.getContent({format:"raw",no_events:1}))&&!m.isNotDirty},getContainer:function(){var m=this;if(!m.container){m.container=l.get(m.editorContainer||m.id+"_parent")}return m.container},getContentAreaContainer:function(){return this.contentAreaContainer},getElement:function(){return l.get(this.settings.content_element||this.id)},getWin:function(){var m=this,n;if(!m.contentWindow){n=l.get(m.id+"_ifr");if(n){m.contentWindow=n.contentWindow}}return m.contentWindow},getDoc:function(){var m=this,n;if(!m.contentDocument){n=m.getWin();if(n){m.contentDocument=n.document}}return m.contentDocument},getBody:function(){return this.bodyElement||this.getDoc().body},convertURL:function(o,n,q){var m=this,p=m.settings;if(p.urlconverter_callback){return m.execCallback("urlconverter_callback",o,q,true,n)}if(!p.convert_urls||(q&&q.nodeName=="LINK")||o.indexOf("file:")===0){return o}if(p.relative_urls){return m.documentBaseURI.toRelative(o)}o=m.documentBaseURI.toAbsolute(o,p.remove_script_host);return o},addVisual:function(q){var n=this,o=n.settings,p=n.dom,m;q=q||n.getBody();if(!d(n.hasVisual)){n.hasVisual=o.visual}i(p.select("table,a",q),function(s){var r;switch(s.nodeName){case"TABLE":m=o.visual_table_class||"mceItemTable";r=p.getAttrib(s,"border");if(!r||r=="0"){if(n.hasVisual){p.addClass(s,m)}else{p.removeClass(s,m)}}return;case"A":if(!p.getAttrib(s,"href",false)){r=p.getAttrib(s,"name")||s.id;m="mceItemAnchor";if(r){if(n.hasVisual){p.addClass(s,m)}else{p.removeClass(s,m)}}}return}});n.onVisualAid.dispatch(n,q,n.hasVisual)},remove:function(){var m=this,o=m.getContainer(),n=m.getDoc();if(!m.removed){m.removed=1;if(b&&n){n.execCommand("SelectAll")}m.save();l.setStyle(m.id,"display",m.orgDisplay);if(!m.settings.content_editable){j.unbind(m.getWin());j.unbind(m.getDoc())}j.unbind(m.getBody());j.clear(o);m.execCallback("remove_instance_callback",m);m.onRemove.dispatch(m);m.onExecCommand.listeners=[];k.remove(m);l.remove(o)}},destroy:function(n){var m=this;if(m.destroyed){return}if(a){j.unbind(m.getDoc());j.unbind(m.getWin());j.unbind(m.getBody())}if(!n){k.removeUnload(m.destroy);tinyMCE.onBeforeUnload.remove(m._beforeUnload);if(m.theme&&m.theme.destroy){m.theme.destroy()}m.controlManager.destroy();m.selection.destroy();m.dom.destroy()}if(m.formElement){m.formElement.submit=m.formElement._mceOldSubmit;m.formElement._mceOldSubmit=null}m.contentAreaContainer=m.formElement=m.container=m.settings.content_element=m.bodyElement=m.contentDocument=m.contentWindow=null;if(m.selection){m.selection=m.selection.win=m.selection.dom=m.selection.dom.doc=null}m.destroyed=1},_refreshContentEditable:function(){var n=this,m,o;if(n._isHidden()){m=n.getBody();o=m.parentNode;o.removeChild(m);o.appendChild(m);m.focus()}},_isHidden:function(){var m;if(!a){return 0}m=this.selection.getSel();return(!m||!m.rangeCount||m.rangeCount===0)}})})(tinymce);(function(a){var b=a.each;a.Editor.prototype.setupEvents=function(){var c=this,d=c.settings;b(["onPreInit","onBeforeRenderUI","onPostRender","onLoad","onInit","onRemove","onActivate","onDeactivate","onClick","onEvent","onMouseUp","onMouseDown","onDblClick","onKeyDown","onKeyUp","onKeyPress","onContextMenu","onSubmit","onReset","onPaste","onPreProcess","onPostProcess","onBeforeSetContent","onBeforeGetContent","onSetContent","onGetContent","onLoadContent","onSaveContent","onNodeChange","onChange","onBeforeExecCommand","onExecCommand","onUndo","onRedo","onVisualAid","onSetProgressState","onSetAttrib"],function(e){c[e]=new a.util.Dispatcher(c)});if(d.cleanup_callback){c.onBeforeSetContent.add(function(e,f){f.content=e.execCallback("cleanup_callback","insert_to_editor",f.content,f)});c.onPreProcess.add(function(e,f){if(f.set){e.execCallback("cleanup_callback","insert_to_editor_dom",f.node,f)}if(f.get){e.execCallback("cleanup_callback","get_from_editor_dom",f.node,f)}});c.onPostProcess.add(function(e,f){if(f.set){f.content=e.execCallback("cleanup_callback","insert_to_editor",f.content,f)}if(f.get){f.content=e.execCallback("cleanup_callback","get_from_editor",f.content,f)}})}if(d.save_callback){c.onGetContent.add(function(e,f){if(f.save){f.content=e.execCallback("save_callback",e.id,f.content,e.getBody())}})}if(d.handle_event_callback){c.onEvent.add(function(f,g,h){if(c.execCallback("handle_event_callback",g,f,h)===false){g.preventDefault();g.stopPropagation()}})}if(d.handle_node_change_callback){c.onNodeChange.add(function(f,e,g){f.execCallback("handle_node_change_callback",f.id,g,-1,-1,true,f.selection.isCollapsed())})}if(d.save_callback){c.onSaveContent.add(function(e,g){var f=e.execCallback("save_callback",e.id,g.content,e.getBody());if(f){g.content=f}})}if(d.onchange_callback){c.onChange.add(function(f,e){f.execCallback("onchange_callback",f,e)})}};a.Editor.prototype.bindNativeEvents=function(){var l=this,f,d=l.settings,e=l.dom,h;h={mouseup:"onMouseUp",mousedown:"onMouseDown",click:"onClick",keyup:"onKeyUp",keydown:"onKeyDown",keypress:"onKeyPress",submit:"onSubmit",reset:"onReset",contextmenu:"onContextMenu",dblclick:"onDblClick",paste:"onPaste"};function c(i,m){var n=i.type;if(l.removed){return}if(l.onEvent.dispatch(l,i,m)!==false){l[h[i.fakeType||i.type]].dispatch(l,i,m)}}function j(i){l.focus(true)}function k(i,m){if(m.keyCode!=65||!a.VK.metaKeyPressed(m)){l.selection.normalize()}l.nodeChanged()}b(h,function(m,n){var i=d.content_editable?l.getBody():l.getDoc();switch(n){case"contextmenu":e.bind(i,n,c);break;case"paste":e.bind(l.getBody(),n,c);break;case"submit":case"reset":e.bind(l.getElement().form||a.DOM.getParent(l.id,"form"),n,c);break;default:e.bind(i,n,c)}});e.bind(d.content_editable?l.getBody():(a.isGecko?l.getDoc():l.getWin()),"focus",function(i){l.focus(true)});if(d.content_editable&&a.isOpera){e.bind(l.getBody(),"click",j);e.bind(l.getBody(),"keydown",j)}l.onMouseUp.add(k);l.onKeyUp.add(function(i,n){var m=n.keyCode;if((m>=33&&m<=36)||(m>=37&&m<=40)||m==13||m==45||m==46||m==8||(a.isMac&&(m==91||m==93))||n.ctrlKey){k(i,n)}});l.onReset.add(function(){l.setContent(l.startContent,{format:"raw"})});function g(m,i){if(m.altKey||m.ctrlKey||m.metaKey){b(l.shortcuts,function(n){var o=a.isMac?m.metaKey:m.ctrlKey;if(n.ctrl!=o||n.alt!=m.altKey||n.shift!=m.shiftKey){return}if(m.keyCode==n.keyCode||(m.charCode&&m.charCode==n.charCode)){m.preventDefault();if(i){n.func.call(n.scope)}return true}})}}l.onKeyUp.add(function(i,m){g(m)});l.onKeyPress.add(function(i,m){g(m)});l.onKeyDown.add(function(i,m){g(m,true)});if(a.isOpera){l.onClick.add(function(i,m){m.preventDefault()})}}})(tinymce);(function(d){var e=d.each,b,a=true,c=false;d.EditorCommands=function(n){var m=n.dom,p=n.selection,j={state:{},exec:{},value:{}},k=n.settings,q=n.formatter,o;function r(z,y,x){var v;z=z.toLowerCase();if(v=j.exec[z]){v(z,y,x);return a}return c}function l(x){var v;x=x.toLowerCase();if(v=j.state[x]){return v(x)}return -1}function h(x){var v;x=x.toLowerCase();if(v=j.value[x]){return v(x)}return c}function u(v,x){x=x||"exec";e(v,function(z,y){e(y.toLowerCase().split(","),function(A){j[x][A]=z})})}d.extend(this,{execCommand:r,queryCommandState:l,queryCommandValue:h,addCommands:u});function f(y,x,v){if(x===b){x=c}if(v===b){v=null}return n.getDoc().execCommand(y,x,v)}function t(v){return q.match(v)}function s(v,x){q.toggle(v,x?{value:x}:b)}function i(v){o=p.getBookmark(v)}function g(){p.moveToBookmark(o)}u({"mceResetDesignMode,mceBeginUndoLevel":function(){},"mceEndUndoLevel,mceAddUndoLevel":function(){n.undoManager.add()},"Cut,Copy,Paste":function(z){var y=n.getDoc(),v;try{f(z)}catch(x){v=a}if(v||!y.queryCommandSupported(z)){if(d.isGecko){n.windowManager.confirm(n.getLang("clipboard_msg"),function(A){if(A){open("http://www.mozilla.org/editor/midasdemo/securityprefs.html","_blank")}})}else{n.windowManager.alert(n.getLang("clipboard_no_support"))}}},unlink:function(v){if(p.isCollapsed()){p.select(p.getNode())}f(v);p.collapse(c)},"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(v){var x=v.substring(7);e("left,center,right,full".split(","),function(y){if(x!=y){q.remove("align"+y)}});s("align"+x);r("mceRepaint")},"InsertUnorderedList,InsertOrderedList":function(y){var v,x;f(y);v=m.getParent(p.getNode(),"ol,ul");if(v){x=v.parentNode;if(/^(H[1-6]|P|ADDRESS|PRE)$/.test(x.nodeName)){i();m.split(x,v);g()}}},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){s(v)},"ForeColor,HiliteColor,FontName":function(y,x,v){s(y,v)},FontSize:function(z,y,x){var v,A;if(x>=1&&x<=7){A=d.explode(k.font_size_style_values);v=d.explode(k.font_size_classes);if(v){x=v[x-1]||x}else{x=A[x-1]||x}}s(z,x)},RemoveFormat:function(v){q.remove(v)},mceBlockQuote:function(v){s("blockquote")},FormatBlock:function(y,x,v){return s(v||"p")},mceCleanup:function(){var v=p.getBookmark();n.setContent(n.getContent({cleanup:a}),{cleanup:a});p.moveToBookmark(v)},mceRemoveNode:function(z,y,x){var v=x||p.getNode();if(v!=n.getBody()){i();n.dom.remove(v,a);g()}},mceSelectNodeDepth:function(z,y,x){var v=0;m.getParent(p.getNode(),function(A){if(A.nodeType==1&&v++==x){p.select(A);return c}},n.getBody())},mceSelectNode:function(y,x,v){p.select(v)},mceInsertContent:function(B,I,K){var y,J,E,z,F,G,D,C,L,x,A,M,v,H;y=n.parser;J=new d.html.Serializer({},n.schema);v='<span id="mce_marker" data-mce-type="bookmark">\uFEFF</span>';G={content:K,format:"html"};p.onBeforeSetContent.dispatch(p,G);K=G.content;if(K.indexOf("{$caret}")==-1){K+="{$caret}"}K=K.replace(/\{\$caret\}/,v);if(!p.isCollapsed()){n.getDoc().execCommand("Delete",false,null)}E=p.getNode();G={context:E.nodeName.toLowerCase()};F=y.parse(K,G);A=F.lastChild;if(A.attr("id")=="mce_marker"){D=A;for(A=A.prev;A;A=A.walk(true)){if(A.type==3||!m.isBlock(A.name)){A.parent.insert(D,A,A.name==="br");break}}}if(!G.invalid){K=J.serialize(F);A=E.firstChild;M=E.lastChild;if(!A||(A===M&&A.nodeName==="BR")){m.setHTML(E,K)}else{p.setContent(K)}}else{p.setContent(v);E=p.getNode();z=n.getBody();if(E.nodeType==9){E=A=z}else{A=E}while(A!==z){E=A;A=A.parentNode}K=E==z?z.innerHTML:m.getOuterHTML(E);K=J.serialize(y.parse(K.replace(/<span (id="mce_marker"|id=mce_marker).+?<\/span>/i,function(){return J.serialize(F)})));if(E==z){m.setHTML(z,K)}else{m.setOuterHTML(E,K)}}D=m.get("mce_marker");C=m.getRect(D);L=m.getViewPort(n.getWin());if((C.y+C.h>L.y+L.h||C.y<L.y)||(C.x>L.x+L.w||C.x<L.x)){H=d.isIE?n.getDoc().documentElement:n.getBody();H.scrollLeft=C.x;H.scrollTop=C.y-L.h+25}x=m.createRng();A=D.previousSibling;if(A&&A.nodeType==3){x.setStart(A,A.nodeValue.length)}else{x.setStartBefore(D);x.setEndBefore(D)}m.remove(D);p.setRng(x);p.onSetContent.dispatch(p,G);n.addVisual()},mceInsertRawHTML:function(y,x,v){p.setContent("tiny_mce_marker");n.setContent(n.getContent().replace(/tiny_mce_marker/g,function(){return v}))},mceToggleFormat:function(y,x,v){s(v)},mceSetContent:function(y,x,v){n.setContent(v)},"Indent,Outdent":function(z){var x,v,y;x=k.indentation;v=/[a-z%]+$/i.exec(x);x=parseInt(x);if(!l("InsertUnorderedList")&&!l("InsertOrderedList")){if(!k.forced_root_block&&!m.getParent(p.getNode(),m.isBlock)){q.apply("div")}e(p.getSelectedBlocks(),function(A){if(z=="outdent"){y=Math.max(0,parseInt(A.style.paddingLeft||0)-x);m.setStyle(A,"paddingLeft",y?y+v:"")}else{m.setStyle(A,"paddingLeft",(parseInt(A.style.paddingLeft||0)+x)+v)}})}else{f(z)}},mceRepaint:function(){var x;if(d.isGecko){try{i(a);if(p.getSel()){p.getSel().selectAllChildren(n.getBody())}p.collapse(a);g()}catch(v){}}},mceToggleFormat:function(y,x,v){q.toggle(v)},InsertHorizontalRule:function(){n.execCommand("mceInsertContent",false,"<hr />")},mceToggleVisualAid:function(){n.hasVisual=!n.hasVisual;n.addVisual()},mceReplaceContent:function(y,x,v){n.execCommand("mceInsertContent",false,v.replace(/\{\$selection\}/g,p.getContent({format:"text"})))},mceInsertLink:function(z,y,x){var v;if(typeof(x)=="string"){x={href:x}}v=m.getParent(p.getNode(),"a");x.href=x.href.replace(" ","%20");if(!v||!x.href){q.remove("link")}if(x.href){q.apply("link",x,v)}},selectAll:function(){var x=m.getRoot(),v=m.createRng();if(p.getRng().setStart){v.setStart(x,0);v.setEnd(x,x.childNodes.length);p.setRng(v)}else{f("SelectAll")}}});u({"JustifyLeft,JustifyCenter,JustifyRight,JustifyFull":function(z){var x="align"+z.substring(7);var v=p.isCollapsed()?[m.getParent(p.getNode(),m.isBlock)]:p.getSelectedBlocks();var y=d.map(v,function(A){return !!q.matchNode(A,x)});return d.inArray(y,a)!==-1},"Bold,Italic,Underline,Strikethrough,Superscript,Subscript":function(v){return t(v)},mceBlockQuote:function(){return t("blockquote")},Outdent:function(){var v;if(k.inline_styles){if((v=m.getParent(p.getStart(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}if((v=m.getParent(p.getEnd(),m.isBlock))&&parseInt(v.style.paddingLeft)>0){return a}}return l("InsertUnorderedList")||l("InsertOrderedList")||(!k.inline_styles&&!!m.getParent(p.getNode(),"BLOCKQUOTE"))},"InsertUnorderedList,InsertOrderedList":function(x){var v=m.getParent(p.getNode(),"ul,ol");return v&&(x==="insertunorderedlist"&&v.tagName==="UL"||x==="insertorderedlist"&&v.tagName==="OL")}},"state");u({"FontSize,FontName":function(y){var x=0,v;if(v=m.getParent(p.getNode(),"span")){if(y=="fontsize"){x=v.style.fontSize}else{x=v.style.fontFamily.replace(/, /g,",").replace(/[\'\"]/g,"").toLowerCase()}}return x}},"value");u({Undo:function(){n.undoManager.undo()},Redo:function(){n.undoManager.redo()}})}})(tinymce);(function(b){var a=b.util.Dispatcher;b.UndoManager=function(h){var l,i=0,e=[],g,k,j,f;function c(){return b.trim(h.getContent({format:"raw",no_events:1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g,""))}function d(){l.typing=false;l.add()}onBeforeAdd=new a(l);k=new a(l);j=new a(l);f=new a(l);k.add(function(m,n){if(m.hasUndo()){return h.onChange.dispatch(h,n,m)}});j.add(function(m,n){return h.onUndo.dispatch(h,n,m)});f.add(function(m,n){return h.onRedo.dispatch(h,n,m)});h.onInit.add(function(){l.add()});h.onBeforeExecCommand.add(function(m,p,o,q,n){if(p!="Undo"&&p!="Redo"&&p!="mceRepaint"&&(!n||!n.skip_undo)){l.beforeChange()}});h.onExecCommand.add(function(m,p,o,q,n){if(p!="Undo"&&p!="Redo"&&p!="mceRepaint"&&(!n||!n.skip_undo)){l.add()}});h.onSaveContent.add(d);h.dom.bind(h.dom.getRoot(),"dragend",d);h.dom.bind(h.getBody(),"focusout",function(m){if(!h.removed&&l.typing){d()}});h.onKeyUp.add(function(m,o){var n=o.keyCode;if((n>=33&&n<=36)||(n>=37&&n<=40)||n==45||n==13||o.ctrlKey){d()}});h.onKeyDown.add(function(m,o){var n=o.keyCode;if((n>=33&&n<=36)||(n>=37&&n<=40)||n==45){if(l.typing){d()}return}if((n<16||n>20)&&n!=224&&n!=91&&!l.typing){l.beforeChange();l.typing=true;l.add()}});h.onMouseDown.add(function(m,n){if(l.typing){d()}});h.addShortcut("ctrl+z","undo_desc","Undo");h.addShortcut("ctrl+y","redo_desc","Redo");l={data:e,typing:false,onBeforeAdd:onBeforeAdd,onAdd:k,onUndo:j,onRedo:f,beforeChange:function(){g=h.selection.getBookmark(2,true)},add:function(p){var m,n=h.settings,o;p=p||{};p.content=c();l.onBeforeAdd.dispatch(l,p);o=e[i];if(o&&o.content==p.content){return null}if(e[i]){e[i].beforeBookmark=g}if(n.custom_undo_redo_levels){if(e.length>n.custom_undo_redo_levels){for(m=0;m<e.length-1;m++){e[m]=e[m+1]}e.length--;i=e.length}}p.bookmark=h.selection.getBookmark(2,true);if(i<e.length-1){e.length=i+1}e.push(p);i=e.length-1;l.onAdd.dispatch(l,p);h.isNotDirty=0;return p},undo:function(){var n,m;if(l.typing){l.add();l.typing=false}if(i>0){n=e[--i];h.setContent(n.content,{format:"raw"});h.selection.moveToBookmark(n.beforeBookmark);l.onUndo.dispatch(l,n)}return n},redo:function(){var m;if(i<e.length-1){m=e[++i];h.setContent(m.content,{format:"raw"});h.selection.moveToBookmark(m.bookmark);l.onRedo.dispatch(l,m)}return m},clear:function(){e=[];i=0;l.typing=false},hasUndo:function(){return i>0||this.typing},hasRedo:function(){return i<e.length-1&&!this.typing}};return l}})(tinymce);tinymce.ForceBlocks=function(c){var b=c.settings,e=c.dom,a=c.selection,d=c.schema.getBlockElements();function f(){var j=a.getStart(),h=c.getBody(),g,k,o,s,q,i,l,m=-16777215,p,r;if(!j||j.nodeType!==1||!b.forced_root_block){return}while(j&&j!=h){if(d[j.nodeName]){return}j=j.parentNode}g=a.getRng();if(g.setStart){k=g.startContainer;o=g.startOffset;s=g.endContainer;q=g.endOffset}else{if(g.item){j=g.item(0);g=c.getDoc().body.createTextRange();g.moveToElementText(j)}r=g.parentElement().ownerDocument===c.getDoc();tmpRng=g.duplicate();tmpRng.collapse(true);o=tmpRng.move("character",m)*-1;if(!tmpRng.collapsed){tmpRng=g.duplicate();tmpRng.collapse(false);q=(tmpRng.move("character",m)*-1)-o}}j=h.firstChild;while(j){if(j.nodeType===3||(j.nodeType==1&&!d[j.nodeName])){if(j.nodeType===3&&j.nodeValue.length==0){l=j;j=j.nextSibling;e.remove(l);continue}if(!i){i=e.create(b.forced_root_block);j.parentNode.insertBefore(i,j);p=true}l=j;j=j.nextSibling;i.appendChild(l)}else{i=null;j=j.nextSibling}}if(p){if(g.setStart){g.setStart(k,o);g.setEnd(s,q);a.setRng(g)}else{if(r){try{g=c.getDoc().body.createTextRange();g.moveToElementText(h);g.collapse(true);g.moveStart("character",o);if(q>0){g.moveEnd("character",q)}g.select()}catch(n){}}}c.nodeChanged()}}if(b.forced_root_block){c.onKeyUp.add(f);c.onNodeChange.add(f)}};(function(c){var b=c.DOM,a=c.dom.Event,d=c.each,e=c.extend;c.create("tinymce.ControlManager",{ControlManager:function(f,j){var h=this,g;j=j||{};h.editor=f;h.controls={};h.onAdd=new c.util.Dispatcher(h);h.onPostRender=new c.util.Dispatcher(h);h.prefix=j.prefix||f.id+"_";h._cls={};h.onPostRender.add(function(){d(h.controls,function(i){i.postRender()})})},get:function(f){return this.controls[this.prefix+f]||this.controls[f]},setActive:function(h,f){var g=null;if(g=this.get(h)){g.setActive(f)}return g},setDisabled:function(h,f){var g=null;if(g=this.get(h)){g.setDisabled(f)}return g},add:function(g){var f=this;if(g){f.controls[g.id]=g;f.onAdd.dispatch(g,f)}return g},createControl:function(j){var o,k,g,h=this,m=h.editor,n,f;if(!h.controlFactories){h.controlFactories=[];d(m.plugins,function(i){if(i.createControl){h.controlFactories.push(i)}})}n=h.controlFactories;for(k=0,g=n.length;k<g;k++){o=n[k].createControl(j,h);if(o){return h.add(o)}}if(j==="|"||j==="separator"){return h.createSeparator()}if(m.buttons&&(o=m.buttons[j])){return h.createButton(j,o)}return h.add(o)},createDropMenu:function(f,n,h){var m=this,i=m.editor,j,g,k,l;n=e({"class":"mceDropDown",constrain:i.settings.constrain_menus},n);n["class"]=n["class"]+" "+i.getParam("skin")+"Skin";if(k=i.getParam("skin_variant")){n["class"]+=" "+i.getParam("skin")+"Skin"+k.substring(0,1).toUpperCase()+k.substring(1)}n["class"]+=i.settings.directionality=="rtl"?" mceRtl":"";f=m.prefix+f;l=h||m._cls.dropmenu||c.ui.DropMenu;j=m.controls[f]=new l(f,n);j.onAddItem.add(function(r,q){var p=q.settings;p.title=i.getLang(p.title,p.title);if(!p.onclick){p.onclick=function(o){if(p.cmd){i.execCommand(p.cmd,p.ui||false,p.value)}}}});i.onRemove.add(function(){j.destroy()});if(c.isIE){j.onShowMenu.add(function(){i.focus();g=i.selection.getBookmark(1)});j.onHideMenu.add(function(){if(g){i.selection.moveToBookmark(g);g=0}})}return m.add(j)},createListBox:function(f,n,h){var l=this,j=l.editor,i,k,m;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,scope:n.scope,control_manager:l},n);f=l.prefix+f;function g(o){return o.settings.use_accessible_selects&&!c.isGecko}if(j.settings.use_native_selects||g(j)){k=new c.ui.NativeListBox(f,n)}else{m=h||l._cls.listbox||c.ui.ListBox;k=new m(f,n,j)}l.controls[f]=k;if(c.isWebKit){k.onPostRender.add(function(p,o){a.add(o,"mousedown",function(){j.bookmark=j.selection.getBookmark(1)});a.add(o,"focus",function(){j.selection.moveToBookmark(j.bookmark);j.bookmark=null})})}if(k.hideMenu){j.onMouseDown.add(k.hideMenu,k)}return l.add(k)},createButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.label=g.translate(i.label);i.scope=i.scope||g;if(!i.onclick&&!i.menu_button){i.onclick=function(){g.execCommand(i.cmd,i.ui||false,i.value)}}i=e({title:i.title,"class":"mce_"+m,unavailable_prefix:g.getLang("unavailable",""),scope:i.scope,control_manager:h},i);m=h.prefix+m;if(i.menu_button){f=l||h._cls.menubutton||c.ui.MenuButton;k=new f(m,i,g);g.onMouseDown.add(k.hideMenu,k)}else{f=h._cls.button||c.ui.Button;k=new f(m,i,g)}return h.add(k)},createMenuButton:function(h,f,g){f=f||{};f.menu_button=1;return this.createButton(h,f,g)},createSplitButton:function(m,i,l){var h=this,g=h.editor,j,k,f;if(h.get(m)){return null}i.title=g.translate(i.title);i.scope=i.scope||g;if(!i.onclick){i.onclick=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}if(!i.onselect){i.onselect=function(n){g.execCommand(i.cmd,i.ui||false,n||i.value)}}i=e({title:i.title,"class":"mce_"+m,scope:i.scope,control_manager:h},i);m=h.prefix+m;f=l||h._cls.splitbutton||c.ui.SplitButton;k=h.add(new f(m,i,g));g.onMouseDown.add(k.hideMenu,k);return k},createColorSplitButton:function(f,n,h){var l=this,j=l.editor,i,k,m,g;if(l.get(f)){return null}n.title=j.translate(n.title);n.scope=n.scope||j;if(!n.onclick){n.onclick=function(o){if(c.isIE){g=j.selection.getBookmark(1)}j.execCommand(n.cmd,n.ui||false,o||n.value)}}if(!n.onselect){n.onselect=function(o){j.execCommand(n.cmd,n.ui||false,o||n.value)}}n=e({title:n.title,"class":"mce_"+f,menu_class:j.getParam("skin")+"Skin",scope:n.scope,more_colors_title:j.getLang("more_colors")},n);f=l.prefix+f;m=h||l._cls.colorsplitbutton||c.ui.ColorSplitButton;k=new m(f,n,j);j.onMouseDown.add(k.hideMenu,k);j.onRemove.add(function(){k.destroy()});if(c.isIE){k.onShowMenu.add(function(){j.focus();g=j.selection.getBookmark(1)});k.onHideMenu.add(function(){if(g){j.selection.moveToBookmark(g);g=0}})}return l.add(k)},createToolbar:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||g._cls.toolbar||c.ui.Toolbar;i=new f(k,h,g.editor);if(g.get(k)){return null}return g.add(i)},createToolbarGroup:function(k,h,j){var i,g=this,f;k=g.prefix+k;f=j||this._cls.toolbarGroup||c.ui.ToolbarGroup;i=new f(k,h,g.editor);if(g.get(k)){return null}return g.add(i)},createSeparator:function(g){var f=g||this._cls.separator||c.ui.Separator;return new f()},setControlType:function(g,f){return this._cls[g.toLowerCase()]=f},destroy:function(){d(this.controls,function(f){f.destroy()});this.controls=null}})})(tinymce);(function(d){var a=d.util.Dispatcher,e=d.each,c=d.isIE,b=d.isOpera;d.create("tinymce.WindowManager",{WindowManager:function(f){var g=this;g.editor=f;g.onOpen=new a(g);g.onClose=new a(g);g.params={};g.features={}},open:function(z,h){var v=this,k="",n,m,i=v.editor.settings.dialog_type=="modal",q,o,j,g=d.DOM.getViewPort(),r;z=z||{};h=h||{};o=b?g.w:screen.width;j=b?g.h:screen.height;z.name=z.name||"mc_"+new Date().getTime();z.width=parseInt(z.width||320);z.height=parseInt(z.height||240);z.resizable=true;z.left=z.left||parseInt(o/2)-(z.width/2);z.top=z.top||parseInt(j/2)-(z.height/2);h.inline=false;h.mce_width=z.width;h.mce_height=z.height;h.mce_auto_focus=z.auto_focus;if(i){if(c){z.center=true;z.help=false;z.dialogWidth=z.width+"px";z.dialogHeight=z.height+"px";z.scroll=z.scrollbars||false}}e(z,function(p,f){if(d.is(p,"boolean")){p=p?"yes":"no"}if(!/^(name|url)$/.test(f)){if(c&&i){k+=(k?";":"")+f+":"+p}else{k+=(k?",":"")+f+"="+p}}});v.features=z;v.params=h;v.onOpen.dispatch(v,z,h);r=z.url||z.file;r=d._addVer(r);try{if(c&&i){q=1;window.showModalDialog(r,window,k)}else{q=window.open(r,z.name,k)}}catch(l){}if(!q){alert(v.editor.getLang("popup_blocked"))}},close:function(f){f.close();this.onClose.dispatch(this)},createInstance:function(i,h,g,m,l,k){var j=d.resolve(i);return new j(h,g,m,l,k)},confirm:function(h,f,i,g){g=g||window;f.call(i||this,g.confirm(this._decode(this.editor.getLang(h,h))))},alert:function(h,f,j,g){var i=this;g=g||window;g.alert(i._decode(i.editor.getLang(h,h)));if(f){f.call(j||i)}},resizeBy:function(f,g,h){h.resizeBy(f,g)},_decode:function(f){return d.DOM.decode(f).replace(/\\n/g,"\n")}})}(tinymce));(function(a){a.Formatter=function(aa){var Q={},T=a.each,c=aa.dom,r=aa.selection,t=a.dom.TreeWalker,N=new a.dom.RangeUtils(c),d=aa.schema.isValidChild,A=a.isArray,H=c.isBlock,m=aa.settings.forced_root_block,s=c.nodeIndex,G="\uFEFF",e=/^(src|href|style)$/,X=false,C=true,P,D,x=c.getContentEditable;function I(ab){return !!aa.schema.getTextBlocks()[ab.toLowerCase()]}function n(ac,ab){return c.getParents(ac,ab,c.getRoot())}function b(ab){return ab.nodeType===1&&ab.id==="_mce_caret"}function j(){l({alignleft:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"left"},defaultBlock:"div"},{selector:"img,table",collapsed:false,styles:{"float":"left"}}],aligncenter:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"center"},defaultBlock:"div"},{selector:"img",collapsed:false,styles:{display:"block",marginLeft:"auto",marginRight:"auto"}},{selector:"table",collapsed:false,styles:{marginLeft:"auto",marginRight:"auto"}}],alignright:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"right"},defaultBlock:"div"},{selector:"img,table",collapsed:false,styles:{"float":"right"}}],alignfull:[{selector:"figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li",styles:{textAlign:"justify"},defaultBlock:"div"}],bold:[{inline:"strong",remove:"all"},{inline:"span",styles:{fontWeight:"bold"}},{inline:"b",remove:"all"}],italic:[{inline:"em",remove:"all"},{inline:"span",styles:{fontStyle:"italic"}},{inline:"i",remove:"all"}],underline:[{inline:"span",styles:{textDecoration:"underline"},exact:true},{inline:"u",remove:"all"}],strikethrough:[{inline:"span",styles:{textDecoration:"line-through"},exact:true},{inline:"strike",remove:"all"}],forecolor:{inline:"span",styles:{color:"%value"},wrap_links:false},hilitecolor:{inline:"span",styles:{backgroundColor:"%value"},wrap_links:false},fontname:{inline:"span",styles:{fontFamily:"%value"}},fontsize:{inline:"span",styles:{fontSize:"%value"}},fontsize_class:{inline:"span",attributes:{"class":"%value"}},blockquote:{block:"blockquote",wrapper:1,remove:"all"},subscript:{inline:"sub"},superscript:{inline:"sup"},link:{inline:"a",selector:"a",remove:"all",split:true,deep:true,onmatch:function(ab){return true},onformat:function(ad,ab,ac){T(ac,function(af,ae){c.setAttrib(ad,ae,af)})}},removeformat:[{selector:"b,strong,em,i,font,u,strike",remove:"all",split:true,expand:false,block_expand:true,deep:true},{selector:"span",attributes:["style","class"],remove:"empty",split:true,expand:false,deep:true},{selector:"*",attributes:["style","class"],split:false,expand:false,deep:true}]});T("p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp".split(/\s/),function(ab){l(ab,{block:ab,remove:"all"})});l(aa.settings.formats)}function W(){aa.addShortcut("ctrl+b","bold_desc","Bold");aa.addShortcut("ctrl+i","italic_desc","Italic");aa.addShortcut("ctrl+u","underline_desc","Underline");for(var ab=1;ab<=6;ab++){aa.addShortcut("ctrl+"+ab,"",["FormatBlock",false,"h"+ab])}aa.addShortcut("ctrl+7","",["FormatBlock",false,"p"]);aa.addShortcut("ctrl+8","",["FormatBlock",false,"div"]);aa.addShortcut("ctrl+9","",["FormatBlock",false,"address"])}function V(ab){return ab?Q[ab]:Q}function l(ab,ac){if(ab){if(typeof(ab)!=="string"){T(ab,function(ae,ad){l(ad,ae)})}else{ac=ac.length?ac:[ac];T(ac,function(ad){if(ad.deep===D){ad.deep=!ad.selector}if(ad.split===D){ad.split=!ad.selector||ad.inline}if(ad.remove===D&&ad.selector&&!ad.inline){ad.remove="none"}if(ad.selector&&ad.inline){ad.mixed=true;ad.block_expand=true}if(typeof(ad.classes)==="string"){ad.classes=ad.classes.split(/\s+/)}});Q[ab]=ac}}}var i=function(ac){var ab;aa.dom.getParent(ac,function(ad){ab=aa.dom.getStyle(ad,"text-decoration");return ab&&ab!=="none"});return ab};var L=function(ab){var ac;if(ab.nodeType===1&&ab.parentNode&&ab.parentNode.nodeType===1){ac=i(ab.parentNode);if(aa.dom.getStyle(ab,"color")&&ac){aa.dom.setStyle(ab,"text-decoration",ac)}else{if(aa.dom.getStyle(ab,"textdecoration")===ac){aa.dom.setStyle(ab,"text-decoration",null)}}}};function Y(ae,al,ag){var ah=V(ae),am=ah[0],ak,ac,aj,ai=r.isCollapsed();function ab(aq,ap){ap=ap||am;if(aq){if(ap.onformat){ap.onformat(aq,ap,al,ag)}T(ap.styles,function(at,ar){c.setStyle(aq,ar,q(at,al))});T(ap.attributes,function(at,ar){c.setAttrib(aq,ar,q(at,al))});T(ap.classes,function(ar){ar=q(ar,al);if(!c.hasClass(aq,ar)){c.addClass(aq,ar)}})}}function af(){function ar(ay,aw){var ax=new t(aw);for(ag=ax.current();ag;ag=ax.prev()){if(ag.childNodes.length>1||ag==ay||ag.tagName=="BR"){return ag}}}var aq=aa.selection.getRng();var av=aq.startContainer;var ap=aq.endContainer;if(av!=ap&&aq.endOffset===0){var au=ar(av,ap);var at=au.nodeType==3?au.length:au.childNodes.length;aq.setEnd(au,at)}return aq}function ad(at,ay,aw,av,aq){var ap=[],ar=-1,ax,aA=-1,au=-1,az;T(at.childNodes,function(aC,aB){if(aC.nodeName==="UL"||aC.nodeName==="OL"){ar=aB;ax=aC;return false}});T(at.childNodes,function(aC,aB){if(aC.nodeName==="SPAN"&&c.getAttrib(aC,"data-mce-type")=="bookmark"){if(aC.id==ay.id+"_start"){aA=aB}else{if(aC.id==ay.id+"_end"){au=aB}}}});if(ar<=0||(aA<ar&&au>ar)){T(a.grep(at.childNodes),aq);return 0}else{az=c.clone(aw,X);T(a.grep(at.childNodes),function(aC,aB){if((aA<ar&&aB<ar)||(aA>ar&&aB>ar)){ap.push(aC);aC.parentNode.removeChild(aC)}});if(aA<ar){at.insertBefore(az,ax)}else{if(aA>ar){at.insertBefore(az,ax.nextSibling)}}av.push(az);T(ap,function(aB){az.appendChild(aB)});return az}}function an(aq,at,aw){var ap=[],av,ar,au=true;av=am.inline||am.block;ar=c.create(av);ab(ar);N.walk(aq,function(ax){var ay;function az(aA){var aF,aD,aB,aC,aE;aE=au;aF=aA.nodeName.toLowerCase();aD=aA.parentNode.nodeName.toLowerCase();if(aA.nodeType===1&&x(aA)){aE=au;au=x(aA)==="true";aC=true}if(g(aF,"br")){ay=0;if(am.block){c.remove(aA)}return}if(am.wrapper&&y(aA,ae,al)){ay=0;return}if(au&&!aC&&am.block&&!am.wrapper&&I(aF)){aA=c.rename(aA,av);ab(aA);ap.push(aA);ay=0;return}if(am.selector){T(ah,function(aG){if("collapsed" in aG&&aG.collapsed!==ai){return}if(c.is(aA,aG.selector)&&!b(aA)){ab(aA,aG);aB=true}});if(!am.inline||aB){ay=0;return}}if(au&&!aC&&d(av,aF)&&d(aD,av)&&!(!aw&&aA.nodeType===3&&aA.nodeValue.length===1&&aA.nodeValue.charCodeAt(0)===65279)&&!b(aA)){if(!ay){ay=c.clone(ar,X);aA.parentNode.insertBefore(ay,aA);ap.push(ay)}ay.appendChild(aA)}else{if(aF=="li"&&at){ay=ad(aA,at,ar,ap,az)}else{ay=0;T(a.grep(aA.childNodes),az);if(aC){au=aE}ay=0}}}T(ax,az)});if(am.wrap_links===false){T(ap,function(ax){function ay(aC){var aB,aA,az;if(aC.nodeName==="A"){aA=c.clone(ar,X);ap.push(aA);az=a.grep(aC.childNodes);for(aB=0;aB<az.length;aB++){aA.appendChild(az[aB])}aC.appendChild(aA)}T(a.grep(aC.childNodes),ay)}ay(ax)})}T(ap,function(az){var ax;function aA(aC){var aB=0;T(aC.childNodes,function(aD){if(!f(aD)&&!K(aD)){aB++}});return aB}function ay(aB){var aD,aC;T(aB.childNodes,function(aE){if(aE.nodeType==1&&!K(aE)&&!b(aE)){aD=aE;return X}});if(aD&&h(aD,am)){aC=c.clone(aD,X);ab(aC);c.replace(aC,aB,C);c.remove(aD,1)}return aC||aB}ax=aA(az);if((ap.length>1||!H(az))&&ax===0){c.remove(az,1);return}if(am.inline||am.wrapper){if(!am.exact&&ax===1){az=ay(az)}T(ah,function(aB){T(c.select(aB.inline,az),function(aD){var aC;if(aB.wrap_links===false){aC=aD.parentNode;do{if(aC.nodeName==="A"){return}}while(aC=aC.parentNode)}Z(aB,al,aD,aB.exact?aD:null)})});if(y(az.parentNode,ae,al)){c.remove(az,1);az=0;return C}if(am.merge_with_parents){c.getParent(az.parentNode,function(aB){if(y(aB,ae,al)){c.remove(az,1);az=0;return C}})}if(az&&am.merge_siblings!==false){az=u(E(az),az);az=u(az,E(az,C))}}})}if(am){if(ag){if(ag.nodeType){ac=c.createRng();ac.setStartBefore(ag);ac.setEndAfter(ag);an(p(ac,ah),null,true)}else{an(ag,null,true)}}else{if(!ai||!am.inline||c.select("td.mceSelected,th.mceSelected").length){var ao=aa.selection.getNode();if(!m&&ah[0].defaultBlock&&!c.getParent(ao,c.isBlock)){Y(ah[0].defaultBlock)}aa.selection.setRng(af());ak=r.getBookmark();an(p(r.getRng(C),ah),ak);if(am.styles&&(am.styles.color||am.styles.textDecoration)){a.walk(ao,L,"childNodes");L(ao)}r.moveToBookmark(ak);R(r.getRng(C));aa.nodeChanged()}else{U("apply",ae,al)}}}}function B(ad,am,af){var ag=V(ad),ao=ag[0],ak,aj,ac,al=true;function ae(av){var au,at,ar,aq,ax,aw;if(av.nodeType===3){return}if(av.nodeType===1&&x(av)){ax=al;al=x(av)==="true";aw=true}au=a.grep(av.childNodes);if(al&&!aw){for(at=0,ar=ag.length;at<ar;at++){if(Z(ag[at],am,av,av)){break}}}if(ao.deep){if(au.length){for(at=0,ar=au.length;at<ar;at++){ae(au[at])}if(aw){al=ax}}}}function ah(aq){var ar;T(n(aq.parentNode).reverse(),function(at){var au;if(!ar&&at.id!="_start"&&at.id!="_end"){au=y(at,ad,am);if(au&&au.split!==false){ar=at}}});return ar}function ab(au,aq,aw,az){var aA,ay,ax,at,av,ar;if(au){ar=au.parentNode;for(aA=aq.parentNode;aA&&aA!=ar;aA=aA.parentNode){ay=c.clone(aA,X);for(av=0;av<ag.length;av++){if(Z(ag[av],am,ay,ay)){ay=0;break}}if(ay){if(ax){ay.appendChild(ax)}if(!at){at=ay}ax=ay}}if(az&&(!ao.mixed||!H(au))){aq=c.split(au,aq)}if(ax){aw.parentNode.insertBefore(ax,aw);at.appendChild(aw)}}return aq}function an(aq){return ab(ah(aq),aq,aq,true)}function ai(at){var ar=c.get(at?"_start":"_end"),aq=ar[at?"firstChild":"lastChild"];if(K(aq)){aq=aq[at?"firstChild":"lastChild"]}c.remove(ar,true);return aq}function ap(aq){var at,au,ar;aq=p(aq,ag,C);if(ao.split){at=M(aq,C);au=M(aq);if(at!=au){if(/^(TR|TD)$/.test(at.nodeName)&&at.firstChild){at=(at.nodeName=="TD"?at.firstChild:at.firstChild.firstChild)||at}at=S(at,"span",{id:"_start","data-mce-type":"bookmark"});au=S(au,"span",{id:"_end","data-mce-type":"bookmark"});an(at);an(au);at=ai(C);au=ai()}else{at=au=an(at)}aq.startContainer=at.parentNode;aq.startOffset=s(at);aq.endContainer=au.parentNode;aq.endOffset=s(au)+1}N.walk(aq,function(av){T(av,function(aw){ae(aw);if(aw.nodeType===1&&aa.dom.getStyle(aw,"text-decoration")==="underline"&&aw.parentNode&&i(aw.parentNode)==="underline"){Z({deep:false,exact:true,inline:"span",styles:{textDecoration:"underline"}},null,aw)}})})}if(af){if(af.nodeType){ac=c.createRng();ac.setStartBefore(af);ac.setEndAfter(af);ap(ac)}else{ap(af)}return}if(!r.isCollapsed()||!ao.inline||c.select("td.mceSelected,th.mceSelected").length){ak=r.getBookmark();ap(r.getRng(C));r.moveToBookmark(ak);if(ao.inline&&k(ad,am,r.getStart())){R(r.getRng(true))}aa.nodeChanged()}else{U("remove",ad,am)}}function F(ac,ae,ad){var ab=V(ac);if(k(ac,ae,ad)&&(!("toggle" in ab[0])||ab[0].toggle)){B(ac,ae,ad)}else{Y(ac,ae,ad)}}function y(ac,ab,ah,af){var ad=V(ab),ai,ag,ae;function aj(an,ap,aq){var am,ao,ak=ap[aq],al;if(ap.onmatch){return ap.onmatch(an,ap,aq)}if(ak){if(ak.length===D){for(am in ak){if(ak.hasOwnProperty(am)){if(aq==="attributes"){ao=c.getAttrib(an,am)}else{ao=O(an,am)}if(af&&!ao&&!ap.exact){return}if((!af||ap.exact)&&!g(ao,q(ak[am],ah))){return}}}}else{for(al=0;al<ak.length;al++){if(aq==="attributes"?c.getAttrib(an,ak[al]):O(an,ak[al])){return ap}}}}return ap}if(ad&&ac){for(ag=0;ag<ad.length;ag++){ai=ad[ag];if(h(ac,ai)&&aj(ac,ai,"attributes")&&aj(ac,ai,"styles")){if(ae=ai.classes){for(ag=0;ag<ae.length;ag++){if(!c.hasClass(ac,ae[ag])){return}}}return ai}}}}function k(ad,af,ae){var ac;function ab(ag){ag=c.getParent(ag,function(ah){return !!y(ah,ad,af,true)});return y(ag,ad,af)}if(ae){return ab(ae)}ae=r.getNode();if(ab(ae)){return C}ac=r.getStart();if(ac!=ae){if(ab(ac)){return C}}return X}function v(ai,ah){var af,ag=[],ae={},ad,ac,ab;af=r.getStart();c.getParent(af,function(al){var ak,aj;for(ak=0;ak<ai.length;ak++){aj=ai[ak];if(!ae[aj]&&y(al,aj,ah)){ae[aj]=true;ag.push(aj)}}},c.getRoot());return ag}function z(af){var ah=V(af),ae,ad,ag,ac,ab;if(ah){ae=r.getStart();ad=n(ae);for(ac=ah.length-1;ac>=0;ac--){ab=ah[ac].selector;if(!ab){return C}for(ag=ad.length-1;ag>=0;ag--){if(c.is(ad[ag],ab)){return C}}}}return X}function J(ab,ae,ac){var ad;if(!P){P={};ad={};aa.onNodeChange.addToTop(function(ag,af,ai){var ah=n(ai),aj={};T(P,function(ak,al){T(ah,function(am){if(y(am,al,{},ak.similar)){if(!ad[al]){T(ak,function(an){an(true,{node:am,format:al,parents:ah})});ad[al]=ak}aj[al]=ak;return false}})});T(ad,function(ak,al){if(!aj[al]){delete ad[al];T(ak,function(am){am(false,{node:ai,format:al,parents:ah})})}})})}T(ab.split(","),function(af){if(!P[af]){P[af]=[];P[af].similar=ac}P[af].push(ae)});return this}a.extend(this,{get:V,register:l,apply:Y,remove:B,toggle:F,match:k,matchAll:v,matchNode:y,canApply:z,formatChanged:J});j();W();function h(ab,ac){if(g(ab,ac.inline)){return C}if(g(ab,ac.block)){return C}if(ac.selector){return c.is(ab,ac.selector)}}function g(ac,ab){ac=ac||"";ab=ab||"";ac=""+(ac.nodeName||ac);ab=""+(ab.nodeName||ab);return ac.toLowerCase()==ab.toLowerCase()}function O(ac,ab){var ad=c.getStyle(ac,ab);if(ab=="color"||ab=="backgroundColor"){ad=c.toHex(ad)}if(ab=="fontWeight"&&ad==700){ad="bold"}return""+ad}function q(ab,ac){if(typeof(ab)!="string"){ab=ab(ac)}else{if(ac){ab=ab.replace(/%(\w+)/g,function(ae,ad){return ac[ad]||ae})}}return ab}function f(ab){return ab&&ab.nodeType===3&&/^([\t \r\n]+|)$/.test(ab.nodeValue)}function S(ad,ac,ab){var ae=c.create(ac,ab);ad.parentNode.insertBefore(ae,ad);ae.appendChild(ad);return ae}function p(ab,am,ae){var ap,an,ah,al,ad=ab.startContainer,ai=ab.startOffset,ar=ab.endContainer,ak=ab.endOffset;function ao(aA){var au,ax,az,aw,av,at;au=ax=aA?ad:ar;av=aA?"previousSibling":"nextSibling";at=c.getRoot();function ay(aB){return aB.nodeName=="BR"&&aB.getAttribute("data-mce-bogus")&&!aB.nextSibling}if(au.nodeType==3&&!f(au)){if(aA?ai>0:ak<au.nodeValue.length){return au}}for(;;){if(!am[0].block_expand&&H(ax)){return ax}for(aw=ax[av];aw;aw=aw[av]){if(!K(aw)&&!f(aw)&&!ay(aw)){return ax}}if(ax.parentNode==at){au=ax;break}ax=ax.parentNode}return au}function ag(at,au){if(au===D){au=at.nodeType===3?at.length:at.childNodes.length}while(at&&at.hasChildNodes()){at=at.childNodes[au];if(at){au=at.nodeType===3?at.length:at.childNodes.length}}return{node:at,offset:au}}if(ad.nodeType==1&&ad.hasChildNodes()){an=ad.childNodes.length-1;ad=ad.childNodes[ai>an?an:ai];if(ad.nodeType==3){ai=0}}if(ar.nodeType==1&&ar.hasChildNodes()){an=ar.childNodes.length-1;ar=ar.childNodes[ak>an?an:ak-1];if(ar.nodeType==3){ak=ar.nodeValue.length}}function aq(au){var at=au;while(at){if(at.nodeType===1&&x(at)){return x(at)==="false"?at:au}at=at.parentNode}return au}function aj(au,ay,aA){var ax,av,az,at;function aw(aC,aE){var aF,aB,aD=aC.nodeValue;if(typeof(aE)=="undefined"){aE=aA?aD.length:0}if(aA){aF=aD.lastIndexOf(" ",aE);aB=aD.lastIndexOf("\u00a0",aE);aF=aF>aB?aF:aB;if(aF!==-1&&!ae){aF++}}else{aF=aD.indexOf(" ",aE);aB=aD.indexOf("\u00a0",aE);aF=aF!==-1&&(aB===-1||aF<aB)?aF:aB}return aF}if(au.nodeType===3){az=aw(au,ay);if(az!==-1){return{container:au,offset:az}}at=au}ax=new t(au,c.getParent(au,H)||aa.getBody());while(av=ax[aA?"prev":"next"]()){if(av.nodeType===3){at=av;az=aw(av);if(az!==-1){return{container:av,offset:az}}}else{if(H(av)){break}}}if(at){if(aA){ay=0}else{ay=at.length}return{container:at,offset:ay}}}function af(au,at){var av,aw,ay,ax;if(au.nodeType==3&&au.nodeValue.length===0&&au[at]){au=au[at]}av=n(au);for(aw=0;aw<av.length;aw++){for(ay=0;ay<am.length;ay++){ax=am[ay];if("collapsed" in ax&&ax.collapsed!==ab.collapsed){continue}if(c.is(av[aw],ax.selector)){return av[aw]}}}return au}function ac(au,at,aw){var av;if(!am[0].wrapper){av=c.getParent(au,am[0].block)}if(!av){av=c.getParent(au.nodeType==3?au.parentNode:au,I)}if(av&&am[0].wrapper){av=n(av,"ul,ol").reverse()[0]||av}if(!av){av=au;while(av[at]&&!H(av[at])){av=av[at];if(g(av,"br")){break}}}return av||au}ad=aq(ad);ar=aq(ar);if(K(ad.parentNode)||K(ad)){ad=K(ad)?ad:ad.parentNode;ad=ad.nextSibling||ad;if(ad.nodeType==3){ai=0}}if(K(ar.parentNode)||K(ar)){ar=K(ar)?ar:ar.parentNode;ar=ar.previousSibling||ar;if(ar.nodeType==3){ak=ar.length}}if(am[0].inline){if(ab.collapsed){al=aj(ad,ai,true);if(al){ad=al.container;ai=al.offset}al=aj(ar,ak);if(al){ar=al.container;ak=al.offset}}ah=ag(ar,ak);if(ah.node){while(ah.node&&ah.offset===0&&ah.node.previousSibling){ah=ag(ah.node.previousSibling)}if(ah.node&&ah.offset>0&&ah.node.nodeType===3&&ah.node.nodeValue.charAt(ah.offset-1)===" "){if(ah.offset>1){ar=ah.node;ar.splitText(ah.offset-1)}}}}if(am[0].inline||am[0].block_expand){if(!am[0].inline||(ad.nodeType!=3||ai===0)){ad=ao(true)}if(!am[0].inline||(ar.nodeType!=3||ak===ar.nodeValue.length)){ar=ao()}}if(am[0].selector&&am[0].expand!==X&&!am[0].inline){ad=af(ad,"previousSibling");ar=af(ar,"nextSibling")}if(am[0].block||am[0].selector){ad=ac(ad,"previousSibling");ar=ac(ar,"nextSibling");if(am[0].block){if(!H(ad)){ad=ao(true)}if(!H(ar)){ar=ao()}}}if(ad.nodeType==1){ai=s(ad);ad=ad.parentNode}if(ar.nodeType==1){ak=s(ar)+1;ar=ar.parentNode}return{startContainer:ad,startOffset:ai,endContainer:ar,endOffset:ak}}function Z(ah,ag,ae,ab){var ad,ac,af;if(!h(ae,ah)){return X}if(ah.remove!="all"){T(ah.styles,function(aj,ai){aj=q(aj,ag);if(typeof(ai)==="number"){ai=aj;ab=0}if(!ab||g(O(ab,ai),aj)){c.setStyle(ae,ai,"")}af=1});if(af&&c.getAttrib(ae,"style")==""){ae.removeAttribute("style");ae.removeAttribute("data-mce-style")}T(ah.attributes,function(ak,ai){var aj;ak=q(ak,ag);if(typeof(ai)==="number"){ai=ak;ab=0}if(!ab||g(c.getAttrib(ab,ai),ak)){if(ai=="class"){ak=c.getAttrib(ae,ai);if(ak){aj="";T(ak.split(/\s+/),function(al){if(/mce\w+/.test(al)){aj+=(aj?" ":"")+al}});if(aj){c.setAttrib(ae,ai,aj);return}}}if(ai=="class"){ae.removeAttribute("className")}if(e.test(ai)){ae.removeAttribute("data-mce-"+ai)}ae.removeAttribute(ai)}});T(ah.classes,function(ai){ai=q(ai,ag);if(!ab||c.hasClass(ab,ai)){c.removeClass(ae,ai)}});ac=c.getAttribs(ae);for(ad=0;ad<ac.length;ad++){if(ac[ad].nodeName.indexOf("_")!==0){return X}}}if(ah.remove!="none"){o(ae,ah);return C}}function o(ad,ae){var ab=ad.parentNode,ac;function af(ah,ag,ai){ah=E(ah,ag,ai);return !ah||(ah.nodeName=="BR"||H(ah))}if(ae.block){if(!m){if(H(ad)&&!H(ab)){if(!af(ad,X)&&!af(ad.firstChild,C,1)){ad.insertBefore(c.create("br"),ad.firstChild)}if(!af(ad,C)&&!af(ad.lastChild,X,1)){ad.appendChild(c.create("br"))}}}else{if(ab==c.getRoot()){if(!ae.list_block||!g(ad,ae.list_block)){T(a.grep(ad.childNodes),function(ag){if(d(m,ag.nodeName.toLowerCase())){if(!ac){ac=S(ag,m)}else{ac.appendChild(ag)}}else{ac=0}})}}}}if(ae.selector&&ae.inline&&!g(ae.inline,ad)){return}c.remove(ad,1)}function E(ac,ab,ad){if(ac){ab=ab?"nextSibling":"previousSibling";for(ac=ad?ac:ac[ab];ac;ac=ac[ab]){if(ac.nodeType==1||!f(ac)){return ac}}}}function K(ab){return ab&&ab.nodeType==1&&ab.getAttribute("data-mce-type")=="bookmark"}function u(af,ae){var ab,ad,ac;function ah(ak,aj){if(ak.nodeName!=aj.nodeName){return X}function ai(am){var an={};T(c.getAttribs(am),function(ao){var ap=ao.nodeName.toLowerCase();if(ap.indexOf("_")!==0&&ap!=="style"){an[ap]=c.getAttrib(am,ap)}});return an}function al(ap,ao){var an,am;for(am in ap){if(ap.hasOwnProperty(am)){an=ao[am];if(an===D){return X}if(ap[am]!=an){return X}delete ao[am]}}for(am in ao){if(ao.hasOwnProperty(am)){return X}}return C}if(!al(ai(ak),ai(aj))){return X}if(!al(c.parseStyle(c.getAttrib(ak,"style")),c.parseStyle(c.getAttrib(aj,"style")))){return X}return C}function ag(aj,ai){for(ad=aj;ad;ad=ad[ai]){if(ad.nodeType==3&&ad.nodeValue.length!==0){return aj}if(ad.nodeType==1&&!K(ad)){return ad}}return aj}if(af&&ae){af=ag(af,"previousSibling");ae=ag(ae,"nextSibling");if(ah(af,ae)){for(ad=af.nextSibling;ad&&ad!=ae;){ac=ad;ad=ad.nextSibling;af.appendChild(ac)}c.remove(ae);T(a.grep(ae.childNodes),function(ai){af.appendChild(ai)});return af}}return ae}function I(ab){return/^(h[1-6]|p|div|pre|address|dl|dt|dd)$/.test(ab)}function M(ac,ag){var ab,af,ad,ae;ab=ac[ag?"startContainer":"endContainer"];af=ac[ag?"startOffset":"endOffset"];if(ab.nodeType==1){ad=ab.childNodes.length-1;if(!ag&&af){af--}ab=ab.childNodes[af>ad?ad:af]}if(ab.nodeType===3&&ag&&af>=ab.nodeValue.length){ab=new t(ab,aa.getBody()).next()||ab}if(ab.nodeType===3&&!ag&&af===0){ab=new t(ab,aa.getBody()).prev()||ab}return ab}function U(ak,ab,ai){var al="_mce_caret",ac=aa.settings.caret_debug;function ad(ap){var ao=c.create("span",{id:al,"data-mce-bogus":true,style:ac?"color:red":""});if(ap){ao.appendChild(aa.getDoc().createTextNode(G))}return ao}function aj(ap,ao){while(ap){if((ap.nodeType===3&&ap.nodeValue!==G)||ap.childNodes.length>1){return false}if(ao&&ap.nodeType===1){ao.push(ap)}ap=ap.firstChild}return true}function ag(ao){while(ao){if(ao.id===al){return ao}ao=ao.parentNode}}function af(ao){var ap;if(ao){ap=new t(ao,ao);for(ao=ap.current();ao;ao=ap.next()){if(ao.nodeType===3){return ao}}}}function ae(aq,ap){var ar,ao;if(!aq){aq=ag(r.getStart());if(!aq){while(aq=c.get(al)){ae(aq,false)}}}else{ao=r.getRng(true);if(aj(aq)){if(ap!==false){ao.setStartBefore(aq);ao.setEndBefore(aq)}c.remove(aq)}else{ar=af(aq);if(ar.nodeValue.charAt(0)===G){ar=ar.deleteData(0,1)}c.remove(aq,1)}r.setRng(ao)}}function ah(){var aq,ao,av,au,ar,ap,at;aq=r.getRng(true);au=aq.startOffset;ap=aq.startContainer;at=ap.nodeValue;ao=ag(r.getStart());if(ao){av=af(ao)}if(at&&au>0&&au<at.length&&/\w/.test(at.charAt(au))&&/\w/.test(at.charAt(au-1))){ar=r.getBookmark();aq.collapse(true);aq=p(aq,V(ab));aq=N.split(aq);Y(ab,ai,aq);r.moveToBookmark(ar)}else{if(!ao||av.nodeValue!==G){ao=ad(true);av=ao.firstChild;aq.insertNode(ao);au=1;Y(ab,ai,ao)}else{Y(ab,ai,ao)}r.setCursorLocation(av,au)}}function am(){var ao=r.getRng(true),ap,ar,av,au,aq,ay,ax=[],at,aw;ap=ao.startContainer;ar=ao.startOffset;aq=ap;if(ap.nodeType==3){if(ar!=ap.nodeValue.length||ap.nodeValue===G){au=true}aq=aq.parentNode}while(aq){if(y(aq,ab,ai)){ay=aq;break}if(aq.nextSibling){au=true}ax.push(aq);aq=aq.parentNode}if(!ay){return}if(au){av=r.getBookmark();ao.collapse(true);ao=p(ao,V(ab),true);ao=N.split(ao);B(ab,ai,ao);r.moveToBookmark(av)}else{aw=ad();aq=aw;for(at=ax.length-1;at>=0;at--){aq.appendChild(c.clone(ax[at],false));aq=aq.firstChild}aq.appendChild(c.doc.createTextNode(G));aq=aq.firstChild;c.insertAfter(aw,ay);r.setCursorLocation(aq,1)}}function an(){var ap,ao,aq;ao=ag(r.getStart());if(ao&&!c.isEmpty(ao)){a.walk(ao,function(ar){if(ar.nodeType==1&&ar.id!==al&&!c.isEmpty(ar)){c.setAttrib(ar,"data-mce-bogus",null)}},"childNodes")}}if(!self._hasCaretEvents){aa.onBeforeGetContent.addToTop(function(){var ao=[],ap;if(aj(ag(r.getStart()),ao)){ap=ao.length;while(ap--){c.setAttrib(ao[ap],"data-mce-bogus","1")}}});a.each("onMouseUp onKeyUp".split(" "),function(ao){aa[ao].addToTop(function(){ae();an()})});aa.onKeyDown.addToTop(function(ao,aq){var ap=aq.keyCode;if(ap==8||ap==37||ap==39){ae(ag(r.getStart()))}an()});r.onSetContent.add(an);self._hasCaretEvents=true}if(ak=="apply"){ah()}else{am()}}function R(ac){var ab=ac.startContainer,ai=ac.startOffset,ae,ah,ag,ad,af;if(ab.nodeType==3&&ai>=ab.nodeValue.length){ai=s(ab);ab=ab.parentNode;ae=true}if(ab.nodeType==1){ad=ab.childNodes;ab=ad[Math.min(ai,ad.length-1)];ah=new t(ab,c.getParent(ab,c.isBlock));if(ai>ad.length-1||ae){ah.next()}for(ag=ah.current();ag;ag=ah.next()){if(ag.nodeType==3&&!f(ag)){af=c.create("a",null,G);ag.parentNode.insertBefore(af,ag);ac.setStart(ag,0);r.setRng(ac);c.remove(af);return}}}}}})(tinymce);tinymce.onAddEditor.add(function(e,a){var d,h,g,c=a.settings;function b(j,i){e.each(i,function(l,k){if(l){g.setStyle(j,k,l)}});g.rename(j,"span")}function f(i,j){g=i.dom;if(c.convert_fonts_to_spans){e.each(g.select("font,u,strike",j.node),function(k){d[k.nodeName.toLowerCase()](a.dom,k)})}}if(c.inline_styles){h=e.explode(c.font_size_legacy_values);d={font:function(j,i){b(i,{backgroundColor:i.style.backgroundColor,color:i.color,fontFamily:i.face,fontSize:h[parseInt(i.size,10)-1]})},u:function(j,i){b(i,{textDecoration:"underline"})},strike:function(j,i){b(i,{textDecoration:"line-through"})}};a.onPreProcess.add(f);a.onSetContent.add(f);a.onInit.add(function(){a.selection.onSetContent.add(f)})}});(function(b){var a=b.dom.TreeWalker;b.EnterKey=function(f){var i=f.dom,e=f.selection,d=f.settings,h=f.undoManager,c=f.schema.getNonEmptyElements();function g(A){var v=e.getRng(true),G,j,z,u,p,M,B,o,k,n,t,J,x,C;function E(N){return N&&i.isBlock(N)&&!/^(TD|TH|CAPTION|FORM)$/.test(N.nodeName)&&!/^(fixed|absolute)/i.test(N.style.position)&&i.getContentEditable(N)!=="true"}function F(O){var N;if(b.isIE&&i.isBlock(O)){N=e.getRng();O.appendChild(i.create("span",null,"\u00a0"));e.select(O);O.lastChild.outerHTML="";e.setRng(N)}}function y(P){var O=P,Q=[],N;while(O=O.firstChild){if(i.isBlock(O)){return}if(O.nodeType==1&&!c[O.nodeName.toLowerCase()]){Q.push(O)}}N=Q.length;while(N--){O=Q[N];if(!O.hasChildNodes()||(O.firstChild==O.lastChild&&O.firstChild.nodeValue==="")){i.remove(O)}else{if(O.nodeName=="A"&&(O.innerText||O.textContent)===" "){i.remove(O)}}}}function m(O){var T,R,N,U,S,Q=O,P;N=i.createRng();if(O.hasChildNodes()){T=new a(O,O);while(R=T.current()){if(R.nodeType==3){N.setStart(R,0);N.setEnd(R,0);break}if(c[R.nodeName.toLowerCase()]){N.setStartBefore(R);N.setEndBefore(R);break}Q=R;R=T.next()}if(!R){N.setStart(Q,0);N.setEnd(Q,0)}}else{if(O.nodeName=="BR"){if(O.nextSibling&&i.isBlock(O.nextSibling)){if(!M||M<9){P=i.create("br");O.parentNode.insertBefore(P,O)}N.setStartBefore(O);N.setEndBefore(O)}else{N.setStartAfter(O);N.setEndAfter(O)}}else{N.setStart(O,0);N.setEnd(O,0)}}e.setRng(N);i.remove(P);S=i.getViewPort(f.getWin());U=i.getPos(O).y;if(U<S.y||U+25>S.y+S.h){f.getWin().scrollTo(0,U<S.y?U:U-S.h+25)}}function r(O){var P=z,R,Q,N;R=O||t=="TABLE"?i.create(O||x):p.cloneNode(false);N=R;if(d.keep_styles!==false){do{if(/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(P.nodeName)){if(P.id=="_mce_caret"){continue}Q=P.cloneNode(false);i.setAttrib(Q,"id","");if(R.hasChildNodes()){Q.appendChild(R.firstChild);R.appendChild(Q)}else{N=Q;R.appendChild(Q)}}}while(P=P.parentNode)}if(!b.isIE){N.innerHTML='<br data-mce-bogus="1">'}return R}function q(Q){var P,O,N;if(z.nodeType==3&&(Q?u>0:u<z.nodeValue.length)){return false}if(z.parentNode==p&&C&&!Q){return true}if(Q&&z.nodeType==1&&z==p.firstChild){return true}if(z.nodeName==="TABLE"||(z.previousSibling&&z.previousSibling.nodeName=="TABLE")){return(C&&!Q)||(!C&&Q)}P=new a(z,p);if(z.nodeType==3){if(Q&&u==0){P.prev()}else{if(!Q&&u==z.nodeValue.length){P.next()}}}while(O=P.current()){if(O.nodeType===1){if(!O.getAttribute("data-mce-bogus")){N=O.nodeName.toLowerCase();if(c[N]&&N!=="br"){return false}}}else{if(O.nodeType===3&&!/^[ \t\r\n]*$/.test(O.nodeValue)){return false}}if(Q){P.prev()}else{P.next()}}return true}function l(N,T){var U,S,P,R,Q,O=x||"P";S=i.getParent(N,i.isBlock);if(!S||!E(S)){S=S||j;if(!S.hasChildNodes()){U=i.create(O);S.appendChild(U);v.setStart(U,0);v.setEnd(U,0);return U}R=N;while(R.parentNode!=S){R=R.parentNode}while(R&&!i.isBlock(R)){P=R;R=R.previousSibling}if(P){U=i.create(O);P.parentNode.insertBefore(U,P);R=P;while(R&&!i.isBlock(R)){Q=R.nextSibling;U.appendChild(R);R=Q}v.setStart(N,T);v.setEnd(N,T)}}return N}function H(){function N(P){var O=n[P?"firstChild":"lastChild"];while(O){if(O.nodeType==1){break}O=O[P?"nextSibling":"previousSibling"]}return O===p}o=x?r(x):i.create("BR");if(N(true)&&N()){i.replace(o,n)}else{if(N(true)){n.parentNode.insertBefore(o,n)}else{if(N()){i.insertAfter(o,n);F(o)}else{G=v.cloneRange();G.setStartAfter(p);G.setEndAfter(n);k=G.extractContents();i.insertAfter(k,n);i.insertAfter(o,n)}}}i.remove(p);m(o);h.add()}function D(){var O=new a(z,p),N;while(N=O.current()){if(N.nodeName=="BR"){return true}N=O.next()}}function L(){var P,O,N;if(z&&z.nodeType==3&&u>=z.nodeValue.length){if(!b.isIE&&!D()){P=i.create("br");v.insertNode(P);v.setStartAfter(P);v.setEndAfter(P);O=true}}P=i.create("br");v.insertNode(P);if(b.isIE&&t=="PRE"&&(!M||M<8)){P.parentNode.insertBefore(i.doc.createTextNode("\r"),P)}N=i.create("span",{},"&nbsp;");P.parentNode.insertBefore(N,P);e.scrollIntoView(N);i.remove(N);if(!O){v.setStartAfter(P);v.setEndAfter(P)}else{v.setStartBefore(P);v.setEndBefore(P)}e.setRng(v);h.add()}function s(N){do{if(N.nodeType===3){N.nodeValue=N.nodeValue.replace(/^[\r\n]+/,"")}N=N.firstChild}while(N)}function K(P){var N=i.getRoot(),O,Q;O=P;while(O!==N&&i.getContentEditable(O)!=="false"){if(i.getContentEditable(O)==="true"){Q=O}O=O.parentNode}return O!==N?Q:N}function I(O){var N;if(!b.isIE){O.normalize();N=O.lastChild;if(!N||(/^(left|right)$/gi.test(i.getStyle(N,"float",true)))){i.add(O,"br")}}}if(!v.collapsed){f.execCommand("Delete");return}if(A.isDefaultPrevented()){return}z=v.startContainer;u=v.startOffset;x=(d.force_p_newlines?"p":"")||d.forced_root_block;x=x?x.toUpperCase():"";M=i.doc.documentMode;B=A.shiftKey;if(z.nodeType==1&&z.hasChildNodes()){C=u>z.childNodes.length-1;z=z.childNodes[Math.min(u,z.childNodes.length-1)]||z;if(C&&z.nodeType==3){u=z.nodeValue.length}else{u=0}}j=K(z);if(!j){return}h.beforeChange();if(!i.isBlock(j)&&j!=i.getRoot()){if(!x||B){L()}return}if((x&&!B)||(!x&&B)){z=l(z,u)}p=i.getParent(z,i.isBlock);n=p?i.getParent(p.parentNode,i.isBlock):null;t=p?p.nodeName.toUpperCase():"";J=n?n.nodeName.toUpperCase():"";if(J=="LI"&&!A.ctrlKey){p=n;t=J}if(t=="LI"){if(!x&&B){L();return}if(i.isEmpty(p)){if(/^(UL|OL|LI)$/.test(n.parentNode.nodeName)){return false}H();return}}if(t=="PRE"&&d.br_in_pre!==false){if(!B){L();return}}else{if((!x&&!B&&t!="LI")||(x&&B)){L();return}}x=x||"P";if(q()){if(/^(H[1-6]|PRE)$/.test(t)&&J!="HGROUP"){o=r(x)}else{o=r()}if(d.end_container_on_empty_block&&E(n)&&i.isEmpty(p)){o=i.split(n,p)}else{i.insertAfter(o,p)}m(o)}else{if(q(true)){o=p.parentNode.insertBefore(r(),p);F(o)}else{G=v.cloneRange();G.setEndAfter(p);k=G.extractContents();s(k);o=k.firstChild;i.insertAfter(k,p);y(o);I(p);m(o)}}i.setAttrib(o,"id","");h.add()}f.onKeyDown.add(function(k,j){if(j.keyCode==13){if(g(j)!==false){j.preventDefault()}}})}})(tinymce); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_popup.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_popup.js
index f859d24e6..bb8e58c88 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_popup.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_popup.js
@@ -2,4 +2,4 @@
// Uncomment and change this document.domain value if you are loading the script cross subdomains
// document.domain = 'moxiecode.com';
-var tinymce=null,tinyMCEPopup,tinyMCE;tinyMCEPopup={init:function(){var b=this,a,c;a=b.getWin();tinymce=a.tinymce;tinyMCE=a.tinyMCE;b.editor=tinymce.EditorManager.activeEditor;b.params=b.editor.windowManager.params;b.features=b.editor.windowManager.features;b.dom=b.editor.windowManager.createInstance("tinymce.dom.DOMUtils",document);if(b.features.popup_css!==false){b.dom.loadCSS(b.features.popup_css||b.editor.settings.popup_css)}b.listeners=[];b.onInit={add:function(e,d){b.listeners.push({func:e,scope:d})}};b.isWindow=!b.getWindowArg("mce_inline");b.id=b.getWindowArg("mce_window_id");b.editor.windowManager.onOpen.dispatch(b.editor.windowManager,window)},getWin:function(){return(!window.frameElement&&window.dialogArguments)||opener||parent||top},getWindowArg:function(c,b){var a=this.params[c];return tinymce.is(a)?a:b},getParam:function(b,a){return this.editor.getParam(b,a)},getLang:function(b,a){return this.editor.getLang(b,a)},execCommand:function(d,c,e,b){b=b||{};b.skip_focus=1;this.restoreSelection();return this.editor.execCommand(d,c,e,b)},resizeToInnerSize:function(){var a=this;setTimeout(function(){var b=a.dom.getViewPort(window);a.editor.windowManager.resizeBy(a.getWindowArg("mce_width")-b.w,a.getWindowArg("mce_height")-b.h,a.id||window)},10)},executeOnLoad:function(s){this.onInit.add(function(){eval(s)})},storeSelection:function(){this.editor.windowManager.bookmark=tinyMCEPopup.editor.selection.getBookmark(1)},restoreSelection:function(){var a=tinyMCEPopup;if(!a.isWindow&&tinymce.isIE){a.editor.selection.moveToBookmark(a.editor.windowManager.bookmark)}},requireLangPack:function(){var b=this,a=b.getWindowArg("plugin_url")||b.getWindowArg("theme_url");if(a&&b.editor.settings.language&&b.features.translate_i18n!==false&&b.editor.settings.language_load!==false){a+="/langs/"+b.editor.settings.language+"_dlg.js";if(!tinymce.ScriptLoader.isDone(a)){document.write('<script type="text/javascript" src="'+tinymce._addVer(a)+'"><\/script>');tinymce.ScriptLoader.markDone(a)}}},pickColor:function(b,a){this.execCommand("mceColorPicker",true,{color:document.getElementById(a).value,func:function(e){document.getElementById(a).value=e;try{document.getElementById(a).onchange()}catch(d){}}})},openBrowser:function(a,c,b){tinyMCEPopup.restoreSelection();this.editor.execCallback("file_browser_callback",a,document.getElementById(a).value,c,window)},confirm:function(b,a,c){this.editor.windowManager.confirm(b,a,c,window)},alert:function(b,a,c){this.editor.windowManager.alert(b,a,c,window)},close:function(){var a=this;function b(){a.editor.windowManager.close(window);tinymce=tinyMCE=a.editor=a.params=a.dom=a.dom.doc=null}if(tinymce.isOpera){a.getWin().setTimeout(b,0)}else{b()}},_restoreSelection:function(){var a=window.event.srcElement;if(a.nodeName=="INPUT"&&(a.type=="submit"||a.type=="button")){tinyMCEPopup.restoreSelection()}},_onDOMLoaded:function(){var b=tinyMCEPopup,d=document.title,e,c,a;if(b.domLoaded){return}b.domLoaded=1;if(b.features.translate_i18n!==false){c=document.body.innerHTML;if(tinymce.isIE){c=c.replace(/ (value|title|alt)=([^"][^\s>]+)/gi,' $1="$2"')}document.dir=b.editor.getParam("directionality","");if((a=b.editor.translate(c))&&a!=c){document.body.innerHTML=a}if((a=b.editor.translate(d))&&a!=d){document.title=d=a}}if(!b.editor.getParam("browser_preferred_colors",false)||!b.isWindow){b.dom.addClass(document.body,"forceColors")}document.body.style.display="";if(tinymce.isIE){document.attachEvent("onmouseup",tinyMCEPopup._restoreSelection);b.dom.add(b.dom.select("head")[0],"base",{target:"_self"})}b.restoreSelection();b.resizeToInnerSize();if(!b.isWindow){b.editor.windowManager.setTitle(window,d)}else{window.focus()}if(!tinymce.isIE&&!b.isWindow){tinymce.dom.Event._add(document,"focus",function(){b.editor.windowManager.focus(b.id)})}tinymce.each(b.dom.select("select"),function(f){f.onkeydown=tinyMCEPopup._accessHandler});tinymce.each(b.listeners,function(f){f.func.call(f.scope,b.editor)});if(b.getWindowArg("mce_auto_focus",true)){window.focus();tinymce.each(document.forms,function(g){tinymce.each(g.elements,function(f){if(b.dom.hasClass(f,"mceFocus")&&!f.disabled){f.focus();return false}})})}document.onkeyup=tinyMCEPopup._closeWinKeyHandler},_accessHandler:function(a){a=a||window.event;if(a.keyCode==13||a.keyCode==32){a=a.target||a.srcElement;if(a.onchange){a.onchange()}return tinymce.dom.Event.cancel(a)}},_closeWinKeyHandler:function(a){a=a||window.event;if(a.keyCode==27){tinyMCEPopup.close()}},_wait:function(){if(document.attachEvent){document.attachEvent("onreadystatechange",function(){if(document.readyState==="complete"){document.detachEvent("onreadystatechange",arguments.callee);tinyMCEPopup._onDOMLoaded()}});if(document.documentElement.doScroll&&window==window.top){(function(){if(tinyMCEPopup.domLoaded){return}try{document.documentElement.doScroll("left")}catch(a){setTimeout(arguments.callee,0);return}tinyMCEPopup._onDOMLoaded()})()}document.attachEvent("onload",tinyMCEPopup._onDOMLoaded)}else{if(document.addEventListener){window.addEventListener("DOMContentLoaded",tinyMCEPopup._onDOMLoaded,false);window.addEventListener("load",tinyMCEPopup._onDOMLoaded,false)}}}};tinyMCEPopup.init();tinyMCEPopup._wait(); \ No newline at end of file
+var tinymce=null,tinyMCEPopup,tinyMCE;tinyMCEPopup={init:function(){var b=this,a,c;a=b.getWin();tinymce=a.tinymce;tinyMCE=a.tinyMCE;b.editor=tinymce.EditorManager.activeEditor;b.params=b.editor.windowManager.params;b.features=b.editor.windowManager.features;b.dom=b.editor.windowManager.createInstance("tinymce.dom.DOMUtils",document,{ownEvents:true,proxy:tinyMCEPopup._eventProxy});b.dom.bind(window,"ready",b._onDOMLoaded,b);if(b.features.popup_css!==false){b.dom.loadCSS(b.features.popup_css||b.editor.settings.popup_css)}b.listeners=[];b.onInit={add:function(e,d){b.listeners.push({func:e,scope:d})}};b.isWindow=!b.getWindowArg("mce_inline");b.id=b.getWindowArg("mce_window_id");b.editor.windowManager.onOpen.dispatch(b.editor.windowManager,window)},getWin:function(){return(!window.frameElement&&window.dialogArguments)||opener||parent||top},getWindowArg:function(c,b){var a=this.params[c];return tinymce.is(a)?a:b},getParam:function(b,a){return this.editor.getParam(b,a)},getLang:function(b,a){return this.editor.getLang(b,a)},execCommand:function(d,c,e,b){b=b||{};b.skip_focus=1;this.restoreSelection();return this.editor.execCommand(d,c,e,b)},resizeToInnerSize:function(){var a=this;setTimeout(function(){var b=a.dom.getViewPort(window);a.editor.windowManager.resizeBy(a.getWindowArg("mce_width")-b.w,a.getWindowArg("mce_height")-b.h,a.id||window)},10)},executeOnLoad:function(s){this.onInit.add(function(){eval(s)})},storeSelection:function(){this.editor.windowManager.bookmark=tinyMCEPopup.editor.selection.getBookmark(1)},restoreSelection:function(){var a=tinyMCEPopup;if(!a.isWindow&&tinymce.isIE){a.editor.selection.moveToBookmark(a.editor.windowManager.bookmark)}},requireLangPack:function(){var b=this,a=b.getWindowArg("plugin_url")||b.getWindowArg("theme_url");if(a&&b.editor.settings.language&&b.features.translate_i18n!==false&&b.editor.settings.language_load!==false){a+="/langs/"+b.editor.settings.language+"_dlg.js";if(!tinymce.ScriptLoader.isDone(a)){document.write('<script type="text/javascript" src="'+tinymce._addVer(a)+'"><\/script>');tinymce.ScriptLoader.markDone(a)}}},pickColor:function(b,a){this.execCommand("mceColorPicker",true,{color:document.getElementById(a).value,func:function(e){document.getElementById(a).value=e;try{document.getElementById(a).onchange()}catch(d){}}})},openBrowser:function(a,c,b){tinyMCEPopup.restoreSelection();this.editor.execCallback("file_browser_callback",a,document.getElementById(a).value,c,window)},confirm:function(b,a,c){this.editor.windowManager.confirm(b,a,c,window)},alert:function(b,a,c){this.editor.windowManager.alert(b,a,c,window)},close:function(){var a=this;function b(){a.editor.windowManager.close(window);tinymce=tinyMCE=a.editor=a.params=a.dom=a.dom.doc=null}if(tinymce.isOpera){a.getWin().setTimeout(b,0)}else{b()}},_restoreSelection:function(){var a=window.event.srcElement;if(a.nodeName=="INPUT"&&(a.type=="submit"||a.type=="button")){tinyMCEPopup.restoreSelection()}},_onDOMLoaded:function(){var b=tinyMCEPopup,d=document.title,e,c,a;if(b.features.translate_i18n!==false){c=document.body.innerHTML;if(tinymce.isIE){c=c.replace(/ (value|title|alt)=([^"][^\s>]+)/gi,' $1="$2"')}document.dir=b.editor.getParam("directionality","");if((a=b.editor.translate(c))&&a!=c){document.body.innerHTML=a}if((a=b.editor.translate(d))&&a!=d){document.title=d=a}}if(!b.editor.getParam("browser_preferred_colors",false)||!b.isWindow){b.dom.addClass(document.body,"forceColors")}document.body.style.display="";if(tinymce.isIE){document.attachEvent("onmouseup",tinyMCEPopup._restoreSelection);b.dom.add(b.dom.select("head")[0],"base",{target:"_self"})}b.restoreSelection();b.resizeToInnerSize();if(!b.isWindow){b.editor.windowManager.setTitle(window,d)}else{window.focus()}if(!tinymce.isIE&&!b.isWindow){b.dom.bind(document,"focus",function(){b.editor.windowManager.focus(b.id)})}tinymce.each(b.dom.select("select"),function(f){f.onkeydown=tinyMCEPopup._accessHandler});tinymce.each(b.listeners,function(f){f.func.call(f.scope,b.editor)});if(b.getWindowArg("mce_auto_focus",true)){window.focus();tinymce.each(document.forms,function(g){tinymce.each(g.elements,function(f){if(b.dom.hasClass(f,"mceFocus")&&!f.disabled){f.focus();return false}})})}document.onkeyup=tinyMCEPopup._closeWinKeyHandler},_accessHandler:function(a){a=a||window.event;if(a.keyCode==13||a.keyCode==32){var b=a.target||a.srcElement;if(b.onchange){b.onchange()}return tinymce.dom.Event.cancel(a)}},_closeWinKeyHandler:function(a){a=a||window.event;if(a.keyCode==27){tinyMCEPopup.close()}},_eventProxy:function(a){return function(b){tinyMCEPopup.dom.events.callNativeHandler(a,b)}}};tinyMCEPopup.init(); \ No newline at end of file
diff --git a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_src.js b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_src.js
index 0866d617b..3bbe9ce95 100644
--- a/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_src.js
+++ b/mod/tinymce/vendor/tinymce/jscripts/tiny_mce/tiny_mce_src.js
@@ -1,13 +1,14 @@
+// FILE IS GENERATED BY COMBINING THE SOURCES IN THE "classes" DIRECTORY SO DON'T MODIFY THIS FILE DIRECTLY
(function(win) {
var whiteSpaceRe = /^\s*|\s*$/g,
- undefined, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';
+ undef, isRegExpBroken = 'B'.replace(/A(.)|B/, '$1') === '$1';
var tinymce = {
majorVersion : '3',
- minorVersion : '4.7',
+ minorVersion : '5.8',
- releaseDate : '2011-11-03',
+ releaseDate : '2012-11-20',
_init : function() {
var t = this, d = document, na = navigator, ua = na.userAgent, i, nl, n, base, p, v;
@@ -50,7 +51,8 @@
// If base element found, add that infront of baseURL
nl = d.getElementsByTagName('base');
for (i=0; i<nl.length; i++) {
- if (v = nl[i].href) {
+ v = nl[i].href;
+ if (v) {
// Host only value like http://site.com or http://site.com:8008
if (/^https?:\/\/[^\/]+$/.test(v))
v += '/';
@@ -103,14 +105,18 @@
is : function(o, t) {
if (!t)
- return o !== undefined;
+ return o !== undef;
- if (t == 'array' && (o.hasOwnProperty && o instanceof Array))
+ if (t == 'array' && tinymce.isArray(o))
return true;
return typeof(o) == t;
},
+ isArray: Array.isArray || function(obj) {
+ return Object.prototype.toString.call(obj) === "[object Array]";
+ },
+
makeMap : function(items, delim, map) {
var i;
@@ -137,7 +143,7 @@
s = s || o;
- if (o.length !== undefined) {
+ if (o.length !== undef) {
// Indexed arrays, needed for Safari
for (n=0, l = o.length; n < l; n++) {
if (cb.call(s, o[n], n, o) === false)
@@ -191,19 +197,23 @@
return -1;
},
- extend : function(o, e) {
- var i, l, a = arguments;
+ extend : function(obj, ext) {
+ var i, l, name, args = arguments, value;
- for (i = 1, l = a.length; i < l; i++) {
- e = a[i];
+ for (i = 1, l = args.length; i < l; i++) {
+ ext = args[i];
+ for (name in ext) {
+ if (ext.hasOwnProperty(name)) {
+ value = ext[name];
- tinymce.each(e, function(v, n) {
- if (v !== undefined)
- o[n] = v;
- });
+ if (value !== undef) {
+ obj[name] = value;
+ }
+ }
+ }
}
- return o;
+ return obj;
},
@@ -346,69 +356,69 @@
},
addUnload : function(f, s) {
- var t = this;
+ var t = this, unload;
- f = {func : f, scope : s || this};
+ unload = function() {
+ var li = t.unloads, o, n;
- if (!t.unloads) {
- function unload() {
- var li = t.unloads, o, n;
+ if (li) {
+ // Call unload handlers
+ for (n in li) {
+ o = li[n];
- if (li) {
- // Call unload handlers
- for (n in li) {
- o = li[n];
+ if (o && o.func)
+ o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
+ }
- if (o && o.func)
- o.func.call(o.scope, 1); // Send in one arg to distinct unload and user destroy
- }
+ // Detach unload function
+ if (win.detachEvent) {
+ win.detachEvent('onbeforeunload', fakeUnload);
+ win.detachEvent('onunload', unload);
+ } else if (win.removeEventListener)
+ win.removeEventListener('unload', unload, false);
- // Detach unload function
- if (win.detachEvent) {
- win.detachEvent('onbeforeunload', fakeUnload);
- win.detachEvent('onunload', unload);
- } else if (win.removeEventListener)
- win.removeEventListener('unload', unload, false);
+ // Destroy references
+ t.unloads = o = li = w = unload = 0;
- // Destroy references
- t.unloads = o = li = w = unload = 0;
+ // Run garbarge collector on IE
+ if (win.CollectGarbage)
+ CollectGarbage();
+ }
+ };
- // Run garbarge collector on IE
- if (win.CollectGarbage)
- CollectGarbage();
- }
- };
+ function fakeUnload() {
+ var d = document;
- function fakeUnload() {
- var d = document;
+ function stop() {
+ // Prevent memory leak
+ d.detachEvent('onstop', stop);
- // Is there things still loading, then do some magic
- if (d.readyState == 'interactive') {
- function stop() {
- // Prevent memory leak
- d.detachEvent('onstop', stop);
+ // Call unload handler
+ if (unload)
+ unload();
- // Call unload handler
- if (unload)
- unload();
+ d = 0;
+ };
- d = 0;
- };
+ // Is there things still loading, then do some magic
+ if (d.readyState == 'interactive') {
+ // Fire unload when the currently loading page is stopped
+ if (d)
+ d.attachEvent('onstop', stop);
- // Fire unload when the currently loading page is stopped
+ // Remove onstop listener after a while to prevent the unload function
+ // to execute if the user presses cancel in an onbeforeunload
+ // confirm dialog and then presses the browser stop button
+ win.setTimeout(function() {
if (d)
- d.attachEvent('onstop', stop);
-
- // Remove onstop listener after a while to prevent the unload function
- // to execute if the user presses cancel in an onbeforeunload
- // confirm dialog and then presses the browser stop button
- win.setTimeout(function() {
- if (d)
- d.detachEvent('onstop', stop);
- }, 0);
- }
- };
+ d.detachEvent('onstop', stop);
+ }, 0);
+ }
+ };
+
+ f = {func : f, scope : s || this};
+ if (!t.unloads) {
// Attach unload handler
if (win.attachEvent) {
win.attachEvent('onunload', unload);
@@ -439,7 +449,11 @@
},
explode : function(s, d) {
- return s ? tinymce.map(s.split(d || ','), tinymce.trim) : s;
+ if (!s || tinymce.is(s, 'array')) {
+ return s;
+ }
+
+ return tinymce.map(s.split(d || ','), tinymce.trim);
},
_addVer : function(u) {
@@ -465,7 +479,7 @@
var val = replace, args = arguments, i;
for (i = 0; i < args.length - 2; i++) {
- if (args[i] === undefined) {
+ if (args[i] === undef) {
val = val.replace(new RegExp('\\$' + i, 'g'), '');
} else {
val = val.replace(new RegExp('\\$' + i, 'g'), args[i]);
@@ -496,52 +510,64 @@
tinymce.create('tinymce.util.Dispatcher', {
scope : null,
listeners : null,
+ inDispatch: false,
- Dispatcher : function(s) {
- this.scope = s || this;
+ Dispatcher : function(scope) {
+ this.scope = scope || this;
this.listeners = [];
},
- add : function(cb, s) {
- this.listeners.push({cb : cb, scope : s || this.scope});
+ add : function(callback, scope) {
+ this.listeners.push({cb : callback, scope : scope || this.scope});
- return cb;
+ return callback;
},
- addToTop : function(cb, s) {
- this.listeners.unshift({cb : cb, scope : s || this.scope});
+ addToTop : function(callback, scope) {
+ var self = this, listener = {cb : callback, scope : scope || self.scope};
+
+ // Create new listeners if addToTop is executed in a dispatch loop
+ if (self.inDispatch) {
+ self.listeners = [listener].concat(self.listeners);
+ } else {
+ self.listeners.unshift(listener);
+ }
- return cb;
+ return callback;
},
- remove : function(cb) {
- var l = this.listeners, o = null;
+ remove : function(callback) {
+ var listeners = this.listeners, output = null;
- tinymce.each(l, function(c, i) {
- if (cb == c.cb) {
- o = cb;
- l.splice(i, 1);
+ tinymce.each(listeners, function(listener, i) {
+ if (callback == listener.cb) {
+ output = listener;
+ listeners.splice(i, 1);
return false;
}
});
- return o;
+ return output;
},
dispatch : function() {
- var s, a = arguments, i, li = this.listeners, c;
+ var self = this, returnValue, args = arguments, i, listeners = self.listeners, listener;
+ self.inDispatch = true;
+
// Needs to be a real loop since the listener count might change while looping
// And this is also more efficient
- for (i = 0; i<li.length; i++) {
- c = li[i];
- s = c.cb.apply(c.scope, a);
+ for (i = 0; i < listeners.length; i++) {
+ listener = listeners[i];
+ returnValue = listener.cb.apply(listener.scope, args.length > 0 ? args : [listener.scope]);
- if (s === false)
+ if (returnValue === false)
break;
}
- return s;
+ self.inDispatch = false;
+
+ return returnValue;
}
});
@@ -571,14 +597,14 @@ tinymce.create('tinymce.util.Dispatcher', {
u = (s.base_uri ? s.base_uri.protocol || 'http' : 'http') + '://mce_host' + u;
// Relative path http:// or protocol relative //path
- if (!/^[\w-]*:?\/\//.test(u)) {
+ if (!/^[\w\-]*:?\/\//.test(u)) {
base_url = s.base_uri ? s.base_uri.path : new tinymce.util.URI(location.href).directory;
u = ((s.base_uri && s.base_uri.protocol) || 'http') + '://mce_host' + t.toAbsPath(base_url, u);
}
// Parse URL (Credits goes to Steave, http://blog.stevenlevithan.com/archives/parseuri)
u = u.replace(/@@/g, '(mce_at)'); // Zope 3 workaround, they use @@something
- u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@]*):?([^:@]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
+ u = /^(?:(?![^:@]+:[^:@\/]*@)([^:\/?#.]+):)?(?:\/\/)?((?:(([^:@\/]*):?([^:@\/]*))?@)?([^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/.exec(u);
each(["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"], function(v, i) {
var s = u[i];
@@ -589,17 +615,18 @@ tinymce.create('tinymce.util.Dispatcher', {
t[v] = s;
});
- if (b = s.base_uri) {
+ b = s.base_uri;
+ if (b) {
if (!t.protocol)
t.protocol = b.protocol;
if (!t.userInfo)
t.userInfo = b.userInfo;
- if (!t.port && t.host == 'mce_host')
+ if (!t.port && t.host === 'mce_host')
t.port = b.port;
- if (!t.host || t.host == 'mce_host')
+ if (!t.host || t.host === 'mce_host')
t.host = b.host;
t.source = '';
@@ -635,6 +662,12 @@ tinymce.create('tinymce.util.Dispatcher', {
if ((u.host != 'mce_host' && t.host != u.host && u.host) || t.port != u.port || t.protocol != u.protocol)
return u.getURI();
+ var tu = t.getURI(), uu = u.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;
+
o = t.toRelPath(t.path, u.path);
// Add query
@@ -649,7 +682,7 @@ tinymce.create('tinymce.util.Dispatcher', {
},
toAbsolute : function(u, nh) {
- var u = new tinymce.util.URI(u, {base_uri : this});
+ u = new tinymce.util.URI(u, {base_uri : this});
return u.getURI(this.host == u.host && this.protocol == u.protocol ? nh : 0);
},
@@ -680,7 +713,7 @@ tinymce.create('tinymce.util.Dispatcher', {
}
}
- if (bp == 1)
+ if (bp === 1)
return path;
for (i = 0, l = base.length - (bp - 1); i < l; i++)
@@ -715,11 +748,11 @@ tinymce.create('tinymce.util.Dispatcher', {
// Merge relURLParts chunks
for (i = path.length - 1, o = []; i >= 0; i--) {
// Ignore empty or .
- if (path[i].length == 0 || path[i] == ".")
+ if (path[i].length === 0 || path[i] === ".")
continue;
// Is parent
- if (path[i] == '..') {
+ if (path[i] === '..') {
nb++;
continue;
}
@@ -830,7 +863,7 @@ tinymce.create('tinymce.util.Dispatcher', {
if (b == -1) {
b = c.indexOf(p);
- if (b != 0)
+ if (b !== 0)
return null;
} else
b += 2;
@@ -851,19 +884,19 @@ tinymce.create('tinymce.util.Dispatcher', {
((s) ? "; secure" : "");
},
- remove : function(n, p) {
- var d = new Date();
+ remove : function(name, path, domain) {
+ var date = new Date();
- d.setTime(d.getTime() - 1000);
+ date.setTime(date.getTime() - 1000);
- this.set(n, '', d, p, d);
+ this.set(name, '', date, path, domain);
}
});
})();
(function() {
function serialize(o, quote) {
- var i, v, t;
+ var i, v, t, name;
quote = quote || '"';
@@ -892,7 +925,7 @@ tinymce.create('tinymce.util.Dispatcher', {
}
if (t == 'object') {
- if (o.hasOwnProperty && o instanceof Array) {
+ if (o.hasOwnProperty && Object.prototype.toString.call(o) === '[object Array]') {
for (i=0, v = '['; i<o.length; i++)
v += (i > 0 ? ',' : '') + serialize(o[i], quote);
@@ -901,9 +934,9 @@ tinymce.create('tinymce.util.Dispatcher', {
v = '{';
- for (i in o) {
- if (o.hasOwnProperty(i)) {
- v += typeof o[i] != 'function' ? (v.length > 1 ? ',' + quote : quote) + i + quote +':' + serialize(o[i], quote) : '';
+ for (name in o) {
+ if (o.hasOwnProperty(name)) {
+ v += typeof o[name] != 'function' ? (v.length > 1 ? ',' + quote : quote) + name + quote +':' + serialize(o[name], quote) : '';
}
}
@@ -931,6 +964,18 @@ tinymce.create('static tinymce.util.XHR', {
send : function(o) {
var x, t, w = window, c = 0;
+ function ready() {
+ if (!o.async || x.readyState == 4 || c++ > 10000) {
+ if (o.success && c < 10000 && x.status == 200)
+ o.success.call(o.success_scope, '' + x.responseText, x, o);
+ else if (o.error)
+ o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
+
+ x = null;
+ } else
+ w.setTimeout(ready, 10);
+ };
+
// Default settings
o.scope = o.scope || this;
o.success_scope = o.success_scope || o.scope;
@@ -964,18 +1009,6 @@ tinymce.create('static tinymce.util.XHR', {
x.send(o.data);
- function ready() {
- if (!o.async || x.readyState == 4 || c++ > 10000) {
- if (o.success && c < 10000 && x.status == 200)
- o.success.call(o.success_scope, '' + x.responseText, x, o);
- else if (o.error)
- o.error.call(o.error_scope, c > 10000 ? 'TIMED_OUT' : 'GENERAL', x, o);
-
- x = null;
- } else
- w.setTimeout(ready, 10);
- };
-
// Syncronous request
if (!o.async)
return ready();
@@ -1042,99 +1075,198 @@ tinymce.create('static tinymce.util.XHR', {
}());
(function(tinymce){
tinymce.VK = {
- DELETE: 46,
BACKSPACE: 8,
+ DELETE: 46,
+ DOWN: 40,
ENTER: 13,
+ LEFT: 37,
+ RIGHT: 39,
+ SPACEBAR: 32,
TAB: 9,
- SPACEBAR: 32,
UP: 38,
- DOWN: 40
- }
+
+ modifierPressed: function (e) {
+ return e.shiftKey || e.ctrlKey || e.altKey;
+ },
+
+ metaKeyPressed: function(e) {
+ // Check if ctrl or meta key is pressed also check if alt is false for Polish users
+ return tinymce.isMac ? e.metaKey : e.ctrlKey && !e.altKey;
+ }
+ };
})(tinymce);
-(function(tinymce) {
- var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE;
+tinymce.util.Quirks = function(editor) {
+ var VK = tinymce.VK, BACKSPACE = VK.BACKSPACE, DELETE = VK.DELETE, dom = editor.dom, selection = editor.selection,
+ settings = editor.settings, parser = editor.parser, serializer = editor.serializer, each = tinymce.each;
- function cleanupStylesWhenDeleting(ed) {
- var dom = ed.dom, selection = ed.selection;
+ function setEditorCommandState(cmd, state) {
+ try {
+ editor.getDoc().execCommand(cmd, false, state);
+ } catch (ex) {
+ // Ignore
+ }
+ }
- ed.onKeyDown.add(function(ed, e) {
- var rng, blockElm, node, clonedSpan, isDelete;
+ function getDocumentMode() {
+ var documentMode = editor.getDoc().documentMode;
- isDelete = e.keyCode == DELETE;
- if (isDelete || e.keyCode == BACKSPACE) {
- e.preventDefault();
- rng = selection.getRng();
+ return documentMode ? documentMode : 6;
+ };
+
+ function isDefaultPrevented(e) {
+ return e.isDefaultPrevented();
+ };
- // Find root block
- blockElm = dom.getParent(rng.startContainer, dom.isBlock);
+ function cleanupStylesWhenDeleting() {
+ function removeMergedFormatSpans(isDelete) {
+ var rng, blockElm, node, clonedSpan;
- // On delete clone the root span of the next block element
- if (isDelete)
- blockElm = dom.getNext(blockElm, dom.isBlock);
+ rng = selection.getRng();
- // Locate root span element and clone it since it would otherwise get merged by the "apple-style-span" on delete/backspace
- if (blockElm) {
- node = blockElm.firstChild;
+ // Find root block
+ blockElm = dom.getParent(rng.startContainer, dom.isBlock);
- // Ignore empty text nodes
- while (node && node.nodeType == 3 && node.nodeValue.length == 0)
- node = node.nextSibling;
+ // On delete clone the root span of the next block element
+ if (isDelete) {
+ blockElm = dom.getNext(blockElm, dom.isBlock);
+ }
- if (node && node.nodeName === 'SPAN') {
- clonedSpan = node.cloneNode(false);
- }
+ // Locate root span element and clone it since it would otherwise get merged by the "apple-style-span" on delete/backspace
+ if (blockElm) {
+ node = blockElm.firstChild;
+
+ // Ignore empty text nodes
+ while (node && node.nodeType == 3 && node.nodeValue.length === 0) {
+ node = node.nextSibling;
}
- // Do the backspace/delete actiopn
- ed.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
+ if (node && node.nodeName === 'SPAN') {
+ clonedSpan = node.cloneNode(false);
+ }
+ }
- // Find all odd apple-style-spans
- blockElm = dom.getParent(rng.startContainer, dom.isBlock);
- tinymce.each(dom.select('span.Apple-style-span,font.Apple-style-span', blockElm), function(span) {
- var bm = selection.getBookmark();
+ each(dom.select('span', blockElm), function(span) {
+ span.setAttribute('data-mce-mark', '1');
+ });
- if (clonedSpan) {
- dom.replace(clonedSpan.cloneNode(false), span, true);
- } else {
- dom.remove(span, true);
- }
+ // Do the backspace/delete action
+ editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
- // Restore the selection
- selection.moveToBookmark(bm);
- });
+ // Find all odd apple-style-spans
+ blockElm = dom.getParent(rng.startContainer, dom.isBlock);
+ each(dom.select('span', blockElm), function(span) {
+ var bm = selection.getBookmark();
+
+ if (clonedSpan) {
+ dom.replace(clonedSpan.cloneNode(false), span, true);
+ } else if (!span.getAttribute('data-mce-mark')) {
+ dom.remove(span, true);
+ } else {
+ span.removeAttribute('data-mce-mark');
+ }
+
+ // Restore the selection
+ selection.moveToBookmark(bm);
+ });
+ }
+
+ editor.onKeyDown.add(function(editor, e) {
+ var isDelete;
+
+ isDelete = e.keyCode == DELETE;
+ if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
+ e.preventDefault();
+ removeMergedFormatSpans(isDelete);
}
});
+
+ editor.addCommand('Delete', function() {removeMergedFormatSpans();});
};
+
+ 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 emptyEditorWhenDeleting(ed) {
- ed.onKeyUp.add(function(ed, e) {
- var keyCode = e.keyCode;
+ function allContentsSelected(rng) {
+ var selection = serializeRng(rng);
+
+ var allRng = dom.createRng();
+ allRng.selectNode(editor.getBody());
+
+ var allSelection = serializeRng(allRng);
+ return selection === allSelection;
+ }
+
+ editor.onKeyDown.add(function(editor, e) {
+ var keyCode = e.keyCode, isCollapsed;
+
+ // Empty the editor if it's needed for example backspace at <p><b>|</b></p>
+ if (!isDefaultPrevented(e) && (keyCode == DELETE || keyCode == BACKSPACE)) {
+ isCollapsed = editor.selection.isCollapsed();
+
+ // Selection is collapsed but the editor isn't empty
+ if (isCollapsed && !dom.isEmpty(editor.getBody())) {
+ return;
+ }
+
+ // IE deletes all contents correctly when everything is selected
+ if (tinymce.isIE && !isCollapsed) {
+ return;
+ }
- if (keyCode == DELETE || keyCode == BACKSPACE) {
- if (ed.dom.isEmpty(ed.getBody())) {
- ed.setContent('', {format : 'raw'});
- ed.nodeChanged();
+ // Selection isn't collapsed but not all the contents is selected
+ if (!isCollapsed && !allContentsSelected(editor.selection.getRng())) {
return;
}
+
+ // Manually empty the editor
+ editor.setContent('');
+ editor.selection.setCursorLocation(editor.getBody(), 0);
+ editor.nodeChanged();
}
});
};
- function inputMethodFocus(ed) {
- ed.dom.bind(ed.getDoc(), 'focusin', function() {
- ed.selection.setRng(ed.selection.getRng());
+ function selectAll() {
+ editor.onKeyDown.add(function(editor, e) {
+ if (!isDefaultPrevented(e) && e.keyCode == 65 && VK.metaKeyPressed(e)) {
+ e.preventDefault();
+ editor.execCommand('SelectAll');
+ }
});
};
- function removeHrOnBackspace(ed) {
- ed.onKeyDown.add(function(ed, e) {
- if (e.keyCode === BACKSPACE) {
- if (ed.selection.isCollapsed() && ed.selection.getRng(true).startOffset === 0) {
- var node = ed.selection.getNode();
+ function inputMethodFocus() {
+ if (!editor.settings.content_editable) {
+ // Case 1 IME doesn't initialize if you focus the document
+ dom.bind(editor.getDoc(), 'focusin', function(e) {
+ selection.setRng(selection.getRng());
+ });
+
+ // Case 2 IME doesn't initialize if you click the documentElement it also doesn't properly fire the focusin event
+ dom.bind(editor.getDoc(), 'mousedown', function(e) {
+ if (e.target == editor.getDoc().documentElement) {
+ editor.getWin().focus();
+ selection.setRng(selection.getRng());
+ }
+ });
+ }
+ };
+
+ function removeHrOnBackspace() {
+ editor.onKeyDown.add(function(editor, e) {
+ if (!isDefaultPrevented(e) && e.keyCode === BACKSPACE) {
+ if (selection.isCollapsed() && selection.getRng(true).startOffset === 0) {
+ var node = selection.getNode();
var previousSibling = node.previousSibling;
+
if (previousSibling && previousSibling.nodeName && previousSibling.nodeName.toLowerCase() === "hr") {
- ed.dom.remove(previousSibling);
+ dom.remove(previousSibling);
tinymce.dom.Event.cancel(e);
}
}
@@ -1142,13 +1274,13 @@ tinymce.create('static tinymce.util.XHR', {
})
}
- function focusBody(ed) {
+ 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 (!Range.prototype.getClientRects) { // Detect getClientRects got introduced in FF 4
- ed.onMouseDown.add(function(ed, e) {
- if (e.target.nodeName === "HTML") {
- var body = ed.getBody();
+ editor.onMouseDown.add(function(editor, e) {
+ if (!isDefaultPrevented(e) && e.target.nodeName === "HTML") {
+ var body = editor.getBody();
// Blur the body it's focused but not correctly focused
body.blur();
@@ -1162,79 +1294,682 @@ tinymce.create('static tinymce.util.XHR', {
}
};
- function selectControlElements(ed) {
- ed.onClick.add(function(ed, e) {
+ function selectControlElements() {
+ editor.onClick.add(function(editor, e) {
e = e.target;
// Workaround for bug, http://bugs.webkit.org/show_bug.cgi?id=12250
// WebKit can't even do simple things like selecting an image
// Needs tobe the setBaseAndExtend or it will fail to select floated images
- if (/^(IMG|HR)$/.test(e.nodeName))
- ed.selection.getSel().setBaseAndExtent(e, 0, e, 1);
+ if (/^(IMG|HR)$/.test(e.nodeName)) {
+ selection.getSel().setBaseAndExtent(e, 0, e, 1);
+ }
- if (e.nodeName == 'A' && ed.dom.hasClass(e, 'mceItemAnchor'))
- ed.selection.select(e);
+ if (e.nodeName == 'A' && dom.hasClass(e, 'mceItemAnchor')) {
+ selection.select(e);
+ }
- ed.nodeChanged();
+ editor.nodeChanged();
});
};
- function selectionChangeNodeChanged(ed) {
+ function removeStylesWhenDeletingAccrossBlockElements() {
+ 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);
+ }
+
+ function blockEvent(editor, e) {
+ e.preventDefault();
+ return false;
+ }
+
+ editor.onKeyPress.add(function(editor, 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();
+ editor.onKeyUp.addToTop(blockEvent);
+
+ setTimeout(function() {
+ applyAttributes();
+ editor.onKeyUp.remove(blockEvent);
+ }, 0);
+ }
+ });
+ }
+
+ function selectionChangeNodeChanged() {
var lastRng, selectionTimer;
- ed.dom.bind(ed.getDoc(), 'selectionchange', function() {
+ dom.bind(editor.getDoc(), 'selectionchange', function() {
if (selectionTimer) {
clearTimeout(selectionTimer);
selectionTimer = 0;
}
selectionTimer = window.setTimeout(function() {
- var rng = ed.selection.getRng();
+ var rng = selection.getRng();
// Compare the ranges to see if it was a real change or not
if (!lastRng || !tinymce.dom.RangeUtils.compareRanges(rng, lastRng)) {
- ed.nodeChanged();
+ editor.nodeChanged();
lastRng = rng;
}
}, 50);
});
}
- function ensureBodyHasRoleApplication(ed) {
+ function ensureBodyHasRoleApplication() {
document.body.setAttribute("role", "application");
}
- tinymce.create('tinymce.util.Quirks', {
- Quirks: function(ed) {
- // WebKit
- if (tinymce.isWebKit) {
- cleanupStylesWhenDeleting(ed);
- emptyEditorWhenDeleting(ed);
- inputMethodFocus(ed);
- selectControlElements(ed);
+ function disableBackspaceIntoATable() {
+ editor.onKeyDown.add(function(editor, 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") {
+ return tinymce.dom.Event.cancel(e);
+ }
+ }
+ }
+ })
+ }
+
+ 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, name) {
+ var i = nodes.length, brNodes, j, brElm, sibling;
- // iOS
- if (tinymce.isIDevice) {
- selectionChangeNodeChanged(ed);
+ 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 tinymce.html.Node('#text', 3), brElm, true).value = '\n';
+ }
}
}
+ });
- // IE
- if (tinymce.isIE) {
- removeHrOnBackspace(ed);
- emptyEditorWhenDeleting(ed);
- ensureBodyHasRoleApplication(ed);
+ // 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, name) {
+ 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$/, '');
+ }
+ }
}
+ });
+ }
- // Gecko
- if (tinymce.isGecko) {
- removeHrOnBackspace(ed);
- focusBody(ed);
+ function removePreSerializedStylesWhenSelectingControls() {
+ dom.bind(editor.getBody(), 'mouseup', function(e) {
+ 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', '');
+ }
+ }
+ });
+ }
+
+ function keepInlineElementOnDeleteBackspace() {
+ editor.onKeyDown.add(function(editor, e) {
+ var isDelete, rng, container, offset, brElm, sibling, collapsed;
+
+ isDelete = e.keyCode == DELETE;
+ if (!isDefaultPrevented(e) && (isDelete || e.keyCode == BACKSPACE) && !VK.modifierPressed(e)) {
+ rng = selection.getRng();
+ container = rng.startContainer;
+ offset = rng.startOffset;
+ collapsed = rng.collapsed;
+
+ // Override delete if the start container is a text node and is at the beginning of text or
+ // just before/after the last character to be deleted in collapsed mode
+ if (container.nodeType == 3 && container.nodeValue.length > 0 && ((offset === 0 && !collapsed) || (collapsed && offset === (isDelete ? 0 : 1)))) {
+ nonEmptyElements = editor.schema.getNonEmptyElements();
+
+ // Prevent default logic since it's broken
+ e.preventDefault();
+
+ // Insert a BR before the text node this will prevent the containing element from being deleted/converted
+ brElm = dom.create('br', {id: '__tmp'});
+ container.parentNode.insertBefore(brElm, container);
+
+ // Do the browser delete
+ editor.getDoc().execCommand(isDelete ? 'ForwardDelete' : 'Delete', false, null);
+
+ // Check if the previous sibling is empty after deleting for example: <p><b></b>|</p>
+ container = selection.getRng().startContainer;
+ sibling = container.previousSibling;
+ if (sibling && sibling.nodeType == 1 && !dom.isBlock(sibling) && dom.isEmpty(sibling) && !nonEmptyElements[sibling.nodeName.toLowerCase()]) {
+ dom.remove(sibling);
+ }
+
+ // Remove the temp element we inserted
+ dom.remove('__tmp');
+ }
+ }
+ });
+ }
+
+ function removeBlockQuoteOnBackSpace() {
+ // Add block quote deletion handler
+ editor.onKeyDown.add(function(editor, 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);
+ }
+ });
+ };
+
+ function setGeckoEditingOptions() {
+ function setOpts() {
+ editor._refreshContentEditable();
+
+ setEditorCommandState("StyleWithCSS", false);
+ setEditorCommandState("enableInlineTableEditing", false);
+
+ if (!settings.object_resizing) {
+ setEditorCommandState("enableObjectResizing", false);
}
+ };
+
+ if (!settings.readonly) {
+ editor.onBeforeExecCommand.add(setOpts);
+ editor.onMouseDown.add(setOpts);
}
- });
-})(tinymce);
+ };
+
+ function addBrAfterLastLinks() {
+ function fixLinks(editor, o) {
+ 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.onExecCommand.add(function(editor, cmd) {
+ if (cmd === 'CreateLink') {
+ fixLinks(editor);
+ }
+ });
+
+ editor.onSetContent.add(selection.onSetContent.add(fixLinks));
+ };
+
+ function setDefaultBlockType() {
+ if (settings.forced_root_block) {
+ editor.onInit.add(function() {
+ setEditorCommandState('DefaultParagraphSeparator', settings.forced_root_block);
+ });
+ }
+ }
+
+ function removeGhostSelection() {
+ function repaint(sender, args) {
+ if (!sender || !args.initial) {
+ editor.execCommand('mceRepaint');
+ }
+ };
+
+ editor.onUndo.add(repaint);
+ editor.onRedo.add(repaint);
+ editor.onSetContent.add(repaint);
+ };
+
+ function deleteControlItemOnBackSpace() {
+ editor.onKeyDown.add(function(editor, 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();
+ }
+ }
+ });
+ };
+
+ 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}');
+ }
+ };
+
+ function fakeImageResize() {
+ var selectedElmX, selectedElmY, selectedElm, selectedElmGhost, selectedHandle, startX, startY, startW, startH, ratio,
+ resizeHandles, width, height, rootDocument = document, editableDoc = editor.getDoc();
+
+ if (!settings.object_resizing || settings.webkit_fake_resize === false) {
+ return;
+ }
+
+ // Try disabling object resizing if WebKit implements resizing in the future
+ setEditorCommandState("enableObjectResizing", false);
+
+ // Details about each resize handle how to scale etc
+ resizeHandles = {
+ // Name: x multiplier, y multiplier, delta size x, delta size y
+ n: [.5, 0, 0, -1],
+ e: [1, .5, 1, 0],
+ s: [.5, 1, 0, 1],
+ w: [0, .5, -1, 0],
+ nw: [0, 0, -1, -1],
+ ne: [1, 0, 1, -1],
+ se: [1, 1, 1, 1],
+ sw : [0, 1, -1, 1]
+ };
+
+ function resizeElement(e) {
+ var deltaX, deltaY;
+
+ // Calc new width/height
+ deltaX = e.screenX - startX;
+ deltaY = e.screenY - startY;
+
+ // Calc new size
+ width = deltaX * selectedHandle[2] + startW;
+ height = deltaY * selectedHandle[3] + startH;
+
+ // Never scale down lower than 5 pixels
+ width = width < 5 ? 5 : width;
+ height = height < 5 ? 5 : height;
+
+ // Constrain proportions when modifier key is pressed or if the nw, ne, sw, se corners are moved on an image
+ if (VK.modifierPressed(e) || (selectedElm.nodeName == "IMG" && selectedHandle[2] * selectedHandle[3] !== 0)) {
+ width = Math.round(height / ratio);
+ height = Math.round(width * ratio);
+ }
+
+ // Update ghost size
+ dom.setStyles(selectedElmGhost, {
+ width: width,
+ height: height
+ });
+
+ // Update ghost X position if needed
+ if (selectedHandle[2] < 0 && selectedElmGhost.clientWidth <= width) {
+ dom.setStyle(selectedElmGhost, 'left', selectedElmX + (startW - width));
+ }
+
+ // Update ghost Y position if needed
+ if (selectedHandle[3] < 0 && selectedElmGhost.clientHeight <= height) {
+ dom.setStyle(selectedElmGhost, 'top', selectedElmY + (startH - height));
+ }
+ }
+
+ function endResize() {
+ function setSizeProp(name, value) {
+ if (value) {
+ // Resize by using style or attribute
+ if (selectedElm.style[name] || !editor.schema.isValid(selectedElm.nodeName.toLowerCase(), name)) {
+ dom.setStyle(selectedElm, name, value);
+ } else {
+ dom.setAttrib(selectedElm, name, value);
+ }
+ }
+ }
+
+ // Set width/height properties
+ setSizeProp('width', width);
+ setSizeProp('height', height);
+
+ dom.unbind(editableDoc, 'mousemove', resizeElement);
+ dom.unbind(editableDoc, 'mouseup', endResize);
+
+ if (rootDocument != editableDoc) {
+ dom.unbind(rootDocument, 'mousemove', resizeElement);
+ dom.unbind(rootDocument, 'mouseup', endResize);
+ }
+
+ // Remove ghost and update resize handle positions
+ dom.remove(selectedElmGhost);
+ showResizeRect(selectedElm);
+ }
+
+ function showResizeRect(targetElm) {
+ var position, targetWidth, targetHeight;
+
+ hideResizeRect();
+
+ // Get position and size of target
+ position = dom.getPos(targetElm);
+ selectedElmX = position.x;
+ selectedElmY = position.y;
+ targetWidth = targetElm.offsetWidth;
+ targetHeight = targetElm.offsetHeight;
+
+ // Reset width/height if user selects a new image/table
+ if (selectedElm != targetElm) {
+ selectedElm = targetElm;
+ width = height = 0;
+ }
+
+ each(resizeHandles, function(handle, name) {
+ var handleElm;
+
+ // Get existing or render resize handle
+ handleElm = dom.get('mceResizeHandle' + name);
+ if (!handleElm) {
+ handleElm = dom.add(editableDoc.documentElement, 'div', {
+ id: 'mceResizeHandle' + name,
+ 'class': 'mceResizeHandle',
+ style: 'cursor:' + name + '-resize; margin:0; padding:0'
+ });
+
+ dom.bind(handleElm, 'mousedown', function(e) {
+ e.preventDefault();
+
+ endResize();
+
+ startX = e.screenX;
+ startY = e.screenY;
+ startW = selectedElm.clientWidth;
+ startH = selectedElm.clientHeight;
+ ratio = startH / startW;
+ selectedHandle = handle;
+
+ selectedElmGhost = selectedElm.cloneNode(true);
+ dom.addClass(selectedElmGhost, 'mceClonedResizable');
+ dom.setStyles(selectedElmGhost, {
+ left: selectedElmX,
+ top: selectedElmY,
+ margin: 0
+ });
+
+ editableDoc.documentElement.appendChild(selectedElmGhost);
+
+ dom.bind(editableDoc, 'mousemove', resizeElement);
+ dom.bind(editableDoc, 'mouseup', endResize);
+
+ if (rootDocument != editableDoc) {
+ dom.bind(rootDocument, 'mousemove', resizeElement);
+ dom.bind(rootDocument, 'mouseup', endResize);
+ }
+ });
+ } else {
+ dom.show(handleElm);
+ }
+
+ // Position element
+ dom.setStyles(handleElm, {
+ left: (targetWidth * handle[0] + selectedElmX) - (handleElm.offsetWidth / 2),
+ top: (targetHeight * handle[1] + selectedElmY) - (handleElm.offsetHeight / 2)
+ });
+ });
+
+ // Only add resize rectangle on WebKit and only on images
+ if (!tinymce.isOpera && selectedElm.nodeName == "IMG") {
+ selectedElm.setAttribute('data-mce-selected', '1');
+ }
+ }
+
+ function hideResizeRect() {
+ if (selectedElm) {
+ selectedElm.removeAttribute('data-mce-selected');
+ }
+
+ for (var name in resizeHandles) {
+ dom.hide('mceResizeHandle' + name);
+ }
+ }
+
+ // Add CSS for resize handles, cloned element and selected
+ editor.contentStyles.push(
+ '.mceResizeHandle {' +
+ 'position: absolute;' +
+ 'border: 1px solid black;' +
+ 'background: #FFF;' +
+ 'width: 5px;' +
+ 'height: 5px;' +
+ 'z-index: 10000' +
+ '}' +
+ '.mceResizeHandle:hover {' +
+ 'background: #000' +
+ '}' +
+ 'img[data-mce-selected] {' +
+ 'outline: 1px solid black' +
+ '}' +
+ 'img.mceClonedResizable, table.mceClonedResizable {' +
+ 'position: absolute;' +
+ 'outline: 1px dashed black;' +
+ 'opacity: .5;' +
+ 'z-index: 10000' +
+ '}'
+ );
+
+ function updateResizeRect() {
+ var controlElm = dom.getParent(selection.getNode(), 'table,img');
+
+ // Remove data-mce-selected from all elements since they might have been copied using Ctrl+c/v
+ each(dom.select('img[data-mce-selected]'), function(img) {
+ img.removeAttribute('data-mce-selected');
+ });
+
+ if (controlElm) {
+ showResizeRect(controlElm);
+ } else {
+ hideResizeRect();
+ }
+ }
+
+ // Show/hide resize rect when image is selected
+ editor.onNodeChange.add(updateResizeRect);
+
+ // Fixes WebKit quirk where it returns IMG on getNode if caret is after last image in container
+ dom.bind(editableDoc, 'selectionchange', updateResizeRect);
+
+ // Remove the internal attribute when serializing the DOM
+ editor.serializer.addAttributeFilter('data-mce-selected', function(nodes, name) {
+ var i = nodes.length;
+
+ while (i--) {
+ nodes[i].attr(name, null);
+ }
+ });
+ }
+
+ 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 = tinymce.html.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 tinymce.html.Node('#text', 3);
+ textNode.value = value;
+ textNode.raw = true;
+ node.append(textNode);
+ }
+ }
+ }
+ });
+ }
+ }
+
+ // All browsers
+ disableBackspaceIntoATable();
+ removeBlockQuoteOnBackSpace();
+ emptyEditorWhenDeleting();
+
+ // WebKit
+ if (tinymce.isWebKit) {
+ keepInlineElementOnDeleteBackspace();
+ cleanupStylesWhenDeleting();
+ inputMethodFocus();
+ selectControlElements();
+ setDefaultBlockType();
+
+ // iOS
+ if (tinymce.isIDevice) {
+ selectionChangeNodeChanged();
+ } else {
+ fakeImageResize();
+ selectAll();
+ }
+ }
+
+ // IE
+ if (tinymce.isIE) {
+ removeHrOnBackspace();
+ ensureBodyHasRoleApplication();
+ addNewLinesBeforeBrInPre();
+ removePreSerializedStylesWhenSelectingControls();
+ deleteControlItemOnBackSpace();
+ renderEmptyBlocksFix();
+ keepNoScriptContents();
+ }
+
+ // Gecko
+ if (tinymce.isGecko) {
+ removeHrOnBackspace();
+ focusBody();
+ removeStylesWhenDeletingAccrossBlockElements();
+ setGeckoEditingOptions();
+ addBrAfterLastLinks();
+ removeGhostSelection();
+ }
+
+ // Opera
+ if (tinymce.isOpera) {
+ fakeImageResize();
+ }
+};
(function(tinymce) {
var namedEntities, baseEntities, reverseEntities,
attrsCharsRegExp = /[&<>\"\u007E-\uD7FF\uE000-\uFFEF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
@@ -1327,8 +2062,7 @@ tinymce.create('static tinymce.util.XHR', {
'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);
+ '811,Dagger,81g,permil,81p,lsaquo,81q,rsaquo,85c,euro', 32);
tinymce.html = tinymce.html || {};
@@ -1539,7 +2273,27 @@ tinymce.html.Styles = function(settings, schema) {
str = str.replace(/\\([\'\";:])/g, "$1");
return str;
- }
+ };
+
+ 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);
+
+ // 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) {
// Encode \" \' % and ; and : inside strings so they don't interfere with the style parsing
@@ -1563,26 +2317,7 @@ tinymce.html.Styles = function(settings, schema) {
value = value.replace(rgbRegExp, toHex);
// Convert URLs and force them into url('value') format
- value = value.replace(urlOrStrRegExp, function(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);
-
- // 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, "\\'") + "')";
- });
-
+ value = value.replace(urlOrStrRegExp, processUrl);
styles[name] = isEncoded ? decode(value, true) : value;
}
@@ -1645,8 +2380,7 @@ tinymce.html.Styles = function(settings, schema) {
};
(function(tinymce) {
- var transitional = {}, boolAttrMap, blockElementsMap, shortEndedElementsMap, nonEmptyElementsMap, customElementsMap = {},
- defaultWhiteSpaceElementsMap, selfClosingElementsMap, makeMap = tinymce.makeMap, each = tinymce.each;
+ var mapCache = {}, makeMap = tinymce.makeMap, each = tinymce.each;
function split(str, delim) {
return str.split(delim || ',');
@@ -1681,142 +2415,286 @@ tinymce.html.Styles = function(settings, schema) {
return elements;
};
- // Build a lookup table for block elements both lowercase and uppercase
- blockElementsMap = 'h1,h2,h3,h4,h5,h6,hr,p,div,address,pre,form,table,tbody,thead,tfoot,' +
- 'th,tr,td,li,ol,ul,caption,blockquote,center,dl,dt,dd,dir,fieldset,' +
- 'noscript,menu,isindex,samp,header,footer,article,section,hgroup';
- blockElementsMap = makeMap(blockElementsMap, ',', makeMap(blockElementsMap.toUpperCase()));
-
- // This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size
- transitional = unpack({
- Z : 'H|K|N|O|P',
- Y : 'X|form|R|Q',
- ZG : 'E|span|width|align|char|charoff|valign',
- X : 'p|T|div|U|W|isindex|fieldset|table',
- ZF : 'E|align|char|charoff|valign',
- W : 'pre|hr|blockquote|address|center|noframes',
- ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',
- ZD : '[E][S]',
- U : 'ul|ol|dl|menu|dir',
- ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',
- T : 'h1|h2|h3|h4|h5|h6',
- ZB : 'X|S|Q',
- S : 'R|P',
- ZA : 'a|G|J|M|O|P',
- R : 'a|H|K|N|O',
- Q : 'noscript|P',
- P : 'ins|del|script',
- O : 'input|select|textarea|label|button',
- N : 'M|L',
- M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',
- L : 'sub|sup',
- K : 'J|I',
- J : 'tt|i|b|u|s|strike',
- I : 'big|small|font|basefont',
- H : 'G|F',
- G : 'br|span|bdo',
- F : 'object|applet|img|map|iframe',
- E : 'A|B|C',
- D : 'accesskey|tabindex|onfocus|onblur',
- C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
- B : 'lang|xml:lang|dir',
- A : 'id|class|style|title'
- }, 'script[id|charset|type|language|src|defer|xml:space][]' +
- 'style[B|id|type|media|title|xml:space][]' +
- 'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' +
- 'param[id|name|value|valuetype|type][]' +
- 'p[E|align][#|S]' +
- 'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' +
- 'br[A|clear][]' +
- 'span[E][#|S]' +
- 'bdo[A|C|B][#|S]' +
- 'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' +
- 'h1[E|align][#|S]' +
- 'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' +
- 'map[B|C|A|name][X|form|Q|area]' +
- 'h2[E|align][#|S]' +
- 'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]' +
- 'h3[E|align][#|S]' +
- 'tt[E][#|S]' +
- 'i[E][#|S]' +
- 'b[E][#|S]' +
- 'u[E][#|S]' +
- 's[E][#|S]' +
- 'strike[E][#|S]' +
- 'big[E][#|S]' +
- 'small[E][#|S]' +
- 'font[A|B|size|color|face][#|S]' +
- 'basefont[id|size|color|face][]' +
- 'em[E][#|S]' +
- 'strong[E][#|S]' +
- 'dfn[E][#|S]' +
- 'code[E][#|S]' +
- 'q[E|cite][#|S]' +
- 'samp[E][#|S]' +
- 'kbd[E][#|S]' +
- 'var[E][#|S]' +
- 'cite[E][#|S]' +
- 'abbr[E][#|S]' +
- 'acronym[E][#|S]' +
- 'sub[E][#|S]' +
- 'sup[E][#|S]' +
- 'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' +
- 'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' +
- 'optgroup[E|disabled|label][option]' +
- 'option[E|selected|disabled|label|value][]' +
- 'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' +
- 'label[E|for|accesskey|onfocus|onblur][#|S]' +
- 'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' +
- 'h4[E|align][#|S]' +
- 'ins[E|cite|datetime][#|Y]' +
- 'h5[E|align][#|S]' +
- 'del[E|cite|datetime][#|Y]' +
- 'h6[E|align][#|S]' +
- 'div[E|align][#|Y]' +
- 'ul[E|type|compact][li]' +
- 'li[E|type|value][#|Y]' +
- 'ol[E|type|compact|start][li]' +
- 'dl[E|compact][dt|dd]' +
- 'dt[E][#|S]' +
- 'dd[E][#|Y]' +
- 'menu[E|compact][li]' +
- 'dir[E|compact][li]' +
- 'pre[E|width|xml:space][#|ZA]' +
- 'hr[E|align|noshade|size|width][]' +
- 'blockquote[E|cite][#|Y]' +
- 'address[E][#|S|p]' +
- 'center[E][#|Y]' +
- 'noframes[E][#|Y]' +
- 'isindex[A|B|prompt][]' +
- 'fieldset[E][#|legend|Y]' +
- 'legend[E|accesskey|align][#|S]' +
- 'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' +
- 'caption[E|align][#|S]' +
- 'col[ZG][]' +
- 'colgroup[ZG][col]' +
- 'thead[ZF][tr]' +
- 'tr[ZF|bgcolor][th|td]' +
- 'th[E|ZE][#|Y]' +
- 'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' +
- 'noscript[E][#|Y]' +
- 'td[E|ZE][#|Y]' +
- 'tfoot[ZF][tr]' +
- 'tbody[ZF][tr]' +
- 'area[E|D|shape|coords|href|nohref|alt|target][]' +
- 'base[id|href|target][]' +
- 'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'
- );
-
- boolAttrMap = makeMap('checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected,autoplay,loop,controls');
- shortEndedElementsMap = makeMap('area,base,basefont,br,col,frame,hr,img,input,isindex,link,meta,param,embed,source');
- nonEmptyElementsMap = tinymce.extend(makeMap('td,th,iframe,video,audio,object'), shortEndedElementsMap);
- defaultWhiteSpaceElementsMap = makeMap('pre,script,style,textarea');
- selfClosingElementsMap = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr');
+ function getHTML5() {
+ var html5 = mapCache.html5;
+
+ if (!html5) {
+ html5 = mapCache.html5 = unpack({
+ A : 'id|accesskey|class|dir|draggable|item|hidden|itemprop|role|spellcheck|style|subject|title|onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
+ B : '#|a|abbr|area|audio|b|bdo|br|button|canvas|cite|code|command|datalist|del|dfn|em|embed|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|meta|' +
+ 'meter|noscript|object|output|progress|q|ruby|samp|script|select|small|span|strong|sub|sup|svg|textarea|time|var|video|wbr',
+ C : '#|a|abbr|area|address|article|aside|audio|b|bdo|blockquote|br|button|canvas|cite|code|command|datalist|del|details|dfn|dialog|div|dl|em|embed|fieldset|' +
+ 'figure|footer|form|h1|h2|h3|h4|h5|h6|header|hgroup|hr|i|iframe|img|input|ins|kbd|keygen|label|link|map|mark|menu|meta|meter|nav|noscript|ol|object|output|' +
+ 'p|pre|progress|q|ruby|samp|script|section|select|small|span|strong|style|sub|sup|svg|table|textarea|time|ul|var|video'
+ }, 'html[A|manifest][body|head]' +
+ 'head[A][base|command|link|meta|noscript|script|style|title]' +
+ 'title[A][#]' +
+ 'base[A|href|target][]' +
+ 'link[A|href|rel|media|type|sizes][]' +
+ 'meta[A|http-equiv|name|content|charset][]' +
+ 'style[A|type|media|scoped][#]' +
+ 'script[A|charset|type|src|defer|async][#]' +
+ 'noscript[A][C]' +
+ 'body[A][C]' +
+ 'section[A][C]' +
+ 'nav[A][C]' +
+ 'article[A][C]' +
+ 'aside[A][C]' +
+ 'h1[A][B]' +
+ 'h2[A][B]' +
+ 'h3[A][B]' +
+ 'h4[A][B]' +
+ 'h5[A][B]' +
+ 'h6[A][B]' +
+ 'hgroup[A][h1|h2|h3|h4|h5|h6]' +
+ 'header[A][C]' +
+ 'footer[A][C]' +
+ 'address[A][C]' +
+ 'p[A][B]' +
+ 'br[A][]' +
+ 'pre[A][B]' +
+ 'dialog[A][dd|dt]' +
+ 'blockquote[A|cite][C]' +
+ 'ol[A|start|reversed][li]' +
+ 'ul[A][li]' +
+ 'li[A|value][C]' +
+ 'dl[A][dd|dt]' +
+ 'dt[A][B]' +
+ 'dd[A][C]' +
+ 'a[A|href|target|ping|rel|media|type][B]' +
+ 'em[A][B]' +
+ 'strong[A][B]' +
+ 'small[A][B]' +
+ 'cite[A][B]' +
+ 'q[A|cite][B]' +
+ 'dfn[A][B]' +
+ 'abbr[A][B]' +
+ 'code[A][B]' +
+ 'var[A][B]' +
+ 'samp[A][B]' +
+ 'kbd[A][B]' +
+ 'sub[A][B]' +
+ 'sup[A][B]' +
+ 'i[A][B]' +
+ 'b[A][B]' +
+ 'mark[A][B]' +
+ 'progress[A|value|max][B]' +
+ 'meter[A|value|min|max|low|high|optimum][B]' +
+ 'time[A|datetime][B]' +
+ 'ruby[A][B|rt|rp]' +
+ 'rt[A][B]' +
+ 'rp[A][B]' +
+ 'bdo[A][B]' +
+ 'span[A][B]' +
+ 'ins[A|cite|datetime][B]' +
+ 'del[A|cite|datetime][B]' +
+ 'figure[A][C|legend|figcaption]' +
+ 'figcaption[A][C]' +
+ 'img[A|alt|src|height|width|usemap|ismap][]' +
+ 'iframe[A|name|src|height|width|sandbox|seamless][]' +
+ 'embed[A|src|height|width|type][]' +
+ 'object[A|data|type|height|width|usemap|name|form|classid][param]' +
+ 'param[A|name|value][]' +
+ 'details[A|open][C|legend]' +
+ 'command[A|type|label|icon|disabled|checked|radiogroup][]' +
+ 'menu[A|type|label][C|li]' +
+ 'legend[A][C|B]' +
+ 'div[A][C]' +
+ 'source[A|src|type|media][]' +
+ 'audio[A|src|autobuffer|autoplay|loop|controls][source]' +
+ 'video[A|src|autobuffer|autoplay|loop|controls|width|height|poster][source]' +
+ 'hr[A][]' +
+ 'form[A|accept-charset|action|autocomplete|enctype|method|name|novalidate|target][C]' +
+ 'fieldset[A|disabled|form|name][C|legend]' +
+ 'label[A|form|for][B]' +
+ 'input[A|type|accept|alt|autocomplete|autofocus|checked|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|height|list|max|maxlength|min|' +
+ 'multiple|pattern|placeholder|readonly|required|size|src|step|width|files|value|name][]' +
+ 'button[A|autofocus|disabled|form|formaction|formenctype|formmethod|formnovalidate|formtarget|name|value|type][B]' +
+ 'select[A|autofocus|disabled|form|multiple|name|size][option|optgroup]' +
+ 'datalist[A][B|option]' +
+ 'optgroup[A|disabled|label][option]' +
+ 'option[A|disabled|selected|label|value][]' +
+ 'textarea[A|autofocus|disabled|form|maxlength|name|placeholder|readonly|required|rows|cols|wrap][]' +
+ 'keygen[A|autofocus|challenge|disabled|form|keytype|name][]' +
+ 'output[A|for|form|name][B]' +
+ 'canvas[A|width|height][]' +
+ 'map[A|name][B|C]' +
+ 'area[A|shape|coords|href|alt|target|media|rel|ping|type][]' +
+ 'mathml[A][]' +
+ 'svg[A][]' +
+ 'table[A|border][caption|colgroup|thead|tfoot|tbody|tr]' +
+ 'caption[A][C]' +
+ 'colgroup[A|span][col]' +
+ 'col[A|span][]' +
+ 'thead[A][tr]' +
+ 'tfoot[A][tr]' +
+ 'tbody[A][tr]' +
+ 'tr[A][th|td]' +
+ 'th[A|headers|rowspan|colspan|scope][B]' +
+ 'td[A|headers|rowspan|colspan][C]' +
+ 'wbr[A][]'
+ );
+ }
+
+ return html5;
+ };
+
+ function getHTML4() {
+ var html4 = mapCache.html4;
+
+ if (!html4) {
+ // This is the XHTML 1.0 transitional elements with it's attributes and children packed to reduce it's size
+ html4 = mapCache.html4 = unpack({
+ Z : 'H|K|N|O|P',
+ Y : 'X|form|R|Q',
+ ZG : 'E|span|width|align|char|charoff|valign',
+ X : 'p|T|div|U|W|isindex|fieldset|table',
+ ZF : 'E|align|char|charoff|valign',
+ W : 'pre|hr|blockquote|address|center|noframes',
+ ZE : 'abbr|axis|headers|scope|rowspan|colspan|align|char|charoff|valign|nowrap|bgcolor|width|height',
+ ZD : '[E][S]',
+ U : 'ul|ol|dl|menu|dir',
+ ZC : 'p|Y|div|U|W|table|br|span|bdo|object|applet|img|map|K|N|Q',
+ T : 'h1|h2|h3|h4|h5|h6',
+ ZB : 'X|S|Q',
+ S : 'R|P',
+ ZA : 'a|G|J|M|O|P',
+ R : 'a|H|K|N|O',
+ Q : 'noscript|P',
+ P : 'ins|del|script',
+ O : 'input|select|textarea|label|button',
+ N : 'M|L',
+ M : 'em|strong|dfn|code|q|samp|kbd|var|cite|abbr|acronym',
+ L : 'sub|sup',
+ K : 'J|I',
+ J : 'tt|i|b|u|s|strike',
+ I : 'big|small|font|basefont',
+ H : 'G|F',
+ G : 'br|span|bdo',
+ F : 'object|applet|img|map|iframe',
+ E : 'A|B|C',
+ D : 'accesskey|tabindex|onfocus|onblur',
+ C : 'onclick|ondblclick|onmousedown|onmouseup|onmouseover|onmousemove|onmouseout|onkeypress|onkeydown|onkeyup',
+ B : 'lang|xml:lang|dir',
+ A : 'id|class|style|title'
+ }, 'script[id|charset|type|language|src|defer|xml:space][]' +
+ 'style[B|id|type|media|title|xml:space][]' +
+ 'object[E|declare|classid|codebase|data|type|codetype|archive|standby|width|height|usemap|name|tabindex|align|border|hspace|vspace][#|param|Y]' +
+ 'param[id|name|value|valuetype|type][]' +
+ 'p[E|align][#|S]' +
+ 'a[E|D|charset|type|name|href|hreflang|rel|rev|shape|coords|target][#|Z]' +
+ 'br[A|clear][]' +
+ 'span[E][#|S]' +
+ 'bdo[A|C|B][#|S]' +
+ 'applet[A|codebase|archive|code|object|alt|name|width|height|align|hspace|vspace][#|param|Y]' +
+ 'h1[E|align][#|S]' +
+ 'img[E|src|alt|name|longdesc|width|height|usemap|ismap|align|border|hspace|vspace][]' +
+ 'map[B|C|A|name][X|form|Q|area]' +
+ 'h2[E|align][#|S]' +
+ 'iframe[A|longdesc|name|src|frameborder|marginwidth|marginheight|scrolling|align|width|height][#|Y]' +
+ 'h3[E|align][#|S]' +
+ 'tt[E][#|S]' +
+ 'i[E][#|S]' +
+ 'b[E][#|S]' +
+ 'u[E][#|S]' +
+ 's[E][#|S]' +
+ 'strike[E][#|S]' +
+ 'big[E][#|S]' +
+ 'small[E][#|S]' +
+ 'font[A|B|size|color|face][#|S]' +
+ 'basefont[id|size|color|face][]' +
+ 'em[E][#|S]' +
+ 'strong[E][#|S]' +
+ 'dfn[E][#|S]' +
+ 'code[E][#|S]' +
+ 'q[E|cite][#|S]' +
+ 'samp[E][#|S]' +
+ 'kbd[E][#|S]' +
+ 'var[E][#|S]' +
+ 'cite[E][#|S]' +
+ 'abbr[E][#|S]' +
+ 'acronym[E][#|S]' +
+ 'sub[E][#|S]' +
+ 'sup[E][#|S]' +
+ 'input[E|D|type|name|value|checked|disabled|readonly|size|maxlength|src|alt|usemap|onselect|onchange|accept|align][]' +
+ 'select[E|name|size|multiple|disabled|tabindex|onfocus|onblur|onchange][optgroup|option]' +
+ 'optgroup[E|disabled|label][option]' +
+ 'option[E|selected|disabled|label|value][]' +
+ 'textarea[E|D|name|rows|cols|disabled|readonly|onselect|onchange][]' +
+ 'label[E|for|accesskey|onfocus|onblur][#|S]' +
+ 'button[E|D|name|value|type|disabled][#|p|T|div|U|W|table|G|object|applet|img|map|K|N|Q]' +
+ 'h4[E|align][#|S]' +
+ 'ins[E|cite|datetime][#|Y]' +
+ 'h5[E|align][#|S]' +
+ 'del[E|cite|datetime][#|Y]' +
+ 'h6[E|align][#|S]' +
+ 'div[E|align][#|Y]' +
+ 'ul[E|type|compact][li]' +
+ 'li[E|type|value][#|Y]' +
+ 'ol[E|type|compact|start][li]' +
+ 'dl[E|compact][dt|dd]' +
+ 'dt[E][#|S]' +
+ 'dd[E][#|Y]' +
+ 'menu[E|compact][li]' +
+ 'dir[E|compact][li]' +
+ 'pre[E|width|xml:space][#|ZA]' +
+ 'hr[E|align|noshade|size|width][]' +
+ 'blockquote[E|cite][#|Y]' +
+ 'address[E][#|S|p]' +
+ 'center[E][#|Y]' +
+ 'noframes[E][#|Y]' +
+ 'isindex[A|B|prompt][]' +
+ 'fieldset[E][#|legend|Y]' +
+ 'legend[E|accesskey|align][#|S]' +
+ 'table[E|summary|width|border|frame|rules|cellspacing|cellpadding|align|bgcolor][caption|col|colgroup|thead|tfoot|tbody|tr]' +
+ 'caption[E|align][#|S]' +
+ 'col[ZG][]' +
+ 'colgroup[ZG][col]' +
+ 'thead[ZF][tr]' +
+ 'tr[ZF|bgcolor][th|td]' +
+ 'th[E|ZE][#|Y]' +
+ 'form[E|action|method|name|enctype|onsubmit|onreset|accept|accept-charset|target][#|X|R|Q]' +
+ 'noscript[E][#|Y]' +
+ 'td[E|ZE][#|Y]' +
+ 'tfoot[ZF][tr]' +
+ 'tbody[ZF][tr]' +
+ 'area[E|D|shape|coords|href|nohref|alt|target][]' +
+ 'base[id|href|target][]' +
+ 'body[E|onload|onunload|background|bgcolor|text|link|vlink|alink][#|Y]'
+ );
+ }
+
+ return html4;
+ };
tinymce.html.Schema = function(settings) {
- var self = this, elements = {}, children = {}, patternElements = [], validStyles, whiteSpaceElementsMap;
+ var self = this, elements = {}, children = {}, patternElements = [], validStyles, schemaItems;
+ var whiteSpaceElementsMap, selfClosingElementsMap, shortEndedElementsMap, boolAttrMap, blockElementsMap, nonEmptyElementsMap, customElementsMap = {};
+
+ // Creates an lookup table map object for the specified option or the default value
+ function createLookupTable(option, default_value, extend) {
+ 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 = tinymce.extend(value, extend);
+
+ mapCache[option] = value;
+ }
+ } else {
+ // Create custom map
+ value = makeMap(value, ',', makeMap(value.toUpperCase(), ' '));
+ }
+
+ return value;
+ };
settings = settings || {};
+ schemaItems = settings.schema == "html5" ? getHTML5() : getHTML4();
// Allow all elements and attributes if verify_html is set to false
if (settings.verify_html === false)
@@ -1832,7 +2710,16 @@ tinymce.html.Styles = function(settings, schema) {
});
}
- whiteSpaceElementsMap = settings.whitespace_elements ? makeMap(settings.whitespace_elements) : defaultWhiteSpaceElementsMap;
+ // Setup map objects
+ whiteSpaceElementsMap = createLookupTable('whitespace_elements', 'pre script noscript style textarea');
+ 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');
+ 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', shortEndedElementsMap);
+ 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 samp option datalist select optgroup', textBlockElementsMap);
// Converts a wildcard expression string to a regexp for example *a will become /.*a/.
function patternToRegExp(str) {
@@ -1844,7 +2731,7 @@ tinymce.html.Styles = function(settings, schema) {
function addValidElements(valid_elements) {
var ei, el, ai, al, yl, matches, element, attr, attrData, elementName, attrName, attrType, attributes, attributesOrder,
prefix, outputName, globalAttributes, globalAttributesOrder, transElement, key, childKey, value,
- elementRuleRegExp = /^([#+-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,
+ elementRuleRegExp = /^([#+\-])?([^\[\/]+)(?:\/([^\[]+))?(?:\[([^\]]+)\])?$/,
attrRuleRegExp = /^([!\-])?(\w+::\w+|[^=:<]+)?(?:([=:<])(.*))?$/,
hasPatternsRegExp = /[*?+]/;
@@ -1986,7 +2873,7 @@ tinymce.html.Styles = function(settings, schema) {
addValidElements(valid_elements);
- each(transitional, function(element, name) {
+ each(schemaItems, function(element, name) {
children[name] = element.children;
});
};
@@ -2006,8 +2893,15 @@ tinymce.html.Styles = function(settings, schema) {
customElementsMap[name] = cloneName;
// If it's not marked as inline then add it to valid block elements
- if (!inline)
+ if (!inline) {
+ blockElementsMap[name.toUpperCase()] = {};
blockElementsMap[name] = {};
+ }
+
+ // Add elements clone if needed
+ if (!elements[name]) {
+ elements[name] = elements[cloneName];
+ }
// Add custom elements at span/div positions
each(children, function(element, child) {
@@ -2066,8 +2960,8 @@ tinymce.html.Styles = function(settings, schema) {
};
if (!settings.valid_elements) {
- // No valid elements defined then clone the elements from the transitional spec
- each(transitional, function(element, name) {
+ // 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
@@ -2076,18 +2970,22 @@ tinymce.html.Styles = function(settings, schema) {
children[name] = element.children;
});
- // Switch these
- each(split('strong/b,em/i'), function(item) {
- item = split(item, '/');
- elements[item[1]].outputName = item[0];
- });
+ // 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
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'), function(name) {
- elements[name].removeEmpty = true;
+ 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
@@ -2104,10 +3002,6 @@ tinymce.html.Styles = function(settings, schema) {
// Todo: Remove this when we fix list handling to be valid
addValidChildren('+ol[ul|ol],+ul[ul|ol]');
- // If the user didn't allow span only allow internal spans
- if (!getElementRule('span'))
- addValidElements('span[!data-mce-type|*]');
-
// Delete invalid elements
if (settings.invalid_elements) {
tinymce.each(tinymce.explode(settings.invalid_elements), function(item) {
@@ -2116,6 +3010,10 @@ tinymce.html.Styles = function(settings, schema) {
});
}
+ // If the user didn't allow span only allow internal spans
+ if (!getElementRule('span'))
+ addValidElements('span[!data-mce-type|*]');
+
self.children = children;
self.styles = validStyles;
@@ -2128,6 +3026,10 @@ tinymce.html.Styles = function(settings, schema) {
return blockElementsMap;
};
+ self.getTextBlockElements = function() {
+ return textBlockElementsMap;
+ };
+
self.getShortEndedElements = function() {
return shortEndedElementsMap;
};
@@ -2150,6 +3052,36 @@ tinymce.html.Styles = function(settings, schema) {
return !!(parent && parent[child]);
};
+ 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;
+ };
+
self.getElementRule = getElementRule;
self.getCustomElements = function() {
@@ -2163,11 +3095,9 @@ tinymce.html.Styles = function(settings, schema) {
self.addCustomElements = addCustomElements;
self.addValidChildren = addValidChildren;
- };
- // Expose boolMap and blockElementMap as static properties for usage in DOMUtils
- tinymce.html.Schema.boolAttrMap = boolAttrMap;
- tinymce.html.Schema.blockElementsMap = blockElementsMap;
+ self.elements = elements;
+ };
})(tinymce);
(function(tinymce) {
@@ -2217,6 +3147,47 @@ tinymce.html.Styles = function(settings, schema) {
}
};
+ function parseAttribute(match, name, value, val2, val3) {
+ var attrRule, i;
+
+ name = name.toLowerCase();
+ value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
+
+ // Validate name and value
+ 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;
+ }
+
+ // 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
@@ -2224,10 +3195,10 @@ tinymce.html.Styles = function(settings, schema) {
'(?:!DOCTYPE([\\w\\W]*?)>)|' + // DOCTYPE
'(?:\\?([^\\s\\/<>]+) ?([\\w\\W]*?)[?/]>)|' + // PI
'(?:\\/([^>]+)>)|' + // End element
- '(?:([^\\s\\/<>]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/)>)' + // Start element
+ '(?:([A-Za-z0-9\\-\\:\\.]+)((?:\\s+[^"\'>]+(?:(?:"[^"]*")|(?:\'[^\']*\')|[^>]*))*|\\/|\\s+)>)' + // Start element
')', 'g');
- attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:\\.|[^\"])*)\")|(?:\'((?:\\.|[^\'])*)\')|([^>\s]+)))?/g;
+ attrRegExp = /([\w:\-]+)(?:\s*=\s*(?:(?:\"((?:[^\"])*)\")|(?:\'((?:[^\'])*)\')|([^>\s]+)))?/g;
specialElements = {
'script' : /<\/script[^>]*>/gi,
'style' : /<\/style[^>]*>/gi,
@@ -2236,7 +3207,7 @@ tinymce.html.Styles = function(settings, schema) {
// Setup lookup tables for empty elements and boolean attributes
shortEndedElements = schema.getShortEndedElements();
- selfClosing = schema.getSelfClosingElements();
+ selfClosing = settings.self_closing_elements || schema.getSelfClosingElements();
fillAttrsMap = schema.getBoolAttrs();
validate = settings.validate;
removeInternalElements = settings.remove_internals;
@@ -2291,46 +3262,7 @@ tinymce.html.Styles = function(settings, schema) {
attrList = [];
attrList.map = {};
- attribsValue.replace(attrRegExp, function(match, name, value, val2, val3) {
- var attrRule, i;
-
- name = name.toLowerCase();
- value = name in fillAttrsMap ? name : decode(value || val2 || val3 || ''); // Handle boolean attribute than value attribute
-
- // Validate name and value
- 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;
- }
-
- // Add attribute to list and map
- attrList.map[name] = value;
- attrList.push({
- name: name,
- value: value
- });
- });
+ attribsValue.replace(attrRegExp, parseAttribute);
} else {
attrList = [];
attrList.map = {};
@@ -2749,11 +3681,15 @@ tinymce.html.Styles = function(settings, schema) {
i = node.attributes.length;
while (i--) {
name = node.attributes[i].name;
- if (name === "name" || name.indexOf('data-') === 0)
+ if (name === "name" || name.indexOf('data-mce-') === 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;
@@ -2801,18 +3737,41 @@ tinymce.html.Styles = function(settings, schema) {
function fixInvalidChildren(nodes) {
var ni, node, parent, parents, newParent, currentNode, tempNode, childNode, i,
- childClone, nonEmptyElements, nonSplitableElements, sibling, nextNode;
+ childClone, nonEmptyElements, nonSplitableElements, textBlockElements, sibling, nextNode;
nonSplitableElements = tinymce.makeMap('tr,td,th,tbody,thead,tfoot,table');
nonEmptyElements = schema.getNonEmptyElements();
+ textBlockElements = schema.getTextBlockElements();
for (ni = 0; ni < nodes.length; ni++) {
node = nodes[ni];
- // Already removed
- if (!node.parent)
+ // 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)
@@ -2947,8 +3906,8 @@ tinymce.html.Styles = function(settings, schema) {
self.parse = function(html, args) {
var parser, rootNode, node, nodes, i, l, fi, fl, list, name, validate,
- blockElements, startWhiteSpaceRegExp, invalidChildren = [],
- endWhiteSpaceRegExp, allWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements, rootBlockName;
+ blockElements, startWhiteSpaceRegExp, invalidChildren = [], isInWhiteSpacePreservedElement,
+ endWhiteSpaceRegExp, allWhiteSpaceRegExp, isAllWhiteSpaceRegExp, whiteSpaceElements, children, nonEmptyElements, rootBlockName;
args = args || {};
matchedNodes = {};
@@ -2963,6 +3922,7 @@ tinymce.html.Styles = function(settings, schema) {
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;
@@ -3018,9 +3978,23 @@ tinymce.html.Styles = function(settings, schema) {
}
};
+ function cloneAndExcludeBlocks(input) {
+ var name, output = {};
+
+ for (name in input) {
+ if (name !== 'li' && name != 'p') {
+ output[name] = input[name];
+ }
+ }
+
+ return output;
+ };
+
parser = new tinymce.html.SaxParser({
validate : validate,
- fix_self_closing : !validate, // Let the DOM parser handle <li> in <li> or <p> in <p> for better results
+
+ // 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;
@@ -3030,7 +4004,7 @@ tinymce.html.Styles = function(settings, schema) {
var textNode;
// Trim all redundant whitespace on non white space elements
- if (!whiteSpaceElements[node.name]) {
+ if (!isInWhiteSpacePreservedElement) {
text = text.replace(allWhiteSpaceRegExp, ' ');
if (node.lastChild && blockElements[node.lastChild.name])
@@ -3100,6 +4074,11 @@ tinymce.html.Styles = function(settings, schema) {
// Change current node if the element wasn't empty i.e not <br /> or <img />
if (!empty)
node = newNode;
+
+ // Check if we are inside a whitespace preserved element
+ if (!isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
+ isInWhiteSpacePreservedElement = true;
+ }
}
},
@@ -3109,11 +4088,13 @@ tinymce.html.Styles = function(settings, schema) {
elementRule = validate ? schema.getElementRule(name) : {};
if (elementRule) {
if (blockElements[name]) {
- if (!whiteSpaceElements[node.name]) {
- // Trim whitespace at beginning of block
- for (textNode = node.firstChild; textNode && textNode.type === 3; ) {
+ 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;
@@ -3122,12 +4103,27 @@ tinymce.html.Styles = function(settings, schema) {
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 at end of block
- for (textNode = node.lastChild; textNode && textNode.type === 3; ) {
+ // 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;
@@ -3136,11 +4132,25 @@ tinymce.html.Styles = function(settings, schema) {
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
- textNode = node.prev;
+ // Removed due to: #5424
+ /*textNode = node.prev;
if (textNode && textNode.type === 3) {
text = textNode.value.replace(startWhiteSpaceRegExp, '');
@@ -3148,7 +4158,12 @@ tinymce.html.Styles = function(settings, schema) {
textNode.value = text;
else
textNode.remove();
- }
+ }*/
+ }
+
+ // Check if we exited a whitespace preserved element
+ if (isInWhiteSpacePreservedElement && whiteSpaceElements[name]) {
+ isInWhiteSpacePreservedElement = false;
}
// Handle empty nodes
@@ -3158,7 +4173,7 @@ tinymce.html.Styles = function(settings, schema) {
node.empty().append(new Node('#text', '3')).value = '\u00a0';
else {
// Leave nodes that have a name like <a name="name">
- if (!node.attributes.map.name) {
+ if (!node.attributes.map.name && !node.attributes.map.id) {
tempNode = node.parent;
node.empty().remove();
node = tempNode;
@@ -3235,8 +4250,8 @@ tinymce.html.Styles = function(settings, schema) {
// these elements and keep br elements that where intended to be there intact
if (settings.remove_trailing_brs) {
self.addNodeFilter('br', function(nodes, name) {
- var i, l = nodes.length, node, blockElements = schema.getBlockElements(),
- nonEmptyElements = schema.getNonEmptyElements(), parent, prev, prevName;
+ var i, l = nodes.length, node, blockElements = tinymce.extend({}, schema.getBlockElements()),
+ nonEmptyElements = schema.getNonEmptyElements(), parent, lastParent, prev, prevName;
// Remove brs from body element as well
blockElements.body = 1;
@@ -3247,7 +4262,7 @@ tinymce.html.Styles = function(settings, schema) {
parent = node.parent;
if (blockElements[node.parent.name] && node === parent.lastChild) {
- // Loop all nodes to the right of the current node and check for other BR elements
+ // Loop all nodes to the left of the current node and check for other BR elements
// excluding bookmarks since they are invisible
prev = node.prev;
while (prev) {
@@ -3278,17 +4293,57 @@ tinymce.html.Styles = function(settings, schema) {
// Remove or padd the element depending on schema rule
if (elementRule) {
- if (elementRule.removeEmpty)
- parent.remove();
- else if (elementRule.paddEmpty)
- parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
- }
- }
+ if (elementRule.removeEmpty)
+ parent.remove();
+ else if (elementRule.paddEmpty)
+ parent.empty().append(new tinymce.html.Node('#text', 3)).value = '\u00a0';
+ }
+ }
+ }
+ } else {
+ // Replaces BR elements inside inline elements like <p><b><i><br></i></b></p> so they become <p><b><i>&nbsp;</i></b></p>
+ lastParent = node;
+ while (parent.firstChild === lastParent && parent.lastChild === lastParent) {
+ lastParent = parent;
+
+ if (blockElements[parent.name]) {
+ break;
+ }
+
+ parent = parent.parent;
+ }
+
+ if (lastParent === parent) {
+ textNode = new tinymce.html.Node('#text', 3);
+ textNode.value = '\u00a0';
+ node.replace(textNode);
}
}
}
});
}
+
+ // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
+ if (!settings.allow_html_in_named_anchor) {
+ self.addAttributeFilter('id,name', function(nodes, name) {
+ var i = nodes.length, sibling, prevSibling, parent, node;
+
+ while (i--) {
+ node = nodes[i];
+ if (node.name === 'a' && node.firstChild && !node.attr('href')) {
+ parent = node.parent;
+
+ // Move children after current node
+ sibling = node.lastChild;
+ do {
+ prevSibling = sibling.prev;
+ parent.insert(sibling, node);
+ sibling = prevSibling;
+ } while (sibling);
+ }
+ }
+ });
+ }
}
})(tinymce);
@@ -3433,7 +4488,7 @@ tinymce.html.Writer = function(settings) {
writer.cdata(node.value);
},
- // Document fragment
+ // Document fragment
11: function(node) {
if ((node = node.firstChild)) {
do {
@@ -3508,6 +4563,592 @@ tinymce.html.Writer = function(settings) {
}
})(tinymce);
+// JSLint defined globals
+/*global tinymce:false, window:false */
+
+tinymce.dom = {};
+
+(function(namespace, expando) {
+ var w3cEventModel = !!document.addEventListener;
+
+ function addEvent(target, name, callback, capture) {
+ if (target.addEventListener) {
+ target.addEventListener(name, callback, capture || false);
+ } else if (target.attachEvent) {
+ target.attachEvent('on' + name, callback);
+ }
+ }
+
+ function removeEvent(target, name, callback, capture) {
+ if (target.removeEventListener) {
+ target.removeEventListener(name, callback, capture || false);
+ } else if (target.detachEvent) {
+ target.detachEvent('on' + name, callback);
+ }
+ }
+
+ function fix(original_event, data) {
+ var name, event = data || {};
+
+ // 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 original_event) {
+ // layerX/layerY is deprecated in Chrome and produces a warning
+ if (name !== "layerX" && name !== "layerY") {
+ event[name] = original_event[name];
+ }
+ }
+
+ // Normalize target IE uses srcElement
+ if (!event.target) {
+ event.target = event.srcElement || document;
+ }
+
+ // Add preventDefault method
+ event.preventDefault = function() {
+ event.isDefaultPrevented = returnTrue;
+
+ // Execute preventDefault on the original event object
+ if (original_event) {
+ if (original_event.preventDefault) {
+ original_event.preventDefault();
+ } else {
+ original_event.returnValue = false; // IE
+ }
+ }
+ };
+
+ // Add stopPropagation
+ event.stopPropagation = function() {
+ event.isPropagationStopped = returnTrue;
+
+ // Execute stopPropagation on the original event object
+ if (original_event) {
+ if (original_event.stopPropagation) {
+ original_event.stopPropagation();
+ } else {
+ original_event.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;
+ }
+
+ return event;
+ }
+
+ function bindOnReady(win, callback, event_utils) {
+ var doc = win.document, event = {type: 'ready'};
+
+ // Gets called when the DOM is ready
+ function readyHandler() {
+ if (!event_utils.domLoaded) {
+ event_utils.domLoaded = true;
+ callback(event);
+ }
+ }
+
+ // Page already loaded then fire it directly
+ if (doc.readyState == "complete") {
+ readyHandler();
+ return;
+ }
+
+ // Use W3C method
+ if (w3cEventModel) {
+ addEvent(win, 'DOMContentLoaded', readyHandler);
+ } else {
+ // Use IE method
+ addEvent(doc, "readystatechange", function() {
+ if (doc.readyState === "complete") {
+ removeEvent(doc, "readystatechange", arguments.callee);
+ readyHandler();
+ }
+ });
+
+ // Wait until we can scroll, when we can the DOM is initialized
+ if (doc.documentElement.doScroll && win === win.top) {
+ (function() {
+ 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) {
+ setTimeout(arguments.callee, 0);
+ return;
+ }
+
+ readyHandler();
+ })();
+ }
+ }
+
+ // Fallback if any of the above methods should fail for some odd reason
+ addEvent(win, 'load', readyHandler);
+ }
+
+ function EventUtils(proxy) {
+ var self = this, events = {}, count, isFocusBlurBound, hasFocusIn, hasMouseEnterLeave, mouseEnterLeave;
+
+ 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;
+
+ function executeHandlers(evt, id) {
+ var callbackList, i, l, callback;
+
+ callbackList = events[id][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;
+ }
+ }
+ }
+ }
+
+ 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];
+
+ if (!events[id]) {
+ events[id] = {};
+ }
+ }
+
+ // 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 || target.readyState == 'complete') && name === "ready") {
+ self.domLoaded = true;
+ 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 bubbeling 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;
+
+ // Add the nativeHandler to the callback list so that we can later unbind it
+ callbackList.nativeHandler = nativeHandler;
+ if (!w3cEventModel) {
+ callbackList.proxyHandler = proxy(id);
+ }
+
+ // Check if the target has native events support
+ if (name === "ready") {
+ bindOnReady(target, nativeHandler, self);
+ } else {
+ addEvent(target, fakeName || name, w3cEventModel ? nativeHandler : callbackList.proxyHandler, capture);
+ }
+ } 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;
+ };
+
+ 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) {
+ callbackList.splice(ci, 1);
+ }
+ }
+ }
+
+ // 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, w3cEventModel ? callbackList.nativeHandler : callbackList.proxyHandler, callbackList.capture);
+ }
+ }
+ }
+ } else {
+ // All events for a specific element
+ for (name in eventMap) {
+ callbackList = eventMap[name];
+ removeEvent(target, callbackList.fakeName || name, w3cEventModel ? callbackList.nativeHandler : callbackList.proxyHandler, 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;
+ };
+
+ self.fire = function(target, name, args) {
+ var id, event;
+
+ // 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
+ event = fix(null, args);
+ event.type = name;
+
+ do {
+ // Found an expando that means there is listeners to execute
+ id = target[expando];
+ if (id) {
+ executeHandlers(event, id);
+ }
+
+ // Walk up the DOM
+ target = target.parentNode || target.ownerDocument || target.defaultView || target.parentWindow;
+ } while (target && !event.isPropagationStopped());
+
+ return self;
+ };
+
+ 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 specificed 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;
+ };
+
+ self.callNativeHandler = function(id, evt) {
+ if (events) {
+ events[id][evt.type].nativeHandler(evt);
+ }
+ };
+
+ self.destory = function() {
+ events = {};
+ };
+
+ // Legacy function calls
+
+ self.add = function(target, events, func, scope) {
+ // Old API supported direct ID assignment
+ if (typeof(target) === "string") {
+ target = document.getElementById(target);
+ }
+
+ // Old API supported multiple targets
+ if (target && target instanceof Array) {
+ var i = target.length;
+
+ while (i--) {
+ self.add(target[i], events, func, scope);
+ }
+
+ return;
+ }
+
+ // Old API called ready init
+ if (events === "init") {
+ events = "ready";
+ }
+
+ return self.bind(target, events instanceof Array ? events.join(' ') : events, func, scope);
+ };
+
+ self.remove = function(target, events, func, scope) {
+ if (!target) {
+ return self;
+ }
+
+ // Old API supported direct ID assignment
+ if (typeof(target) === "string") {
+ target = document.getElementById(target);
+ }
+
+ // Old API supported multiple targets
+ if (target instanceof Array) {
+ var i = target.length;
+
+ while (i--) {
+ self.remove(target[i], events, func, scope);
+ }
+
+ return self;
+ }
+
+ return self.unbind(target, events instanceof Array ? events.join(' ') : events, func);
+ };
+
+ self.clear = function(target) {
+ // Old API supported direct ID assignment
+ if (typeof(target) === "string") {
+ target = document.getElementById(target);
+ }
+
+ return self.clean(target);
+ };
+
+ self.cancel = function(e) {
+ if (e) {
+ self.prevent(e);
+ self.stop(e);
+ }
+
+ return false;
+ };
+
+ self.prevent = function(e) {
+ if (!e.preventDefault) {
+ e = fix(e);
+ }
+
+ e.preventDefault();
+
+ return false;
+ };
+
+ self.stop = function(e) {
+ if (!e.stopPropagation) {
+ e = fix(e);
+ }
+
+ e.stopPropagation();
+
+ return false;
+ };
+ }
+
+ namespace.EventUtils = EventUtils;
+
+ namespace.Event = new EventUtils(function(id) {
+ return function(evt) {
+ tinymce.dom.Event.callNativeHandler(id, evt);
+ };
+ });
+
+ // Bind ready event when tinymce script is loaded
+ namespace.Event.bind(window, 'ready', function() {});
+
+ namespace = 0;
+})(tinymce.dom, 'data-mce-expando'); // Namespace and expando
+
+tinymce.dom.TreeWalker = function(start_node, root_node) {
+ var node = start_node;
+
+ function findSibling(node, start_name, sibling_name, shallow) {
+ var sibling, parent;
+
+ if (node) {
+ // Walk into nodes if it has a start
+ if (!shallow && node[start_name])
+ return node[start_name];
+
+ // Return the sibling if it has one
+ if (node != root_node) {
+ sibling = node[sibling_name];
+ if (sibling)
+ return sibling;
+
+ // Walk up the parents to look for siblings
+ for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
+ sibling = parent[sibling_name];
+ if (sibling)
+ return sibling;
+ }
+ }
+ }
+ };
+
+ this.current = function() {
+ return node;
+ };
+
+ this.next = function(shallow) {
+ return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));
+ };
+
+ this.prev = function(shallow) {
+ return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));
+ };
+};
+
(function(tinymce) {
// Shorten names
var each = tinymce.each,
@@ -3516,7 +5157,6 @@ tinymce.html.Writer = function(settings) {
isIE = tinymce.isIE,
Entities = tinymce.html.Entities,
simpleSelectorRe = /^([a-z0-9],?)+$/i,
- blockElementsMap = tinymce.html.Schema.blockElementsMap,
whiteSpaceRegExp = /^[ \t\r\n]*$/;
tinymce.create('tinymce.dom.DOMUtils', {
@@ -3540,7 +5180,7 @@ tinymce.html.Writer = function(settings) {
},
DOMUtils : function(d, s) {
- var t = this, globalStyle, name;
+ var t = this, globalStyle, name, blockElementsMap;
t.doc = d;
t.win = window;
@@ -3571,23 +5211,83 @@ tinymce.html.Writer = function(settings) {
}
}
- if (isIE && s.schema) {
+ t.fixDoc(d);
+ t.events = s.ownEvents ? new tinymce.dom.EventUtils(s.proxy) : tinymce.dom.Event;
+ tinymce.addUnload(t.destroy, t);
+ blockElementsMap = s.schema ? s.schema.getBlockElements() : {};
+
+ t.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];
+ };
+ },
+
+ 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) {
- d.createElement(name);
+ doc.createElement(name);
});
// Create all custom elements
- for (name in s.schema.getCustomElements()) {
- d.createElement(name);
+ for (name in settings.schema.getCustomElements()) {
+ doc.createElement(name);
}
}
+ },
- tinymce.addUnload(t.destroy, t);
+ 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;
+ }
+/*
+ // Setup HTML5 patched document fragment
+ if (!self.frag) {
+ self.frag = doc.createDocumentFragment();
+ self.fixDoc(self.frag);
+ }
+
+ // Make a deep copy by adding it to the document fragment then removing it this removed the :section
+ clone = doc.createElement('div');
+ self.frag.appendChild(clone);
+ clone.innerHTML = node.outerHTML;
+ self.frag.removeChild(clone);
+*/
+ return clone.firstChild;
},
getRoot : function() {
@@ -3643,8 +5343,8 @@ tinymce.html.Writer = function(settings) {
h = 0;
return {
- w : parseInt(w) || e.offsetWidth || e.clientWidth,
- h : parseInt(h) || e.offsetHeight || e.clientHeight
+ w : parseInt(w, 10) || e.offsetWidth || e.clientWidth,
+ h : parseInt(h, 10) || e.offsetHeight || e.clientHeight
};
},
@@ -3929,6 +5629,7 @@ tinymce.html.Writer = function(settings) {
return this.run(e, function(e) {
var s = t.settings;
+ var originalValue = e.getAttribute(n);
if (v !== null) {
switch (n) {
case "style":
@@ -3975,6 +5676,12 @@ tinymce.html.Writer = function(settings) {
e.setAttribute(n, '' + v, 2);
else
e.removeAttribute(n, 2);
+
+ // fire onChangeAttrib event for attributes that have changed
+ if (tinyMCE.activeEditor && originalValue != v) {
+ var ed = tinyMCE.activeEditor;
+ ed.onSetAttrib.dispatch(ed, e, n, v);
+ }
});
},
@@ -4155,13 +5862,39 @@ tinymce.html.Writer = function(settings) {
return this.styles.serialize(o, name);
},
+ addStyle: function(cssText) {
+ var doc = this.doc, head;
+
+ // 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));
+ }
+ },
+
loadCSS : function(u) {
var t = this, d = t.doc, head;
if (!u)
u = '';
- head = t.select('head')[0];
+ head = d.getElementsByTagName('head')[0];
each(u.split(','), function(u) {
var link;
@@ -4278,13 +6011,13 @@ tinymce.html.Writer = function(settings) {
// This seems to fix this problem
// Create new div with HTML contents and a BR infront to keep comments
- element = self.create('div');
- element.innerHTML = '<br />' + html;
+ var newElement = self.create('div');
+ newElement.innerHTML = '<br />' + html;
// Add all children from div to target
- each (element.childNodes, function(node, i) {
+ each (tinymce.grep(newElement.childNodes), function(node, i) {
// Skip br element
- if (i)
+ if (i && element.canHaveHTML)
element.appendChild(node);
});
}
@@ -4376,16 +6109,6 @@ tinymce.html.Writer = function(settings) {
});
},
- isBlock : function(node) {
- 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];
- },
-
replace : function(n, o, k) {
var t = this;
@@ -4447,7 +6170,7 @@ tinymce.html.Writer = function(settings) {
var c = /^\s*rgb\s*?\(\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?,\s*?([0-9]+)\s*?\)\s*$/i.exec(s);
function hex(s) {
- s = parseInt(s).toString(16);
+ s = parseInt(s, 10).toString(16);
return s.length > 1 ? s : '0' + s; // 0 -> 00
};
@@ -4581,11 +6304,11 @@ tinymce.html.Writer = function(settings) {
},
isEmpty : function(node, elements) {
- var self = this, i, attributes, type, walker, name, parentNode;
+ var self = this, i, attributes, type, walker, name, brCount = 0;
node = node.firstChild;
if (node) {
- walker = new tinymce.dom.TreeWalker(node);
+ walker = new tinymce.dom.TreeWalker(node, node.parentNode);
elements = elements || self.schema ? self.schema.getNonEmptyElements() : null;
do {
@@ -4599,9 +6322,9 @@ tinymce.html.Writer = function(settings) {
// Keep empty elements like <img />
name = node.nodeName.toLowerCase();
if (elements && elements[name]) {
- // Ignore single BR elements in blocks like <p><br /></p>
- parentNode = node.parentNode;
- if (name === 'br' && self.isBlock(parentNode) && parentNode.firstChild === node && parentNode.lastChild === node) {
+ // Ignore single BR elements in blocks like <p><br /></p> or <p><span><br /></span></p>
+ if (name === 'br') {
+ brCount++;
continue;
}
@@ -4618,22 +6341,23 @@ tinymce.html.Writer = function(settings) {
}
}
+ // Keep comment nodes
+ if (type == 8)
+ return false;
+
// Keep non whitespace text nodes
if ((type === 3 && !whiteSpaceRegExp.test(node.nodeValue)))
return false;
} while (node = walker.next());
}
- return true;
+ return brCount <= 1;
},
destroy : function(s) {
var t = this;
- if (t.events)
- t.events.destroy();
-
- t.win = t.doc = t.root = t.events = null;
+ t.win = t.doc = t.root = t.events = t.frag = null;
// Manual destroy then remove unload handler
if (!s)
@@ -4680,6 +6404,12 @@ tinymce.html.Writer = function(settings) {
function trim(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;
@@ -4690,7 +6420,10 @@ tinymce.html.Writer = function(settings) {
// 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 "<p> </p>"
- if (!t.isBlock(node.parentNode) || tinymce.trim(node.nodeValue).length > 0)
+ // Also keep text nodes with only spaces if surrounded by spans.
+ // eg. "<p><span>a</span> <span>b</span></p>" should keep space between a and b
+ var trimmedLength = tinymce.trim(node.nodeValue).length;
+ if (!t.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
@@ -4727,9 +6460,9 @@ tinymce.html.Writer = function(settings) {
// Insert middle chunk
if (re)
- pa.replaceChild(re, e);
- else
- pa.insertBefore(e, pe);
+ pa.replaceChild(re, e);
+ else
+ pa.insertBefore(e, pe);
// Insert after chunk
pa.insertBefore(trim(aft), pe);
@@ -4740,21 +6473,34 @@ tinymce.html.Writer = function(settings) {
},
bind : function(target, name, func, scope) {
- var t = this;
+ return this.events.add(target, name, func, scope || this);
+ },
- if (!t.events)
- t.events = new tinymce.dom.EventUtils();
+ unbind : function(target, name, func) {
+ return this.events.remove(target, name, func);
+ },
- return t.events.add(target, name, func, scope || this);
+ fire : function(target, name, evt) {
+ return this.events.fire(target, name, evt);
},
- unbind : function(target, name, func) {
- var t = this;
+ // Returns the content editable state of a node
+ getContentEditable: function(node) {
+ var contentEditable;
- if (!t.events)
- t.events = new tinymce.dom.EventUtils();
+ // Check type
+ if (node.nodeType != 1) {
+ return null;
+ }
+
+ // Check for fake content editable
+ contentEditable = node.getAttribute("data-mce-contenteditable");
+ if (contentEditable && contentEditable !== "inherit") {
+ return contentEditable;
+ }
- return t.events.remove(target, name, func);
+ // Check for real content editable
+ return node.contentEditable !== "inherit" ? node.contentEditable : null;
},
@@ -4866,9 +6612,14 @@ tinymce.html.Writer = function(settings) {
cloneContents : cloneContents,
insertNode : insertNode,
surroundContents : surroundContents,
- cloneRange : cloneRange
+ cloneRange : cloneRange,
+ toStringIE : toStringIE
});
+ function createDocumentFragment() {
+ return doc.createDocumentFragment();
+ };
+
function setStart(n, o) {
_setEndPoint(TRUE, n, o);
};
@@ -5201,10 +6952,10 @@ tinymce.html.Writer = function(settings) {
};
function _traverseSameContainer(how) {
- var frag, s, sub, n, cnt, sibling, xferNode;
+ var frag, s, sub, n, cnt, sibling, xferNode, start, len;
if (how != DELETE)
- frag = doc.createDocumentFragment();
+ frag = createDocumentFragment();
// If selection is empty, just return the fragment
if (t[START_OFFSET] == t[END_OFFSET])
@@ -5218,7 +6969,15 @@ tinymce.html.Writer = function(settings) {
// set the original text node to its new value
if (how != CLONE) {
- t[START_CONTAINER].deleteData(t[START_OFFSET], t[END_OFFSET] - t[START_OFFSET]);
+ n = t[START_CONTAINER];
+ start = t[START_OFFSET];
+ len = t[END_OFFSET] - t[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
t.collapse(TRUE);
@@ -5227,7 +6986,10 @@ tinymce.html.Writer = function(settings) {
if (how == DELETE)
return;
- frag.appendChild(doc.createTextNode(sub));
+ if (sub.length > 0) {
+ frag.appendChild(doc.createTextNode(sub));
+ }
+
return frag;
}
@@ -5235,7 +6997,7 @@ tinymce.html.Writer = function(settings) {
n = _getSelectedNode(t[START_CONTAINER], t[START_OFFSET]);
cnt = t[END_OFFSET] - t[START_OFFSET];
- while (cnt > 0) {
+ while (n && cnt > 0) {
sibling = n.nextSibling;
xferNode = _traverseFullySelected(n, how);
@@ -5257,7 +7019,7 @@ tinymce.html.Writer = function(settings) {
var frag, n, endIdx, cnt, sibling, xferNode;
if (how != DELETE)
- frag = doc.createDocumentFragment();
+ frag = createDocumentFragment();
n = _traverseRightBoundary(endAncestor, how);
@@ -5304,7 +7066,7 @@ tinymce.html.Writer = function(settings) {
var frag, startIdx, n, cnt, sibling, xferNode;
if (how != DELETE)
- frag = doc.createDocumentFragment();
+ frag = createDocumentFragment();
n = _traverseLeftBoundary(startAncestor, how);
if (frag)
@@ -5315,7 +7077,7 @@ tinymce.html.Writer = function(settings) {
cnt = t[END_OFFSET] - startIdx;
n = startAncestor.nextSibling;
- while (cnt > 0) {
+ while (n && cnt > 0) {
sibling = n.nextSibling;
xferNode = _traverseFullySelected(n, how);
@@ -5338,7 +7100,7 @@ tinymce.html.Writer = function(settings) {
var n, frag, commonParent, startOffset, endOffset, cnt, sibling, nextSibling;
if (how != DELETE)
- frag = doc.createDocumentFragment();
+ frag = createDocumentFragment();
n = _traverseLeftBoundary(startAncestor, how);
if (frag)
@@ -5473,7 +7235,7 @@ tinymce.html.Writer = function(settings) {
if (how == DELETE)
return;
- newNode = n.cloneNode(FALSE);
+ newNode = dom.clone(n, FALSE);
newNode.nodeValue = newNodeValue;
return newNode;
@@ -5482,18 +7244,29 @@ tinymce.html.Writer = function(settings) {
if (how == DELETE)
return;
- return n.cloneNode(FALSE);
+ return dom.clone(n, FALSE);
};
function _traverseFullySelected(n, how) {
if (how != DELETE)
- return how == CLONE ? n.cloneNode(TRUE) : n;
+ return how == CLONE ? dom.clone(n, TRUE) : n;
n.parentNode.removeChild(n);
};
+
+ function toStringIE() {
+ return dom.create('body', null, cloneContents()).outerText;
+ }
+
+ return t;
};
ns.Range = Range;
+
+ // 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();
+ };
})(tinymce.dom);
(function() {
@@ -5557,30 +7330,29 @@ tinymce.html.Writer = function(settings) {
} else
checkRng.collapse(false);
- checkRng.setEndPoint(start ? 'EndToStart' : 'EndToEnd', rng);
-
- // Fix for edge case: <div style="width: 100px; height:100px;"><table>..</table>ab|c</div>
- if (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) > 0) {
- checkRng = rng.duplicate();
- checkRng.collapse(start);
-
- offset = -1;
- while (parent == checkRng.parentElement()) {
- if (checkRng.move('character', -1) == 0)
- break;
-
- offset++;
+ // Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one
+ // We need to walk char by char since rng.text or rng.htmlText will trim line endings
+ offset = 0;
+ while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
+ if (checkRng.move('character', 1) === 0 || parent != checkRng.parentElement()) {
+ break;
}
- }
- offset = offset || checkRng.text.replace('\r\n', ' ').length;
+ offset++;
+ }
} else {
// Child position is after the selection endpoint
checkRng.collapse(true);
- checkRng.setEndPoint(start ? 'StartToStart' : 'StartToEnd', rng);
- // Get the length of the text to find where the endpoint is relative to it's container
- offset = checkRng.text.replace('\r\n', ' ').length;
+ // Walk character by character in text node until we hit the selected range endpoint, hit the end of document or parent isn't the right one
+ offset = 0;
+ while (checkRng.compareEndPoints(start ? 'StartToStart' : 'StartToEnd', rng) !== 0) {
+ if (checkRng.move('character', -1) === 0 || parent != checkRng.parentElement()) {
+ break;
+ }
+
+ offset++;
+ }
}
return {node : child, position : position, offset : offset, inside : inside};
@@ -5741,7 +7513,7 @@ tinymce.html.Writer = function(settings) {
var rng = selection.getRng(), start, end, bookmark = {};
function getIndexes(node) {
- var node, parent, root, children, i, indexes = [];
+ var parent, root, children, i, indexes = [];
parent = node.parentNode;
root = dom.getRoot().parentNode;
@@ -5850,7 +7622,8 @@ tinymce.html.Writer = function(settings) {
};
this.addRange = function(rng) {
- var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, doc = selection.dom.doc, body = doc.body;
+ var ieRng, ctrlRng, startContainer, startOffset, endContainer, endOffset, sibling,
+ doc = selection.dom.doc, body = doc.body, nativeRng, ctrlElm;
function setEndPoint(start) {
var container, offset, marker, tmpRng, nodes;
@@ -5882,12 +7655,13 @@ tinymce.html.Writer = function(settings) {
}
tmpRng.moveToElementText(marker);
- } else {
+ } else if (container.canHaveHTML) {
// Empty node selection for example <div>|</div>
- marker = doc.createTextNode('\uFEFF');
- container.appendChild(marker);
- tmpRng.moveToElementText(marker.parentNode);
- tmpRng.collapse(TRUE);
+ // Setting innerHTML with a span marker then remove that marker seems to keep empty block elements open
+ container.innerHTML = '<span>\uFEFF</span>';
+ marker = container.firstChild;
+ tmpRng.moveToElementText(marker);
+ tmpRng.collapse(FALSE); // Collapse false works better than true for some odd reason
}
ieRng.setEndPoint(start ? 'StartToStart' : 'EndToEnd', tmpRng);
@@ -5903,13 +7677,49 @@ tinymce.html.Writer = function(settings) {
ieRng = body.createTextRange();
// If single element selection then try making a control selection out of it
- if (startContainer == endContainer && startContainer.nodeType == 1 && startOffset == endOffset - 1) {
+ if (startContainer == endContainer && startContainer.nodeType == 1) {
+ // Trick to place the caret inside an empty block element like <p></p>
+ if (startOffset == endOffset && !startContainer.hasChildNodes()) {
+ if (startContainer.canHaveHTML) {
+ // Check if previous sibling is an empty block if it is then we need to render it
+ // IE would otherwise move the caret into the sibling instead of the empty startContainer see: #5236
+ // Example this: <p></p><p>|</p> would become this: <p>|</p><p></p>
+ sibling = startContainer.previousSibling;
+ if (sibling && !sibling.hasChildNodes() && dom.isBlock(sibling)) {
+ sibling.innerHTML = '\uFEFF';
+ } else {
+ sibling = null;
+ }
+
+ startContainer.innerHTML = '<span>\uFEFF</span><span>\uFEFF</span>';
+ ieRng.moveToElementText(startContainer.lastChild);
+ ieRng.select();
+ dom.doc.selection.clear();
+ startContainer.innerHTML = '';
+
+ if (sibling) {
+ sibling.innerHTML = '';
+ }
+ return;
+ } else {
+ startOffset = dom.nodeIndex(startContainer);
+ startContainer = startContainer.parentNode;
+ }
+ }
+
if (startOffset == endOffset - 1) {
try {
+ ctrlElm = startContainer.childNodes[startOffset];
ctrlRng = body.createControlRange();
- ctrlRng.addElement(startContainer.childNodes[startOffset]);
+ ctrlRng.addElement(ctrlElm);
ctrlRng.select();
- return;
+
+ // Check if the range produced is on the correct element and is a control range
+ // On IE 8 it will select the parent contentEditable container if you select an inner element see: #5398
+ nativeRng = selection.getRng();
+ if (nativeRng.item && ctrlElm === nativeRng.item(0)) {
+ return;
+ }
} catch (ex) {
// Ignore
}
@@ -5934,29 +7744,33 @@ tinymce.html.Writer = function(settings) {
/*
- * Sizzle CSS Selector Engine - v1.0
- * Copyright 2009, The Dojo Foundation
+ * Sizzle CSS Selector Engine
+ * Copyright, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ expando = "sizcache",
done = 0,
toString = Object.prototype.toString,
hasDuplicate = false,
- baseHasDuplicate = true;
+ baseHasDuplicate = true,
+ rBackslash = /\\/g,
+ rReturn = /\r\n/g,
+ rNonWord = /\W/;
// Here we check if the JavaScript engine is using some sort of
// optimization where it does not always call our comparision
// function. If that is the case, discard the hasDuplicate value.
// Thus far that includes Google Chrome.
-[0, 0].sort(function(){
+[0, 0].sort(function() {
baseHasDuplicate = false;
return 0;
});
-var Sizzle = function(selector, context, results, seed) {
+var Sizzle = function( selector, context, results, seed ) {
results = results || [];
context = context || document;
@@ -5965,24 +7779,27 @@ var Sizzle = function(selector, context, results, seed) {
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
return [];
}
-
+
if ( !selector || typeof selector !== "string" ) {
return results;
}
- var parts = [], m, set, checkSet, extra, prune = true, contextXML = Sizzle.isXML(context),
- soFar = selector, ret, cur, pop, i;
-
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
// Reset the position of the chunker regexp (start from head)
do {
- chunker.exec("");
- m = chunker.exec(soFar);
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
if ( m ) {
soFar = m[3];
-
+
parts.push( m[1] );
-
+
if ( m[2] ) {
extra = m[3];
break;
@@ -5991,8 +7808,10 @@ var Sizzle = function(selector, context, results, seed) {
} while ( m );
if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
- set = posProcess( parts[0] + parts[1], context );
+ set = posProcess( parts[0] + parts[1], context, seed );
+
} else {
set = Expr.relative[ parts[0] ] ?
[ context ] :
@@ -6004,27 +7823,35 @@ var Sizzle = function(selector, context, results, seed) {
if ( Expr.relative[ selector ] ) {
selector += parts.shift();
}
-
- set = posProcess( selector, set );
+
+ set = posProcess( selector, set, seed );
}
}
+
} else {
// Take a shortcut and set the context if the root selector is an ID
// (but not if it'll be faster if the inner selector is an ID)
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
ret = Sizzle.find( parts.shift(), context, contextXML );
- context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
}
if ( context ) {
ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
- set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
+
+ set = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set ) :
+ ret.set;
if ( parts.length > 0 ) {
- checkSet = makeArray(set);
+ checkSet = makeArray( set );
+
} else {
prune = false;
}
@@ -6045,6 +7872,7 @@ var Sizzle = function(selector, context, results, seed) {
Expr.relative[ cur ]( checkSet, pop, contextXML );
}
+
} else {
checkSet = parts = [];
}
@@ -6061,12 +7889,14 @@ var Sizzle = function(selector, context, results, seed) {
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
+
} else if ( context && context.nodeType === 1 ) {
for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
+
} else {
for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
@@ -6074,6 +7904,7 @@ var Sizzle = function(selector, context, results, seed) {
}
}
}
+
} else {
makeArray( checkSet, results );
}
@@ -6086,15 +7917,15 @@ var Sizzle = function(selector, context, results, seed) {
return results;
};
-Sizzle.uniqueSort = function(results){
+Sizzle.uniqueSort = function( results ) {
if ( sortOrder ) {
hasDuplicate = baseHasDuplicate;
- results.sort(sortOrder);
+ results.sort( sortOrder );
if ( hasDuplicate ) {
for ( var i = 1; i < results.length; i++ ) {
- if ( results[i] === results[i-1] ) {
- results.splice(i--, 1);
+ if ( results[i] === results[ i - 1 ] ) {
+ results.splice( i--, 1 );
}
}
}
@@ -6103,27 +7934,32 @@ Sizzle.uniqueSort = function(results){
return results;
};
-Sizzle.matches = function(expr, set){
- return Sizzle(expr, null, null, set);
+Sizzle.matches = function( expr, set ) {
+ return Sizzle( expr, null, null, set );
+};
+
+Sizzle.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
};
-Sizzle.find = function(expr, context, isXML){
- var set;
+Sizzle.find = function( expr, context, isXML ) {
+ var set, i, len, match, type, left;
if ( !expr ) {
return [];
}
- for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
- var type = Expr.order[i], match;
-
+ for ( i = 0, len = Expr.order.length; i < len; i++ ) {
+ type = Expr.order[i];
+
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
- var left = match[1];
- match.splice(1,1);
+ left = match[1];
+ match.splice( 1, 1 );
if ( left.substr( left.length - 1 ) !== "\\" ) {
- match[1] = (match[1] || "").replace(/\\/g, "");
+ match[1] = (match[1] || "").replace( rBackslash, "" );
set = Expr.find[ type ]( match, context, isXML );
+
if ( set != null ) {
expr = expr.replace( Expr.match[ type ], "" );
break;
@@ -6133,20 +7969,29 @@ Sizzle.find = function(expr, context, isXML){
}
if ( !set ) {
- set = context.getElementsByTagName("*");
+ set = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( "*" ) :
+ [];
}
- return {set: set, expr: expr};
+ return { set: set, expr: expr };
};
-Sizzle.filter = function(expr, set, inplace, not){
- var old = expr, result = [], curLoop = set, match, anyFound,
- isXMLFilter = set && set[0] && Sizzle.isXML(set[0]);
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ type, found, item, filter, left,
+ i, pass,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
while ( expr && set.length ) {
- for ( var type in Expr.filter ) {
+ for ( type in Expr.filter ) {
if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
- var filter = Expr.filter[ type ], found, item, left = match[1];
+ filter = Expr.filter[ type ];
+ left = match[1];
+
anyFound = false;
match.splice(1,1);
@@ -6164,23 +8009,26 @@ Sizzle.filter = function(expr, set, inplace, not){
if ( !match ) {
anyFound = found = true;
+
} else if ( match === true ) {
continue;
}
}
if ( match ) {
- for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
+ for ( i = 0; (item = curLoop[i]) != null; i++ ) {
if ( item ) {
found = filter( item, match, i, curLoop );
- var pass = not ^ !!found;
+ pass = not ^ found;
if ( inplace && found != null ) {
if ( pass ) {
anyFound = true;
+
} else {
curLoop[i] = false;
}
+
} else if ( pass ) {
result.push( item );
anyFound = true;
@@ -6209,6 +8057,7 @@ Sizzle.filter = function(expr, set, inplace, not){
if ( expr === old ) {
if ( anyFound == null ) {
Sizzle.error( expr );
+
} else {
break;
}
@@ -6221,35 +8070,78 @@ Sizzle.filter = function(expr, set, inplace, not){
};
Sizzle.error = function( msg ) {
- throw "Syntax error, unrecognized expression: " + msg;
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+var getText = Sizzle.getText = function( elem ) {
+ var i, node,
+ nodeType = elem.nodeType,
+ ret = "";
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent || innerText for elements
+ if ( typeof elem.textContent === 'string' ) {
+ return elem.textContent;
+ } else if ( typeof elem.innerText === 'string' ) {
+ // Replace IE's carriage returns
+ return elem.innerText.replace( rReturn, '' );
+ } else {
+ // Traverse it's children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( i = 0; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ if ( node.nodeType !== 8 ) {
+ ret += getText( node );
+ }
+ }
+ }
+ return ret;
};
var Expr = Sizzle.selectors = {
order: [ "ID", "NAME", "TAG" ],
+
match: {
ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
- ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
+ ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
- CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+\-]*)\))?/,
+ CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
},
+
leftMatch: {},
+
attrMap: {
"class": "className",
"for": "htmlFor"
},
+
attrHandle: {
- href: function(elem){
- return elem.getAttribute("href");
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ },
+ type: function( elem ) {
+ return elem.getAttribute( "type" );
}
},
+
relative: {
"+": function(checkSet, part){
var isPartStr = typeof part === "string",
- isTag = isPartStr && !/\W/.test(part),
+ isTag = isPartStr && !rNonWord.test( part ),
isPartStrNotTag = isPartStr && !isTag;
if ( isTag ) {
@@ -6270,23 +8162,29 @@ var Expr = Sizzle.selectors = {
Sizzle.filter( part, checkSet, true );
}
},
- ">": function(checkSet, part){
- var isPartStr = typeof part === "string",
- elem, i = 0, l = checkSet.length;
- if ( isPartStr && !/\W/.test(part) ) {
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !rNonWord.test( part ) ) {
part = part.toLowerCase();
for ( ; i < l; i++ ) {
elem = checkSet[i];
+
if ( elem ) {
var parent = elem.parentNode;
checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
}
}
+
} else {
for ( ; i < l; i++ ) {
elem = checkSet[i];
+
if ( elem ) {
checkSet[i] = isPartStr ?
elem.parentNode :
@@ -6299,39 +8197,50 @@ var Expr = Sizzle.selectors = {
}
}
},
+
"": function(checkSet, part, isXML){
- var doneName = done++, checkFn = dirCheck, nodeCheck;
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
- if ( typeof part === "string" && !/\W/.test(part) ) {
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
part = part.toLowerCase();
nodeCheck = part;
checkFn = dirNodeCheck;
}
- checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
},
- "~": function(checkSet, part, isXML){
- var doneName = done++, checkFn = dirCheck, nodeCheck;
- if ( typeof part === "string" && !/\W/.test(part) ) {
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
part = part.toLowerCase();
nodeCheck = part;
checkFn = dirNodeCheck;
}
- checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
+ checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
}
},
+
find: {
- ID: function(match, context, isXML){
+ ID: function( match, context, isXML ) {
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
- return m ? [m] : [];
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
}
},
- NAME: function(match, context){
+
+ NAME: function( match, context ) {
if ( typeof context.getElementsByName !== "undefined" ) {
- var ret = [], results = context.getElementsByName(match[1]);
+ var ret = [],
+ results = context.getElementsByName( match[1] );
for ( var i = 0, l = results.length; i < l; i++ ) {
if ( results[i].getAttribute("name") === match[1] ) {
@@ -6342,13 +8251,16 @@ var Expr = Sizzle.selectors = {
return ret.length === 0 ? null : ret;
}
},
- TAG: function(match, context){
- return context.getElementsByTagName(match[1]);
+
+ TAG: function( match, context ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( match[1] );
+ }
}
},
preFilter: {
- CLASS: function(match, curLoop, inplace, result, not, isXML){
- match = " " + match[1].replace(/\\/g, "") + " ";
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace( rBackslash, "" ) + " ";
if ( isXML ) {
return match;
@@ -6356,10 +8268,11 @@ var Expr = Sizzle.selectors = {
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
- if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
if ( !inplace ) {
result.push( elem );
}
+
} else if ( inplace ) {
curLoop[i] = false;
}
@@ -6368,16 +8281,25 @@ var Expr = Sizzle.selectors = {
return false;
},
- ID: function(match){
- return match[1].replace(/\\/g, "");
+
+ ID: function( match ) {
+ return match[1].replace( rBackslash, "" );
},
- TAG: function(match, curLoop){
- return match[1].toLowerCase();
+
+ TAG: function( match, curLoop ) {
+ return match[1].replace( rBackslash, "" ).toLowerCase();
},
- CHILD: function(match){
+
+ CHILD: function( match ) {
if ( match[1] === "nth" ) {
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ match[2] = match[2].replace(/^\+|\s*/g, '');
+
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
- var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
+ var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
@@ -6385,141 +8307,196 @@ var Expr = Sizzle.selectors = {
match[2] = (test[1] + (test[2] || 1)) - 0;
match[3] = test[3] - 0;
}
+ else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
// TODO: Move to normal caching system
match[0] = done++;
return match;
},
- ATTR: function(match, curLoop, inplace, result, not, isXML){
- var name = match[1].replace(/\\/g, "");
-
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1] = match[1].replace( rBackslash, "" );
+
if ( !isXML && Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
}
+ // Handle if an un-quoted value was used
+ match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
if ( match[2] === "~=" ) {
match[4] = " " + match[4] + " ";
}
return match;
},
- PSEUDO: function(match, curLoop, inplace, result, not){
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
if ( match[1] === "not" ) {
// If we're dealing with a complex expression, or a simple one
if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
match[3] = Sizzle(match[3], null, null, curLoop);
+
} else {
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
+
if ( !inplace ) {
result.push.apply( result, ret );
}
+
return false;
}
+
} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
return true;
}
-
+
return match;
},
- POS: function(match){
+
+ POS: function( match ) {
match.unshift( true );
+
return match;
}
},
+
filters: {
- enabled: function(elem){
+ enabled: function( elem ) {
return elem.disabled === false && elem.type !== "hidden";
},
- disabled: function(elem){
+
+ disabled: function( elem ) {
return elem.disabled === true;
},
- checked: function(elem){
+
+ checked: function( elem ) {
return elem.checked === true;
},
- selected: function(elem){
+
+ selected: function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
- elem.parentNode.selectedIndex;
+ if ( elem.parentNode ) {
+ elem.parentNode.selectedIndex;
+ }
+
return elem.selected === true;
},
- parent: function(elem){
+
+ parent: function( elem ) {
return !!elem.firstChild;
},
- empty: function(elem){
+
+ empty: function( elem ) {
return !elem.firstChild;
},
- has: function(elem, i, match){
+
+ has: function( elem, i, match ) {
return !!Sizzle( match[3], elem ).length;
},
- header: function(elem){
+
+ header: function( elem ) {
return (/h\d/i).test( elem.nodeName );
},
- text: function(elem){
- return "text" === elem.type;
+
+ text: function( elem ) {
+ var attr = elem.getAttribute( "type" ), type = elem.type;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
},
- radio: function(elem){
- return "radio" === elem.type;
+
+ radio: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
},
- checkbox: function(elem){
- return "checkbox" === elem.type;
+
+ checkbox: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
},
- file: function(elem){
- return "file" === elem.type;
+
+ file: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
},
- password: function(elem){
- return "password" === elem.type;
+
+ password: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
},
- submit: function(elem){
- return "submit" === elem.type;
+
+ submit: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "submit" === elem.type;
},
- image: function(elem){
- return "image" === elem.type;
+
+ image: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
},
- reset: function(elem){
- return "reset" === elem.type;
+
+ reset: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "reset" === elem.type;
},
- button: function(elem){
- return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
+
+ button: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && "button" === elem.type || name === "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
},
- input: function(elem){
- return (/input|select|textarea|button/i).test(elem.nodeName);
+
+ focus: function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
}
},
setFilters: {
- first: function(elem, i){
+ first: function( elem, i ) {
return i === 0;
},
- last: function(elem, i, match, array){
+
+ last: function( elem, i, match, array ) {
return i === array.length - 1;
},
- even: function(elem, i){
+
+ even: function( elem, i ) {
return i % 2 === 0;
},
- odd: function(elem, i){
+
+ odd: function( elem, i ) {
return i % 2 === 1;
},
- lt: function(elem, i, match){
+
+ lt: function( elem, i, match ) {
return i < match[3] - 0;
},
- gt: function(elem, i, match){
+
+ gt: function( elem, i, match ) {
return i > match[3] - 0;
},
- nth: function(elem, i, match){
+
+ nth: function( elem, i, match ) {
return match[3] - 0 === i;
},
- eq: function(elem, i, match){
+
+ eq: function( elem, i, match ) {
return match[3] - 0 === i;
}
},
filter: {
- PSEUDO: function(elem, match, i, array){
- var name = match[1], filter = Expr.filters[ name ];
+ PSEUDO: function( elem, match, i, array ) {
+ var name = match[1],
+ filter = Expr.filters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
+
} else if ( name === "contains" ) {
- return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
+ return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
} else if ( name === "not" ) {
var not = match[3];
@@ -6530,72 +8507,96 @@ var Expr = Sizzle.selectors = {
}
return true;
+
} else {
- Sizzle.error( "Syntax error, unrecognized expression: " + name );
+ Sizzle.error( name );
}
},
- CHILD: function(elem, match){
- var type = match[1], node = elem;
- switch (type) {
- case 'only':
- case 'first':
- while ( (node = node.previousSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
+
+ CHILD: function( elem, match ) {
+ var first, last,
+ doneName, parent, cache,
+ count, diff,
+ type = match[1],
+ node = elem;
+
+ switch ( type ) {
+ case "only":
+ case "first":
+ while ( (node = node.previousSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
}
}
- if ( type === "first" ) {
- return true;
+
+ if ( type === "first" ) {
+ return true;
}
+
node = elem;
- case 'last':
- while ( (node = node.nextSibling) ) {
- if ( node.nodeType === 1 ) {
- return false;
+
+ /* falls through */
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
}
}
+
return true;
- case 'nth':
- var first = match[2], last = match[3];
+
+ case "nth":
+ first = match[2];
+ last = match[3];
if ( first === 1 && last === 0 ) {
return true;
}
-
- var doneName = match[0],
- parent = elem.parentNode;
-
- if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
- var count = 0;
+
+ doneName = match[0];
+ parent = elem.parentNode;
+
+ if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
+ count = 0;
+
for ( node = parent.firstChild; node; node = node.nextSibling ) {
if ( node.nodeType === 1 ) {
node.nodeIndex = ++count;
}
- }
- parent.sizcache = doneName;
+ }
+
+ parent[ expando ] = doneName;
}
-
- var diff = elem.nodeIndex - last;
+
+ diff = elem.nodeIndex - last;
+
if ( first === 0 ) {
return diff === 0;
+
} else {
return ( diff % first === 0 && diff / first >= 0 );
}
}
},
- ID: function(elem, match){
+
+ ID: function( elem, match ) {
return elem.nodeType === 1 && elem.getAttribute("id") === match;
},
- TAG: function(elem, match){
- return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
+
+ TAG: function( elem, match ) {
+ return (match === "*" && elem.nodeType === 1) || !!elem.nodeName && elem.nodeName.toLowerCase() === match;
},
- CLASS: function(elem, match){
+
+ CLASS: function( elem, match ) {
return (" " + (elem.className || elem.getAttribute("class")) + " ")
.indexOf( match ) > -1;
},
- ATTR: function(elem, match){
+
+ ATTR: function( elem, match ) {
var name = match[1],
- result = Expr.attrHandle[ name ] ?
+ result = Sizzle.attr ?
+ Sizzle.attr( elem, name ) :
+ Expr.attrHandle[ name ] ?
Expr.attrHandle[ name ]( elem ) :
elem[ name ] != null ?
elem[ name ] :
@@ -6606,6 +8607,8 @@ var Expr = Sizzle.selectors = {
return result == null ?
type === "!=" :
+ !type && Sizzle.attr ?
+ result != null :
type === "=" ?
value === check :
type === "*=" ?
@@ -6624,8 +8627,10 @@ var Expr = Sizzle.selectors = {
value === check || value.substr(0, check.length + 1) === check + "-" :
false;
},
- POS: function(elem, match, i, array){
- var name = match[2], filter = Expr.setFilters[ name ];
+
+ POS: function( elem, match, i, array ) {
+ var name = match[2],
+ filter = Expr.setFilters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
@@ -6643,15 +8648,18 @@ for ( var type in Expr.match ) {
Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
}
+// Expose origPOS
+// "global" as in regardless of relation to brackets/parens
+Expr.match.globalPOS = origPOS;
-var makeArray = function(array, results) {
+var makeArray = function( array, results ) {
array = Array.prototype.slice.call( array, 0 );
if ( results ) {
results.push.apply( results, array );
return results;
}
-
+
return array;
};
@@ -6663,17 +8671,20 @@ try {
Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
// Provide a fallback method if it does not work
-} catch(e){
- makeArray = function(array, results) {
- var ret = results || [], i = 0;
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
if ( toString.call(array) === "[object Array]" ) {
Array.prototype.push.apply( ret, array );
+
} else {
if ( typeof array.length === "number" ) {
for ( var l = array.length; i < l; i++ ) {
ret.push( array[i] );
}
+
} else {
for ( ; array[i]; i++ ) {
ret.push( array[i] );
@@ -6685,110 +8696,141 @@ try {
};
}
-var sortOrder;
+var sortOrder, siblingCheck;
if ( document.documentElement.compareDocumentPosition ) {
sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
- if ( a == b ) {
- hasDuplicate = true;
- }
return a.compareDocumentPosition ? -1 : 1;
}
- var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
- if ( ret === 0 ) {
- hasDuplicate = true;
- }
- return ret;
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
};
-} else if ( "sourceIndex" in document.documentElement ) {
+
+} else {
sortOrder = function( a, b ) {
- if ( !a.sourceIndex || !b.sourceIndex ) {
- if ( a == b ) {
- hasDuplicate = true;
- }
- return a.sourceIndex ? -1 : 1;
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
}
- var ret = a.sourceIndex - b.sourceIndex;
- if ( ret === 0 ) {
- hasDuplicate = true;
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
}
- return ret;
- };
-} else if ( document.createRange ) {
- sortOrder = function( a, b ) {
- if ( !a.ownerDocument || !b.ownerDocument ) {
- if ( a == b ) {
- hasDuplicate = true;
- }
- return a.ownerDocument ? -1 : 1;
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
}
- var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
- aRange.setStart(a, 0);
- aRange.setEnd(a, 0);
- bRange.setStart(b, 0);
- bRange.setEnd(b, 0);
- var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
- if ( ret === 0 ) {
- hasDuplicate = true;
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
}
- return ret;
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
};
-}
-// Utility function for retreiving the text value of an array of DOM nodes
-Sizzle.getText = function( elems ) {
- var ret = "", elem;
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
- for ( var i = 0; elems[i]; i++ ) {
- elem = elems[i];
+ var cur = a.nextSibling;
- // Get the text from text nodes and CDATA nodes
- if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
- ret += elem.nodeValue;
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
- // Traverse everything else, except comment nodes
- } else if ( elem.nodeType !== 8 ) {
- ret += Sizzle.getText( elem.childNodes );
+ cur = cur.nextSibling;
}
- }
- return ret;
-};
+ return 1;
+ };
+}
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
// We're going to inject a fake input element with a specified name
var form = document.createElement("div"),
- id = "script" + (new Date()).getTime();
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
form.innerHTML = "<a name='" + id + "'/>";
// Inject it into the root element, check its status, and remove it quickly
- var root = document.documentElement;
root.insertBefore( form, root.firstChild );
// The workaround has to do additional checks after a getElementById
// Which slows things down for other browsers (hence the branching)
if ( document.getElementById( id ) ) {
- Expr.find.ID = function(match, context, isXML){
+ Expr.find.ID = function( match, context, isXML ) {
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
- return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
+
+ return m ?
+ m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
+ [m] :
+ undefined :
+ [];
}
};
- Expr.filter.ID = function(elem, match){
+ Expr.filter.ID = function( elem, match ) {
var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
+
return elem.nodeType === 1 && node && node.nodeValue === match;
};
}
root.removeChild( form );
- root = form = null; // release memory in IE
+
+ // release memory in IE
+ root = form = null;
})();
(function(){
@@ -6801,8 +8843,8 @@ Sizzle.getText = function( elems ) {
// Make sure no comments are found
if ( div.getElementsByTagName("*").length > 0 ) {
- Expr.find.TAG = function(match, context){
- var results = context.getElementsByTagName(match[1]);
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
// Filter out possible comments
if ( match[1] === "*" ) {
@@ -6823,19 +8865,25 @@ Sizzle.getText = function( elems ) {
// Check to see if an attribute returns normalized href attributes
div.innerHTML = "<a href='#'></a>";
+
if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
div.firstChild.getAttribute("href") !== "#" ) {
- Expr.attrHandle.href = function(elem){
- return elem.getAttribute("href", 2);
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
};
}
- div = null; // release memory in IE
+ // release memory in IE
+ div = null;
})();
if ( document.querySelectorAll ) {
(function(){
- var oldSizzle = Sizzle, div = document.createElement("div");
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
div.innerHTML = "<p class='TEST'></p>";
// Safari can't handle uppercase or unicode characters when
@@ -6843,18 +8891,89 @@ if ( document.querySelectorAll ) {
if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
return;
}
-
- Sizzle = function(query, context, extra, seed){
+
+ Sizzle = function( query, context, extra, seed ) {
context = context || document;
// Only use querySelectorAll on non-XML documents
// (ID selectors don't work in non-HTML documents)
- if ( !seed && context.nodeType === 9 && !Sizzle.isXML(context) ) {
- try {
- return makeArray( context.querySelectorAll(query), extra );
- } catch(e){}
+ if ( !seed && !Sizzle.isXML(context) ) {
+ // See if we find a selector to speed up
+ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+ // Speed-up: Sizzle("TAG")
+ if ( match[1] ) {
+ return makeArray( context.getElementsByTagName( query ), extra );
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+ return makeArray( context.getElementsByClassName( match[2] ), extra );
+ }
+ }
+
+ if ( context.nodeType === 9 ) {
+ // Speed-up: Sizzle("body")
+ // The body element only exists once, optimize finding it
+ if ( query === "body" && context.body ) {
+ return makeArray( [ context.body ], extra );
+
+ // Speed-up: Sizzle("#ID")
+ } else if ( match && match[3] ) {
+ var elem = context.getElementById( match[3] );
+
+ // 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[3] ) {
+ return makeArray( [ elem ], extra );
+ }
+
+ } else {
+ return makeArray( [], extra );
+ }
+ }
+
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // 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
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || id,
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ } else {
+ nid = nid.replace( /'/g, "\\$&" );
+ }
+ if ( relativeHierarchySelector && hasParent ) {
+ context = context.parentNode;
+ }
+
+ try {
+ if ( !relativeHierarchySelector || hasParent ) {
+ return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+ }
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+ }
}
-
+
return oldSizzle(query, context, extra, seed);
};
@@ -6862,11 +8981,56 @@ if ( document.querySelectorAll ) {
Sizzle[ prop ] = oldSizzle[ prop ];
}
- div = null; // release memory in IE
+ // release memory in IE
+ div = null;
})();
}
(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
+
+ if ( matches ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9 fails this)
+ var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+ var ret = matches.call( node, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || !disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9, so check for that
+ node.document && node.document.nodeType !== 11 ) {
+ return ret;
+ }
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
var div = document.createElement("div");
div.innerHTML = "<div class='test e'></div><div class='test'></div>";
@@ -6883,32 +9047,35 @@ if ( document.querySelectorAll ) {
if ( div.getElementsByClassName("e").length === 1 ) {
return;
}
-
+
Expr.order.splice(1, 0, "CLASS");
- Expr.find.CLASS = function(match, context, isXML) {
+ Expr.find.CLASS = function( match, context, isXML ) {
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
return context.getElementsByClassName(match[1]);
}
};
- div = null; // release memory in IE
+ // release memory in IE
+ div = null;
})();
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
+
if ( elem ) {
- elem = elem[dir];
var match = false;
+ elem = elem[dir];
+
while ( elem ) {
- if ( elem.sizcache === doneName ) {
+ if ( elem[ expando ] === doneName ) {
match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 && !isXML ){
- elem.sizcache = doneName;
+ elem[ expando ] = doneName;
elem.sizset = i;
}
@@ -6928,21 +9095,24 @@ function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
+
if ( elem ) {
- elem = elem[dir];
var match = false;
+ elem = elem[dir];
+
while ( elem ) {
- if ( elem.sizcache === doneName ) {
+ if ( elem[ expando ] === doneName ) {
match = checkSet[elem.sizset];
break;
}
if ( elem.nodeType === 1 ) {
if ( !isXML ) {
- elem.sizcache = doneName;
+ elem[ expando ] = doneName;
elem.sizset = i;
}
+
if ( typeof cur !== "string" ) {
if ( elem === cur ) {
match = true;
@@ -6963,21 +9133,34 @@ function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
}
}
-Sizzle.contains = document.compareDocumentPosition ? function(a, b){
- return !!(a.compareDocumentPosition(b) & 16);
-} : function(a, b){
- return a !== b && (a.contains ? a.contains(b) : true);
-};
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
-Sizzle.isXML = function(elem){
+Sizzle.isXML = function( elem ) {
// documentElement is verified for cases where it doesn't yet exist
- // (such as loading iframes in IE - #4833)
+ // (such as loading iframes in IE - #4833)
var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
-var posProcess = function(selector, context){
- var tmpSet = [], later = "", match,
+var posProcess = function( selector, context, seed ) {
+ var match,
+ tmpSet = [],
+ later = "",
root = context.nodeType ? [context] : context;
// Position selectors must be done after the filter
@@ -6990,7 +9173,7 @@ var posProcess = function(selector, context){
selector = Expr.relative[selector] ? selector + "*" : selector;
for ( var i = 0, l = root.length; i < l; i++ ) {
- Sizzle( selector, root[i], tmpSet );
+ Sizzle( selector, root[i], tmpSet, seed );
}
return Sizzle.filter( later, tmpSet );
@@ -7004,290 +9187,6 @@ window.tinymce.dom.Sizzle = Sizzle;
(function(tinymce) {
- // Shorten names
- var each = tinymce.each, DOM = tinymce.DOM, isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, Event;
-
- tinymce.create('tinymce.dom.EventUtils', {
- EventUtils : function() {
- this.inits = [];
- this.events = [];
- },
-
- add : function(o, n, f, s) {
- var cb, t = this, el = t.events, r;
-
- if (n instanceof Array) {
- r = [];
-
- each(n, function(n) {
- r.push(t.add(o, n, f, s));
- });
-
- return r;
- }
-
- // Handle array
- if (o && o.hasOwnProperty && o instanceof Array) {
- r = [];
-
- each(o, function(o) {
- o = DOM.get(o);
- r.push(t.add(o, n, f, s));
- });
-
- return r;
- }
-
- o = DOM.get(o);
-
- if (!o)
- return;
-
- // Setup event callback
- cb = function(e) {
- // Is all events disabled
- if (t.disabled)
- return;
-
- e = e || window.event;
-
- // Patch in target, preventDefault and stopPropagation in IE it's W3C valid
- if (e && isIE) {
- if (!e.target)
- e.target = e.srcElement;
-
- // Patch in preventDefault, stopPropagation methods for W3C compatibility
- tinymce.extend(e, t._stoppers);
- }
-
- if (!s)
- return f(e);
-
- return f.call(s, e);
- };
-
- if (n == 'unload') {
- tinymce.unloads.unshift({func : cb});
- return cb;
- }
-
- if (n == 'init') {
- if (t.domLoaded)
- cb();
- else
- t.inits.push(cb);
-
- return cb;
- }
-
- // Store away listener reference
- el.push({
- obj : o,
- name : n,
- func : f,
- cfunc : cb,
- scope : s
- });
-
- t._add(o, n, cb);
-
- return f;
- },
-
- remove : function(o, n, f) {
- var t = this, a = t.events, s = false, r;
-
- // Handle array
- if (o && o.hasOwnProperty && o instanceof Array) {
- r = [];
-
- each(o, function(o) {
- o = DOM.get(o);
- r.push(t.remove(o, n, f));
- });
-
- return r;
- }
-
- o = DOM.get(o);
-
- each(a, function(e, i) {
- if (e.obj == o && e.name == n && (!f || (e.func == f || e.cfunc == f))) {
- a.splice(i, 1);
- t._remove(o, n, e.cfunc);
- s = true;
- return false;
- }
- });
-
- return s;
- },
-
- clear : function(o) {
- var t = this, a = t.events, i, e;
-
- if (o) {
- o = DOM.get(o);
-
- for (i = a.length - 1; i >= 0; i--) {
- e = a[i];
-
- if (e.obj === o) {
- t._remove(e.obj, e.name, e.cfunc);
- e.obj = e.cfunc = null;
- a.splice(i, 1);
- }
- }
- }
- },
-
- cancel : function(e) {
- if (!e)
- return false;
-
- this.stop(e);
-
- return this.prevent(e);
- },
-
- stop : function(e) {
- if (e.stopPropagation)
- e.stopPropagation();
- else
- e.cancelBubble = true;
-
- return false;
- },
-
- prevent : function(e) {
- if (e.preventDefault)
- e.preventDefault();
- else
- e.returnValue = false;
-
- return false;
- },
-
- destroy : function() {
- var t = this;
-
- each(t.events, function(e, i) {
- t._remove(e.obj, e.name, e.cfunc);
- e.obj = e.cfunc = null;
- });
-
- t.events = [];
- t = null;
- },
-
- _add : function(o, n, f) {
- if (o.attachEvent)
- o.attachEvent('on' + n, f);
- else if (o.addEventListener)
- o.addEventListener(n, f, false);
- else
- o['on' + n] = f;
- },
-
- _remove : function(o, n, f) {
- if (o) {
- try {
- if (o.detachEvent)
- o.detachEvent('on' + n, f);
- else if (o.removeEventListener)
- o.removeEventListener(n, f, false);
- else
- o['on' + n] = null;
- } catch (ex) {
- // Might fail with permission denined on IE so we just ignore that
- }
- }
- },
-
- _pageInit : function(win) {
- var t = this;
-
- // Keep it from running more than once
- if (t.domLoaded)
- return;
-
- t.domLoaded = true;
-
- each(t.inits, function(c) {
- c();
- });
-
- t.inits = [];
- },
-
- _wait : function(win) {
- var t = this, doc = win.document;
-
- // No need since the document is already loaded
- if (win.tinyMCE_GZ && tinyMCE_GZ.loaded) {
- t.domLoaded = 1;
- return;
- }
-
- // Use IE method
- if (doc.attachEvent) {
- doc.attachEvent("onreadystatechange", function() {
- if (doc.readyState === "complete") {
- doc.detachEvent("onreadystatechange", arguments.callee);
- t._pageInit(win);
- }
- });
-
- if (doc.documentElement.doScroll && win == win.top) {
- (function() {
- if (t.domLoaded)
- return;
-
- 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) {
- setTimeout(arguments.callee, 0);
- return;
- }
-
- t._pageInit(win);
- })();
- }
- } else if (doc.addEventListener) {
- t._add(win, 'DOMContentLoaded', function() {
- t._pageInit(win);
- });
- }
-
- t._add(win, 'load', function() {
- t._pageInit(win);
- });
- },
-
- _stoppers : {
- preventDefault : function() {
- this.returnValue = false;
- },
-
- stopPropagation : function() {
- this.cancelBubble = true;
- }
- }
- });
-
- Event = tinymce.dom.Event = new tinymce.dom.EventUtils();
-
- // Dispatch DOM content loaded event for IE and Safari
- Event._wait(window);
-
- tinymce.addUnload(function() {
- Event.destroy();
- });
-})(tinymce);
-
-(function(tinymce) {
tinymce.dom.Element = function(id, settings) {
var t = this, dom, el;
@@ -7303,20 +9202,20 @@ window.tinymce.dom.Sizzle = Sizzle;
('getPos,getRect,getParent,add,setStyle,getStyle,setStyles,' +
'setAttrib,setAttribs,getAttrib,addClass,removeClass,' +
'hasClass,getOuterHTML,setOuterHTML,remove,show,hide,' +
- 'isHidden,setHTML,get').split(/,/)
- , function(k) {
- t[k] = function() {
- var a = [id], i;
+ 'isHidden,setHTML,get').split(/,/), function(k) {
+ t[k] = function() {
+ var a = [id], i;
- for (i = 0; i < arguments.length; i++)
- a.push(arguments[i]);
+ for (i = 0; i < arguments.length; i++)
+ a.push(arguments[i]);
- a = dom[k].apply(dom, a);
- t.update(k);
+ a = dom[k].apply(dom, a);
+ t.update(k);
- return a;
- };
- });
+ return a;
+ };
+ }
+ );
tinymce.extend(t, {
on : function(n, f, s) {
@@ -7402,15 +9301,16 @@ window.tinymce.dom.Sizzle = Sizzle;
};
// Shorten names
- var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each;
+ var is = tinymce.is, isIE = tinymce.isIE, each = tinymce.each, TreeWalker = tinymce.dom.TreeWalker;
tinymce.create('tinymce.dom.Selection', {
- Selection : function(dom, win, serializer) {
+ Selection : function(dom, win, serializer, editor) {
var t = this;
t.dom = dom;
t.win = win;
t.serializer = serializer;
+ t.editor = editor;
// Add events
each([
@@ -7509,7 +9409,7 @@ window.tinymce.dom.Sizzle = Sizzle;
} else {
rng.deleteContents();
- if (doc.body.childNodes.length == 0) {
+ if (doc.body.childNodes.length === 0) {
doc.body.innerHTML = content;
} else {
// createContextualFragment doesn't exists in IE 9 DOMRanges
@@ -7566,7 +9466,7 @@ window.tinymce.dom.Sizzle = Sizzle;
},
getStart : function() {
- var rng = this.getRng(), startElement, parentElement, checkRng, node;
+ var self = this, rng = self.getRng(), startElement, parentElement, checkRng, node;
if (rng.duplicate || rng.item) {
// Control selection, return first item
@@ -7577,6 +9477,9 @@ window.tinymce.dom.Sizzle = Sizzle;
checkRng = rng.duplicate();
checkRng.collapse(1);
startElement = checkRng.parentElement();
+ if (startElement.ownerDocument !== self.dom.doc) {
+ startElement = self.dom.getRoot();
+ }
// Check if range parent is inside the start element, then return the inner parent element
// This will fix issues when a single element is selected, IE would otherwise return the wrong start element
@@ -7603,31 +9506,34 @@ window.tinymce.dom.Sizzle = Sizzle;
},
getEnd : function() {
- var t = this, r = t.getRng(), e, eo;
+ var self = this, rng = self.getRng(), endElement, endOffset;
- if (r.duplicate || r.item) {
- if (r.item)
- return r.item(0);
+ if (rng.duplicate || rng.item) {
+ if (rng.item)
+ return rng.item(0);
- r = r.duplicate();
- r.collapse(0);
- e = r.parentElement();
+ rng = rng.duplicate();
+ rng.collapse(0);
+ endElement = rng.parentElement();
+ if (endElement.ownerDocument !== self.dom.doc) {
+ endElement = self.dom.getRoot();
+ }
- if (e && e.nodeName == 'BODY')
- return e.lastChild || e;
+ if (endElement && endElement.nodeName == 'BODY')
+ return endElement.lastChild || endElement;
- return e;
+ return endElement;
} else {
- e = r.endContainer;
- eo = r.endOffset;
+ endElement = rng.endContainer;
+ endOffset = rng.endOffset;
- if (e.nodeType == 1 && e.hasChildNodes())
- e = e.childNodes[eo > 0 ? eo - 1 : eo];
+ if (endElement.nodeType == 1 && endElement.hasChildNodes())
+ endElement = endElement.childNodes[endOffset > 0 ? endOffset - 1 : endOffset];
- if (e && e.nodeType == 3)
- return e.parentNode;
+ if (endElement && endElement.nodeType == 3)
+ return endElement.parentNode;
- return e;
+ return endElement;
}
},
@@ -7645,46 +9551,69 @@ window.tinymce.dom.Sizzle = Sizzle;
return index;
};
- if (type == 2) {
- function getLocation() {
- var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};
+ function normalizeTableCellSelection(rng) {
+ function moveEndPoint(start) {
+ var container, offset, childNodes, prefix = start ? 'start' : 'end';
- function getPoint(rng, start) {
- var container = rng[start ? 'startContainer' : 'endContainer'],
- offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
+ container = rng[prefix + 'Container'];
+ offset = rng[prefix + 'Offset'];
- if (container.nodeType == 3) {
- if (normalized) {
- for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)
- offset += node.nodeValue.length;
- }
+ if (container.nodeType == 1 && container.nodeName == "TR") {
+ childNodes = container.childNodes;
+ container = childNodes[Math.min(start ? offset : offset - 1, childNodes.length - 1)];
+ if (container) {
+ offset = start ? 0 : container.childNodes.length;
+ rng['set' + (start ? 'Start' : 'End')](container, offset);
+ }
+ }
+ };
- point.push(offset);
- } else {
- childNodes = container.childNodes;
+ moveEndPoint(true);
+ moveEndPoint();
- if (offset >= childNodes.length && childNodes.length) {
- after = 1;
- offset = Math.max(0, childNodes.length - 1);
- }
+ return rng;
+ };
- point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);
+ function getLocation() {
+ var rng = t.getRng(true), root = dom.getRoot(), bookmark = {};
+
+ function getPoint(rng, start) {
+ var container = rng[start ? 'startContainer' : 'endContainer'],
+ offset = rng[start ? 'startOffset' : 'endOffset'], point = [], node, childNodes, after = 0;
+
+ if (container.nodeType == 3) {
+ if (normalized) {
+ for (node = container.previousSibling; node && node.nodeType == 3; node = node.previousSibling)
+ offset += node.nodeValue.length;
}
- for (; container && container != root; container = container.parentNode)
- point.push(t.dom.nodeIndex(container, normalized));
+ point.push(offset);
+ } else {
+ childNodes = container.childNodes;
- return point;
- };
+ if (offset >= childNodes.length && childNodes.length) {
+ after = 1;
+ offset = Math.max(0, childNodes.length - 1);
+ }
- bookmark.start = getPoint(rng, true);
+ point.push(t.dom.nodeIndex(childNodes[offset], normalized) + after);
+ }
- if (!t.isCollapsed())
- bookmark.end = getPoint(rng);
+ for (; container && container != root; container = container.parentNode)
+ point.push(t.dom.nodeIndex(container, normalized));
- return bookmark;
+ return point;
};
+ bookmark.start = getPoint(rng, true);
+
+ if (!t.isCollapsed())
+ bookmark.end = getPoint(rng);
+
+ return bookmark;
+ };
+
+ if (type == 2) {
if (t.tridentSel)
return t.tridentSel.getBookmark(type);
@@ -7717,7 +9646,7 @@ window.tinymce.dom.Sizzle = Sizzle;
// Detect the empty space after block elements in IE and move the end back one character <p></p>] becomes <p>]</p>
rng.moveToElementText(rng2.parentElement());
- if (rng.compareEndPoints('StartToEnd', rng2) == 0)
+ if (rng.compareEndPoints('StartToEnd', rng2) === 0)
rng2.move('character', -1);
rng2.pasteHTML('<span data-mce-type="bookmark" id="' + id + '_end" style="' + styles + '">' + chr + '</span>');
@@ -7740,7 +9669,7 @@ window.tinymce.dom.Sizzle = Sizzle;
return {name : name, index : findIndex(name, element)};
// W3C method
- rng2 = rng.cloneRange();
+ rng2 = normalizeTableCellSelection(rng.cloneRange());
// Insert end marker
if (!collapsed) {
@@ -7748,6 +9677,7 @@ window.tinymce.dom.Sizzle = Sizzle;
rng2.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_end', style : styles}, chr));
}
+ rng = normalizeTableCellSelection(rng);
rng.collapse(true);
rng.insertNode(dom.create('span', {'data-mce-type' : "bookmark", id : id + '_start', style : styles}, chr));
}
@@ -7760,122 +9690,122 @@ window.tinymce.dom.Sizzle = Sizzle;
moveToBookmark : function(bookmark) {
var t = this, dom = t.dom, marker1, marker2, rng, root, startContainer, endContainer, startOffset, endOffset;
- if (bookmark) {
- if (bookmark.start) {
- rng = dom.createRng();
- root = dom.getRoot();
+ function setEndPoint(start) {
+ var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
- function setEndPoint(start) {
- var point = bookmark[start ? 'start' : 'end'], i, node, offset, children;
+ if (point) {
+ offset = point[0];
- if (point) {
- offset = point[0];
+ // Find container node
+ for (node = root, i = point.length - 1; i >= 1; i--) {
+ children = node.childNodes;
- // Find container node
- for (node = root, i = point.length - 1; i >= 1; i--) {
- children = node.childNodes;
+ if (point[i] > children.length - 1)
+ return;
- if (point[i] > children.length - 1)
- return;
+ node = children[point[i]];
+ }
- node = children[point[i]];
- }
+ // Move text offset to best suitable location
+ if (node.nodeType === 3)
+ offset = Math.min(point[0], node.nodeValue.length);
- // Move text offset to best suitable location
- if (node.nodeType === 3)
- offset = Math.min(point[0], node.nodeValue.length);
+ // Move element offset to best suitable location
+ if (node.nodeType === 1)
+ offset = Math.min(point[0], node.childNodes.length);
- // Move element offset to best suitable location
- if (node.nodeType === 1)
- offset = Math.min(point[0], node.childNodes.length);
+ // Set offset within container node
+ if (start)
+ rng.setStart(node, offset);
+ else
+ rng.setEnd(node, offset);
+ }
- // Set offset within container node
- if (start)
- rng.setStart(node, offset);
- else
- rng.setEnd(node, offset);
- }
+ return true;
+ };
- return true;
- };
+ function restoreEndPoint(suffix) {
+ var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
- if (t.tridentSel)
- return t.tridentSel.moveToBookmark(bookmark);
+ if (marker) {
+ node = marker.parentNode;
- if (setEndPoint(true) && setEndPoint()) {
- t.setRng(rng);
+ if (suffix == 'start') {
+ if (!keep) {
+ idx = dom.nodeIndex(marker);
+ } else {
+ node = marker.firstChild;
+ idx = 1;
+ }
+
+ startContainer = endContainer = node;
+ startOffset = endOffset = idx;
+ } else {
+ if (!keep) {
+ idx = dom.nodeIndex(marker);
+ } else {
+ node = marker.firstChild;
+ idx = 1;
+ }
+
+ endContainer = node;
+ endOffset = idx;
}
- } else if (bookmark.id) {
- function restoreEndPoint(suffix) {
- var marker = dom.get(bookmark.id + '_' + suffix), node, idx, next, prev, keep = bookmark.keep;
- if (marker) {
- node = marker.parentNode;
+ if (!keep) {
+ prev = marker.previousSibling;
+ next = marker.nextSibling;
- if (suffix == 'start') {
- if (!keep) {
- idx = dom.nodeIndex(marker);
- } else {
- node = marker.firstChild;
- idx = 1;
- }
+ // Remove all marker text nodes
+ each(tinymce.grep(marker.childNodes), function(node) {
+ if (node.nodeType == 3)
+ node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
+ });
+
+ // Remove marker but keep children if for example contents where inserted into the marker
+ // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature
+ while (marker = dom.get(bookmark.id + '_' + suffix))
+ dom.remove(marker, 1);
- startContainer = endContainer = node;
+ // If siblings are text nodes then merge them unless it's Opera since it some how removes the node
+ // and we are sniffing since adding a lot of detection code for a browser with 3% of the market isn't worth the effort. Sorry, Opera but it's just a fact
+ if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) {
+ idx = prev.nodeValue.length;
+ prev.appendData(next.nodeValue);
+ dom.remove(next);
+
+ if (suffix == 'start') {
+ startContainer = endContainer = prev;
startOffset = endOffset = idx;
} else {
- if (!keep) {
- idx = dom.nodeIndex(marker);
- } else {
- node = marker.firstChild;
- idx = 1;
- }
-
- endContainer = node;
+ endContainer = prev;
endOffset = idx;
}
+ }
+ }
+ }
+ };
- if (!keep) {
- prev = marker.previousSibling;
- next = marker.nextSibling;
-
- // Remove all marker text nodes
- each(tinymce.grep(marker.childNodes), function(node) {
- if (node.nodeType == 3)
- node.nodeValue = node.nodeValue.replace(/\uFEFF/g, '');
- });
+ function addBogus(node) {
+ // Adds a bogus BR element for empty block elements
+ if (dom.isBlock(node) && !node.innerHTML && !isIE)
+ node.innerHTML = '<br data-mce-bogus="1" />';
- // Remove marker but keep children if for example contents where inserted into the marker
- // Also remove duplicated instances of the marker for example by a split operation or by WebKit auto split on paste feature
- while (marker = dom.get(bookmark.id + '_' + suffix))
- dom.remove(marker, 1);
-
- // If siblings are text nodes then merge them unless it's Opera since it some how removes the node
- // and we are sniffing since adding a lot of detection code for a browser with 3% of the market isn't worth the effort. Sorry, Opera but it's just a fact
- if (prev && next && prev.nodeType == next.nodeType && prev.nodeType == 3 && !tinymce.isOpera) {
- idx = prev.nodeValue.length;
- prev.appendData(next.nodeValue);
- dom.remove(next);
-
- if (suffix == 'start') {
- startContainer = endContainer = prev;
- startOffset = endOffset = idx;
- } else {
- endContainer = prev;
- endOffset = idx;
- }
- }
- }
- }
- };
+ return node;
+ };
- function addBogus(node) {
- // Adds a bogus BR element for empty block elements or just a space on IE since it renders BR elements incorrectly
- if (dom.isBlock(node) && !node.innerHTML)
- node.innerHTML = !isIE ? '<br data-mce-bogus="1" />' : ' ';
+ if (bookmark) {
+ if (bookmark.start) {
+ rng = dom.createRng();
+ root = dom.getRoot();
- return node;
- };
+ if (t.tridentSel)
+ return t.tridentSel.moveToBookmark(bookmark);
+ if (setEndPoint(true) && setEndPoint()) {
+ t.setRng(rng);
+ }
+ } else if (bookmark.id) {
// Restore start/end points
restoreEndPoint('start');
restoreEndPoint('end');
@@ -7896,6 +9826,32 @@ window.tinymce.dom.Sizzle = Sizzle;
select : function(node, content) {
var t = this, dom = t.dom, rng = dom.createRng(), idx;
+ function setPoint(node, start) {
+ var walker = new TreeWalker(node, node);
+
+ do {
+ // Text node
+ if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length !== 0) {
+ if (start)
+ rng.setStart(node, 0);
+ else
+ rng.setEnd(node, node.nodeValue.length);
+
+ return;
+ }
+
+ // BR element
+ if (node.nodeName == 'BR') {
+ if (start)
+ rng.setStartBefore(node);
+ else
+ rng.setEndBefore(node);
+
+ return;
+ }
+ } while (node = (start ? walker.next() : walker.prev()));
+ };
+
if (node) {
idx = dom.nodeIndex(node);
rng.setStart(node.parentNode, idx);
@@ -7903,32 +9859,6 @@ window.tinymce.dom.Sizzle = Sizzle;
// Find first/last text node or BR element
if (content) {
- function setPoint(node, start) {
- var walker = new tinymce.dom.TreeWalker(node, node);
-
- do {
- // Text node
- if (node.nodeType == 3 && tinymce.trim(node.nodeValue).length != 0) {
- if (start)
- rng.setStart(node, 0);
- else
- rng.setEnd(node, node.nodeValue.length);
-
- return;
- }
-
- // BR element
- if (node.nodeName == 'BR') {
- if (start)
- rng.setStartBefore(node);
- else
- rng.setEndBefore(node);
-
- return;
- }
- } while (node = (start ? walker.next() : walker.prev()));
- };
-
setPoint(node, 1);
setPoint(node);
}
@@ -7972,50 +9902,60 @@ window.tinymce.dom.Sizzle = Sizzle;
},
getRng : function(w3c) {
- var t = this, s, r, elm, doc = t.win.document;
+ var self = this, selection, rng, elm, doc = self.win.document;
// Found tridentSel object then we need to use that one
- if (w3c && t.tridentSel)
- return t.tridentSel.getRangeAt(0);
+ if (w3c && self.tridentSel) {
+ return self.tridentSel.getRangeAt(0);
+ }
try {
- if (s = t.getSel())
- r = s.rangeCount > 0 ? s.getRangeAt(0) : (s.createRange ? s.createRange() : doc.createRange());
+ if (selection = self.getSel()) {
+ rng = selection.rangeCount > 0 ? selection.getRangeAt(0) : (selection.createRange ? selection.createRange() : doc.createRange());
+ }
} catch (ex) {
// IE throws unspecified error here if TinyMCE is placed in a frame/iframe
}
// We have W3C ranges and it's IE then fake control selection since IE9 doesn't handle that correctly yet
- if (tinymce.isIE && r && r.setStart && doc.selection.createRange().item) {
+ if (tinymce.isIE && rng && rng.setStart && doc.selection.createRange().item) {
elm = doc.selection.createRange().item(0);
- r = doc.createRange();
- r.setStartBefore(elm);
- r.setEndAfter(elm);
+ rng = doc.createRange();
+ rng.setStartBefore(elm);
+ rng.setEndAfter(elm);
}
// No range found then create an empty one
// This can occur when the editor is placed in a hidden container element on Gecko
// Or on IE when there was an exception
- if (!r)
- r = doc.createRange ? doc.createRange() : doc.body.createTextRange();
+ if (!rng) {
+ rng = doc.createRange ? doc.createRange() : doc.body.createTextRange();
+ }
+
+ // If range is at start of document then move it to start of body
+ if (rng.setStart && rng.startContainer.nodeType === 9 && rng.collapsed) {
+ elm = self.dom.getRoot();
+ rng.setStart(elm, 0);
+ rng.setEnd(elm, 0);
+ }
- if (t.selectedRange && t.explicitRange) {
- if (r.compareBoundaryPoints(r.START_TO_START, t.selectedRange) === 0 && r.compareBoundaryPoints(r.END_TO_END, t.selectedRange) === 0) {
+ if (self.selectedRange && self.explicitRange) {
+ if (rng.compareBoundaryPoints(rng.START_TO_START, self.selectedRange) === 0 && rng.compareBoundaryPoints(rng.END_TO_END, self.selectedRange) === 0) {
// Safari, Opera and Chrome only ever select text which causes the range to change.
// This lets us use the originally set range if the selection hasn't been changed by the user.
- r = t.explicitRange;
+ rng = self.explicitRange;
} else {
- t.selectedRange = null;
- t.explicitRange = null;
+ self.selectedRange = null;
+ self.explicitRange = null;
}
}
- return r;
+ return rng;
},
- setRng : function(r) {
+ setRng : function(r, forward) {
var s, t = this;
-
+
if (!t.tridentSel) {
s = t.getSel();
@@ -8029,13 +9969,25 @@ window.tinymce.dom.Sizzle = Sizzle;
}
s.addRange(r);
- t.selectedRange = s.getRangeAt(0);
+
+ // Forward is set to false and we have an extend function
+ if (forward === false && s.extend) {
+ s.collapse(r.endContainer, r.endOffset);
+ s.extend(r.startContainer, r.startOffset);
+ }
+
+ // adding range isn't always successful so we need to check range count otherwise an exception can occur
+ t.selectedRange = s.rangeCount > 0 ? s.getRangeAt(0) : null;
}
} else {
// Is W3C Range
if (r.cloneRange) {
- t.tridentSel.addRange(r);
- return;
+ try {
+ t.tridentSel.addRange(r);
+ return;
+ } catch (ex) {
+ //IE9 throws an error here if called before selection is placed in the editor
+ }
}
// Is IE specific range
@@ -8058,6 +10010,14 @@ window.tinymce.dom.Sizzle = Sizzle;
getNode : function() {
var t = this, rng = t.getRng(), sel = t.getSel(), elm, start = rng.startContainer, end = rng.endContainer;
+ function skipEmptyTextNodes(n, forwards) {
+ var orig = n;
+ while (n && n.nodeType === 3 && n.length === 0) {
+ n = forwards ? n.nextSibling : n.previousSibling;
+ }
+ return n || orig;
+ };
+
// Range maybe lost after the editor is made visible again
if (!rng)
return t.dom.getRoot();
@@ -8075,19 +10035,12 @@ window.tinymce.dom.Sizzle = Sizzle;
}
// If the anchor node is a element instead of a text node then return this element
- //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
+ //if (tinymce.isWebKit && sel.anchorNode && sel.anchorNode.nodeType == 1)
// return sel.anchorNode.childNodes[sel.anchorOffset];
// Handle cases where the selection is immediately wrapped around a node and return that node instead of it's parent.
// This happens when you double click an underlined word in FireFox.
if (start.nodeType === 3 && end.nodeType === 3) {
- function skipEmptyTextNodes(n, forwards) {
- var orig = n;
- while (n && n.nodeType === 3 && n.length === 0) {
- n = forwards ? n.nextSibling : n.previousSibling;
- }
- return n || orig;
- }
if (start.length === rng.startOffset) {
start = skipEmptyTextNodes(start.nextSibling, true);
} else {
@@ -8125,7 +10078,7 @@ window.tinymce.dom.Sizzle = Sizzle;
if (sb && eb && sb != eb) {
n = sb;
- var walker = new tinymce.dom.TreeWalker(sb, dom.getRoot());
+ var walker = new TreeWalker(sb, dom.getRoot());
while ((n = walker.next()) && n != eb) {
if (dom.isBlock(n))
bl.push(n);
@@ -8138,96 +10091,270 @@ window.tinymce.dom.Sizzle = Sizzle;
return bl;
},
- normalize : function() {
- var self = this, rng, normalized;
+ isForward: function(){
+ var dom = this.dom, sel = this.getSel(), anchorRange, focusRange;
- // Normalize only on non IE browsers for now
- if (tinymce.isIE)
- return;
+ // No support for selection direction then always return true
+ if (!sel || sel.anchorNode == null || sel.focusNode == null) {
+ return true;
+ }
+
+ anchorRange = dom.createRng();
+ anchorRange.setStart(sel.anchorNode, sel.anchorOffset);
+ anchorRange.collapse(true);
+
+ focusRange = dom.createRng();
+ focusRange.setStart(sel.focusNode, sel.focusOffset);
+ focusRange.collapse(true);
+
+ return anchorRange.compareBoundaryPoints(anchorRange.START_TO_START, focusRange) <= 0;
+ },
+
+ normalize : function() {
+ var self = this, rng, normalized, collapsed, node, sibling;
function normalizeEndPoint(start) {
- var container, offset, walker, dom = self.dom, body = dom.getRoot(), node;
+ var container, offset, walker, dom = self.dom, body = dom.getRoot(), node, nonEmptyElementsMap, 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;
+ }
+ }
+ };
+
+ // 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;
+
+ startNode = startNode || container;
+ walker = new TreeWalker(startNode, dom.getParent(startNode.parentNode, dom.isBlock) || body);
+
+ // Walk left until we hit a text node we can move to or a block/br/img
+ while (node = walker[left ? 'prev' : 'next']()) {
+ // 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'];
+ nonEmptyElementsMap = dom.schema.getNonEmptyElements();
// If the container is a document move it to the body element
if (container.nodeType === 9) {
- container = container.body;
+ container = dom.getRoot();
offset = 0;
}
// If the container is body try move it into the closest text node or position
- // TODO: Add more logic here to handle element selection cases
if (container === body) {
+ // If start is before/after a image, table etc
+ if (start) {
+ node = container.childNodes[offset > 0 ? offset - 1 : 0];
+ if (node) {
+ nodeName = node.nodeName.toLowerCase();
+ if (nonEmptyElementsMap[node.nodeName] || node.nodeName == "TABLE") {
+ return;
+ }
+ }
+ }
+
// Resolve the index
if (container.hasChildNodes()) {
container = container.childNodes[Math.min(!start && offset > 0 ? offset - 1 : offset, container.childNodes.length - 1)];
offset = 0;
// Don't walk into elements that doesn't have any child nodes like a IMG
- if (container.hasChildNodes()) {
+ 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 tinymce.dom.TreeWalker(container, body);
+ walker = new TreeWalker(container, body);
+
do {
// Found a text node use that position
- if (node.nodeType === 3) {
- offset = start ? 0 : node.nodeValue.length - 1;
+ if (node.nodeType === 3 && node.nodeValue.length > 0) {
+ offset = start ? 0 : node.nodeValue.length;
container = node;
+ normalized = true;
break;
}
- // Found a BR element that we can place the caret before
- if (node.nodeName === 'BR') {
+ // Found a BR/IMG element that we can place the caret before
+ if (nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
offset = dom.nodeIndex(node);
container = node.parentNode;
+
+ // Put caret after image when moving the end point
+ if (node.nodeName == "IMG" && !start) {
+ offset++;
+ }
+
+ normalized = true;
break;
}
} while (node = (start ? walker.next() : walker.prev()));
+ }
+ }
+ }
- normalized = true;
+ // Lean the caret to the left if possible
+ if (collapsed) {
+ // So this: <b>x</b><i>|x</i>
+ // Becomes: <b>x|</b><i>x</i>
+ // 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: <i><b></b><i>|<br></i>
+ // Becomes: <i><b>|</b><i><br></i>
+ // Seems that only gecko has issues with this
+ if (container.nodeType === 1) {
+ node = container.childNodes[offset];
+ if(node && node.nodeName === 'BR' && !hasBrBeforeAfter(node) && !hasBrBeforeAfter(node, true)) {
+ findTextNodeRelative(true, container.childNodes[offset]);
}
}
}
+ // Lean the start of the selection right if possible
+ // So this: x[<b>x]</b>
+ // Becomes: x<b>[x]</b>
+ if (start && !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);
};
+ // Normalize only on non IE browsers for now
+ if (tinymce.isIE)
+ return;
+
rng = self.getRng();
+ collapsed = rng.collapsed;
// Normalize the end points
normalizeEndPoint(true);
-
- if (rng.collapsed)
+
+ if (!collapsed)
normalizeEndPoint();
// Set the selection if it was normalized
if (normalized) {
+ // If it was collapsed then make sure it still is
+ if (collapsed) {
+ rng.collapse(true);
+ }
+
//console.log(self.dom.dumpRng(rng));
- self.setRng(rng);
+ self.setRng(rng, self.isForward());
}
},
- destroy : function(s) {
- var t = this;
+ selectorChanged: function(selector, callback) {
+ var self = this, currentSelectors;
+
+ if (!self.selectorChangedData) {
+ self.selectorChangedData = {};
+ currentSelectors = {};
+
+ self.editor.onNodeChange.addToTop(function(ed, cm, node) {
+ var dom = self.dom, parents = dom.getParents(node, null, dom.getRoot()), matchedSelectors = {};
+
+ // Check for new matching selectors
+ each(self.selectorChangedData, function(callbacks, selector) {
+ each(parents, function(node) {
+ if (dom.is(node, selector)) {
+ if (!currentSelectors[selector]) {
+ // Execute callbacks
+ each(callbacks, function(callback) {
+ callback(true, {node: node, selector: selector, parents: parents});
+ });
+
+ currentSelectors[selector] = callbacks;
+ }
- t.win = null;
+ matchedSelectors[selector] = callbacks;
+ return false;
+ }
+ });
+ });
+
+ // Check if current selectors still match
+ each(currentSelectors, function(callbacks, selector) {
+ if (!matchedSelectors[selector]) {
+ delete currentSelectors[selector];
+
+ each(callbacks, function(callback) {
+ callback(false, {node: node, selector: selector, parents: parents});
+ });
+ }
+ });
+ });
+ }
+
+ // Add selector listeners
+ if (!self.selectorChangedData[selector]) {
+ self.selectorChangedData[selector] = [];
+ }
+
+ self.selectorChangedData[selector].push(callback);
+
+ return self;
+ },
+
+ scrollIntoView: function(elm) {
+ var y, viewPort, self = this, dom = self.dom;
+
+ viewPort = dom.getViewPort(self.editor.getWin());
+ y = dom.getPos(elm).y;
+ if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {
+ self.editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25);
+ }
+ },
+
+ destroy : function(manual) {
+ var self = this;
+
+ self.win = null;
// Manual destroy then remove unload handler
- if (!s)
- tinymce.removeUnload(t.destroy);
+ if (!manual)
+ tinymce.removeUnload(self.destroy);
},
// IE has an issue where you can't select/move the caret by clicking outside the body if the document is in standards mode
_fixIESelection : function() {
var dom = this.dom, doc = dom.doc, body = doc.body, started, startRng, htmlElm;
- // Make HTML element unselectable since we are going to handle selection by hand
- doc.documentElement.unselectable = true;
-
// Return range from point or null if it failed
function rngFromPoint(x, y) {
var rng = body.createTextRange();
@@ -8277,6 +10404,9 @@ window.tinymce.dom.Sizzle = Sizzle;
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') {
@@ -8313,12 +10443,11 @@ window.tinymce.dom.Sizzle = Sizzle;
if (!settings.apply_source_formatting)
settings.indent = false;
- settings.remove_trailing_brs = true;
-
// Default DOM and Schema if they are undefined
dom = dom || tinymce.DOM;
schema = schema || new tinymce.html.Schema(settings);
settings.entity_encoding = settings.entity_encoding || 'named';
+ settings.remove_trailing_brs = "remove_trailing_brs" in settings ? settings.remove_trailing_brs : true;
onPreProcess = new tinymce.util.Dispatcher(self);
@@ -8352,13 +10481,13 @@ window.tinymce.dom.Sizzle = Sizzle;
}
});
- // Remove internal classes mceItem<..>
+ // Remove internal classes mceItem<..> or mceSelected
htmlParser.addAttributeFilter('class', function(nodes, name) {
var i = nodes.length, node, value;
while (i--) {
node = nodes[i];
- value = node.attr('class').replace(/\s*mce(Item\w+|Selected)\s*/g, '');
+ value = node.attr('class').replace(/(?:^|\s)mce(Item\w+|Selected)(?!\S)/g, '');
node.attr('class', value.length > 0 ? value : null);
}
});
@@ -8375,6 +10504,27 @@ window.tinymce.dom.Sizzle = Sizzle;
}
});
+ // Remove expando attributes
+ htmlParser.addAttributeFilter('data-mce-expando', function(nodes, name, args) {
+ var i = nodes.length;
+
+ while (i--) {
+ nodes[i].attr(name, null);
+ }
+ });
+
+ htmlParser.addNodeFilter('noscript', function(nodes) {
+ var i = nodes.length, node;
+
+ while (i--) {
+ node = nodes[i].firstChild;
+
+ if (node) {
+ node.value = tinymce.html.Entities.decode(node.value);
+ }
+ }
+ });
+
// Force script into CDATA sections and remove the mce- prefix also add comments around styles
htmlParser.addNodeFilter('script,style', function(nodes, name) {
var i = nodes.length, node, value;
@@ -8382,8 +10532,8 @@ window.tinymce.dom.Sizzle = Sizzle;
function trim(value) {
return value.replace(/(<!--\[CDATA\[|\]\]-->)/g, '\n')
.replace(/^[\r\n]*|[\r\n]*$/g, '')
- .replace(/^\s*(\/\/\s*<!--|\/\/\s*<!\[CDATA\[|<!--|<!\[CDATA\[)[\r\n]*/g, '')
- .replace(/\s*(\/\/\s*\]\]>|\/\/\s*-->|\]\]>|-->|\]\]-->)\s*$/g, '');
+ .replace(/^\s*((<!--)?(\s*\/\/)?\s*<!\[CDATA\[|(<!--\s*)?\/\*\s*<!\[CDATA\[\s*\*\/|(\/\/)?\s*<!--|\/\*\s*<!--\s*\*\/)\s*[\r\n]*/gi, '')
+ .replace(/\s*(\/\*\s*\]\]>\s*\*\/(-->)?|\s*\/\/\s*\]\]>(-->)?|\/\/\s*(-->)?|\]\]>|\/\*\s*-->\s*\*\/|\s*-->\s*)\s*$/g, '');
};
while (i--) {
@@ -8527,12 +10677,12 @@ window.tinymce.dom.Sizzle = Sizzle;
// Parse and serialize HTML
args.content = htmlSerializer.serialize(
- htmlParser.parse(args.getInner ? node.innerHTML : tinymce.trim(dom.getOuterHTML(node), args), args)
+ htmlParser.parse(tinymce.trim(args.getInner ? node.innerHTML : dom.getOuterHTML(node)), args)
);
// Replace all BOM characters for now until we can find a better solution
if (!args.cleanup)
- args.content = args.content.replace(/\uFEFF|\u200B/g, '');
+ args.content = args.content.replace(/\uFEFF/g, '');
// Post process
if (!args.no_events)
@@ -8567,7 +10717,7 @@ window.tinymce.dom.Sizzle = Sizzle;
scriptLoadedCallbacks = {},
queueLoadedCallbacks = [],
loading = 0,
- undefined;
+ undef;
function loadScript(url, callback) {
var t = this, dom = tinymce.DOM, elm, uri, loc, id;
@@ -8626,11 +10776,10 @@ window.tinymce.dom.Sizzle = Sizzle;
}
// Create new script element
- elm = dom.create('script', {
- id : id,
- type : 'text/javascript',
- src : tinymce._addVer(url)
- });
+ elm = document.createElement('script');
+ elm.id = id;
+ elm.type = 'text/javascript';
+ elm.src = tinymce._addVer(url);
// Add onload listener for non IE browsers since IE9
// fires onload event before the script is parsed and executed
@@ -8676,7 +10825,7 @@ window.tinymce.dom.Sizzle = Sizzle;
var item, state = states[url];
// Add url to load queue
- if (state == undefined) {
+ if (state == undef) {
queue.push(url);
states[url] = QUEUED;
}
@@ -8706,7 +10855,7 @@ window.tinymce.dom.Sizzle = Sizzle;
callback.func.call(callback.scope);
});
- scriptLoadedCallbacks[url] = undefined;
+ scriptLoadedCallbacks[url] = undef;
};
queueLoadedCallbacks.push({
@@ -8763,46 +10912,6 @@ window.tinymce.dom.Sizzle = Sizzle;
tinymce.ScriptLoader = new tinymce.dom.ScriptLoader();
})(tinymce);
-tinymce.dom.TreeWalker = function(start_node, root_node) {
- var node = start_node;
-
- function findSibling(node, start_name, sibling_name, shallow) {
- var sibling, parent;
-
- if (node) {
- // Walk into nodes if it has a start
- if (!shallow && node[start_name])
- return node[start_name];
-
- // Return the sibling if it has one
- if (node != root_node) {
- sibling = node[sibling_name];
- if (sibling)
- return sibling;
-
- // Walk up the parents to look for siblings
- for (parent = node.parentNode; parent && parent != root_node; parent = parent.parentNode) {
- sibling = parent[sibling_name];
- if (sibling)
- return sibling;
- }
- }
- }
- };
-
- this.current = function() {
- return node;
- };
-
- this.next = function(shallow) {
- return (node = findSibling(node, 'firstChild', 'nextSibling', shallow));
- };
-
- this.prev = function(shallow) {
- return (node = findSibling(node, 'lastChild', 'previousSibling', shallow));
- };
-};
-
(function(tinymce) {
tinymce.dom.RangeUtils = function(dom) {
var INVISIBLE_CHAR = '\uFEFF';
@@ -9034,12 +11143,15 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
t.destroy = function() {
each(items, function(item) {
- dom.unbind(dom.get(item.id), 'focus', itemFocussed);
- dom.unbind(dom.get(item.id), 'blur', itemBlurred);
+ var elm = dom.get(item.id);
+
+ dom.unbind(elm, 'focus', itemFocussed);
+ dom.unbind(elm, 'blur', itemBlurred);
});
- dom.unbind(dom.get(root), 'focus', rootFocussed);
- dom.unbind(dom.get(root), 'keydown', rootKeydown);
+ var rootElm = dom.get(root);
+ dom.unbind(rootElm, 'focus', rootFocussed);
+ dom.unbind(rootElm, 'keydown', rootKeydown);
items = dom = root = t.focus = itemFocussed = itemBlurred = rootKeydown = rootFocussed = null;
t.destroy = function() {};
@@ -9118,21 +11230,23 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
// Set up state and listeners for each item.
each(items, function(item, idx) {
- var tabindex;
+ var tabindex, elm;
if (!item.id) {
item.id = dom.uniqueId('_mce_item_');
}
+ elm = dom.get(item.id);
+
if (excludeFromTabOrder) {
- dom.bind(item.id, 'blur', itemBlurred);
+ dom.bind(elm, 'blur', itemBlurred);
tabindex = '-1';
} else {
tabindex = (idx === 0 ? '0' : '-1');
}
- dom.setAttrib(item.id, 'tabindex', tabindex);
- dom.bind(dom.get(item.id), 'focus', itemFocussed);
+ elm.setAttribute('tabindex', tabindex);
+ dom.bind(elm, 'focus', itemFocussed);
});
// Setup initial state for root element.
@@ -9141,10 +11255,11 @@ tinymce.dom.TreeWalker = function(start_node, root_node) {
}
dom.setAttrib(root, 'tabindex', '-1');
-
+
// Setup listeners for root element.
- dom.bind(dom.get(root), 'focus', rootFocussed);
- dom.bind(dom.get(root), 'keydown', rootKeydown);
+ var rootElm = dom.get(root);
+ dom.bind(rootElm, 'focus', rootFocussed);
+ dom.bind(rootElm, 'keydown', rootKeydown);
}
});
})(tinymce);
@@ -9463,8 +11578,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
update : function() {
var t = this, s = t.settings, tb = DOM.get('menu_' + t.id + '_tbl'), co = DOM.get('menu_' + t.id + '_co'), tw, th;
- tw = s.max_width ? Math.min(tb.clientWidth, s.max_width) : tb.clientWidth;
- th = s.max_height ? Math.min(tb.clientHeight, s.max_height) : tb.clientHeight;
+ tw = s.max_width ? Math.min(tb.offsetWidth, s.max_width) : tb.offsetWidth;
+ th = s.max_height ? Math.min(tb.offsetHeight, s.max_height) : tb.offsetHeight;
if (!DOM.boxModel)
t.element.setStyles({width : tw + 2, height : th + 2});
@@ -9554,7 +11669,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
if (m.settings.onclick)
m.settings.onclick(e);
- return Event.cancel(e); // Cancel to fix onbeforeunload problem
+ return false; // Cancel to fix onbeforeunload problem
}
});
@@ -9684,7 +11799,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
// Internal functions
_setupKeyboardNav : function(){
var contextMenu, menuItems, t=this;
- contextMenu = DOM.select('#menu_' + t.id)[0];
+ contextMenu = DOM.get('menu_' + t.id);
menuItems = DOM.select('a[role=option]', 'menu_' + t.id);
menuItems.splice(0,0,contextMenu);
t.keyboardNav = new tinymce.ui.KeyboardNavigation({
@@ -9747,8 +11862,12 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
n = DOM.add(n, s.element || 'span', {'class' : 'mceText', title : o.settings.title}, o.settings.title);
- if (o.settings.style)
+ if (o.settings.style) {
+ if (typeof o.settings.style == "function")
+ o.settings.style = o.settings.style();
+
DOM.setAttrib(n, 'style', o.settings.style);
+ }
if (tb.childNodes.length == 1)
DOM.addClass(ro, 'mceFirst');
@@ -9781,7 +11900,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
l = DOM.encode(s.label || '');
h = '<a role="button" id="' + this.id + '" href="javascript:;" class="' + cp + ' ' + cp + 'Enabled ' + s['class'] + (l ? ' ' + cp + 'Labeled' : '') +'" onmousedown="return false;" onclick="return false;" aria-labelledby="' + this.id + '_voice" title="' + DOM.encode(s.title) + '">';
if (s.image && !(this.editor &&this.editor.forcedHighContrastMode) )
- h += '<img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" />' + l;
+ h += '<span class="mceIcon ' + s['class'] + '"><img class="mceIcon" src="' + s.image + '" alt="' + DOM.encode(s.title) + '" /></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');
else
h += '<span class="mceIcon ' + s['class'] + '"></span>' + (l ? '<span class="' + cp + 'Label">' + l + '</span>' : '');
@@ -9791,10 +11910,27 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
},
postRender : function() {
- var t = this, s = t.settings;
-
+ var t = this, s = t.settings, imgBookmark;
+
+ // In IE a large image that occupies the entire editor area will be deselected when a button is clicked, so
+ // need to keep the selection in case the selection is lost
+ if (tinymce.isIE && t.editor) {
+ tinymce.dom.Event.add(t.id, 'mousedown', function(e) {
+ var nodeName = t.editor.selection.getNode().nodeName;
+ imgBookmark = nodeName === 'IMG' ? t.editor.selection.getBookmark() : null;
+ });
+ }
tinymce.dom.Event.add(t.id, 'click', function(e) {
- if (!t.isDisabled())
+ if (!t.isDisabled()) {
+ // restore the selection in case the selection is lost in IE
+ if (tinymce.isIE && t.editor && imgBookmark !== null) {
+ t.editor.selection.moveToBookmark(imgBookmark);
+ }
+ return s.onclick.call(s.scope, e);
+ }
+ });
+ tinymce.dom.Event.add(t.id, 'keyup', function(e) {
+ if (!t.isDisabled() && e.keyCode==tinymce.VK.SPACEBAR)
return s.onclick.call(s.scope, e);
});
}
@@ -9802,7 +11938,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
})(tinymce);
(function(tinymce) {
- var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
+ var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;
tinymce.create('tinymce.ui.ListBox:tinymce.ui.Control', {
ListBox : function(id, s, ed) {
@@ -9821,16 +11957,19 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
t.onRenderMenu = new tinymce.util.Dispatcher(this);
t.classPrefix = 'mceListBox';
+ t.marked = {};
},
select : function(va) {
var t = this, fv, f;
- if (va == undefined)
+ t.marked = {};
+
+ if (va == undef)
return t.selectByIndex(-1);
// Is string or number make function selector
- if (va && va.call)
+ if (va && typeof(va)=="function")
f = va;
else {
f = function(v) {
@@ -9857,6 +11996,8 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
selectByIndex : function(idx) {
var t = this, e, o, label;
+ t.marked = {};
+
if (idx != t.selectedIndex) {
e = DOM.get(t.id + '_text');
label = DOM.get(t.id + '_voiceDesc');
@@ -9880,6 +12021,10 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
}
},
+ mark : function(value) {
+ this.marked[value] = true;
+ },
+
add : function(n, v, o) {
var t = this;
@@ -9912,7 +12057,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
showMenu : function() {
var t = this, p2, e = DOM.get(this.id), m;
- if (t.isDisabled() || t.items.length == 0)
+ if (t.isDisabled() || t.items.length === 0)
return;
if (t.menu && t.menu.isMenuVisible)
@@ -9931,13 +12076,19 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
m.settings.keyboard_focus = !tinymce.isOpera; // Opera is buggy when it comes to auto focus
// Select in menu
- if (t.oldID)
- m.items[t.oldID].setSelected(0);
+ each(t.items, function(o) {
+ if (m.items[o.id]) {
+ m.items[o.id].setSelected(0);
+ }
+ });
each(t.items, function(o) {
+ if (m.items[o.id] && t.marked[o.value]) {
+ m.items[o.id].setSelected(1);
+ }
+
if (o.value === t.selectedValue) {
m.items[o.id].setSelected(1);
- t.oldID = o.id;
}
});
@@ -9973,7 +12124,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
m = t.settings.control_manager.createDropMenu(t.id + '_menu', {
menu_line : 1,
'class' : t.classPrefix + 'Menu mceNoIcons',
- max_width : 150,
+ max_width : 250,
max_height : 150
});
@@ -9993,7 +12144,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
each(t.items, function(o) {
// No value then treat it as a title
- if (o.value === undefined) {
+ if (o.value === undef) {
m.add({
title : o.title,
role : "option",
@@ -10083,7 +12234,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
})(tinymce);
(function(tinymce) {
- var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher;
+ var DOM = tinymce.DOM, Event = tinymce.dom.Event, each = tinymce.each, Dispatcher = tinymce.util.Dispatcher, undef;
tinymce.create('tinymce.ui.NativeListBox:tinymce.ui.ListBox', {
NativeListBox : function(id, s) {
@@ -10103,11 +12254,11 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
select : function(va) {
var t = this, fv, f;
- if (va == undefined)
+ if (va == undef)
return t.selectByIndex(-1);
// Is string or number make function selector
- if (va && va.call)
+ if (va && typeof(va)=="function")
f = va;
else {
f = function(v) {
@@ -10256,7 +12407,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
m.settings.vp_offset_x = p2.x;
m.settings.vp_offset_y = p2.y;
m.settings.keyboard_focus = t._focused;
- m.showMenu(0, e.clientHeight);
+ m.showMenu(0, e.firstChild.clientHeight);
Event.add(DOM.doc, 'mousedown', t.hideMenu, t);
t.setState('Selected', 1);
@@ -10440,7 +12591,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
p2 = DOM.getPos(e);
DOM.setStyles(t.id + '_menu', {
left : p2.x,
- top : p2.y + e.clientHeight,
+ top : p2.y + e.firstChild.clientHeight,
zIndex : 200000
});
e = 0;
@@ -10457,6 +12608,16 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
DOM.select('a', t.id + '_menu')[0].focus(); // Select first link
}
+ t.keyboardNav = new tinymce.ui.KeyboardNavigation({
+ root: t.id + '_menu',
+ items: DOM.select('a', t.id + '_menu'),
+ onCancel: function() {
+ t.hideMenu();
+ t.focus();
+ }
+ });
+
+ t.keyboardNav.focus();
t.isMenuVisible = 1;
},
@@ -10477,13 +12638,14 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
t.isMenuVisible = 0;
t.onHideMenu.dispatch();
+ t.keyboardNav.destroy();
}
},
renderMenu : function() {
var t = this, m, i = 0, s = t.settings, n, tb, tr, w, context;
- w = DOM.add(s.menu_container, 'div', {role: 'listbox', id : t.id + '_menu', 'class' : s['menu_class'] + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
+ w = DOM.add(s.menu_container, 'div', {role: 'listbox', id : t.id + '_menu', 'class' : s.menu_class + ' ' + s['class'], style : 'position:absolute;left:0;top:-1000px;'});
m = DOM.add(w, 'div', {'class' : s['class'] + ' mceSplitButtonMenu'});
DOM.add(m, 'span', {'class' : 'mceMenuLine'});
@@ -10501,15 +12663,21 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
}
n = DOM.add(tr, 'td');
- n = DOM.add(n, 'a', {
- role : 'option',
+ var settings = {
href : 'javascript:;',
style : {
backgroundColor : '#' + c
},
'title': t.editor.getLang('colors.' + c, c),
'data-mce-color' : '#' + c
- });
+ };
+
+ // adding a proper ARIA role = button causes JAWS to read things incorrectly on IE.
+ if (!tinymce.isIE ) {
+ settings.role = 'option';
+ }
+
+ n = DOM.add(n, 'a', settings);
if (t.editor.forcedHighContrastMode) {
n = DOM.add(n, 'canvas', { width: 16, height: 16, 'aria-hidden': 'true' });
@@ -10535,15 +12703,6 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
}
DOM.addClass(m, 'mceColorSplitMenu');
-
- new tinymce.ui.KeyboardNavigation({
- root: t.id + '_menu',
- items: DOM.select('a', t.id + '_menu'),
- onCancel: function() {
- t.hideMenu();
- t.focus();
- }
- });
// Prevent IE from scrolling and hindering click to occur #4019
Event.add(t.id + '_menu', 'mousedown', function(e) {return Event.cancel(e);});
@@ -10556,7 +12715,7 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
if (e && e.nodeName.toLowerCase() == 'a' && (c = e.getAttribute('data-mce-color')))
t.setColor(c);
- return Event.cancel(e); // Prevent IE auto save warning
+ return false; // Prevent IE auto save warning
});
return w;
@@ -10585,11 +12744,17 @@ tinymce.create('tinymce.ui.Separator:tinymce.ui.Control', {
},
destroy : function() {
- this.parent();
+ var self = this;
- Event.clear(this.id + '_menu');
- Event.clear(this.id + '_more');
- DOM.remove(this.id + '_menu');
+ self.parent();
+
+ Event.clear(self.id + '_menu');
+ Event.clear(self.id + '_more');
+ DOM.remove(self.id + '_menu');
+
+ if (self.keyboardNav) {
+ self.keyboardNav.destroy();
+ }
}
});
})(tinymce);
@@ -10802,7 +12967,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (typeof u === "object")
url = u.prefix + u.resource + u.suffix;
- if (url.indexOf('/') != 0 && url.indexOf('://') == -1)
+ if (url.indexOf('/') !== 0 && url.indexOf('://') == -1)
url = tinymce.baseURL + '/' + url;
t.urls[n] = url.substring(0, url.lastIndexOf('/'));
@@ -10826,7 +12991,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
DOM = tinymce.DOM, Event = tinymce.dom.Event,
ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
explode = tinymce.explode,
- Dispatcher = tinymce.util.Dispatcher, undefined, instanceCounter = 0;
+ Dispatcher = tinymce.util.Dispatcher, undef, instanceCounter = 0;
// Setup some URLs where the editor API is located and where the document is
tinymce.documentBaseURL = window.location.href.replace(/[\?#].*$/, '').replace(/[\/\\][^\/]+$/, '');
@@ -10861,6 +13026,26 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
init : function(s) {
var t = this, pl, sl = tinymce.ScriptLoader, e, el = [], ed;
+ function createId(elm) {
+ var id = elm.id;
+
+ // Use element id, or unique name or generate a unique id
+ if (!id) {
+ id = elm.name;
+
+ if (id && !DOM.get(id)) {
+ id = elm.name;
+ } else {
+ // Generate unique name
+ id = DOM.uniqueId();
+ }
+
+ elm.setAttribute('id', id);
+ }
+
+ return id;
+ };
+
function execCallback(se, n, s) {
var f = se[n];
@@ -10876,15 +13061,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return f.apply(s || this, Array.prototype.slice.call(arguments, 2));
};
- s = extend({
- theme : "simple",
- language : "en"
- }, s);
+ function hasClass(n, c) {
+ return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
+ };
t.settings = s;
// Legacy call
- Event.add(document, 'init', function() {
+ Event.bind(window, 'ready', function() {
var l, co;
execCallback(s, 'onpageload');
@@ -10919,30 +13103,36 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
case "textareas":
case "specific_textareas":
- function hasClass(n, c) {
- return c.constructor === RegExp ? c.test(n.className) : DOM.hasClass(n, c);
- };
-
- each(DOM.select('textarea'), function(v) {
- if (s.editor_deselector && hasClass(v, s.editor_deselector))
+ each(DOM.select('textarea'), function(elm) {
+ if (s.editor_deselector && hasClass(elm, s.editor_deselector))
return;
- if (!s.editor_selector || hasClass(v, s.editor_selector)) {
- // Can we use the name
- e = DOM.get(v.name);
- if (!v.id && !e)
- v.id = v.name;
-
- // Generate unique name if missing or already exists
- if (!v.id || t.get(v.id))
- v.id = DOM.uniqueId();
-
- ed = new tinymce.Editor(v.id, s);
+ if (!s.editor_selector || hasClass(elm, s.editor_selector)) {
+ ed = new tinymce.Editor(createId(elm), s);
el.push(ed);
ed.render(1);
}
});
break;
+
+ default:
+ if (s.types) {
+ // Process type specific selector
+ each(s.types, function(type) {
+ each(DOM.select(type.selector), function(elm) {
+ var editor = new tinymce.Editor(createId(elm), tinymce.extend({}, s, type));
+ el.push(editor);
+ editor.render(1);
+ });
+ });
+ } else if (s.selector) {
+ // Process global selector
+ each(DOM.select(s.selector), function(elm) {
+ var editor = new tinymce.Editor(createId(elm), s);
+ el.push(editor);
+ editor.render(1);
+ });
+ }
}
// Call onInit when all editors are initialized
@@ -10973,9 +13163,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
get : function(id) {
- if (id === undefined)
+ if (id === undef)
return this.editors;
+ if (!this.editors.hasOwnProperty(id))
+ return undef;
+
return this.editors[id];
},
@@ -11026,6 +13219,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
execCommand : function(c, u, v) {
var t = this, ed = t.get(v), w;
+ function clr() {
+ ed.destroy();
+ w.detachEvent('onunload', clr);
+ w = w.tinyMCE = w.tinymce = null; // IE leak
+ };
+
// Manager commands
switch (c) {
case "mceFocus":
@@ -11054,12 +13253,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Fix IE memory leaks
if (tinymce.isIE) {
- function clr() {
- ed.destroy();
- w.detachEvent('onunload', clr);
- w = w.tinyMCE = w.tinymce = null; // IE leak
- };
-
w.attachEvent('onunload', clr);
}
@@ -11142,163 +13335,84 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
(function(tinymce) {
// Shorten these names
var DOM = tinymce.DOM, Event = tinymce.dom.Event, extend = tinymce.extend,
- Dispatcher = tinymce.util.Dispatcher, each = tinymce.each, isGecko = tinymce.isGecko,
+ each = tinymce.each, isGecko = tinymce.isGecko,
isIE = tinymce.isIE, isWebKit = tinymce.isWebKit, is = tinymce.is,
ThemeManager = tinymce.ThemeManager, PluginManager = tinymce.PluginManager,
- inArray = tinymce.inArray, grep = tinymce.grep, explode = tinymce.explode;
+ explode = tinymce.explode;
tinymce.create('tinymce.Editor', {
- Editor : function(id, s) {
- var t = this;
-
- t.id = t.editorId = id;
-
- t.execCommands = {};
- t.queryStateCommands = {};
- t.queryValueCommands = {};
-
- t.isNotDirty = false;
-
- t.plugins = {};
-
- // Add events to the editor
- each([
- 'onPreInit',
-
- 'onBeforeRenderUI',
-
- 'onPostRender',
-
- 'onInit',
-
- 'onRemove',
-
- 'onActivate',
-
- 'onDeactivate',
-
- 'onClick',
-
- 'onEvent',
-
- 'onMouseUp',
-
- 'onMouseDown',
-
- 'onDblClick',
-
- 'onKeyDown',
-
- 'onKeyUp',
-
- 'onKeyPress',
-
- 'onContextMenu',
-
- 'onSubmit',
-
- 'onReset',
-
- 'onPaste',
-
- 'onPreProcess',
-
- 'onPostProcess',
-
- 'onBeforeSetContent',
+ Editor : function(id, settings) {
+ var self = this, TRUE = true;
- 'onBeforeGetContent',
-
- 'onSetContent',
-
- 'onGetContent',
-
- 'onLoadContent',
-
- 'onSaveContent',
-
- 'onNodeChange',
-
- 'onChange',
-
- 'onBeforeExecCommand',
-
- 'onExecCommand',
-
- 'onUndo',
-
- 'onRedo',
-
- 'onVisualAid',
-
- 'onSetProgressState'
- ], function(e) {
- t[e] = new Dispatcher(t);
- });
-
- t.settings = s = extend({
+ self.settings = settings = extend({
id : id,
language : 'en',
- docs_language : 'en',
- theme : 'simple',
+ theme : 'advanced',
skin : 'default',
delta_width : 0,
delta_height : 0,
popup_css : '',
plugins : '',
document_base_url : tinymce.documentBaseURL,
- add_form_submit_trigger : 1,
- submit_patch : 1,
- add_unload_trigger : 1,
- convert_urls : 1,
- relative_urls : 1,
- remove_script_host : 1,
- table_inline_editing : 0,
- object_resizing : 1,
- cleanup : 1,
- accessibility_focus : 1,
- custom_shortcuts : 1,
- custom_undo_redo_keyboard_shortcuts : 1,
- custom_undo_redo_restore_selection : 1,
- custom_undo_redo : 1,
+ add_form_submit_trigger : TRUE,
+ submit_patch : TRUE,
+ add_unload_trigger : TRUE,
+ convert_urls : TRUE,
+ relative_urls : TRUE,
+ remove_script_host : TRUE,
+ table_inline_editing : false,
+ object_resizing : TRUE,
+ accessibility_focus : TRUE,
doctype : tinymce.isIE6 ? '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">' : '<!DOCTYPE>', // Use old doctype on IE 6 to avoid horizontal scroll
- visual_table_class : 'mceItemTable',
- visual : 1,
+ visual : TRUE,
font_size_style_values : 'xx-small,x-small,small,medium,large,x-large,xx-large',
font_size_legacy_values : 'xx-small,small,medium,large,x-large,xx-large,300%', // See: http://www.w3.org/TR/CSS2/fonts.html#propdef-font-size
- apply_source_formatting : 1,
+ apply_source_formatting : TRUE,
directionality : 'ltr',
forced_root_block : 'p',
- hidden_input : 1,
- padd_empty_editor : 1,
- render_ui : 1,
- init_theme : 1,
- force_p_newlines : 1,
+ hidden_input : TRUE,
+ padd_empty_editor : TRUE,
+ render_ui : TRUE,
indentation : '30px',
- keep_styles : 1,
- fix_table_elements : 1,
- inline_styles : 1,
- convert_fonts_to_spans : true,
+ fix_table_elements : TRUE,
+ 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,ul,li,area,table,thead,tfoot,tbody,tr',
- indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr',
- validate : true,
+ indent_before : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
+ indent_after : 'p,h1,h2,h3,h4,h5,h6,blockquote,div,title,style,pre,script,td,ul,li,area,table,thead,tfoot,tbody,tr,section,article,hgroup,aside,figure,option,optgroup,datalist',
+ validate : TRUE,
entity_encoding : 'named',
- url_converter : t.convertURL,
- url_converter_scope : t,
- ie7_compat : true
- }, s);
+ url_converter : self.convertURL,
+ url_converter_scope : self,
+ ie7_compat : TRUE
+ }, settings);
- t.documentBaseURI = new tinymce.util.URI(s.document_base_url || tinymce.documentBaseURL, {
+ self.id = self.editorId = id;
+
+ self.isNotDirty = false;
+
+ self.plugins = {};
+
+ self.documentBaseURI = new tinymce.util.URI(settings.document_base_url || tinymce.documentBaseURL, {
base_uri : tinyMCE.baseURI
});
- t.baseURI = tinymce.baseURI;
+ self.baseURI = tinymce.baseURI;
- t.contentCSS = [];
+ self.contentCSS = [];
+
+ self.contentStyles = [];
+
+ // Creates all events like onClick, onSetContent etc see Editor.Events.js for the actual logic
+ self.setupEvents();
+
+ // Internal command handler objects
+ self.execCommands = {};
+ self.queryStateCommands = {};
+ self.queryValueCommands = {};
// Call setup
- t.execCallback('setup', t);
+ self.execCallback('setup', self);
},
render : function(nst) {
@@ -11306,7 +13420,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Page is not loaded yet, wait for it
if (!Event.domLoaded) {
- Event.add(document, 'init', function() {
+ Event.add(window, 'ready', function() {
t.render();
});
return;
@@ -11319,8 +13433,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return;
// Is a iPad/iPhone and not on iOS5, then skip initialization. We need to sniff
- // here since the browser says it has contentEditable support but there is no visible
- // caret We will remove this check ones Apple implements full contentEditable support
+ // here since the browser says it has contentEditable support but there is no visible caret.
if (tinymce.isIDevice && !tinymce.isIOS5)
return;
@@ -11328,6 +13441,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (!/TEXTAREA|INPUT/i.test(t.getElement().nodeName) && s.hidden_input && DOM.getParent(id, 'form'))
DOM.insertAfter(DOM.create('input', {type : 'hidden', name : id}), id);
+ // Hide target element early to prevent content flashing
+ if (!s.content_editable) {
+ t.orgVisibility = t.getElement().style.visibility;
+ t.getElement().style.visibility = 'hidden';
+ }
+
if (tinymce.WindowManager)
t.windowManager = new tinymce.WindowManager(t);
@@ -11389,7 +13508,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (s.language && s.language_load !== false)
sl.add(tinymce.baseURL + '/langs/' + s.language + '.js');
- if (s.theme && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
+ if (s.theme && typeof s.theme != "function" && s.theme.charAt(0) != '-' && !ThemeManager.urls[s.theme])
ThemeManager.load(s.theme, 'themes/' + s.theme + '/editor_template' + tinymce.suffix + '.js');
each(explode(s.plugins), function(p) {
@@ -11399,9 +13518,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var dependencies = PluginManager.dependencies(p);
each(dependencies, function(dep) {
var defaultSettings = {prefix:'plugins/', resource: dep, suffix:'/editor_plugin' + tinymce.suffix + '.js'};
- var dep = PluginManager.createUrl(defaultSettings, dep);
+ dep = PluginManager.createUrl(defaultSettings, dep);
PluginManager.load(dep.resource, dep);
-
});
} else {
// Skip safari plugin, since it is removed as of 3.3b1
@@ -11424,20 +13542,25 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
init : function() {
- var n, t = this, s = t.settings, w, h, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];
+ var n, t = this, s = t.settings, w, h, mh, e = t.getElement(), o, ti, u, bi, bc, re, i, initializedPlugins = [];
tinymce.add(t);
s.aria_label = s.aria_label || DOM.getAttrib(e, 'aria-label', t.getLang('aria.rich_text_area'));
if (s.theme) {
- s.theme = s.theme.replace(/-/, '');
- o = ThemeManager.get(s.theme);
- t.theme = new o();
+ if (typeof s.theme != "function") {
+ s.theme = s.theme.replace(/-/, '');
+ o = ThemeManager.get(s.theme);
+ t.theme = new o();
- if (t.theme.init && s.init_theme)
- t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
+ if (t.theme.init)
+ t.theme.init(t, ThemeManager.urls[s.theme] || tinymce.documentBaseURL.replace(/\/$/, ''));
+ } else {
+ t.theme = s.theme;
+ }
}
+
function initPlugin(p) {
var c = PluginManager.get(p), u = PluginManager.urls[p] || tinymce.documentBaseURL.replace(/\/$/, ''), po;
if (c && tinymce.inArray(initializedPlugins,p) === -1) {
@@ -11471,85 +13594,93 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
t.controlManager = new tinymce.ControlManager(t);
- if (s.custom_undo_redo) {
- t.onBeforeExecCommand.add(function(ed, cmd, ui, val, a) {
- if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
- t.undoManager.beforeChange();
- });
-
- t.onExecCommand.add(function(ed, cmd, ui, val, a) {
- if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!a || !a.skip_undo))
- t.undoManager.add();
- });
- }
-
- t.onExecCommand.add(function(ed, c) {
- // Don't refresh the select lists until caret move
- if (!/^(FontName|FontSize)$/.test(c))
- t.nodeChanged();
- });
-
- // Remove ghost selections on images and tables in Gecko
- if (isGecko) {
- function repaint(a, o) {
- if (!o || !o.initial)
- t.execCommand('mceRepaint');
- };
-
- t.onUndo.add(repaint);
- t.onRedo.add(repaint);
- t.onSetContent.add(repaint);
- }
-
// Enables users to override the control factory
t.onBeforeRenderUI.dispatch(t, t.controlManager);
// Measure box
- if (s.render_ui) {
- w = s.width || e.style.width || e.offsetWidth;
- h = s.height || e.style.height || e.offsetHeight;
+ if (s.render_ui && t.theme) {
t.orgDisplay = e.style.display;
- re = /^[0-9\.]+(|px)$/i;
- if (re.test('' + w))
- w = Math.max(parseInt(w) + (o.deltaWidth || 0), 100);
+ if (typeof s.theme != "function") {
+ w = s.width || e.style.width || e.offsetWidth;
+ h = s.height || e.style.height || e.offsetHeight;
+ mh = s.min_height || 100;
+ re = /^[0-9\.]+(|px)$/i;
+
+ if (re.test('' + w))
+ w = Math.max(parseInt(w, 10) + (o.deltaWidth || 0), 100);
+
+ if (re.test('' + h))
+ h = Math.max(parseInt(h, 10) + (o.deltaHeight || 0), mh);
+
+ // Render UI
+ o = t.theme.renderUI({
+ targetNode : e,
+ width : w,
+ height : h,
+ deltaWidth : s.delta_width,
+ deltaHeight : s.delta_height
+ });
- if (re.test('' + h))
- h = Math.max(parseInt(h) + (o.deltaHeight || 0), 100);
+ // Resize editor
+ DOM.setStyles(o.sizeContainer || o.editorContainer, {
+ width : w,
+ height : h
+ });
- // Render UI
- o = t.theme.renderUI({
- targetNode : e,
- width : w,
- height : h,
- deltaWidth : s.delta_width,
- deltaHeight : s.delta_height
- });
+ h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
+ if (h < mh)
+ h = mh;
+ } else {
+ o = s.theme(t, e);
- t.editorContainer = o.editorContainer;
- }
+ // Convert element type to id:s
+ if (o.editorContainer.nodeType) {
+ o.editorContainer = o.editorContainer.id = o.editorContainer.id || t.id + "_parent";
+ }
+ // Convert element type to id:s
+ if (o.iframeContainer.nodeType) {
+ o.iframeContainer = o.iframeContainer.id = o.iframeContainer.id || t.id + "_iframecontainer";
+ }
- // User specified a document.domain value
- if (document.domain && location.hostname != document.domain)
- tinymce.relaxedDomain = document.domain;
+ // Use specified iframe height or the targets offsetHeight
+ h = o.iframeHeight || e.offsetHeight;
- // Resize editor
- DOM.setStyles(o.sizeContainer || o.editorContainer, {
- width : w,
- height : h
- });
+ // Store away the selection when it's changed to it can be restored later with a editor.focus() call
+ if (isIE) {
+ t.onInit.add(function(ed) {
+ ed.dom.bind(ed.getBody(), 'beforedeactivate keydown', function() {
+ ed.lastIERng = ed.selection.getRng();
+ });
+ });
+ }
+ }
+
+ t.editorContainer = o.editorContainer;
+ }
// Load specified content CSS last
if (s.content_css) {
- tinymce.each(explode(s.content_css), function(u) {
+ each(explode(s.content_css), function(u) {
t.contentCSS.push(t.documentBaseURI.toAbsolute(u));
});
}
- h = (o.iframeHeight || h) + (typeof(h) == 'number' ? (o.deltaHeight || 0) : '');
- if (h < 100)
- h = 100;
+ // Load specified content CSS last
+ if (s.content_style) {
+ t.contentStyles.push(s.content_style);
+ }
+
+ // Content editable mode ends here
+ if (s.content_editable) {
+ e = n = o = null; // Fix IE leak
+ return t.initContentBody();
+ }
+
+ // User specified a document.domain value
+ if (document.domain && location.hostname != document.domain)
+ tinymce.relaxedDomain = document.domain;
t.iframeHTML = s.doctype + '<html><head xmlns="http://www.w3.org/1999/xhtml">';
@@ -11559,10 +13690,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
t.iframeHTML += '<base href="' + t.documentBaseURI.getURI() + '" />';
// IE8 doesn't support carets behind images setting ie7_compat would force IE8+ to run in IE7 compat mode.
- if (s.ie7_compat)
- t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
- else
- t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';
+ if (tinymce.isIE8) {
+ if (s.ie7_compat)
+ t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=7" />';
+ else
+ t.iframeHTML += '<meta http-equiv="X-UA-Compatible" content="IE=edge" />';
+ }
t.iframeHTML += '<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />';
@@ -11571,6 +13704,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
t.iframeHTML += '<link type="text/css" rel="stylesheet" href="' + t.contentCSS[i] + '" />';
}
+ t.contentCSS = [];
+
bi = s.body_id || 'tinymce';
if (bi.indexOf('=') != -1) {
bi = t.getParam('body_id', '', 'hash');
@@ -11583,12 +13718,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
bc = bc[t.id] || '';
}
- t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '"><br></body></html>';
+ t.iframeHTML += '</head><body id="' + bi + '" class="mceContentBody ' + bc + '" onload="window.parent.tinyMCE.get(\'' + t.id + '\').onLoad.dispatch();"><br></body></html>';
// Domain relaxing enabled, then set document domain
if (tinymce.relaxedDomain && (isIE || (tinymce.isOpera && parseFloat(opera.version()) < 11))) {
// We need to write the contents here in IE since multiple writes messes up refresh button and back button
- u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.setupIframe();})()';
+ u = 'javascript:(function(){document.open();document.domain="' + document.domain + '";var ed = window.parent.tinyMCE.get("' + t.id + '");document.write(ed.iframeHTML);document.close();ed.initContentBody();})()';
}
// Create iframe
@@ -11607,78 +13742,73 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
});
t.contentAreaContainer = o.iframeContainer;
- DOM.get(o.editorContainer).style.display = t.orgDisplay;
+
+ if (o.editorContainer) {
+ DOM.get(o.editorContainer).style.display = t.orgDisplay;
+ }
+
+ // Restore visibility on target element
+ e.style.visibility = t.orgVisibility;
+
DOM.get(t.id).style.display = 'none';
DOM.setAttrib(t.id, 'aria-hidden', true);
if (!tinymce.relaxedDomain || !u)
- t.setupIframe();
+ t.initContentBody();
e = n = o = null; // Cleanup
},
- setupIframe : function() {
- var t = this, s = t.settings, e = DOM.get(t.id), d = t.getDoc(), h, b;
+ initContentBody : function() {
+ var self = this, settings = self.settings, targetElm = DOM.get(self.id), doc = self.getDoc(), html, body, contentCssText;
// Setup iframe body
- if (!isIE || !tinymce.relaxedDomain) {
- d.open();
- d.write(t.iframeHTML);
- d.close();
+ if ((!isIE || !tinymce.relaxedDomain) && !settings.content_editable) {
+ doc.open();
+ doc.write(self.iframeHTML);
+ doc.close();
if (tinymce.relaxedDomain)
- d.domain = tinymce.relaxedDomain;
+ doc.domain = tinymce.relaxedDomain;
+ }
+
+ if (settings.content_editable) {
+ DOM.addClass(targetElm, 'mceContentBody');
+ 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;
}
// It will not steal focus while setting contentEditable
- b = t.getBody();
- b.disabled = true;
+ body = self.getBody();
+ body.disabled = true;
- if (!s.readonly)
- b.contentEditable = true;
+ if (!settings.readonly)
+ body.contentEditable = self.getParam('content_editable_state', true);
- b.disabled = false;
+ body.disabled = false;
- t.schema = new tinymce.html.Schema(s);
+ self.schema = new tinymce.html.Schema(settings);
- t.dom = new tinymce.dom.DOMUtils(t.getDoc(), {
+ self.dom = new tinymce.dom.DOMUtils(doc, {
keep_values : true,
- url_converter : t.convertURL,
- url_converter_scope : t,
- hex_colors : s.force_hex_style_colors,
- class_filter : s.class_filter,
- update_styles : 1,
- fix_ie_paragraphs : 1,
- schema : t.schema
+ 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 : settings.content_editable ? self.id : null,
+ schema : self.schema
});
- t.parser = new tinymce.html.DomParser(s, t.schema);
-
- // Force anchor names closed, unless the setting "allow_html_in_named_anchor" is explicitly included.
- if (!t.settings.allow_html_in_named_anchor) {
- t.parser.addAttributeFilter('name', function(nodes, name) {
- var i = nodes.length, sibling, prevSibling, parent, node;
-
- while (i--) {
- node = nodes[i];
- if (node.name === 'a' && node.firstChild) {
- parent = node.parent;
-
- // Move children after current node
- sibling = node.lastChild;
- do {
- prevSibling = sibling.prev;
- parent.insert(sibling, node);
- sibling = prevSibling;
- } while (sibling);
- }
- }
- });
- }
+ self.parser = new tinymce.html.DomParser(settings, self.schema);
// Convert src and href into data-mce-src, data-mce-href and data-mce-style
- t.parser.addAttributeFilter('src,href,style', function(nodes, name) {
- var i = nodes.length, node, dom = t.dom, value, internalName;
+ self.parser.addAttributeFilter('src,href,style', function(nodes, name) {
+ var i = nodes.length, node, dom = self.dom, value, internalName;
while (i--) {
node = nodes[i];
@@ -11690,13 +13820,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (name === "style")
node.attr(internalName, dom.serializeStyle(dom.parseStyle(value), node.name));
else
- node.attr(internalName, t.convertURL(value, name, node.name));
+ node.attr(internalName, self.convertURL(value, name, node.name));
}
}
});
// Keep scripts from executing
- t.parser.addNodeFilter('script', function(nodes, name) {
+ self.parser.addNodeFilter('script', function(nodes, name) {
var i = nodes.length, node;
while (i--) {
@@ -11705,7 +13835,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
});
- t.parser.addNodeFilter('#cdata', function(nodes, name) {
+ self.parser.addNodeFilter('#cdata', function(nodes, name) {
var i = nodes.length, node;
while (i--) {
@@ -11716,8 +13846,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
});
- t.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {
- var i = nodes.length, node, nonEmptyElements = t.schema.getNonEmptyElements();
+ self.parser.addNodeFilter('p,h1,h2,h3,h4,h5,h6,div', function(nodes, name) {
+ var i = nodes.length, node, nonEmptyElements = self.schema.getNonEmptyElements();
while (i--) {
node = nodes[i];
@@ -11727,307 +13857,106 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
});
- t.serializer = new tinymce.dom.Serializer(s, t.dom, t.schema);
-
- t.selection = new tinymce.dom.Selection(t.dom, t.getWin(), t.serializer);
-
- t.formatter = new tinymce.Formatter(this);
-
- // Register default formats
- t.formatter.register({
- alignleft : [
- {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}},
- {selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}
- ],
-
- aligncenter : [
- {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}},
- {selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},
- {selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}
- ],
-
- alignright : [
- {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}},
- {selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}
- ],
-
- alignfull : [
- {selector : 'p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}}
- ],
-
- bold : [
- {inline : 'strong', remove : 'all'},
- {inline : 'span', styles : {fontWeight : 'bold'}},
- {inline : 'b', remove : 'all'}
- ],
-
- italic : [
- {inline : 'em', remove : 'all'},
- {inline : 'span', styles : {fontStyle : 'italic'}},
- {inline : 'i', remove : 'all'}
- ],
-
- underline : [
- {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},
- {inline : 'u', remove : 'all'}
- ],
-
- strikethrough : [
- {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},
- {inline : 'strike', remove : 'all'}
- ],
-
- forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},
- hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},
- fontname : {inline : 'span', styles : {fontFamily : '%value'}},
- fontsize : {inline : 'span', styles : {fontSize : '%value'}},
- fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},
- blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},
- subscript : {inline : 'sub'},
- superscript : {inline : 'sup'},
-
- link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,
- onmatch : function(node) {
- return true;
- },
-
- onformat : function(elm, fmt, vars) {
- each(vars, function(value, key) {
- t.dom.setAttrib(elm, key, value);
- });
- }
- },
-
- removeformat : [
- {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
- {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
- {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}
- ]
- });
-
- // Register default block formats
- each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {
- t.formatter.register(name, {block : name, remove : 'all'});
- });
+ self.serializer = new tinymce.dom.Serializer(settings, self.dom, self.schema);
- // Register user defined formats
- t.formatter.register(t.settings.formats);
+ self.selection = new tinymce.dom.Selection(self.dom, self.getWin(), self.serializer, self);
- t.undoManager = new tinymce.UndoManager(t);
+ self.formatter = new tinymce.Formatter(self);
- // Pass through
- t.undoManager.onAdd.add(function(um, l) {
- if (um.hasUndo())
- return t.onChange.dispatch(t, l, um);
- });
+ self.undoManager = new tinymce.UndoManager(self);
- t.undoManager.onUndo.add(function(um, l) {
- return t.onUndo.dispatch(t, l, um);
- });
-
- t.undoManager.onRedo.add(function(um, l) {
- return t.onRedo.dispatch(t, l, um);
- });
+ self.forceBlocks = new tinymce.ForceBlocks(self);
+ self.enterKey = new tinymce.EnterKey(self);
+ self.editorCommands = new tinymce.EditorCommands(self);
- t.forceBlocks = new tinymce.ForceBlocks(t, {
- forced_root_block : s.forced_root_block
+ self.onExecCommand.add(function(editor, command) {
+ // Don't refresh the select lists until caret move
+ if (!/^(FontName|FontSize)$/.test(command))
+ self.nodeChanged();
});
- t.editorCommands = new tinymce.EditorCommands(t);
-
// Pass through
- t.serializer.onPreProcess.add(function(se, o) {
- return t.onPreProcess.dispatch(t, o, se);
+ self.serializer.onPreProcess.add(function(se, o) {
+ return self.onPreProcess.dispatch(self, o, se);
});
- t.serializer.onPostProcess.add(function(se, o) {
- return t.onPostProcess.dispatch(t, o, se);
+ self.serializer.onPostProcess.add(function(se, o) {
+ return self.onPostProcess.dispatch(self, o, se);
});
- t.onPreInit.dispatch(t);
-
- if (!s.gecko_spellcheck)
- t.getBody().spellcheck = 0;
+ self.onPreInit.dispatch(self);
- if (!s.readonly)
- t._addEvents();
+ if (!settings.browser_spellcheck && !settings.gecko_spellcheck)
+ doc.body.spellcheck = false;
- t.controlManager.onPostRender.dispatch(t, t.controlManager);
- t.onPostRender.dispatch(t);
-
- t.quirks = new tinymce.util.Quirks(this);
-
- if (s.directionality)
- t.getBody().dir = s.directionality;
-
- if (s.nowrap)
- t.getBody().style.whiteSpace = "nowrap";
-
- if (s.handle_node_change_callback) {
- t.onNodeChange.add(function(ed, cm, n) {
- t.execCallback('handle_node_change_callback', t.id, n, -1, -1, true, t.selection.isCollapsed());
- });
+ if (!settings.readonly) {
+ self.bindNativeEvents();
}
- if (s.save_callback) {
- t.onSaveContent.add(function(ed, o) {
- var h = t.execCallback('save_callback', t.id, o.content, t.getBody());
+ self.controlManager.onPostRender.dispatch(self, self.controlManager);
+ self.onPostRender.dispatch(self);
- if (h)
- o.content = h;
- });
- }
+ self.quirks = tinymce.util.Quirks(self);
- if (s.onchange_callback) {
- t.onChange.add(function(ed, l) {
- t.execCallback('onchange_callback', t, l);
- });
- }
+ if (settings.directionality)
+ body.dir = settings.directionality;
- if (s.protect) {
- t.onBeforeSetContent.add(function(ed, o) {
- if (s.protect) {
- each(s.protect, function(pattern) {
- o.content = o.content.replace(pattern, function(str) {
- return '<!--mce:protected ' + escape(str) + '-->';
- });
- });
- }
- });
- }
-
- if (s.convert_newlines_to_brs) {
- t.onBeforeSetContent.add(function(ed, o) {
- if (o.initial)
- o.content = o.content.replace(/\r?\n/g, '<br />');
- });
- }
+ if (settings.nowrap)
+ body.style.whiteSpace = "nowrap";
- if (s.preformatted) {
- t.onPostProcess.add(function(ed, o) {
- o.content = o.content.replace(/^\s*<pre.*?>/, '');
- o.content = o.content.replace(/<\/pre>\s*$/, '');
-
- if (o.set)
- o.content = '<pre class="mceItemHidden">' + o.content + '</pre>';
- });
- }
-
- if (s.verify_css_classes) {
- t.serializer.attribValueFilter = function(n, v) {
- var s, cl;
-
- if (n == 'class') {
- // Build regexp for classes
- if (!t.classesRE) {
- cl = t.dom.getClasses();
-
- if (cl.length > 0) {
- s = '';
-
- each (cl, function(o) {
- s += (s ? '|' : '') + o['class'];
- });
-
- t.classesRE = new RegExp('(' + s + ')', 'gi');
- }
- }
-
- return !t.classesRE || /(\bmceItem\w+\b|\bmceTemp\w+\b)/g.test(v) || t.classesRE.test(v) ? v : '';
- }
-
- return v;
- };
- }
-
- if (s.cleanup_callback) {
- t.onBeforeSetContent.add(function(ed, o) {
- o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
- });
-
- t.onPreProcess.add(function(ed, o) {
- if (o.set)
- t.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
-
- if (o.get)
- t.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
- });
-
- t.onPostProcess.add(function(ed, o) {
- if (o.set)
- o.content = t.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
-
- if (o.get)
- o.content = t.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
- });
- }
-
- if (s.save_callback) {
- t.onGetContent.add(function(ed, o) {
- if (o.save)
- o.content = t.execCallback('save_callback', t.id, o.content, t.getBody());
- });
- }
-
- if (s.handle_event_callback) {
- t.onEvent.add(function(ed, e, o) {
- if (t.execCallback('handle_event_callback', e, ed, o) === false)
- Event.cancel(e);
+ if (settings.protect) {
+ self.onBeforeSetContent.add(function(ed, o) {
+ each(settings.protect, function(pattern) {
+ o.content = o.content.replace(pattern, function(str) {
+ return '<!--mce:protected ' + escape(str) + '-->';
+ });
+ });
});
}
// Add visual aids when new contents is added
- t.onSetContent.add(function() {
- t.addVisual(t.getBody());
+ self.onSetContent.add(function() {
+ self.addVisual(self.getBody());
});
// Remove empty contents
- if (s.padd_empty_editor) {
- t.onPostProcess.add(function(ed, o) {
+ if (settings.padd_empty_editor) {
+ self.onPostProcess.add(function(ed, o) {
o.content = o.content.replace(/^(<p[^>]*>(&nbsp;|&#160;|\s|\u00a0|)<\/p>[\r\n]*|<br \/>[\r\n]*)$/, '');
});
}
- if (isGecko) {
- // Fix 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
- function fixLinks(ed, o) {
- each(ed.dom.select('a'), function(n) {
- var pn = n.parentNode;
-
- if (ed.dom.isBlock(pn) && pn.lastChild === n)
- ed.dom.add(pn, 'br', {'data-mce-bogus' : 1});
- });
- };
+ self.load({initial : true, format : 'html'});
+ self.startContent = self.getContent({format : 'raw'});
- t.onExecCommand.add(function(ed, cmd) {
- if (cmd === 'CreateLink')
- fixLinks(ed);
- });
+ self.initialized = true;
- t.onSetContent.add(t.selection.onSetContent.add(fixLinks));
- }
+ self.onInit.dispatch(self);
+ self.execCallback('setupcontent_callback', self.id, body, doc);
+ self.execCallback('init_instance_callback', self);
+ self.focus(true);
+ self.nodeChanged({initial : true});
- t.load({initial : true, format : 'html'});
- t.startContent = t.getContent({format : 'raw'});
- t.undoManager.add();
- t.initialized = true;
+ // Add editor specific CSS styles
+ if (self.contentStyles.length > 0) {
+ contentCssText = '';
- t.onInit.dispatch(t);
- t.execCallback('setupcontent_callback', t.id, t.getBody(), t.getDoc());
- t.execCallback('init_instance_callback', t);
- t.focus(true);
- t.nodeChanged({initial : 1});
+ each(self.contentStyles, function(style) {
+ contentCssText += style + "\r\n";
+ });
+
+ self.dom.addStyle(contentCssText);
+ }
// Load specified content CSS last
- each(t.contentCSS, function(u) {
- t.dom.loadCSS(u);
+ each(self.contentCSS, function(url) {
+ self.dom.loadCSS(url);
});
// Handle auto focus
- if (s.auto_focus) {
+ if (settings.auto_focus) {
setTimeout(function () {
- var ed = tinymce.get(s.auto_focus);
+ var ed = tinymce.get(settings.auto_focus);
ed.selection.select(ed.getBody(), 1);
ed.selection.collapse(1);
@@ -12036,30 +13965,45 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}, 100);
}
- e = null;
+ // Clean up references for IE
+ targetElm = doc = body = null;
},
+ focus : function(skip_focus) {
+ var oed, self = this, selection = self.selection, contentEditable = self.settings.content_editable, ieRng, controlElm, doc = self.getDoc(), body;
- focus : function(sf) {
- var oed, t = this, selection = t.selection, ce = t.settings.content_editable, ieRng, controlElm, doc = t.getDoc();
+ if (!skip_focus) {
+ if (self.lastIERng) {
+ selection.setRng(self.lastIERng);
+ }
- if (!sf) {
// Get selected control element
ieRng = selection.getRng();
if (ieRng.item) {
controlElm = ieRng.item(0);
}
- t._refreshContentEditable();
- selection.normalize();
+ self._refreshContentEditable();
- // Is not content editable
- if (!ce)
- t.getWin().focus();
+ // Focus the window iframe
+ if (!contentEditable) {
+ self.getWin().focus();
+ }
// Focus the body as well since it's contentEditable
- if (tinymce.isGecko) {
- t.getBody().focus();
+ if (tinymce.isGecko || contentEditable) {
+ body = self.getBody();
+
+ // Check for setActive since it doesn't scroll to the element
+ if (body.setActive) {
+ body.setActive();
+ } else {
+ body.focus();
+ }
+
+ if (contentEditable) {
+ selection.normalize();
+ }
}
// Restore selected control element
@@ -12070,17 +14014,16 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
ieRng.addElement(controlElm);
ieRng.select();
}
-
}
- if (tinymce.activeEditor != t) {
+ if (tinymce.activeEditor != self) {
if ((oed = tinymce.activeEditor) != null)
- oed.onDeactivate.dispatch(oed, t);
+ oed.onDeactivate.dispatch(oed, self);
- t.onActivate.dispatch(t, oed);
+ self.onActivate.dispatch(self, oed);
}
- tinymce._setActive(t);
+ tinymce._setActive(self);
},
execCallback : function(n) {
@@ -12112,7 +14055,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (!s)
return '';
- return i18n[c + '.' + s] || s.replace(/{\#([^}]+)\}/g, function(a, b) {
+ return i18n[c + '.' + s] || s.replace(/\{\#([^\}]+)\}/g, function(a, b) {
return i18n[c + '.' + b] || '{#' + b + '}';
});
},
@@ -12146,37 +14089,40 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
nodeChanged : function(o) {
- var t = this, s = t.selection, n = s.getStart() || t.getBody();
+ var self = this, selection = self.selection, node;
// Fix for bug #1896577 it seems that this can not be fired while the editor is loading
- if (t.initialized) {
+ if (self.initialized) {
o = o || {};
- n = isIE && n.ownerDocument != t.getDoc() ? t.getBody() : n; // Fix for IE initial state
+
+ // Get start node
+ node = selection.getStart() || self.getBody();
+ node = isIE && node.ownerDocument != self.getDoc() ? self.getBody() : node; // Fix for IE initial state
// Get parents and add them to object
o.parents = [];
- t.dom.getParent(n, function(node) {
+ self.dom.getParent(node, function(node) {
if (node.nodeName == 'BODY')
return true;
o.parents.push(node);
});
- t.onNodeChange.dispatch(
- t,
- o ? o.controlManager || t.controlManager : t.controlManager,
- n,
- s.isCollapsed(),
+ self.onNodeChange.dispatch(
+ self,
+ o ? o.controlManager || self.controlManager : self.controlManager,
+ node,
+ selection.isCollapsed(),
o
);
}
},
- addButton : function(n, s) {
- var t = this;
+ addButton : function(name, settings) {
+ var self = this;
- t.buttons = t.buttons || {};
- t.buttons[n] = s;
+ self.buttons = self.buttons || {};
+ self.buttons[name] = settings;
},
addCommand : function(name, callback, scope) {
@@ -12194,7 +14140,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
addShortcut : function(pa, desc, cmd_func, sc) {
var t = this, c;
- if (!t.settings.custom_shortcuts)
+ if (t.settings.custom_shortcuts === false)
return false;
t.shortcuts = t.shortcuts || {};
@@ -12219,7 +14165,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var o = {
func : cmd_func,
scope : sc || this,
- desc : desc,
+ desc : t.translate(desc),
alt : false,
ctrl : false,
shift : false
@@ -12251,9 +14197,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (!/^(mceAddUndoLevel|mceEndUndoLevel|mceBeginUndoLevel|mceRepaint|SelectAll)$/.test(cmd) && (!a || !a.skip_focus))
t.focus();
- o = {};
- t.onBeforeExecCommand.dispatch(t, cmd, ui, val, o);
- if (o.terminate)
+ a = extend({}, a);
+ t.onBeforeExecCommand.dispatch(t, cmd, ui, val, a);
+ if (a.terminate)
return false;
// Command callback
@@ -12361,24 +14307,26 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
show : function() {
- var t = this;
+ var self = this;
- DOM.show(t.getContainer());
- DOM.hide(t.id);
- t.load();
+ DOM.show(self.getContainer());
+ DOM.hide(self.id);
+ self.load();
},
hide : function() {
- var t = this, d = t.getDoc();
+ var self = this, doc = self.getDoc();
// Fixed bug where IE has a blinking cursor left from the editor
- if (isIE && d)
- d.execCommand('SelectAll');
+ if (isIE && doc)
+ doc.execCommand('SelectAll');
// We must save before we hide so Safari doesn't crash
- t.save();
- DOM.hide(t.getContainer());
- DOM.setStyle(t.id, 'display', t.orgDisplay);
+ self.save();
+
+ // defer the call to hide to prevent an IE9 crash #4921
+ DOM.hide(self.getContainer());
+ DOM.setStyle(self.id, 'display', self.orgDisplay);
},
isHidden : function() {
@@ -12420,12 +14368,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
o = o || {};
o.save = true;
- // Add undo level will trigger onchange event
- if (!o.no_events) {
- t.undoManager.typing = false;
- t.undoManager.add();
- }
-
o.element = e;
h = o.content = t.getContent(o);
@@ -12499,18 +14441,22 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (!args.no_events)
self.onSetContent.dispatch(self, args);
- self.selection.normalize();
+ // Don't normalize selection if the focused element isn't the body in content editable mode since it will steal focus otherwise
+ if (!self.settings.content_editable || document.activeElement === self.getBody()) {
+ self.selection.normalize();
+ }
return args.content;
},
getContent : function(args) {
- var self = this, content;
+ var self = this, content, body = self.getBody();
// Setup args object
args = args || {};
args.format = args.format || 'html';
args.get = true;
+ args.getInner = true;
// Do preprocessing
if (!args.no_events)
@@ -12518,11 +14464,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Get raw contents or by default the cleaned contents
if (args.format == 'raw')
- content = self.getBody().innerHTML;
+ content = body.innerHTML;
+ else if (args.format == 'text')
+ content = body.innerText || body.textContent;
else
- content = self.serializer.serialize(self.getBody(), args);
+ content = self.serializer.serialize(body, args);
- args.content = tinymce.trim(content);
+ // Trim whitespace in beginning/end of HTML
+ if (args.format != 'text') {
+ args.content = tinymce.trim(content);
+ } else {
+ args.content = content;
+ }
// Do post processing
if (!args.no_events)
@@ -12538,12 +14491,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
getContainer : function() {
- var t = this;
+ var self = this;
- if (!t.container)
- t.container = DOM.get(t.editorContainer || t.id + '_parent');
+ if (!self.container)
+ self.container = DOM.get(self.editorContainer || self.id + '_parent');
- return t.container;
+ return self.container;
},
getContentAreaContainer : function() {
@@ -12555,111 +14508,135 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
getWin : function() {
- var t = this, e;
+ var self = this, elm;
- if (!t.contentWindow) {
- e = DOM.get(t.id + "_ifr");
+ if (!self.contentWindow) {
+ elm = DOM.get(self.id + "_ifr");
- if (e)
- t.contentWindow = e.contentWindow;
+ if (elm)
+ self.contentWindow = elm.contentWindow;
}
- return t.contentWindow;
+ return self.contentWindow;
},
getDoc : function() {
- var t = this, w;
+ var self = this, win;
- if (!t.contentDocument) {
- w = t.getWin();
+ if (!self.contentDocument) {
+ win = self.getWin();
- if (w)
- t.contentDocument = w.document;
+ if (win)
+ self.contentDocument = win.document;
}
- return t.contentDocument;
+ return self.contentDocument;
},
getBody : function() {
return this.bodyElement || this.getDoc().body;
},
- convertURL : function(u, n, e) {
- var t = this, s = t.settings;
+ convertURL : function(url, name, elm) {
+ var self = this, settings = self.settings;
// Use callback instead
- if (s.urlconverter_callback)
- return t.execCallback('urlconverter_callback', u, e, true, n);
+ if (settings.urlconverter_callback)
+ return self.execCallback('urlconverter_callback', url, elm, true, name);
// Don't convert link href since thats the CSS files that gets loaded into the editor also skip local file URLs
- if (!s.convert_urls || (e && e.nodeName == 'LINK') || u.indexOf('file:') === 0)
- return u;
+ if (!settings.convert_urls || (elm && elm.nodeName == 'LINK') || url.indexOf('file:') === 0)
+ return url;
// Convert to relative
- if (s.relative_urls)
- return t.documentBaseURI.toRelative(u);
+ if (settings.relative_urls)
+ return self.documentBaseURI.toRelative(url);
// Convert to absolute
- u = t.documentBaseURI.toAbsolute(u, s.remove_script_host);
+ url = self.documentBaseURI.toAbsolute(url, settings.remove_script_host);
- return u;
+ return url;
},
- addVisual : function(e) {
- var t = this, s = t.settings;
+ addVisual : function(elm) {
+ var self = this, settings = self.settings, dom = self.dom, cls;
- e = e || t.getBody();
+ elm = elm || self.getBody();
- if (!is(t.hasVisual))
- t.hasVisual = s.visual;
+ if (!is(self.hasVisual))
+ self.hasVisual = settings.visual;
- each(t.dom.select('table,a', e), function(e) {
- var v;
+ each(dom.select('table,a', elm), function(elm) {
+ var value;
- switch (e.nodeName) {
+ switch (elm.nodeName) {
case 'TABLE':
- v = t.dom.getAttrib(e, 'border');
+ cls = settings.visual_table_class || 'mceItemTable';
+ value = dom.getAttrib(elm, 'border');
- if (!v || v == '0') {
- if (t.hasVisual)
- t.dom.addClass(e, s.visual_table_class);
+ if (!value || value == '0') {
+ if (self.hasVisual)
+ dom.addClass(elm, cls);
else
- t.dom.removeClass(e, s.visual_table_class);
+ dom.removeClass(elm, cls);
}
return;
case 'A':
- v = t.dom.getAttrib(e, 'name');
+ if (!dom.getAttrib(elm, 'href', false)) {
+ value = dom.getAttrib(elm, 'name') || elm.id;
+ cls = 'mceItemAnchor';
- if (v) {
- if (t.hasVisual)
- t.dom.addClass(e, 'mceItemAnchor');
- else
- t.dom.removeClass(e, 'mceItemAnchor');
+ if (value) {
+ if (self.hasVisual)
+ dom.addClass(elm, cls);
+ else
+ dom.removeClass(elm, cls);
+ }
}
return;
}
});
- t.onVisualAid.dispatch(t, e, t.hasVisual);
+ self.onVisualAid.dispatch(self, elm, self.hasVisual);
},
remove : function() {
- var t = this, e = t.getContainer();
+ var self = this, elm = self.getContainer(), doc = self.getDoc();
+
+ if (!self.removed) {
+ self.removed = 1; // Cancels post remove event execution
+
+ // Fixed bug where IE has a blinking cursor left from the editor
+ if (isIE && doc)
+ doc.execCommand('SelectAll');
+
+ // We must save before we hide so Safari doesn't crash
+ self.save();
+
+ DOM.setStyle(self.id, 'display', self.orgDisplay);
+
+ // Don't clear the window or document if content editable
+ // is enabled since other instances might still be present
+ if (!self.settings.content_editable) {
+ Event.unbind(self.getWin());
+ Event.unbind(self.getDoc());
+ }
- t.removed = 1; // Cancels post remove event execution
- t.hide();
+ Event.unbind(self.getBody());
+ Event.clear(elm);
- t.execCallback('remove_instance_callback', t);
- t.onRemove.dispatch(t);
+ self.execCallback('remove_instance_callback', self);
+ self.onRemove.dispatch(self);
- // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
- t.onExecCommand.listeners = [];
+ // Clear all execCommand listeners this is required to avoid errors if the editor was removed inside another command
+ self.onExecCommand.listeners = [];
- tinymce.remove(t);
- DOM.remove(e);
+ tinymce.remove(self);
+ DOM.remove(elm);
+ }
},
destroy : function(s) {
@@ -12669,6 +14646,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (t.destroyed)
return;
+ // We must unbind on Gecko since it would otherwise produce the pesky "attempt to run compile-and-go script on a cleared scope" message
+ if (isGecko) {
+ Event.unbind(t.getDoc());
+ Event.unbind(t.getWin());
+ Event.unbind(t.getBody());
+ }
+
if (!s) {
tinymce.removeUnload(t.destroy);
tinyMCE.onBeforeUnload.remove(t._beforeUnload);
@@ -12681,18 +14665,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
t.controlManager.destroy();
t.selection.destroy();
t.dom.destroy();
-
- // Remove all events
-
- // Don't clear the window or document if content editable
- // is enabled since other instances might still be present
- if (!t.settings.content_editable) {
- Event.clear(t.getWin());
- Event.clear(t.getDoc());
- }
-
- Event.clear(t.getBody());
- Event.clear(t.formElement);
}
if (t.formElement) {
@@ -12710,422 +14682,322 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Internal functions
- _addEvents : function() {
- // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
- var t = this, i, s = t.settings, dom = t.dom, lo = {
- mouseup : 'onMouseUp',
- mousedown : 'onMouseDown',
- click : 'onClick',
- keyup : 'onKeyUp',
- keydown : 'onKeyDown',
- keypress : 'onKeyPress',
- submit : 'onSubmit',
- reset : 'onReset',
- contextmenu : 'onContextMenu',
- dblclick : 'onDblClick',
- paste : 'onPaste' // Doesn't work in all browsers yet
- };
+ _refreshContentEditable : function() {
+ var self = this, body, parent;
- function eventHandler(e, o) {
- var ty = e.type;
+ // Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
+ if (self._isHidden()) {
+ body = self.getBody();
+ parent = body.parentNode;
- // Don't fire events when it's removed
- if (t.removed)
- return;
+ parent.removeChild(body);
+ parent.appendChild(body);
- // Generic event handler
- if (t.onEvent.dispatch(t, e, o) !== false) {
- // Specific event handler
- t[lo[e.fakeType || e.type]].dispatch(t, e, o);
- }
- };
+ body.focus();
+ }
+ },
- // Add DOM events
- each(lo, function(v, k) {
- switch (k) {
- case 'contextmenu':
- dom.bind(t.getDoc(), k, eventHandler);
- break;
+ _isHidden : function() {
+ var s;
- case 'paste':
- dom.bind(t.getBody(), k, function(e) {
- eventHandler(e);
- });
- break;
+ if (!isGecko)
+ return 0;
- case 'submit':
- case 'reset':
- dom.bind(t.getElement().form || DOM.getParent(t.id, 'form'), k, eventHandler);
- break;
+ // Weird, wheres that cursor selection?
+ s = this.selection.getSel();
+ return (!s || !s.rangeCount || s.rangeCount === 0);
+ }
+ });
+})(tinymce);
+(function(tinymce) {
+ var each = tinymce.each;
- default:
- dom.bind(s.content_editable ? t.getBody() : t.getDoc(), k, eventHandler);
- }
- });
+ tinymce.Editor.prototype.setupEvents = function() {
+ var self = this, settings = self.settings;
- dom.bind(s.content_editable ? t.getBody() : (isGecko ? t.getDoc() : t.getWin()), 'focus', function(e) {
- t.focus(true);
- });
+ // Add events to the editor
+ each([
+ 'onPreInit',
+ 'onBeforeRenderUI',
- // Fixes bug where a specified document_base_uri could result in broken images
- // This will also fix drag drop of images in Gecko
- if (tinymce.isGecko) {
- dom.bind(t.getDoc(), 'DOMNodeInserted', function(e) {
- var v;
+ 'onPostRender',
- e = e.target;
+ 'onLoad',
- if (e.nodeType === 1 && e.nodeName === 'IMG' && (v = e.getAttribute('data-mce-src')))
- e.src = t.documentBaseURI.toAbsolute(v);
- });
- }
+ 'onInit',
- // Set various midas options in Gecko
- if (isGecko) {
- function setOpts() {
- var t = this, d = t.getDoc(), s = t.settings;
+ 'onRemove',
- if (isGecko && !s.readonly) {
- t._refreshContentEditable();
+ 'onActivate',
- try {
- // Try new Gecko method
- d.execCommand("styleWithCSS", 0, false);
- } catch (ex) {
- // Use old method
- if (!t._isHidden())
- try {d.execCommand("useCSS", 0, true);} catch (ex) {}
- }
+ 'onDeactivate',
- if (!s.table_inline_editing)
- try {d.execCommand('enableInlineTableEditing', false, false);} catch (ex) {}
+ 'onClick',
- if (!s.object_resizing)
- try {d.execCommand('enableObjectResizing', false, false);} catch (ex) {}
- }
- };
+ 'onEvent',
- t.onBeforeExecCommand.add(setOpts);
- t.onMouseDown.add(setOpts);
- }
+ 'onMouseUp',
- // Add node change handlers
- t.onMouseUp.add(t.nodeChanged);
- //t.onClick.add(t.nodeChanged);
- t.onKeyUp.add(function(ed, e) {
- var c = e.keyCode;
+ 'onMouseDown',
- if ((c >= 33 && c <= 36) || (c >= 37 && c <= 40) || c == 13 || c == 45 || c == 46 || c == 8 || (tinymce.isMac && (c == 91 || c == 93)) || e.ctrlKey)
- t.nodeChanged();
- });
+ 'onDblClick',
+ 'onKeyDown',
- // Add block quote deletion handler
- t.onKeyDown.add(function(ed, e) {
- // Was the BACKSPACE key pressed?
- if (e.keyCode != 8)
- return;
+ 'onKeyUp',
- var n = ed.selection.getRng().startContainer;
- var offset = ed.selection.getRng().startOffset;
+ 'onKeyPress',
- while (n && n.nodeType && n.nodeType != 1 && n.parentNode)
- n = n.parentNode;
-
- // Is the cursor at the beginning of a blockquote?
- if (n && n.parentNode && n.parentNode.tagName === 'BLOCKQUOTE' && n.parentNode.firstChild == n && offset == 0) {
- // Remove the blockquote
- ed.formatter.toggle('blockquote', null, n.parentNode);
-
- // Move the caret to the beginning of n
- var rng = ed.selection.getRng();
- rng.setStart(n, 0);
- rng.setEnd(n, 0);
- ed.selection.setRng(rng);
- ed.selection.collapse(false);
- }
- });
-
+ 'onContextMenu',
+ 'onSubmit',
- // Add reset handler
- t.onReset.add(function() {
- t.setContent(t.startContent, {format : 'raw'});
- });
+ 'onReset',
- // Add shortcuts
- if (s.custom_shortcuts) {
- if (s.custom_undo_redo_keyboard_shortcuts) {
- t.addShortcut('ctrl+z', t.getLang('undo_desc'), 'Undo');
- t.addShortcut('ctrl+y', t.getLang('redo_desc'), 'Redo');
- }
+ 'onPaste',
- // Add default shortcuts for gecko
- t.addShortcut('ctrl+b', t.getLang('bold_desc'), 'Bold');
- t.addShortcut('ctrl+i', t.getLang('italic_desc'), 'Italic');
- t.addShortcut('ctrl+u', t.getLang('underline_desc'), 'Underline');
+ 'onPreProcess',
- // BlockFormat shortcuts keys
- for (i=1; i<=6; i++)
- t.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
+ 'onPostProcess',
- t.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
- t.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
- t.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
+ 'onBeforeSetContent',
- function find(e) {
- var v = null;
+ 'onBeforeGetContent',
- if (!e.altKey && !e.ctrlKey && !e.metaKey)
- return v;
+ 'onSetContent',
- each(t.shortcuts, function(o) {
- if (tinymce.isMac && o.ctrl != e.metaKey)
- return;
- else if (!tinymce.isMac && o.ctrl != e.ctrlKey)
- return;
+ 'onGetContent',
- if (o.alt != e.altKey)
- return;
+ 'onLoadContent',
- if (o.shift != e.shiftKey)
- return;
+ 'onSaveContent',
- if (e.keyCode == o.keyCode || (e.charCode && e.charCode == o.charCode)) {
- v = o;
- return false;
- }
- });
+ 'onNodeChange',
- return v;
- };
+ 'onChange',
- t.onKeyUp.add(function(ed, e) {
- var o = find(e);
+ 'onBeforeExecCommand',
- if (o)
- return Event.cancel(e);
- });
+ 'onExecCommand',
- t.onKeyPress.add(function(ed, e) {
- var o = find(e);
+ 'onUndo',
- if (o)
- return Event.cancel(e);
- });
+ 'onRedo',
- t.onKeyDown.add(function(ed, e) {
- var o = find(e);
+ 'onVisualAid',
- if (o) {
- o.func.call(o.scope);
- return Event.cancel(e);
- }
- });
- }
+ 'onSetProgressState',
- if (tinymce.isIE) {
- // Fix so resize will only update the width and height attributes not the styles of an image
- // It will also block mceItemNoResize items
- dom.bind(t.getDoc(), 'controlselect', function(e) {
- var re = t.resizeInfo, cb;
+ 'onSetAttrib'
+ ], function(name) {
+ self[name] = new tinymce.util.Dispatcher(self);
+ });
- e = e.target;
+ // Handle legacy cleanup_callback option
+ if (settings.cleanup_callback) {
+ self.onBeforeSetContent.add(function(ed, o) {
+ o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
+ });
- // Don't do this action for non image elements
- if (e.nodeName !== 'IMG')
- return;
+ self.onPreProcess.add(function(ed, o) {
+ if (o.set)
+ ed.execCallback('cleanup_callback', 'insert_to_editor_dom', o.node, o);
- if (re)
- dom.unbind(re.node, re.ev, re.cb);
+ if (o.get)
+ ed.execCallback('cleanup_callback', 'get_from_editor_dom', o.node, o);
+ });
- if (!dom.hasClass(e, 'mceItemNoResize')) {
- ev = 'resizeend';
- cb = dom.bind(e, ev, function(e) {
- var v;
+ self.onPostProcess.add(function(ed, o) {
+ if (o.set)
+ o.content = ed.execCallback('cleanup_callback', 'insert_to_editor', o.content, o);
- e = e.target;
+ if (o.get)
+ o.content = ed.execCallback('cleanup_callback', 'get_from_editor', o.content, o);
+ });
+ }
- if (v = dom.getStyle(e, 'width')) {
- dom.setAttrib(e, 'width', v.replace(/[^0-9%]+/g, ''));
- dom.setStyle(e, 'width', '');
- }
+ // Handle legacy save_callback option
+ if (settings.save_callback) {
+ self.onGetContent.add(function(ed, o) {
+ if (o.save)
+ o.content = ed.execCallback('save_callback', ed.id, o.content, ed.getBody());
+ });
+ }
- if (v = dom.getStyle(e, 'height')) {
- dom.setAttrib(e, 'height', v.replace(/[^0-9%]+/g, ''));
- dom.setStyle(e, 'height', '');
- }
- });
- } else {
- ev = 'resizestart';
- cb = dom.bind(e, 'resizestart', Event.cancel, Event);
- }
+ // Handle legacy handle_event_callback option
+ if (settings.handle_event_callback) {
+ self.onEvent.add(function(ed, e, o) {
+ if (self.execCallback('handle_event_callback', e, ed, o) === false) {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+ });
+ }
- re = t.resizeInfo = {
- node : e,
- ev : ev,
- cb : cb
- };
- });
- }
+ // Handle legacy handle_node_change_callback option
+ if (settings.handle_node_change_callback) {
+ self.onNodeChange.add(function(ed, cm, n) {
+ ed.execCallback('handle_node_change_callback', ed.id, n, -1, -1, true, ed.selection.isCollapsed());
+ });
+ }
- if (tinymce.isOpera) {
- t.onClick.add(function(ed, e) {
- Event.prevent(e);
- });
- }
+ // Handle legacy save_callback option
+ if (settings.save_callback) {
+ self.onSaveContent.add(function(ed, o) {
+ var h = ed.execCallback('save_callback', ed.id, o.content, ed.getBody());
- // Add custom undo/redo handlers
- if (s.custom_undo_redo) {
- function addUndo() {
- t.undoManager.typing = false;
- t.undoManager.add();
- };
+ if (h)
+ o.content = h;
+ });
+ }
- dom.bind(t.getDoc(), 'focusout', function(e) {
- if (!t.removed && t.undoManager.typing)
- addUndo();
- });
+ // Handle legacy onchange_callback option
+ if (settings.onchange_callback) {
+ self.onChange.add(function(ed, l) {
+ ed.execCallback('onchange_callback', ed, l);
+ });
+ }
+ };
- // Add undo level when contents is drag/dropped within the editor
- t.dom.bind(t.dom.getRoot(), 'dragend', function(e) {
- addUndo();
- });
+ tinymce.Editor.prototype.bindNativeEvents = function() {
+ // 'focus', 'blur', 'dblclick', 'beforedeactivate', submit, reset
+ var self = this, i, settings = self.settings, dom = self.dom, nativeToDispatcherMap;
+
+ nativeToDispatcherMap = {
+ mouseup : 'onMouseUp',
+ mousedown : 'onMouseDown',
+ click : 'onClick',
+ keyup : 'onKeyUp',
+ keydown : 'onKeyDown',
+ keypress : 'onKeyPress',
+ submit : 'onSubmit',
+ reset : 'onReset',
+ contextmenu : 'onContextMenu',
+ dblclick : 'onDblClick',
+ paste : 'onPaste' // Doesn't work in all browsers yet
+ };
- t.onKeyUp.add(function(ed, e) {
- var keyCode = e.keyCode;
+ // Handler that takes a native event and sends it out to a dispatcher like onKeyDown
+ function eventHandler(evt, args) {
+ var type = evt.type;
- if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || e.ctrlKey)
- addUndo();
- });
+ // Don't fire events when it's removed
+ if (self.removed)
+ return;
- t.onKeyDown.add(function(ed, e) {
- var keyCode = e.keyCode, sel;
+ // Sends the native event out to a global dispatcher then to the specific event dispatcher
+ if (self.onEvent.dispatch(self, evt, args) !== false) {
+ self[nativeToDispatcherMap[evt.fakeType || evt.type]].dispatch(self, evt, args);
+ }
+ };
- if (keyCode == 8) {
- sel = t.getDoc().selection;
+ // Opera doesn't support focus event for contentEditable elements so we need to fake it
+ function doOperaFocus(e) {
+ self.focus(true);
+ };
- // Fix IE control + backspace browser bug
- if (sel && sel.createRange && sel.createRange().item) {
- t.undoManager.beforeChange();
- ed.dom.remove(sel.createRange().item(0));
- addUndo();
+ function nodeChanged(ed, e) {
+ // Normalize selection for example <b>a</b><i>|a</i> becomes <b>a|</b><i>a</i> except for Ctrl+A since it selects everything
+ if (e.keyCode != 65 || !tinymce.VK.metaKeyPressed(e)) {
+ self.selection.normalize();
+ }
- return Event.cancel(e);
- }
- }
+ self.nodeChanged();
+ }
- // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
- if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45) {
- // Add position before enter key is pressed, used by IE since it still uses the default browser behavior
- // Todo: Remove this once we normalize enter behavior on IE
- if (tinymce.isIE && keyCode == 13)
- t.undoManager.beforeChange();
+ // Add DOM events
+ each(nativeToDispatcherMap, function(dispatcherName, nativeName) {
+ var root = settings.content_editable ? self.getBody() : self.getDoc();
- if (t.undoManager.typing)
- addUndo();
+ switch (nativeName) {
+ case 'contextmenu':
+ dom.bind(root, nativeName, eventHandler);
+ break;
- return;
- }
+ case 'paste':
+ dom.bind(self.getBody(), nativeName, eventHandler);
+ break;
- // If key isn't shift,ctrl,alt,capslock,metakey
- if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !t.undoManager.typing) {
- t.undoManager.beforeChange();
- t.undoManager.typing = true;
- t.undoManager.add();
- }
- });
+ case 'submit':
+ case 'reset':
+ dom.bind(self.getElement().form || tinymce.DOM.getParent(self.id, 'form'), nativeName, eventHandler);
+ break;
- t.onMouseDown.add(function() {
- if (t.undoManager.typing)
- addUndo();
- });
+ default:
+ dom.bind(root, nativeName, eventHandler);
}
+ });
- // Bug fix for FireFox keeping styles from end of selection instead of start.
- if (tinymce.isGecko) {
- function getAttributeApplyFunction() {
- var template = t.dom.getAttribs(t.selection.getStart().cloneNode(false));
-
- return function() {
- var target = t.selection.getStart();
+ // Set the editor as active when focused
+ dom.bind(settings.content_editable ? self.getBody() : (tinymce.isGecko ? self.getDoc() : self.getWin()), 'focus', function(e) {
+ self.focus(true);
+ });
- if (target !== t.getBody()) {
- t.dom.setAttrib(target, "style", null);
+ if (settings.content_editable && tinymce.isOpera) {
+ dom.bind(self.getBody(), 'click', doOperaFocus);
+ dom.bind(self.getBody(), 'keydown', doOperaFocus);
+ }
- each(template, function(attr) {
- target.setAttributeNode(attr.cloneNode(true));
- });
- }
- };
- }
+ // Add node change handler
+ self.onMouseUp.add(nodeChanged);
- function isSelectionAcrossElements() {
- var s = t.selection;
+ self.onKeyUp.add(function(ed, e) {
+ var keyCode = e.keyCode;
- return !s.isCollapsed() && s.getStart() != s.getEnd();
- }
+ if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 13 || keyCode == 45 || keyCode == 46 || keyCode == 8 || (tinymce.isMac && (keyCode == 91 || keyCode == 93)) || e.ctrlKey)
+ nodeChanged(ed, e);
+ });
- t.onKeyPress.add(function(ed, e) {
- var applyAttributes;
+ // Add reset handler
+ self.onReset.add(function() {
+ self.setContent(self.startContent, {format : 'raw'});
+ });
- if ((e.keyCode == 8 || e.keyCode == 46) && isSelectionAcrossElements()) {
- applyAttributes = getAttributeApplyFunction();
- t.getDoc().execCommand('delete', false, null);
- applyAttributes();
+ // Add shortcuts
+ function handleShortcut(e, execute) {
+ if (e.altKey || e.ctrlKey || e.metaKey) {
+ each(self.shortcuts, function(shortcut) {
+ var ctrlState = tinymce.isMac ? e.metaKey : e.ctrlKey;
- return Event.cancel(e);
- }
- });
+ if (shortcut.ctrl != ctrlState || shortcut.alt != e.altKey || shortcut.shift != e.shiftKey)
+ return;
- t.dom.bind(t.getDoc(), 'cut', function(e) {
- var applyAttributes;
+ if (e.keyCode == shortcut.keyCode || (e.charCode && e.charCode == shortcut.charCode)) {
+ e.preventDefault();
- if (isSelectionAcrossElements()) {
- applyAttributes = getAttributeApplyFunction();
- t.onKeyUp.addToTop(Event.cancel, Event);
+ if (execute) {
+ shortcut.func.call(shortcut.scope);
+ }
- setTimeout(function() {
- applyAttributes();
- t.onKeyUp.remove(Event.cancel, Event);
- }, 0);
+ return true;
}
});
}
- },
-
- _refreshContentEditable : function() {
- var self = this, body, parent;
-
- // Check if the editor was hidden and the re-initalize contentEditable mode by removing and adding the body again
- if (self._isHidden()) {
- body = self.getBody();
- parent = body.parentNode;
-
- parent.removeChild(body);
- parent.appendChild(body);
+ };
- body.focus();
- }
- },
+ self.onKeyUp.add(function(ed, e) {
+ handleShortcut(e);
+ });
- _isHidden : function() {
- var s;
+ self.onKeyPress.add(function(ed, e) {
+ handleShortcut(e);
+ });
- if (!isGecko)
- return 0;
+ self.onKeyDown.add(function(ed, e) {
+ handleShortcut(e, true);
+ });
- // Weird, wheres that cursor selection?
- s = this.selection.getSel();
- return (!s || !s.rangeCount || s.rangeCount == 0);
+ if (tinymce.isOpera) {
+ self.onClick.add(function(ed, e) {
+ e.preventDefault();
+ });
}
- });
+ };
})(tinymce);
-
(function(tinymce) {
// Added for compression purposes
- var each = tinymce.each, undefined, TRUE = true, FALSE = false;
+ var each = tinymce.each, undef, TRUE = true, FALSE = false;
tinymce.EditorCommands = function(editor) {
var dom = editor.dom,
@@ -13188,10 +15060,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Private methods
function execNativeCommand(command, ui, value) {
- if (ui === undefined)
+ if (ui === undef)
ui = FALSE;
- if (value === undefined)
+ if (value === undef)
value = null;
return editor.getDoc().execCommand(command, ui, value);
@@ -13202,7 +15074,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
function toggleFormat(name, value) {
- formatter.toggle(name, value ? {value : value} : undefined);
+ formatter.toggle(name, value ? {value : value} : undef);
};
function storeSelection(type) {
@@ -13368,6 +15240,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var parser, serializer, parentNode, rootNode, fragment, args,
marker, nodeRect, viewPortRect, rng, node, node2, bookmarkHtml, viewportBodyElement;
+ //selection.normalize();
+
// Setup parser and serializer
parser = editor.parser;
serializer = new tinymce.html.Serializer({}, editor.schema);
@@ -13425,7 +15299,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Insert bookmark node and get the parent
selection.setContent(bookmarkHtml);
- parentNode = editor.selection.getNode();
+ parentNode = selection.getNode();
rootNode = editor.getBody();
// Opera will return the document node when selection is in root
@@ -13499,6 +15373,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
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);
},
@@ -13512,6 +15390,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
intentValue = parseInt(intentValue);
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 (command == 'outdent') {
value = Math.max(0, parseInt(element.style.paddingLeft || 0) - intentValue);
@@ -13583,10 +15466,15 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
selectAll : function() {
var root = dom.getRoot(), rng = dom.createRng();
- rng.setStart(root, 0);
- rng.setEnd(root, root.childNodes.length);
+ // Old IE does a better job with selectall than new versions
+ if (selection.getRng().setStart) {
+ rng.setStart(root, 0);
+ rng.setEnd(root, root.childNodes.length);
- editor.selection.setRng(rng);
+ selection.setRng(rng);
+ } else {
+ execNativeCommand('SelectAll');
+ }
}
});
@@ -13594,7 +15482,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
addCommands({
// Override justify commands
'JustifyLeft,JustifyCenter,JustifyRight,JustifyFull' : function(command) {
- return isFormatMatch('align' + command.substring(7));
+ var name = 'align' + command.substring(7);
+ var nodes = selection.isCollapsed() ? [dom.getParent(selection.getNode(), dom.isBlock)] : selection.getSelectedBlocks();
+ var matches = tinymce.map(nodes, function(node) {
+ return !!formatter.matchNode(node, name);
+ });
+ return tinymce.inArray(matches, TRUE) !== -1;
},
'Bold,Italic,Underline,Strikethrough,Superscript,Subscript' : function(command) {
@@ -13620,7 +15513,10 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
},
'InsertUnorderedList,InsertOrderedList' : function(command) {
- return dom.getParent(selection.getNode(), command == 'insertunorderedlist' ? 'UL' : 'OL');
+ var list = dom.getParent(selection.getNode(), 'ul,ol');
+ return list &&
+ (command === 'insertunorderedlist' && list.tagName === 'UL'
+ || command === 'insertorderedlist' && list.tagName === 'OL');
}
}, 'state');
@@ -13641,17 +15537,15 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}, 'value');
// Add undo manager logic
- if (settings.custom_undo_redo) {
- addCommands({
- Undo : function() {
- editor.undoManager.undo();
- },
+ addCommands({
+ Undo : function() {
+ editor.undoManager.undo();
+ },
- Redo : function() {
- editor.undoManager.redo();
- }
- });
- }
+ Redo : function() {
+ editor.undoManager.redo();
+ }
+ });
};
})(tinymce);
@@ -13659,20 +15553,119 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var Dispatcher = tinymce.util.Dispatcher;
tinymce.UndoManager = function(editor) {
- var self, index = 0, data = [], beforeBookmark;
+ var self, index = 0, data = [], beforeBookmark, onAdd, onUndo, onRedo;
function getContent() {
- return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}));
+ // Remove whitespace before/after and remove pure bogus nodes
+ return tinymce.trim(editor.getContent({format : 'raw', no_events : 1}).replace(/<span[^>]+data-mce-bogus[^>]+>[\u200B\uFEFF]+<\/span>/g, ''));
+ };
+
+ function addNonTypingUndoLevel() {
+ self.typing = false;
+ self.add();
};
- return self = {
+ // Create event instances
+ onBeforeAdd = new Dispatcher(self);
+ onAdd = new Dispatcher(self);
+ onUndo = new Dispatcher(self);
+ onRedo = new Dispatcher(self);
+
+ // Pass though onAdd event from UndoManager to Editor as onChange
+ onAdd.add(function(undoman, level) {
+ if (undoman.hasUndo())
+ return editor.onChange.dispatch(editor, level, undoman);
+ });
+
+ // Pass though onUndo event from UndoManager to Editor
+ onUndo.add(function(undoman, level) {
+ return editor.onUndo.dispatch(editor, level, undoman);
+ });
+
+ // Pass though onRedo event from UndoManager to Editor
+ onRedo.add(function(undoman, level) {
+ return editor.onRedo.dispatch(editor, level, undoman);
+ });
+
+ // Add initial undo level when the editor is initialized
+ editor.onInit.add(function() {
+ self.add();
+ });
+
+ // Get position before an execCommand is processed
+ editor.onBeforeExecCommand.add(function(ed, cmd, ui, val, args) {
+ if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {
+ self.beforeChange();
+ }
+ });
+
+ // Add undo level after an execCommand call was made
+ editor.onExecCommand.add(function(ed, cmd, ui, val, args) {
+ if (cmd != 'Undo' && cmd != 'Redo' && cmd != 'mceRepaint' && (!args || !args.skip_undo)) {
+ self.add();
+ }
+ });
+
+ // Add undo level on save contents, drag end and blur/focusout
+ editor.onSaveContent.add(addNonTypingUndoLevel);
+ editor.dom.bind(editor.dom.getRoot(), 'dragend', addNonTypingUndoLevel);
+ editor.dom.bind(editor.getBody(), 'focusout', function(e) {
+ if (!editor.removed && self.typing) {
+ addNonTypingUndoLevel();
+ }
+ });
+
+ editor.onKeyUp.add(function(editor, e) {
+ var keyCode = e.keyCode;
+
+ if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45 || keyCode == 13 || e.ctrlKey) {
+ addNonTypingUndoLevel();
+ }
+ });
+
+ editor.onKeyDown.add(function(editor, e) {
+ var keyCode = e.keyCode;
+
+ // Is caracter positon keys left,right,up,down,home,end,pgdown,pgup,enter
+ if ((keyCode >= 33 && keyCode <= 36) || (keyCode >= 37 && keyCode <= 40) || keyCode == 45) {
+ if (self.typing) {
+ addNonTypingUndoLevel();
+ }
+
+ return;
+ }
+
+ // If key isn't shift,ctrl,alt,capslock,metakey
+ if ((keyCode < 16 || keyCode > 20) && keyCode != 224 && keyCode != 91 && !self.typing) {
+ self.beforeChange();
+ self.typing = true;
+ self.add();
+ }
+ });
+
+ editor.onMouseDown.add(function(editor, e) {
+ if (self.typing) {
+ addNonTypingUndoLevel();
+ }
+ });
+
+ // Add keyboard shortcuts for undo/redo keys
+ editor.addShortcut('ctrl+z', 'undo_desc', 'Undo');
+ editor.addShortcut('ctrl+y', 'redo_desc', 'Redo');
+
+ self = {
+ // Explose for debugging reasons
+ data : data,
+
typing : false,
+
+ onBeforeAdd: onBeforeAdd,
- onAdd : new Dispatcher(self),
+ onAdd : onAdd,
- onUndo : new Dispatcher(self),
+ onUndo : onUndo,
- onRedo : new Dispatcher(self),
+ onRedo : onRedo,
beforeChange : function() {
beforeBookmark = editor.selection.getBookmark(2, true);
@@ -13683,6 +15676,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
level = level || {};
level.content = getContent();
+
+ self.onBeforeAdd.dispatch(self, level);
// Add undo level if needed
lastLevel = data[index];
@@ -13769,631 +15764,116 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return index < data.length - 1 && !this.typing;
}
};
- };
-})(tinymce);
-(function(tinymce) {
- // Shorten names
- var Event = tinymce.dom.Event,
- isIE = tinymce.isIE,
- isGecko = tinymce.isGecko,
- isOpera = tinymce.isOpera,
- each = tinymce.each,
- extend = tinymce.extend,
- TRUE = true,
- FALSE = false;
-
- function cloneFormats(node) {
- var clone, temp, inner;
-
- do {
- if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(node.nodeName)) {
- if (clone) {
- temp = node.cloneNode(false);
- temp.appendChild(clone);
- clone = temp;
- } else {
- clone = inner = node.cloneNode(false);
- }
-
- clone.removeAttribute('id');
- }
- } while (node = node.parentNode);
-
- if (clone)
- return {wrapper : clone, inner : inner};
+ return self;
};
+})(tinymce);
- // Checks if the selection/caret is at the end of the specified block element
- function isAtEnd(rng, par) {
- var rng2 = par.ownerDocument.createRange();
-
- rng2.setStart(rng.endContainer, rng.endOffset);
- rng2.setEndAfter(par);
-
- // Get number of characters to the right of the cursor if it's zero then we are at the end and need to merge the next block element
- return rng2.cloneContents().textContent.length == 0;
- };
+tinymce.ForceBlocks = function(editor) {
+ var settings = editor.settings, dom = editor.dom, selection = editor.selection, blockElements = editor.schema.getBlockElements();
- function splitList(selection, dom, li) {
- var listBlock, block;
+ function addRootBlocks() {
+ var node = selection.getStart(), rootNode = editor.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF, wrapped, isInEditorDocument;
- if (dom.isEmpty(li)) {
- listBlock = dom.getParent(li, 'ul,ol');
+ if (!node || node.nodeType !== 1 || !settings.forced_root_block)
+ return;
- if (!dom.getParent(listBlock.parentNode, 'ul,ol')) {
- dom.split(listBlock, li);
- block = dom.create('p', 0, '<br data-mce-bogus="1" />');
- dom.replace(block, li);
- selection.select(block, 1);
- }
+ // Check if node is wrapped in block
+ while (node && node != rootNode) {
+ if (blockElements[node.nodeName])
+ return;
- return FALSE;
+ node = node.parentNode;
}
- return TRUE;
- };
-
- tinymce.create('tinymce.ForceBlocks', {
- ForceBlocks : function(ed) {
- var t = this, s = ed.settings, elm;
-
- t.editor = ed;
- t.dom = ed.dom;
- elm = (s.forced_root_block || 'p').toLowerCase();
- s.element = elm.toUpperCase();
-
- ed.onPreInit.add(t.setup, t);
- },
-
- setup : function() {
- var t = this, ed = t.editor, s = ed.settings, dom = ed.dom, selection = ed.selection, blockElements = ed.schema.getBlockElements();
-
- // Force root blocks
- if (s.forced_root_block) {
- function addRootBlocks() {
- var node = selection.getStart(), rootNode = ed.getBody(), rng, startContainer, startOffset, endContainer, endOffset, rootBlockNode, tempNode, offset = -0xFFFFFF;
-
- if (!node || node.nodeType !== 1)
- return;
-
- // Check if node is wrapped in block
- while (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;
- } else {
- // Force control range into text range
- if (rng.item) {
- rng = ed.getDoc().body.createTextRange();
- rng.moveToElementText(rng.item(0));
- }
-
- 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
- for (node = rootNode.firstChild; node; node) {
- if (node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName])) {
- if (!rootBlockNode) {
- rootBlockNode = dom.create(s.forced_root_block);
- node.parentNode.insertBefore(rootBlockNode, node);
- }
-
- tempNode = node;
- node = node.nextSibling;
- rootBlockNode.appendChild(tempNode);
- } else {
- rootBlockNode = null;
- node = node.nextSibling;
- }
- }
-
- if (rng.setStart) {
- rng.setStart(startContainer, startOffset);
- rng.setEnd(endContainer, endOffset);
- selection.setRng(rng);
- } else {
- try {
- rng = ed.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
- }
- }
-
- ed.nodeChanged();
- };
-
- ed.onKeyUp.add(addRootBlocks);
- ed.onClick.add(addRootBlocks);
- }
-
- if (s.force_br_newlines) {
- // Force IE to produce BRs on enter
- if (isIE) {
- ed.onKeyPress.add(function(ed, e) {
- var n;
-
- if (e.keyCode == 13 && selection.getNode().nodeName != 'LI') {
- selection.setContent('<br id="__" /> ', {format : 'raw'});
- n = dom.get('__');
- n.removeAttribute('id');
- selection.select(n);
- selection.collapse();
- return Event.cancel(e);
- }
- });
- }
- }
-
- if (s.force_p_newlines) {
- if (!isIE) {
- ed.onKeyPress.add(function(ed, e) {
- if (e.keyCode == 13 && !e.shiftKey && !t.insertPara(e))
- Event.cancel(e);
- });
- } else {
- // Ungly hack to for IE to preserve the formatting when you press
- // enter at the end of a block element with formatted contents
- // This logic overrides the browsers default logic with
- // custom logic that enables us to control the output
- tinymce.addUnload(function() {
- t._previousFormats = 0; // Fix IE leak
- });
-
- ed.onKeyPress.add(function(ed, e) {
- t._previousFormats = 0;
-
- // Clone the current formats, this will later be applied to the new block contents
- if (e.keyCode == 13 && !e.shiftKey && ed.selection.isCollapsed() && s.keep_styles)
- t._previousFormats = cloneFormats(ed.selection.getStart());
- });
-
- ed.onKeyUp.add(function(ed, e) {
- // Let IE break the element and the wrap the new caret location in the previous formats
- if (e.keyCode == 13 && !e.shiftKey) {
- var parent = ed.selection.getStart(), fmt = t._previousFormats;
-
- // Parent is an empty block
- if (!parent.hasChildNodes() && fmt) {
- parent = dom.getParent(parent, dom.isBlock);
-
- if (parent && parent.nodeName != 'LI') {
- parent.innerHTML = '';
-
- if (t._previousFormats) {
- parent.appendChild(fmt.wrapper);
- fmt.inner.innerHTML = '\uFEFF';
- } else
- parent.innerHTML = '\uFEFF';
-
- selection.select(parent, 1);
- selection.collapse(true);
- ed.getDoc().execCommand('Delete', false, null);
- t._previousFormats = 0;
- }
- }
- }
- });
- }
-
- if (isGecko) {
- ed.onKeyDown.add(function(ed, e) {
- if ((e.keyCode == 8 || e.keyCode == 46) && !e.shiftKey)
- t.backspaceDelete(e, e.keyCode == 8);
- });
- }
+ // Get current selection
+ rng = selection.getRng();
+ if (rng.setStart) {
+ startContainer = rng.startContainer;
+ startOffset = rng.startOffset;
+ endContainer = rng.endContainer;
+ endOffset = rng.endOffset;
+ } else {
+ // Force control range into text range
+ if (rng.item) {
+ node = rng.item(0);
+ rng = editor.getDoc().body.createTextRange();
+ rng.moveToElementText(node);
}
- // Workaround for missing shift+enter support, http://bugs.webkit.org/show_bug.cgi?id=16973
- if (tinymce.isWebKit) {
- function insertBr(ed) {
- var rng = selection.getRng(), br, div = dom.create('div', null, ' '), divYPos, vpHeight = dom.getViewPort(ed.getWin()).h;
-
- // Insert BR element
- rng.insertNode(br = dom.create('br'));
+ isInEditorDocument = rng.parentElement().ownerDocument === editor.getDoc();
+ tmpRng = rng.duplicate();
+ tmpRng.collapse(true);
+ startOffset = tmpRng.move('character', offset) * -1;
- // Place caret after BR
- rng.setStartAfter(br);
- rng.setEndAfter(br);
- selection.setRng(rng);
-
- // Could not place caret after BR then insert an nbsp entity and move the caret
- if (selection.getSel().focusNode == br.previousSibling) {
- selection.select(dom.insertAfter(dom.doc.createTextNode('\u00a0'), br));
- selection.collapse(TRUE);
- }
-
- // Create a temporary DIV after the BR and get the position as it
- // seems like getPos() returns 0 for text nodes and BR elements.
- dom.insertAfter(div, br);
- divYPos = dom.getPos(div).y;
- dom.remove(div);
-
- // Scroll to new position, scrollIntoView can't be used due to bug: http://bugs.webkit.org/show_bug.cgi?id=16117
- if (divYPos > vpHeight) // It is not necessary to scroll if the DIV is inside the view port.
- ed.getWin().scrollTo(0, divYPos);
- };
-
- ed.onKeyPress.add(function(ed, e) {
- if (e.keyCode == 13 && (e.shiftKey || (s.force_br_newlines && !dom.getParent(selection.getNode(), 'h1,h2,h3,h4,h5,h6,ol,ul')))) {
- insertBr(ed);
- Event.cancel(e);
- }
- });
+ if (!tmpRng.collapsed) {
+ tmpRng = rng.duplicate();
+ tmpRng.collapse(false);
+ endOffset = (tmpRng.move('character', offset) * -1) - startOffset;
}
+ }
- // IE specific fixes
- if (isIE) {
- // Replaces IE:s auto generated paragraphs with the specified element name
- if (s.element != 'P') {
- ed.onKeyPress.add(function(ed, e) {
- t.lastElm = selection.getNode().nodeName;
- });
-
- ed.onKeyUp.add(function(ed, e) {
- var bl, n = selection.getNode(), b = ed.getBody();
-
- if (b.childNodes.length === 1 && n.nodeName == 'P') {
- n = dom.rename(n, s.element);
- selection.select(n);
- selection.collapse();
- ed.nodeChanged();
- } else if (e.keyCode == 13 && !e.shiftKey && t.lastElm != 'P') {
- bl = dom.getParent(n, 'p');
-
- if (bl) {
- dom.rename(bl, s.element);
- ed.nodeChanged();
- }
- }
- });
+ // Wrap non block elements and text nodes
+ node = rootNode.firstChild;
+ while (node) {
+ if (node.nodeType === 3 || (node.nodeType == 1 && !blockElements[node.nodeName])) {
+ // Remove empty text nodes
+ if (node.nodeType === 3 && node.nodeValue.length == 0) {
+ tempNode = node;
+ node = node.nextSibling;
+ dom.remove(tempNode);
+ continue;
}
- }
- },
-
- getParentBlock : function(n) {
- var d = this.dom;
-
- return d.getParent(n, d.isBlock);
- },
-
- insertPara : function(e) {
- var t = this, ed = t.editor, dom = ed.dom, d = ed.getDoc(), se = ed.settings, s = ed.selection.getSel(), r = s.getRangeAt(0), b = d.body;
- var rb, ra, dir, sn, so, en, eo, sb, eb, bn, bef, aft, sc, ec, n, vp = dom.getViewPort(ed.getWin()), y, ch, car;
-
- ed.undoManager.beforeChange();
-
- // If root blocks are forced then use Operas default behavior since it's really good
-// Removed due to bug: #1853816
-// if (se.forced_root_block && isOpera)
-// return TRUE;
-
- // Setup before range
- rb = d.createRange();
-
- // If is before the first block element and in body, then move it into first block element
- rb.setStart(s.anchorNode, s.anchorOffset);
- rb.collapse(TRUE);
- // Setup after range
- ra = d.createRange();
-
- // If is before the first block element and in body, then move it into first block element
- ra.setStart(s.focusNode, s.focusOffset);
- ra.collapse(TRUE);
-
- // Setup start/end points
- dir = rb.compareBoundaryPoints(rb.START_TO_END, ra) < 0;
- sn = dir ? s.anchorNode : s.focusNode;
- so = dir ? s.anchorOffset : s.focusOffset;
- en = dir ? s.focusNode : s.anchorNode;
- eo = dir ? s.focusOffset : s.anchorOffset;
-
- // If selection is in empty table cell
- if (sn === en && /^(TD|TH)$/.test(sn.nodeName)) {
- if (sn.firstChild.nodeName == 'BR')
- dom.remove(sn.firstChild); // Remove BR
-
- // Create two new block elements
- if (sn.childNodes.length == 0) {
- ed.dom.add(sn, se.element, null, '<br />');
- aft = ed.dom.add(sn, se.element, null, '<br />');
- } else {
- n = sn.innerHTML;
- sn.innerHTML = '';
- ed.dom.add(sn, se.element, null, n);
- aft = ed.dom.add(sn, se.element, null, '<br />');
+ if (!rootBlockNode) {
+ rootBlockNode = dom.create(settings.forced_root_block);
+ node.parentNode.insertBefore(rootBlockNode, node);
+ wrapped = true;
}
- // Move caret into the last one
- r = d.createRange();
- r.selectNodeContents(aft);
- r.collapse(1);
- ed.selection.setRng(r);
-
- return FALSE;
- }
-
- // If the caret is in an invalid location in FF we need to move it into the first block
- if (sn == b && en == b && b.firstChild && ed.dom.isBlock(b.firstChild)) {
- sn = en = sn.firstChild;
- so = eo = 0;
- rb = d.createRange();
- rb.setStart(sn, 0);
- ra = d.createRange();
- ra.setStart(en, 0);
- }
-
- // If the body is totally empty add a BR element this might happen on webkit
- if (!d.body.hasChildNodes()) {
- d.body.appendChild(dom.create('br'));
- }
-
- // Never use body as start or end node
- sn = sn.nodeName == "HTML" ? d.body : sn; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
- sn = sn.nodeName == "BODY" ? sn.firstChild : sn;
- en = en.nodeName == "HTML" ? d.body : en; // Fix for Opera bug: https://bugs.opera.com/show_bug.cgi?id=273224&comments=yes
- en = en.nodeName == "BODY" ? en.firstChild : en;
-
- // Get start and end blocks
- sb = t.getParentBlock(sn);
- eb = t.getParentBlock(en);
- bn = sb ? sb.nodeName : se.element; // Get block name to create
-
- // Return inside list use default browser behavior
- if (n = t.dom.getParent(sb, 'li,pre')) {
- if (n.nodeName == 'LI')
- return splitList(ed.selection, t.dom, n);
-
- return TRUE;
- }
-
- // If caption or absolute layers then always generate new blocks within
- if (sb && (sb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
- bn = se.element;
- sb = null;
- }
-
- // If caption or absolute layers then always generate new blocks within
- if (eb && (eb.nodeName == 'CAPTION' || /absolute|relative|fixed/gi.test(dom.getStyle(sb, 'position', 1)))) {
- bn = se.element;
- eb = null;
- }
-
- // Use P instead
- if (/(TD|TABLE|TH|CAPTION)/.test(bn) || (sb && bn == "DIV" && /left|right/gi.test(dom.getStyle(sb, 'float', 1)))) {
- bn = se.element;
- sb = eb = null;
- }
-
- // Setup new before and after blocks
- bef = (sb && sb.nodeName == bn) ? sb.cloneNode(0) : ed.dom.create(bn);
- aft = (eb && eb.nodeName == bn) ? eb.cloneNode(0) : ed.dom.create(bn);
-
- // Remove id from after clone
- aft.removeAttribute('id');
-
- // Is header and cursor is at the end, then force paragraph under
- if (/^(H[1-6])$/.test(bn) && isAtEnd(r, sb))
- aft = ed.dom.create(se.element);
-
- // Find start chop node
- n = sc = sn;
- do {
- if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
- break;
-
- sc = n;
- } while ((n = n.previousSibling ? n.previousSibling : n.parentNode));
-
- // Find end chop node
- n = ec = en;
- do {
- if (n == b || n.nodeType == 9 || t.dom.isBlock(n) || /(TD|TABLE|TH|CAPTION)/.test(n.nodeName))
- break;
-
- ec = n;
- } while ((n = n.nextSibling ? n.nextSibling : n.parentNode));
-
- // Place first chop part into before block element
- if (sc.nodeName == bn)
- rb.setStart(sc, 0);
- else
- rb.setStartBefore(sc);
-
- rb.setEnd(sn, so);
- bef.appendChild(rb.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
-
- // Place secnd chop part within new block element
- try {
- ra.setEndAfter(ec);
- } catch(ex) {
- //console.debug(s.focusNode, s.focusOffset);
- }
-
- ra.setStart(en, eo);
- aft.appendChild(ra.cloneContents() || d.createTextNode('')); // Empty text node needed for Safari
-
- // Create range around everything
- r = d.createRange();
- if (!sc.previousSibling && sc.parentNode.nodeName == bn) {
- r.setStartBefore(sc.parentNode);
+ tempNode = node;
+ node = node.nextSibling;
+ rootBlockNode.appendChild(tempNode);
} else {
- if (rb.startContainer.nodeName == bn && rb.startOffset == 0)
- r.setStartBefore(rb.startContainer);
- else
- r.setStart(rb.startContainer, rb.startOffset);
+ rootBlockNode = null;
+ node = node.nextSibling;
}
+ }
- if (!ec.nextSibling && ec.parentNode.nodeName == bn)
- r.setEndAfter(ec.parentNode);
- else
- r.setEnd(ra.endContainer, ra.endOffset);
-
- // Delete and replace it with new block elements
- r.deleteContents();
-
- if (isOpera)
- ed.getWin().scrollTo(0, vp.y);
-
- // Never wrap blocks in blocks
- if (bef.firstChild && bef.firstChild.nodeName == bn)
- bef.innerHTML = bef.firstChild.innerHTML;
-
- if (aft.firstChild && aft.firstChild.nodeName == bn)
- aft.innerHTML = aft.firstChild.innerHTML;
-
- function appendStyles(e, en) {
- var nl = [], nn, n, i;
-
- e.innerHTML = '';
-
- // Make clones of style elements
- if (se.keep_styles) {
- n = en;
- do {
- // We only want style specific elements
- if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(n.nodeName)) {
- nn = n.cloneNode(FALSE);
- dom.setAttrib(nn, 'id', ''); // Remove ID since it needs to be unique
- nl.push(nn);
- }
- } while (n = n.parentNode);
- }
-
- // Append style elements to aft
- if (nl.length > 0) {
- for (i = nl.length - 1, nn = e; i >= 0; i--)
- nn = nn.appendChild(nl[i]);
-
- // Padd most inner style element
- nl[0].innerHTML = isOpera ? '\u00a0' : '<br />'; // Extra space for Opera so that the caret can move there
- return nl[0]; // Move caret to most inner element
- } else
- e.innerHTML = isOpera ? '\u00a0' : '<br />'; // Extra space for Opera so that the caret can move there
- };
-
- // Padd empty blocks
- if (dom.isEmpty(bef))
- appendStyles(bef, sn);
-
- // Fill empty afterblook with current style
- if (dom.isEmpty(aft))
- car = appendStyles(aft, en);
-
- // Opera needs this one backwards for older versions
- if (isOpera && parseFloat(opera.version()) < 9.5) {
- r.insertNode(bef);
- r.insertNode(aft);
+ if (wrapped) {
+ if (rng.setStart) {
+ rng.setStart(startContainer, startOffset);
+ rng.setEnd(endContainer, endOffset);
+ selection.setRng(rng);
} else {
- r.insertNode(aft);
- r.insertNode(bef);
- }
-
- // Normalize
- aft.normalize();
- bef.normalize();
-
- // Move cursor and scroll into view
- ed.selection.select(aft, true);
- ed.selection.collapse(true);
-
- // scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
- y = ed.dom.getPos(aft).y;
- //ch = aft.clientHeight;
-
- // Is element within viewport
- if (y < vp.y || y + 25 > vp.y + vp.h) {
- ed.getWin().scrollTo(0, y < vp.y ? y : y - vp.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks
-
- /*console.debug(
- 'Element: y=' + y + ', h=' + ch + ', ' +
- 'Viewport: y=' + vp.y + ", h=" + vp.h + ', bottom=' + (vp.y + vp.h)
- );*/
- }
-
- ed.undoManager.add();
-
- return FALSE;
- },
-
- backspaceDelete : function(e, bs) {
- var t = this, ed = t.editor, b = ed.getBody(), dom = ed.dom, n, se = ed.selection, r = se.getRng(), sc = r.startContainer, n, w, tn, walker;
+ // Only select if the previous selection was inside the document to prevent auto focus in quirks mode
+ if (isInEditorDocument) {
+ try {
+ rng = editor.getDoc().body.createTextRange();
+ rng.moveToElementText(rootNode);
+ rng.collapse(true);
+ rng.moveStart('character', startOffset);
- // Delete when caret is behind a element doesn't work correctly on Gecko see #3011651
- if (!bs && r.collapsed && sc.nodeType == 1 && r.startOffset == sc.childNodes.length) {
- walker = new tinymce.dom.TreeWalker(sc.lastChild, sc);
+ if (endOffset > 0)
+ rng.moveEnd('character', endOffset);
- // Walk the dom backwards until we find a text node
- for (n = sc.lastChild; n; n = walker.prev()) {
- if (n.nodeType == 3) {
- r.setStart(n, n.nodeValue.length);
- r.collapse(true);
- se.setRng(r);
- return;
+ rng.select();
+ } catch (ex) {
+ // Ignore
}
}
}
- // The caret sometimes gets stuck in Gecko if you delete empty paragraphs
- // This workaround removes the element by hand and moves the caret to the previous element
- if (sc && ed.dom.isBlock(sc) && !/^(TD|TH)$/.test(sc.nodeName) && bs) {
- if (sc.childNodes.length == 0 || (sc.childNodes.length == 1 && sc.firstChild.nodeName == 'BR')) {
- // Find previous block element
- n = sc;
- while ((n = n.previousSibling) && !ed.dom.isBlock(n)) ;
-
- if (n) {
- if (sc != b.firstChild) {
- // Find last text node
- w = ed.dom.doc.createTreeWalker(n, NodeFilter.SHOW_TEXT, null, FALSE);
- while (tn = w.nextNode())
- n = tn;
-
- // Place caret at the end of last text node
- r = ed.getDoc().createRange();
- r.setStart(n, n.nodeValue ? n.nodeValue.length : 0);
- r.setEnd(n, n.nodeValue ? n.nodeValue.length : 0);
- se.setRng(r);
-
- // Remove the target container
- ed.dom.remove(sc);
- }
-
- return Event.cancel(e);
- }
- }
- }
+ editor.nodeChanged();
}
- });
-})(tinymce);
+ };
+
+ // Force root blocks
+ if (settings.forced_root_block) {
+ editor.onKeyUp.add(addRootBlocks);
+ editor.onNodeChange.add(addRootBlocks);
+ }
+};
(function(tinymce) {
// Shorten names
@@ -14451,28 +15931,40 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return c;
},
- createControl : function(n) {
- var c, t = this, ed = t.editor;
+ createControl : function(name) {
+ var ctrl, i, l, self = this, editor = self.editor, factories, ctrlName;
+
+ // Build control factory cache
+ if (!self.controlFactories) {
+ self.controlFactories = [];
+ each(editor.plugins, function(plugin) {
+ if (plugin.createControl) {
+ self.controlFactories.push(plugin);
+ }
+ });
+ }
- each(ed.plugins, function(p) {
- if (p.createControl) {
- c = p.createControl(n, t);
+ // Create controls by asking cached factories
+ factories = self.controlFactories;
+ for (i = 0, l = factories.length; i < l; i++) {
+ ctrl = factories[i].createControl(name, self);
- if (c)
- return false;
+ if (ctrl) {
+ return self.add(ctrl);
}
- });
+ }
- switch (n) {
- case "|":
- case "separator":
- return t.createSeparator();
+ // Create sepearator
+ if (name === "|" || name === "separator") {
+ return self.createSeparator();
}
- if (!c && ed.buttons && (c = ed.buttons[n]))
- return t.createButton(n, c);
+ // Create control from button collection
+ if (editor.buttons && (ctrl = editor.buttons[name])) {
+ return self.createButton(name, ctrl);
+ }
- return t.add(c);
+ return self.add(ctrl);
},
createDropMenu : function(id, s, cc) {
@@ -14487,6 +15979,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (v = ed.getParam('skin_variant'))
s['class'] += ' ' + ed.getParam('skin') + 'Skin' + v.substring(0, 1).toUpperCase() + v.substring(1);
+ s['class'] += ed.settings.directionality == "rtl" ? ' mceRtl' : '';
+
id = t.prefix + id;
cls = cc || t._cls.dropmenu || tinymce.ui.DropMenu;
c = t.controls[id] = new cls(id, s);
@@ -14898,6 +16392,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
TreeWalker = tinymce.dom.TreeWalker,
rangeUtils = new tinymce.dom.RangeUtils(dom),
isValid = ed.schema.isValidChild,
+ isArray = tinymce.isArray,
isBlock = dom.isBlock,
forcedRootBlock = ed.settings.forced_root_block,
nodeIndex = dom.nodeIndex,
@@ -14905,18 +16400,117 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
MCE_ATTR_RE = /^(src|href|style)$/,
FALSE = false,
TRUE = true,
- undefined;
+ formatChangeData,
+ undef,
+ getContentEditable = dom.getContentEditable;
- function isArray(obj) {
- return obj instanceof Array;
- };
+ function isTextBlock(name) {
+ return !!ed.schema.getTextBlocks()[name.toLowerCase()];
+ }
function getParents(node, selector) {
return dom.getParents(node, selector, dom.getRoot());
};
function isCaretNode(node) {
- return node.nodeType === 1 && (node.face === 'mceinline' || node.style.fontFamily === 'mceinline');
+ return node.nodeType === 1 && node.id === '_mce_caret';
+ };
+
+ function defaultFormats() {
+ register({
+ alignleft : [
+ {selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'left'}, defaultBlock: 'div'},
+ {selector : 'img,table', collapsed : false, styles : {'float' : 'left'}}
+ ],
+
+ aligncenter : [
+ {selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'center'}, defaultBlock: 'div'},
+ {selector : 'img', collapsed : false, styles : {display : 'block', marginLeft : 'auto', marginRight : 'auto'}},
+ {selector : 'table', collapsed : false, styles : {marginLeft : 'auto', marginRight : 'auto'}}
+ ],
+
+ alignright : [
+ {selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'right'}, defaultBlock: 'div'},
+ {selector : 'img,table', collapsed : false, styles : {'float' : 'right'}}
+ ],
+
+ alignfull : [
+ {selector : 'figure,p,h1,h2,h3,h4,h5,h6,td,th,div,ul,ol,li', styles : {textAlign : 'justify'}, defaultBlock: 'div'}
+ ],
+
+ bold : [
+ {inline : 'strong', remove : 'all'},
+ {inline : 'span', styles : {fontWeight : 'bold'}},
+ {inline : 'b', remove : 'all'}
+ ],
+
+ italic : [
+ {inline : 'em', remove : 'all'},
+ {inline : 'span', styles : {fontStyle : 'italic'}},
+ {inline : 'i', remove : 'all'}
+ ],
+
+ underline : [
+ {inline : 'span', styles : {textDecoration : 'underline'}, exact : true},
+ {inline : 'u', remove : 'all'}
+ ],
+
+ strikethrough : [
+ {inline : 'span', styles : {textDecoration : 'line-through'}, exact : true},
+ {inline : 'strike', remove : 'all'}
+ ],
+
+ forecolor : {inline : 'span', styles : {color : '%value'}, wrap_links : false},
+ hilitecolor : {inline : 'span', styles : {backgroundColor : '%value'}, wrap_links : false},
+ fontname : {inline : 'span', styles : {fontFamily : '%value'}},
+ fontsize : {inline : 'span', styles : {fontSize : '%value'}},
+ fontsize_class : {inline : 'span', attributes : {'class' : '%value'}},
+ blockquote : {block : 'blockquote', wrapper : 1, remove : 'all'},
+ subscript : {inline : 'sub'},
+ superscript : {inline : 'sup'},
+
+ link : {inline : 'a', selector : 'a', remove : 'all', split : true, deep : true,
+ onmatch : function(node) {
+ return true;
+ },
+
+ onformat : function(elm, fmt, vars) {
+ each(vars, function(value, key) {
+ dom.setAttrib(elm, key, value);
+ });
+ }
+ },
+
+ removeformat : [
+ {selector : 'b,strong,em,i,font,u,strike', remove : 'all', split : true, expand : false, block_expand : true, deep : true},
+ {selector : 'span', attributes : ['style', 'class'], remove : 'empty', split : true, expand : false, deep : true},
+ {selector : '*', attributes : ['style', 'class'], split : false, expand : false, deep : true}
+ ]
+ });
+
+ // Register default block formats
+ each('p h1 h2 h3 h4 h5 h6 div address pre div code dt dd samp'.split(/\s/), function(name) {
+ register(name, {block : name, remove : 'all'});
+ });
+
+ // Register user defined formats
+ register(ed.settings.formats);
+ };
+
+ function addKeyboardShortcuts() {
+ // Add some inline shortcuts
+ ed.addShortcut('ctrl+b', 'bold_desc', 'Bold');
+ ed.addShortcut('ctrl+i', 'italic_desc', 'Italic');
+ ed.addShortcut('ctrl+u', 'underline_desc', 'Underline');
+
+ // BlockFormat shortcuts keys
+ for (var i = 1; i <= 6; i++) {
+ ed.addShortcut('ctrl+' + i, '', ['FormatBlock', false, 'h' + i]);
+ }
+
+ ed.addShortcut('ctrl+7', '', ['FormatBlock', false, 'p']);
+ ed.addShortcut('ctrl+8', '', ['FormatBlock', false, 'div']);
+ ed.addShortcut('ctrl+9', '', ['FormatBlock', false, 'address']);
};
// Public functions
@@ -14938,15 +16532,15 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
each(format, function(format) {
// Set deep to false by default on selector formats this to avoid removing
// alignment on images inside paragraphs when alignment is changed on paragraphs
- if (format.deep === undefined)
+ if (format.deep === undef)
format.deep = !format.selector;
// Default to true
- if (format.split === undefined)
+ if (format.split === undef)
format.split = !format.selector || format.inline;
// Default to true
- if (format.remove === undefined && format.selector && !format.inline)
+ if (format.remove === undef && format.selector && !format.inline)
format.remove = 'none';
// Mark format as a mixed format inline + block level
@@ -14991,30 +16585,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
function apply(name, vars, node) {
var formatList = get(name), format = formatList[0], bookmark, rng, i, isCollapsed = selection.isCollapsed();
- function moveStart(rng) {
- var container = rng.startContainer,
- offset = rng.startOffset,
- walker, node;
-
- // Move startContainer/startOffset in to a suitable node
- if (container.nodeType == 1 || container.nodeValue === "") {
- container = container.nodeType == 1 ? container.childNodes[offset] : container;
-
- // Might fail if the offset is behind the last element in it's container
- if (container) {
- walker = new TreeWalker(container, container.parentNode);
- for (node = walker.current(); node; node = walker.next()) {
- if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
- rng.setStart(node, 0);
- break;
- }
- }
- }
- }
-
- return rng;
- };
-
function setElementFormat(elm, fmt) {
fmt = fmt || format;
@@ -15043,7 +16613,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
function findSelectionEnd(start, end) {
var walker = new TreeWalker(end);
for (node = walker.current(); node; node = walker.prev()) {
- if (node.childNodes.length > 1 || node == start) {
+ if (node.childNodes.length > 1 || node == start || node.tagName == 'BR') {
return node;
}
}
@@ -15055,7 +16625,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var start = rng.startContainer;
var end = rng.endContainer;
- if (start != end && rng.endOffset == 0) {
+ if (start != end && rng.endOffset === 0) {
var newEnd = findSelectionEnd(start, end);
var endOffset = newEnd.nodeType == 3 ? newEnd.length : newEnd.childNodes.length;
@@ -15093,8 +16663,8 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
each(tinymce.grep(node.childNodes), process);
return 0;
} else {
- currentWrapElm = wrapElm.cloneNode(FALSE);
-
+ currentWrapElm = dom.clone(wrapElm, FALSE);
+
// create a list of the nodes on the same side of the list as the selection
each(tinymce.grep(node.childNodes), function(n, index) {
if ((startIndex < listIndex && index < listIndex) || (startIndex > listIndex && index > listIndex)) {
@@ -15102,7 +16672,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
n.parentNode.removeChild(n);
}
});
-
+
// insert the wrapping element either before or after the list.
if (startIndex < listIndex) {
node.insertBefore(currentWrapElm, list);
@@ -15120,9 +16690,9 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return currentWrapElm;
}
};
-
+
function applyRngStyle(rng, bookmark, node_specific) {
- var newWrappers = [], wrapName, wrapElm;
+ var newWrappers = [], wrapName, wrapElm, contentEditable = true;
// Setup wrapper element
wrapName = format.inline || format.block;
@@ -15133,7 +16703,18 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var currentWrapElm;
function process(node) {
- var nodeName = node.nodeName.toLowerCase(), parentName = node.parentNode.nodeName.toLowerCase(), found;
+ var nodeName, parentName, found, hasContentEditableState, lastContentEditable;
+
+ lastContentEditable = contentEditable;
+ nodeName = node.nodeName.toLowerCase();
+ parentName = node.parentNode.nodeName.toLowerCase();
+
+ // Node has a contentEditable value
+ if (node.nodeType === 1 && getContentEditable(node)) {
+ lastContentEditable = contentEditable;
+ contentEditable = getContentEditable(node) === "true";
+ hasContentEditableState = true; // We don't want to wrap the container only it's children
+ }
// Stop wrapping on br elements
if (isEq(nodeName, 'br')) {
@@ -15153,7 +16734,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
// Can we rename the block
- if (format.block && !format.wrapper && isTextBlock(nodeName)) {
+ if (contentEditable && !hasContentEditableState && format.block && !format.wrapper && isTextBlock(nodeName)) {
node = dom.rename(node, wrapName);
setElementFormat(node);
newWrappers.push(node);
@@ -15184,12 +16765,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
// Is it valid to wrap this item
- if (isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
- !(!node_specific && node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279) && node.id !== '_mce_caret') {
+ if (contentEditable && !hasContentEditableState && isValid(wrapName, nodeName) && isValid(parentName, wrapName) &&
+ !(!node_specific && node.nodeType === 3 && node.nodeValue.length === 1 && node.nodeValue.charCodeAt(0) === 65279) && !isCaretNode(node)) {
// Start wrapping
if (!currentWrapElm) {
// Wrap the node
- currentWrapElm = wrapElm.cloneNode(FALSE);
+ currentWrapElm = dom.clone(wrapElm, FALSE);
node.parentNode.insertBefore(currentWrapElm, node);
newWrappers.push(currentWrapElm);
}
@@ -15201,9 +16782,13 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
} else {
// Start a new wrapper for possible children
currentWrapElm = 0;
-
+
each(tinymce.grep(node.childNodes), process);
+ if (hasContentEditableState) {
+ contentEditable = lastContentEditable; // Restore last contentEditable state from stack
+ }
+
// End the last wrapper
currentWrapElm = 0;
}
@@ -15220,7 +16805,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
var i, currentWrapElm, children;
if (node.nodeName === 'A') {
- currentWrapElm = wrapElm.cloneNode(FALSE);
+ currentWrapElm = dom.clone(wrapElm, FALSE);
newWrappers.push(currentWrapElm);
children = tinymce.grep(node.childNodes);
@@ -15238,6 +16823,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
// Cleanup
+
each(newWrappers, function(node) {
var childCount;
@@ -15264,7 +16850,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// If child was found and of the same type as the current node
if (child && matchName(child, format)) {
- clone = child.cloneNode(FALSE);
+ clone = dom.clone(child, FALSE);
setElementFormat(clone);
dom.replace(clone, node, TRUE);
@@ -15353,6 +16939,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Obtain selection node before selection is unselected by applyRngStyle()
var curSelNode = ed.selection.getNode();
+ // If the formats have a default block and we can't find a parent block then start wrapping it with a DIV this is for forced_root_blocks: false
+ // It's kind of a hack but people should be using the default block type P since all desktop editors work that way
+ if (!forcedRootBlock && formatList[0].defaultBlock && !dom.getParent(curSelNode, dom.isBlock)) {
+ apply(formatList[0].defaultBlock);
+ }
+
// Apply formatting to selection
ed.selection.setRng(adjustSelectionToVisibleSelection());
bookmark = selection.getBookmark();
@@ -15365,7 +16957,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
selection.moveToBookmark(bookmark);
- selection.setRng(moveStart(selection.getRng(TRUE)));
+ moveStart(selection.getRng(TRUE));
ed.nodeChanged();
} else
performCaretAction('apply', name, vars);
@@ -15374,63 +16966,45 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
function remove(name, vars, node) {
- var formatList = get(name), format = formatList[0], bookmark, i, rng;
- function moveStart(rng) {
- var container = rng.startContainer,
- offset = rng.startOffset,
- walker, node, nodes, tmpNode;
-
- // Convert text node into index if possible
- if (container.nodeType == 3 && offset >= container.nodeValue.length - 1) {
- container = container.parentNode;
- offset = nodeIndex(container) + 1;
- }
-
- // Move startContainer/startOffset in to a suitable node
- if (container.nodeType == 1) {
- nodes = container.childNodes;
- container = nodes[Math.min(offset, nodes.length - 1)];
- walker = new TreeWalker(container);
+ var formatList = get(name), format = formatList[0], bookmark, i, rng, contentEditable = true;
- // If offset is at end of the parent node walk to the next one
- if (offset > nodes.length - 1)
- walker.next();
-
- for (node = walker.current(); node; node = walker.next()) {
- if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
- // IE has a "neat" feature where it moves the start node into the closest element
- // we can avoid this by inserting an element before it and then remove it after we set the selection
- tmpNode = dom.create('a', null, INVISIBLE_CHAR);
- node.parentNode.insertBefore(tmpNode, node);
-
- // Set selection and remove tmpNode
- rng.setStart(node, 0);
- selection.setRng(rng);
- dom.remove(tmpNode);
+ // Merges the styles for each node
+ function process(node) {
+ var children, i, l, localContentEditable, lastContentEditable, hasContentEditableState;
- return;
- }
- }
+ // Skip on text nodes as they have neither format to remove nor children
+ if (node.nodeType === 3) {
+ return;
}
- };
- // Merges the styles for each node
- function process(node) {
- var children, i, l;
+ // Node has a contentEditable value
+ if (node.nodeType === 1 && getContentEditable(node)) {
+ lastContentEditable = contentEditable;
+ contentEditable = getContentEditable(node) === "true";
+ hasContentEditableState = true; // We don't want to wrap the container only it's children
+ }
// Grab the children first since the nodelist might be changed
children = tinymce.grep(node.childNodes);
// Process current node
- for (i = 0, l = formatList.length; i < l; i++) {
- if (removeFormat(formatList[i], vars, node, node))
- break;
+ if (contentEditable && !hasContentEditableState) {
+ for (i = 0, l = formatList.length; i < l; i++) {
+ if (removeFormat(formatList[i], vars, node, node))
+ break;
+ }
}
// Process the children
if (format.deep) {
- for (i = 0, l = children.length; i < l; i++)
- process(children[i]);
+ if (children.length) {
+ for (i = 0, l = children.length; i < l; i++)
+ process(children[i]);
+
+ if (hasContentEditableState) {
+ contentEditable = lastContentEditable; // Restore last contentEditable state from stack
+ }
+ }
}
};
@@ -15461,7 +17035,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
formatRootParent = format_root.parentNode;
for (parent = container.parentNode; parent && parent != formatRootParent; parent = parent.parentNode) {
- clone = parent.cloneNode(FALSE);
+ clone = dom.clone(parent, FALSE);
for (i = 0; i < formatList.length; i++) {
if (removeFormat(formatList[i], vars, clone, clone)) {
@@ -15516,7 +17090,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
function removeRngStyle(rng) {
- var startContainer, endContainer;
+ var startContainer, endContainer, node;
rng = expandRng(rng, formatList, TRUE);
@@ -15525,6 +17099,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
endContainer = getContainer(rng);
if (startContainer != endContainer) {
+ // WebKit will render the table incorrectly if we wrap a TD in a SPAN so lets see if the can use the first child instead
+ // This will happen if you tripple click a table cell and use remove formatting
+ if (/^(TR|TD)$/.test(startContainer.nodeName) && startContainer.firstChild) {
+ startContainer = (startContainer.nodeName == "TD" ? startContainer.firstChild : startContainer.firstChild.firstChild) || startContainer;
+ }
+
// Wrap start/end nodes in span element since these might be cloned/moved
startContainer = wrap(startContainer, 'span', {id : '_start', 'data-mce-type' : 'bookmark'});
endContainer = wrap(endContainer, 'span', {id : '_end', 'data-mce-type' : 'bookmark'});
@@ -15586,17 +17166,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
ed.nodeChanged();
} else
performCaretAction('remove', name, vars);
-
- // When you remove formatting from a table cell in WebKit (cell, not the contents of a cell) there is a rendering issue with column width
- if (tinymce.isWebKit) {
- ed.execCommand('mceCleanup');
- }
};
function toggle(name, vars, node) {
var fmt = get(name);
- if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0]['toggle']))
+ if (match(name, vars, node) && (!('toggle' in fmt[0]) || fmt[0].toggle))
remove(name, vars, node);
else
apply(name, vars, node);
@@ -15616,7 +17191,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Check all items
if (items) {
// Non indexed object
- if (items.length === undefined) {
+ if (items.length === undef) {
for (key in items) {
if (items.hasOwnProperty(key)) {
if (item_name === 'attributes')
@@ -15712,7 +17287,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
matchedFormatNames.push(name);
}
}
- });
+ }, dom.getRoot());
return matchedFormatNames;
};
@@ -15741,6 +17316,62 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return FALSE;
};
+ function formatChanged(formats, callback, similar) {
+ var currentFormats;
+
+ // Setup format node change logic
+ if (!formatChangeData) {
+ formatChangeData = {};
+ currentFormats = {};
+
+ ed.onNodeChange.addToTop(function(ed, cm, node) {
+ var parents = getParents(node), matchedFormats = {};
+
+ // Check for new formats
+ each(formatChangeData, function(callbacks, format) {
+ each(parents, function(node) {
+ if (matchNode(node, format, {}, callbacks.similar)) {
+ if (!currentFormats[format]) {
+ // Execute callbacks
+ each(callbacks, function(callback) {
+ callback(true, {node: node, format: format, parents: parents});
+ });
+
+ currentFormats[format] = callbacks;
+ }
+
+ matchedFormats[format] = callbacks;
+ return false;
+ }
+ });
+ });
+
+ // Check if current formats still match
+ each(currentFormats, function(callbacks, format) {
+ if (!matchedFormats[format]) {
+ delete currentFormats[format];
+
+ each(callbacks, function(callback) {
+ callback(false, {node: node, format: format, parents: parents});
+ });
+ }
+ });
+ });
+ }
+
+ // Add format listeners
+ each(formats.split(','), function(format) {
+ if (!formatChangeData[format]) {
+ formatChangeData[format] = [];
+ formatChangeData[format].similar = similar;
+ }
+
+ formatChangeData[format].push(callback);
+ });
+
+ return this;
+ };
+
// Expose to public
tinymce.extend(this, {
get : get,
@@ -15751,9 +17382,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
match : match,
matchAll : matchAll,
matchNode : matchNode,
- canApply : canApply
+ canApply : canApply,
+ formatChanged: formatChanged
});
+ // Initialize
+ defaultFormats();
+ addKeyboardShortcuts();
+
// Private functions
function matchName(node, format) {
@@ -15820,19 +17456,24 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
function expandRng(rng, format, remove) {
- var startContainer = rng.startContainer,
+ var sibling, lastIdx, leaf, endPoint,
+ startContainer = rng.startContainer,
startOffset = rng.startOffset,
endContainer = rng.endContainer,
- endOffset = rng.endOffset, sibling, lastIdx, leaf, endPoint;
+ endOffset = rng.endOffset;
// This function walks up the tree if there is no siblings before/after the node
function findParentContainer(start) {
- var container, parent, child, sibling, siblingName;
+ var container, parent, child, sibling, siblingName, root;
container = parent = start ? startContainer : endContainer;
siblingName = start ? 'previousSibling' : 'nextSibling';
root = dom.getRoot();
+ function isBogusBr(node) {
+ return node.nodeName == "BR" && node.getAttribute('data-mce-bogus') && !node.nextSibling;
+ };
+
// If it's a text node and the offset is inside the text
if (container.nodeType == 3 && !isWhiteSpaceNode(container)) {
if (start ? startOffset > 0 : endOffset < container.nodeValue.length) {
@@ -15841,18 +17482,23 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
for (;;) {
- // Stop expanding on block elements or root depending on format
- if (parent == root || (!format[0].block_expand && isBlock(parent)))
+ // Stop expanding on block elements
+ if (!format[0].block_expand && isBlock(parent))
return parent;
// Walk left/right
for (sibling = parent[siblingName]; sibling; sibling = sibling[siblingName]) {
- if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling)) {
+ if (!isBookmarkNode(sibling) && !isWhiteSpaceNode(sibling) && !isBogusBr(sibling)) {
return parent;
}
}
// Check if we can move up are we at root level or body level
+ if (parent.parentNode == root) {
+ container = parent;
+ break;
+ }
+
parent = parent.parentNode;
}
@@ -15862,7 +17508,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// This function walks down the tree to find the leaf at the selection.
// The offset is also returned as if node initially a leaf, the offset may be in the middle of the text node.
function findLeaf(node, offset) {
- if (offset === undefined)
+ if (offset === undef)
offset = node.nodeType === 3 ? node.length : node.childNodes.length;
while (node && node.hasChildNodes()) {
node = node.childNodes[offset];
@@ -15890,89 +17536,163 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
endOffset = endContainer.nodeValue.length;
}
- // Exclude bookmark nodes if possible
- if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
- startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
- startContainer = startContainer.nextSibling || startContainer;
+ // Expands the node to the closes contentEditable false element if it exists
+ function findParentContentEditable(node) {
+ var parent = node;
- if (startContainer.nodeType == 3)
- startOffset = 0;
- }
+ while (parent) {
+ if (parent.nodeType === 1 && getContentEditable(parent)) {
+ return getContentEditable(parent) === "false" ? parent : node;
+ }
- if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
- endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
- endContainer = endContainer.previousSibling || endContainer;
+ parent = parent.parentNode;
+ }
- if (endContainer.nodeType == 3)
- endOffset = endContainer.length;
- }
+ return node;
+ };
- if (format[0].inline) {
- if (rng.collapsed) {
- function findWordEndPoint(container, offset, start) {
- var walker, node, pos, lastTextNode;
+ function findWordEndPoint(container, offset, start) {
+ var walker, node, pos, lastTextNode;
- function findSpace(node, offset) {
- var pos, pos2, str = node.nodeValue;
+ function findSpace(node, offset) {
+ var pos, pos2, str = node.nodeValue;
- if (typeof(offset) == "undefined") {
- offset = start ? str.length : 0;
- }
+ if (typeof(offset) == "undefined") {
+ offset = start ? str.length : 0;
+ }
- if (start) {
- pos = str.lastIndexOf(' ', offset);
- pos2 = str.lastIndexOf('\u00a0', offset);
- pos = pos > pos2 ? pos : pos2;
+ if (start) {
+ pos = str.lastIndexOf(' ', offset);
+ pos2 = str.lastIndexOf('\u00a0', offset);
+ pos = pos > pos2 ? pos : pos2;
- // Include the space on remove to avoid tag soup
- if (pos !== -1 && !remove) {
- pos++;
- }
- } else {
- pos = str.indexOf(' ', offset);
- pos2 = str.indexOf('\u00a0', offset);
- pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
- }
+ // Include the space on remove to avoid tag soup
+ if (pos !== -1 && !remove) {
+ pos++;
+ }
+ } else {
+ pos = str.indexOf(' ', offset);
+ pos2 = str.indexOf('\u00a0', offset);
+ pos = pos !== -1 && (pos2 === -1 || pos < pos2) ? pos : pos2;
+ }
- return pos;
- };
+ return pos;
+ };
- if (container.nodeType === 3) {
- pos = findSpace(container, offset);
+ if (container.nodeType === 3) {
+ pos = findSpace(container, offset);
- if (pos !== -1) {
- return {container : container, offset : pos};
- }
+ if (pos !== -1) {
+ return {container : container, offset : pos};
+ }
- lastTextNode = container;
- }
+ lastTextNode = container;
+ }
- // Walk the nodes inside the block
- walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
- while (node = walker[start ? 'prev' : 'next']()) {
- if (node.nodeType === 3) {
- lastTextNode = node;
- pos = findSpace(node);
+ // Walk the nodes inside the block
+ walker = new TreeWalker(container, dom.getParent(container, isBlock) || ed.getBody());
+ while (node = walker[start ? 'prev' : 'next']()) {
+ if (node.nodeType === 3) {
+ lastTextNode = node;
+ pos = findSpace(node);
- if (pos !== -1) {
- return {container : node, offset : pos};
- }
- } else if (isBlock(node)) {
- break;
- }
+ if (pos !== -1) {
+ return {container : node, offset : pos};
}
+ } else if (isBlock(node)) {
+ break;
+ }
+ }
- if (lastTextNode) {
- if (start) {
- offset = 0;
- } else {
- offset = lastTextNode.length;
- }
+ if (lastTextNode) {
+ if (start) {
+ offset = 0;
+ } else {
+ offset = lastTextNode.length;
+ }
- return {container: lastTextNode, offset: offset};
- }
+ return {container: lastTextNode, offset: offset};
+ }
+ };
+
+ function findSelectorEndPoint(container, sibling_name) {
+ var parents, i, y, curFormat;
+
+ if (container.nodeType == 3 && container.nodeValue.length === 0 && container[sibling_name])
+ container = container[sibling_name];
+
+ parents = getParents(container);
+ for (i = 0; i < parents.length; i++) {
+ for (y = 0; y < format.length; y++) {
+ curFormat = format[y];
+
+ // If collapsed state is set then skip formats that doesn't match that
+ if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed)
+ continue;
+
+ if (dom.is(parents[i], curFormat.selector))
+ return parents[i];
+ }
+ }
+
+ return container;
+ };
+
+ function findBlockEndPoint(container, sibling_name, sibling_name2) {
+ var node;
+
+ // Expand to block of similar type
+ if (!format[0].wrapper)
+ node = dom.getParent(container, format[0].block);
+
+ // Expand to first wrappable block element or any block element
+ if (!node)
+ node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isTextBlock);
+
+ // Exclude inner lists from wrapping
+ if (node && format[0].wrapper)
+ node = getParents(node, 'ul,ol').reverse()[0] || node;
+
+ // Didn't find a block element look for first/last wrappable element
+ if (!node) {
+ node = container;
+
+ while (node[sibling_name] && !isBlock(node[sibling_name])) {
+ node = node[sibling_name];
+
+ // Break on BR but include it will be removed later on
+ // we can't remove it now since we need to check if it can be wrapped
+ if (isEq(node, 'br'))
+ break;
}
+ }
+
+ return node || container;
+ };
+
+ // Expand to closest contentEditable element
+ startContainer = findParentContentEditable(startContainer);
+ endContainer = findParentContentEditable(endContainer);
+
+ // Exclude bookmark nodes if possible
+ if (isBookmarkNode(startContainer.parentNode) || isBookmarkNode(startContainer)) {
+ startContainer = isBookmarkNode(startContainer) ? startContainer : startContainer.parentNode;
+ startContainer = startContainer.nextSibling || startContainer;
+
+ if (startContainer.nodeType == 3)
+ startOffset = 0;
+ }
+
+ if (isBookmarkNode(endContainer.parentNode) || isBookmarkNode(endContainer)) {
+ endContainer = isBookmarkNode(endContainer) ? endContainer : endContainer.parentNode;
+ endContainer = endContainer.previousSibling || endContainer;
+
+ if (endContainer.nodeType == 3)
+ endOffset = endContainer.length;
+ }
+ if (format[0].inline) {
+ if (rng.collapsed) {
// Expand left to closest word boundery
endPoint = findWordEndPoint(startContainer, startOffset, true);
if (endPoint) {
@@ -16000,9 +17720,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
if (leaf.offset > 1) {
endContainer = leaf.node;
endContainer.splitText(leaf.offset - 1);
- } else if (leaf.node.previousSibling) {
- // TODO: Figure out why this is in here
- //endContainer = leaf.node.previousSibling;
}
}
}
@@ -16024,29 +17741,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Expand start/end container to matching selector
if (format[0].selector && format[0].expand !== FALSE && !format[0].inline) {
- function findSelectorEndPoint(container, sibling_name) {
- var parents, i, y, curFormat;
-
- if (container.nodeType == 3 && container.nodeValue.length == 0 && container[sibling_name])
- container = container[sibling_name];
-
- parents = getParents(container);
- for (i = 0; i < parents.length; i++) {
- for (y = 0; y < format.length; y++) {
- curFormat = format[y];
-
- // If collapsed state is set then skip formats that doesn't match that
- if ("collapsed" in curFormat && curFormat.collapsed !== rng.collapsed)
- continue;
-
- if (dom.is(parents[i], curFormat.selector))
- return parents[i];
- }
- }
-
- return container;
- };
-
// Find new startContainer/endContainer if there is better one
startContainer = findSelectorEndPoint(startContainer, 'previousSibling');
endContainer = findSelectorEndPoint(endContainer, 'nextSibling');
@@ -16054,38 +17748,6 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Expand start/end container to matching block element or text node
if (format[0].block || format[0].selector) {
- function findBlockEndPoint(container, sibling_name, sibling_name2) {
- var node;
-
- // Expand to block of similar type
- if (!format[0].wrapper)
- node = dom.getParent(container, format[0].block);
-
- // Expand to first wrappable block element or any block element
- if (!node)
- node = dom.getParent(container.nodeType == 3 ? container.parentNode : container, isBlock);
-
- // Exclude inner lists from wrapping
- if (node && format[0].wrapper)
- node = getParents(node, 'ul,ol').reverse()[0] || node;
-
- // Didn't find a block element look for first/last wrappable element
- if (!node) {
- node = container;
-
- while (node[sibling_name] && !isBlock(node[sibling_name])) {
- node = node[sibling_name];
-
- // Break on BR but include it will be removed later on
- // we can't remove it now since we need to check if it can be wrapped
- if (isEq(node, 'br'))
- break;
- }
- }
-
- return node || container;
- };
-
// Find new startContainer/endContainer if there is better one
startContainer = findBlockEndPoint(startContainer, 'previousSibling');
endContainer = findBlockEndPoint(endContainer, 'nextSibling');
@@ -16222,14 +17884,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
function removeNode(node, format) {
var parentNode = node.parentNode, rootBlockElm;
- if (format.block) {
- if (!forcedRootBlock) {
- function find(node, next, inc) {
- node = getNonWhiteSpaceSibling(node, next, inc);
+ function find(node, next, inc) {
+ node = getNonWhiteSpaceSibling(node, next, inc);
- return !node || (node.nodeName == 'BR' || isBlock(node));
- };
+ return !node || (node.nodeName == 'BR' || isBlock(node));
+ };
+ if (format.block) {
+ if (!forcedRootBlock) {
// Append BR elements if needed before we remove the block
if (isBlock(node) && !isBlock(parentNode)) {
if (!find(node, FALSE) && !find(node.firstChild, TRUE, 1))
@@ -16309,7 +17971,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
value = obj2[name];
// Obj2 doesn't have obj1 item
- if (value === undefined)
+ if (value === undef)
return FALSE;
// Obj2 item has a different value
@@ -16342,20 +18004,20 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
return TRUE;
};
- // Check if next/prev exists and that they are elements
- if (prev && next) {
- function findElementSibling(node, sibling_name) {
- for (sibling = node; sibling; sibling = sibling[sibling_name]) {
- if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0)
- return node;
+ function findElementSibling(node, sibling_name) {
+ for (sibling = node; sibling; sibling = sibling[sibling_name]) {
+ if (sibling.nodeType == 3 && sibling.nodeValue.length !== 0)
+ return node;
- if (sibling.nodeType == 1 && !isBookmarkNode(sibling))
- return sibling;
- }
+ if (sibling.nodeType == 1 && !isBookmarkNode(sibling))
+ return sibling;
+ }
- return node;
- };
+ return node;
+ };
+ // Check if next/prev exists and that they are elements
+ if (prev && next) {
// If previous sibling is empty then jump over it
prev = findElementSibling(prev, 'previousSibling');
next = findElementSibling(next, 'nextSibling');
@@ -16409,7 +18071,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
// If end text node is excluded then walk to the previous node
- if (container.nodeType === 3 && !start && offset == 0) {
+ if (container.nodeType === 3 && !start && offset === 0) {
container = new TreeWalker(container, ed.getBody()).prev() || container;
}
@@ -16417,17 +18079,14 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
};
function performCaretAction(type, name, vars) {
- var invisibleChar, caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
-
- // Setup invisible character use zero width space on Gecko since it doesn't change the heigt of the container
- invisibleChar = tinymce.isGecko ? '\u200B' : INVISIBLE_CHAR;
+ var caretContainerId = '_mce_caret', debug = ed.settings.caret_debug;
// Creates a caret container bogus element
function createCaretContainer(fill) {
var caretContainer = dom.create('span', {id: caretContainerId, 'data-mce-bogus': true, style: debug ? 'color:red' : ''});
if (fill) {
- caretContainer.appendChild(ed.getDoc().createTextNode(invisibleChar));
+ caretContainer.appendChild(ed.getDoc().createTextNode(INVISIBLE_CHAR));
}
return caretContainer;
@@ -16435,7 +18094,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
function isCaretContainerEmpty(node, nodes) {
while (node) {
- if ((node.nodeType === 3 && node.nodeValue !== invisibleChar) || node.childNodes.length > 1) {
+ if ((node.nodeType === 3 && node.nodeValue !== INVISIBLE_CHAR) || node.childNodes.length > 1) {
return false;
}
@@ -16500,7 +18159,11 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
dom.remove(node);
} else {
child = findFirstTextNode(node);
- child = child.deleteData(0, 1);
+
+ if (child.nodeValue.charAt(0) === INVISIBLE_CHAR) {
+ child = child.deleteData(0, 1);
+ }
+
dom.remove(node, 1);
}
@@ -16540,7 +18203,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
// Move selection back to caret position
selection.moveToBookmark(bookmark);
} else {
- if (!caretContainer || textNode.nodeValue !== invisibleChar) {
+ if (!caretContainer || textNode.nodeValue !== INVISIBLE_CHAR) {
caretContainer = createCaretContainer(true);
textNode = caretContainer.firstChild;
@@ -16566,7 +18229,7 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
node = container;
if (container.nodeType == 3) {
- if (offset != container.nodeValue.length || container.nodeValue === invisibleChar) {
+ if (offset != container.nodeValue.length || container.nodeValue === INVISIBLE_CHAR) {
hasContentAfter = true;
}
@@ -16614,12 +18277,12 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
node = caretContainer;
for (i = parents.length - 1; i >= 0; i--) {
- node.appendChild(parents[i].cloneNode(false));
+ node.appendChild(dom.clone(parents[i], false));
node = node.firstChild;
}
// Insert invisible character into inner most format element
- node.appendChild(dom.doc.createTextNode(invisibleChar));
+ node.appendChild(dom.doc.createTextNode(INVISIBLE_CHAR));
node = node.firstChild;
// Insert caret container after the formated node
@@ -16630,34 +18293,60 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
}
};
- // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
- ed.onBeforeGetContent.addToTop(function() {
- var nodes = [], i;
+ // Checks if the parent caret container node isn't empty if that is the case it
+ // will remove the bogus state on all children that isn't empty
+ function unmarkBogusCaretParents() {
+ var i, caretContainer, node;
- if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
- // Mark children
- i = nodes.length;
- while (i--) {
- dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
- }
+ caretContainer = getParentCaretContainer(selection.getStart());
+ if (caretContainer && !dom.isEmpty(caretContainer)) {
+ tinymce.walk(caretContainer, function(node) {
+ if (node.nodeType == 1 && node.id !== caretContainerId && !dom.isEmpty(node)) {
+ dom.setAttrib(node, 'data-mce-bogus', null);
+ }
+ }, 'childNodes');
}
- });
+ };
- // Remove caret container on mouse up and on key up
- tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
- ed[name].addToTop(function() {
- removeCaretContainer();
+ // Only bind the caret events once
+ if (!self._hasCaretEvents) {
+ // Mark current caret container elements as bogus when getting the contents so we don't end up with empty elements
+ ed.onBeforeGetContent.addToTop(function() {
+ var nodes = [], i;
+
+ if (isCaretContainerEmpty(getParentCaretContainer(selection.getStart()), nodes)) {
+ // Mark children
+ i = nodes.length;
+ while (i--) {
+ dom.setAttrib(nodes[i], 'data-mce-bogus', '1');
+ }
+ }
+ });
+
+ // Remove caret container on mouse up and on key up
+ tinymce.each('onMouseUp onKeyUp'.split(' '), function(name) {
+ ed[name].addToTop(function() {
+ removeCaretContainer();
+ unmarkBogusCaretParents();
+ });
});
- });
- // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
- ed.onKeyDown.addToTop(function(ed, e) {
- var keyCode = e.keyCode;
+ // Remove caret container on keydown and it's a backspace, enter or left/right arrow keys
+ ed.onKeyDown.addToTop(function(ed, e) {
+ var keyCode = e.keyCode;
- if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
- removeCaretContainer(getParentCaretContainer(selection.getStart()));
- }
- });
+ if (keyCode == 8 || keyCode == 37 || keyCode == 39) {
+ removeCaretContainer(getParentCaretContainer(selection.getStart()));
+ }
+
+ unmarkBogusCaretParents();
+ });
+
+ // Remove bogus state if they got filled by contents using editor.selection.setContent
+ selection.onSetContent.add(unmarkBogusCaretParents);
+
+ self._hasCaretEvents = true;
+ }
// Do apply or remove caret format
if (type == "apply") {
@@ -16666,23 +18355,74 @@ tinymce.create('tinymce.ui.Toolbar:tinymce.ui.Container', {
removeCaretFormat();
}
};
+
+ function moveStart(rng) {
+ var container = rng.startContainer,
+ offset = rng.startOffset, isAtEndOfText,
+ walker, node, nodes, tmpNode;
+
+ // Convert text node into index if possible
+ if (container.nodeType == 3 && offset >= container.nodeValue.length) {
+ // Get the parent container location and walk from there
+ offset = nodeIndex(container);
+ container = container.parentNode;
+ isAtEndOfText = true;
+ }
+
+ // Move startContainer/startOffset in to a suitable node
+ if (container.nodeType == 1) {
+ nodes = container.childNodes;
+ container = nodes[Math.min(offset, nodes.length - 1)];
+ walker = new TreeWalker(container, dom.getParent(container, dom.isBlock));
+
+ // If offset is at end of the parent node walk to the next one
+ if (offset > nodes.length - 1 || isAtEndOfText)
+ walker.next();
+
+ for (node = walker.current(); node; node = walker.next()) {
+ if (node.nodeType == 3 && !isWhiteSpaceNode(node)) {
+ // IE has a "neat" feature where it moves the start node into the closest element
+ // we can avoid this by inserting an element before it and then remove it after we set the selection
+ tmpNode = dom.create('a', null, INVISIBLE_CHAR);
+ node.parentNode.insertBefore(tmpNode, node);
+
+ // Set selection and remove tmpNode
+ rng.setStart(node, 0);
+ selection.setRng(rng);
+ dom.remove(tmpNode);
+
+ return;
+ }
+ }
+ }
+ };
};
})(tinymce);
tinymce.onAddEditor.add(function(tinymce, ed) {
var filters, fontSizes, dom, settings = ed.settings;
- if (settings.inline_styles) {
- fontSizes = tinymce.explode(settings.font_size_legacy_values);
+ function replaceWithSpan(node, styles) {
+ tinymce.each(styles, function(value, name) {
+ if (value)
+ dom.setStyle(node, name, value);
+ });
+
+ dom.rename(node, 'span');
+ };
+
+ function convert(editor, params) {
+ dom = editor.dom;
- function replaceWithSpan(node, styles) {
- tinymce.each(styles, function(value, name) {
- if (value)
- dom.setStyle(node, name, value);
+ if (settings.convert_fonts_to_spans) {
+ tinymce.each(dom.select('font,u,strike', params.node), function(node) {
+ filters[node.nodeName.toLowerCase()](ed.dom, node);
});
+ }
+ };
- dom.rename(node, 'span');
- };
+ if (settings.inline_styles) {
+ fontSizes = tinymce.explode(settings.font_size_legacy_values);
filters = {
font : function(dom, node) {
@@ -16690,7 +18430,7 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
backgroundColor : node.style.backgroundColor,
color : node.color,
fontFamily : node.face,
- fontSize : fontSizes[parseInt(node.size) - 1]
+ fontSize : fontSizes[parseInt(node.size, 10) - 1]
});
},
@@ -16707,16 +18447,6 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
}
};
- function convert(editor, params) {
- dom = editor.dom;
-
- if (settings.convert_fonts_to_spans) {
- tinymce.each(dom.select('font,u,strike', params.node), function(node) {
- filters[node.nodeName.toLowerCase()](ed.dom, node);
- });
- }
- };
-
ed.onPreProcess.add(convert);
ed.onSetContent.add(convert);
@@ -16726,3 +18456,575 @@ tinymce.onAddEditor.add(function(tinymce, ed) {
}
});
+(function(tinymce) {
+ var TreeWalker = tinymce.dom.TreeWalker;
+
+ tinymce.EnterKey = function(editor) {
+ var dom = editor.dom, selection = editor.selection, settings = editor.settings, undoManager = editor.undoManager, nonEmptyElementsMap = editor.schema.getNonEmptyElements();
+
+ function handleEnterKey(evt) {
+ var rng = selection.getRng(true), tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
+ newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
+
+ // Returns true if the block can be split into two blocks or not
+ function canSplitBlock(node) {
+ return node &&
+ dom.isBlock(node) &&
+ !/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
+ !/^(fixed|absolute)/i.test(node.style.position) &&
+ dom.getContentEditable(node) !== "true";
+ };
+
+ // Renders empty block on IE
+ function renderBlockOnIE(block) {
+ var oldRng;
+
+ if (tinymce.isIE && dom.isBlock(block)) {
+ oldRng = selection.getRng();
+ block.appendChild(dom.create('span', null, '\u00a0'));
+ selection.select(block);
+ block.lastChild.outerHTML = '';
+ selection.setRng(oldRng);
+ }
+ };
+
+ // Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
+ function trimInlineElementsOnLeftSideOfBlock(block) {
+ var node = block, firstChilds = [], i;
+
+ // Find inner most first child ex: <p><i><b>*</b></i></p>
+ while (node = node.firstChild) {
+ if (dom.isBlock(node)) {
+ return;
+ }
+
+ if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
+ firstChilds.push(node);
+ }
+ }
+
+ i = firstChilds.length;
+ while (i--) {
+ node = firstChilds[i];
+ if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
+ dom.remove(node);
+ } else {
+ // Remove <a> </a> see #5381
+ if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
+ dom.remove(node);
+ }
+ }
+ }
+ };
+
+ // Moves the caret to a suitable position within the root for example in the first non pure whitespace text node or before an image
+ function moveToCaretPosition(root) {
+ var walker, node, rng, y, viewPort, lastNode = root, tempElm;
+
+ rng = dom.createRng();
+
+ 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 (nonEmptyElementsMap[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);
+
+ viewPort = dom.getViewPort(editor.getWin());
+
+ // scrollIntoView seems to scroll the parent window in most browsers now including FF 3.0b4 so it's time to stop using it and do it our selfs
+ y = dom.getPos(root).y;
+ if (y < viewPort.y || y + 25 > viewPort.y + viewPort.h) {
+ editor.getWin().scrollTo(0, y < viewPort.y ? y : y - viewPort.h + 25); // Needs to be hardcoded to roughly one line of text if a huge text block is broken into two blocks
+ }
+ };
+
+ // 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;
+
+ block = name || parentBlockName == "TABLE" ? dom.create(name || newBlockName) : parentBlock.cloneNode(false);
+ caretNode = block;
+
+ // Clone any parent styles
+ if (settings.keep_styles !== false) {
+ do {
+ if (/^(SPAN|STRONG|B|EM|I|FONT|STRIKE|U)$/.test(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);
+ }
+
+ // BR is needed in empty blocks on non IE browsers
+ if (!tinymce.isIE) {
+ caretNode.innerHTML = '<br data-mce-bogus="1">';
+ }
+
+ 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 <img /> <input /> but not trailing br:s like <p>text|<br></p>
+ 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, 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.hasChildNodes()) {
+ newBlock = dom.create(blockName);
+ 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) {
+ newBlock = dom.create(blockName);
+ 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;
+ };
+
+ newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
+
+ if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
+ // Is first and last list item then replace the OL/UL with a text block
+ dom.replace(newBlock, containerBlock);
+ } else if (isFirstOrLastLi(true)) {
+ // 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 temove LI and add text block after list
+ dom.insertAfter(newBlock, containerBlock);
+ 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
+ tmpRng = rng.cloneRange();
+ tmpRng.setStartAfter(parentBlock);
+ tmpRng.setEndAfter(containerBlock);
+ fragment = tmpRng.extractContents();
+ dom.insertAfter(fragment, containerBlock);
+ dom.insertAfter(newBlock, containerBlock);
+ }
+
+ dom.remove(parentBlock);
+ moveToCaretPosition(newBlock);
+ undoManager.add();
+ };
+
+ // Walks the parent block to the right and look for BR elements
+ function hasRightSideBr() {
+ var walker = new TreeWalker(container, parentBlock), node;
+
+ while (node = walker.current()) {
+ if (node.nodeName == 'BR') {
+ return true;
+ }
+
+ node = walker.next();
+ }
+ }
+
+ // Inserts a BR element if the forced_root_block option is set to false or empty string
+ function insertBr() {
+ var brElm, extraBr, marker;
+
+ if (container && container.nodeType == 3 && offset >= container.nodeValue.length) {
+ // Insert extra BR element at the end block elements
+ if (!tinymce.isIE && !hasRightSideBr()) {
+ 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
+ if (tinymce.isIE && parentBlockName == 'PRE' && (!documentMode || documentMode < 8)) {
+ brElm.parentNode.insertBefore(dom.doc.createTextNode('\r'), brElm);
+ }
+
+ // Insert temp marker and scroll to that
+ marker = dom.create('span', {}, '&nbsp;');
+ 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);
+ undoManager.add();
+ };
+
+ // 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 (!tinymce.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');
+ }
+ }
+ };
+
+ // Delete any selected contents
+ if (!rng.collapsed) {
+ editor.execCommand('Delete');
+ return;
+ }
+
+ // Event is blocked by some other handler for example the lists plugin
+ if (evt.isDefaultPrevented()) {
+ return;
+ }
+
+ // Setup range items and newBlockName
+ 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 normaly 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 <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
+ // 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;
+ }
+
+ // Handle enter in LI
+ if (parentBlockName == 'LI') {
+ if (!newBlockName && shiftKey) {
+ insertBr();
+ return;
+ }
+
+ // Handle enter inside an empty list item
+ if (dom.isEmpty(parentBlock)) {
+ // Let the list plugin or browser handle nested lists for now
+ if (/^(UL|OL|LI)$/.test(containerBlock.parentNode.nodeName)) {
+ return false;
+ }
+
+ 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;
+ }
+ }
+
+ // Default block name if it's not configured
+ newBlockName = newBlockName || 'P';
+
+ // Insert new block before/after the parent block depending on caret location
+ if (isCaretAtStartOrEndOfBlock()) {
+ // 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)$/.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);
+ } else if (isCaretAtStartOrEndOfBlock(true)) {
+ // Insert new block before
+ newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
+ renderBlockOnIE(newBlock);
+ } 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);
+ moveToCaretPosition(newBlock);
+ }
+
+ dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
+ undoManager.add();
+ }
+
+ editor.onKeyDown.add(function(ed, evt) {
+ if (evt.keyCode == 13) {
+ if (handleEnterKey(evt) !== false) {
+ evt.preventDefault();
+ }
+ }
+ });
+ };
+})(tinymce);
+
diff --git a/mod/tinymce/views/default/js/tinymce.php b/mod/tinymce/views/default/js/tinymce.php
index 51e99c223..344d71b14 100644
--- a/mod/tinymce/views/default/js/tinymce.php
+++ b/mod/tinymce/views/default/js/tinymce.php
@@ -39,7 +39,7 @@ elgg.tinymce.init = function() {
editor_selector : "elgg-input-longtext",
theme : "advanced",
language : "<?php echo tinymce_get_site_language(); ?>",
- plugins : "lists,spellchecker,autosave,fullscreen,paste",
+ plugins : "lists,spellchecker,autosave,fullscreen,paste,inlinepopups",
relative_urls : false,
remove_script_host : false,
document_base_url : elgg.config.wwwroot,
@@ -66,6 +66,18 @@ elgg.tinymce.init = function() {
var text = elgg.echo('tinymce:word_count') + strip.split(' ').length + ' ';
tinymce.DOM.setHTML(tinymce.DOM.get(tinyMCE.activeEditor.id + '_path_row'), text);
});
+
+ ed.onInit.add(function(ed) {
+ // prevent Firefox from dragging/dropping files into editor
+ if (tinymce.isGecko) {
+ tinymce.dom.Event.add(ed.getBody().parentNode, "drop", function(e) {
+ if (e.dataTransfer.files.length > 0) {
+ e.preventDefault();
+ }
+ });
+ }
+ });
+
},
content_css: elgg.config.wwwroot + 'mod/tinymce/css/elgg_tinymce.css'
});
diff --git a/mod/twitter/graphics/thewire_speech_bubble.gif b/mod/twitter/graphics/thewire_speech_bubble.gif
deleted file mode 100644
index d0e8606a1..000000000
--- a/mod/twitter/graphics/thewire_speech_bubble.gif
+++ /dev/null
Binary files differ
diff --git a/mod/twitter/graphics/twitter16px.png b/mod/twitter/graphics/twitter16px.png
deleted file mode 100644
index de51c6953..000000000
--- a/mod/twitter/graphics/twitter16px.png
+++ /dev/null
Binary files differ
diff --git a/mod/twitter/languages/en.php b/mod/twitter/languages/en.php
deleted file mode 100644
index 11e745ba1..000000000
--- a/mod/twitter/languages/en.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-/**
- * Twitter widget language file
- */
-
-$english = array(
- 'twitter:title' => 'Twitter',
- 'twitter:info' => 'Display your latest tweets',
- 'twitter:username' => 'Your twitter username',
- 'twitter:num' => 'Number of tweets to show*',
- 'twitter:visit' => 'visit my twitter',
- 'twitter:notset' => 'This widget needs to be configured. To display your latest tweets, click the customize icon and fill in your Twitter username.',
- 'twitter:invalid' => 'This widget is configured with an invalid Twitter username. Click the customize icon to correct it.',
- 'twitter:apibug' => "*Due to a bug in the Twitter 1.0 API, you may see fewer tweets than you ask for.",
-);
-
-add_translation("en", $english);
diff --git a/mod/twitter/manifest.xml b/mod/twitter/manifest.xml
deleted file mode 100644
index 18fa8c957..000000000
--- a/mod/twitter/manifest.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
- <name>Twitter Widget</name>
- <author>Core developers</author>
- <version>1.7</version>
- <category>bundled</category>
- <category>widget</category>
- <description>Elgg simple twitter widget</description>
- <website>http://www.elgg.org/</website>
- <copyright>See COPYRIGHT.txt</copyright>
- <license>GNU General Public License version 2</license>
- <requires>
- <type>elgg_release</type>
- <version>1.8</version>
- </requires>
-</plugin_manifest>
diff --git a/mod/twitter/start.php b/mod/twitter/start.php
deleted file mode 100644
index b793eadf0..000000000
--- a/mod/twitter/start.php
+++ /dev/null
@@ -1,14 +0,0 @@
-<?php
-/**
- * Elgg twitter widget
- * This plugin allows users to pull in their twitter feed to display on their profile
- *
- * @package ElggTwitter
- */
-
-elgg_register_event_handler('init', 'system', 'twitter_init');
-
-function twitter_init() {
- elgg_extend_view('css/elgg', 'twitter/css');
- elgg_register_widget_type('twitter', elgg_echo('twitter:title'), elgg_echo('twitter:info'));
-}
diff --git a/mod/twitter/views/default/twitter/css.php b/mod/twitter/views/default/twitter/css.php
deleted file mode 100644
index eb0cda98a..000000000
--- a/mod/twitter/views/default/twitter/css.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-/**
- * Elgg Twitter CSS
- *
- * @package ElggTwitter
- */
-?>
-
-#twitter_widget {
- margin:0 10px 0 10px;
-}
-#twitter_widget ul {
- margin:0;
- padding:0;
-}
-#twitter_widget li {
- list-style-image:none;
- list-style-position:outside;
- list-style-type:none;
- margin:0 0 5px 0;
- padding:0;
- overflow-x: hidden;
- border: 2px solid #dedede;
- -webkit-border-radius: 8px;
- -moz-border-radius: 8px;
- border-radius: 8px;
-}
-#twitter_widget li span {
- color:#666666;
- background:white;
-
- -webkit-border-radius: 8px;
- -moz-border-radius: 8px;
- border-radius: 8px;
-
- padding:5px;
- display:block;
-}
-p.visit_twitter a {
- background:url(<?php echo elgg_get_site_url(); ?>mod/twitter/graphics/twitter16px.png) left no-repeat;
- padding:0 0 0 20px;
- margin:0;
-}
-p.twitter_username .input-text {
- width:200px;
-}
-.visit_twitter {
- background:white;
-
- -webkit-border-radius: 8px;
- -moz-border-radius: 8px;
- border-radius: 8px;
-
- padding:2px;
- margin:0 0 5px 0;
-}
-#twitter_widget li > a {
- display:block;
- margin:0 0 0 4px;
-}
-#twitter_widget li span a {
- display:inline !important;
-} \ No newline at end of file
diff --git a/mod/twitter/views/default/widgets/twitter/content.php b/mod/twitter/views/default/widgets/twitter/content.php
deleted file mode 100644
index caefd369a..000000000
--- a/mod/twitter/views/default/widgets/twitter/content.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-/**
- * Elgg twitter view page
- *
- * @package ElggTwitter
- */
-
-$username = $vars['entity']->twitter_username;
-
-if (empty($username)) {
- echo "<p>" . elgg_echo("twitter:notset") . "</p>";
- return;
-}
-
-$username_is_valid = preg_match('~^[a-zA-Z0-9_]{1,20}$~', $username);
-if (!$username_is_valid) {
- echo "<p>" . elgg_echo("twitter:invalid") . "</p>";
- return;
-}
-
-
-$num = $vars['entity']->twitter_num;
-if (empty($num)) {
- $num = 5;
-}
-
-// @todo upgrade to 1.1 API https://dev.twitter.com/docs/api/1.1/get/statuses/home_timeline
-$script_url = "https://api.twitter.com/1/statuses/user_timeline/" . urlencode($username) . ".json"
- . "?callback=twitterCallback2&count=" . (int) $num;
-
-?>
-<div id="twitter_widget">
- <ul id="twitter_update_list"></ul>
- <p class="visit_twitter"><?php echo elgg_view('output/url', array(
- 'text' => elgg_echo("twitter:visit"),
- 'href' => 'http://twitter.com/' . urlencode($username),
- 'is_trusted' => true,
- )) ?></p>
- <script type="text/javascript" src="http://twitter.com/javascripts/blogger.js"></script>
- <script type="text/javascript" src="<?php echo htmlspecialchars($script_url, ENT_QUOTES, 'UTF-8') ?>"></script>
-</div>
diff --git a/mod/twitter/views/default/widgets/twitter/edit.php b/mod/twitter/views/default/widgets/twitter/edit.php
deleted file mode 100644
index c3fc6f0d5..000000000
--- a/mod/twitter/views/default/widgets/twitter/edit.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-/**
- * Elgg twitter edit page
- *
- * @package ElggTwitter
- */
-
-?>
-<div>
- <?php echo elgg_echo("twitter:username"); ?>
- <?php echo elgg_view('input/text', array(
- 'name' => 'params[twitter_username]',
- 'value' => $vars['entity']->twitter_username,
- )) ?>
-</div>
-<div>
- <?php echo elgg_echo("twitter:num"); ?>
- <?php echo elgg_view('input/text', array(
- 'name' => 'params[twitter_num]',
- 'value' => $vars['entity']->twitter_num,
- )) ?>
- <span class="elgg-text-help"><?php echo elgg_echo("twitter:apibug"); ?></span>
-</div> \ No newline at end of file
diff --git a/mod/twitter_api/languages/en.php b/mod/twitter_api/languages/en.php
index f4b3c7f94..a6f4b40a5 100644
--- a/mod/twitter_api/languages/en.php
+++ b/mod/twitter_api/languages/en.php
@@ -25,7 +25,9 @@ $english = array(
'twitter_api:revoke:success' => 'Twitter access has been revoked.',
- 'twitter_api:login' => 'Allow existing users who have connected their Twitter account to sign in with Twitter?',
+ 'twitter_api:post_to_twitter' => "Send users' wire posts to Twitter?",
+
+ 'twitter_api:login' => 'Allow users to sign in with Twitter?',
'twitter_api:new_users' => 'Allow new users to sign up using their Twitter account even if user registration is disabled?',
'twitter_api:login:success' => 'You have been logged in.',
'twitter_api:login:error' => 'Unable to login with Twitter.',
diff --git a/mod/twitter_api/lib/twitter_api.php b/mod/twitter_api/lib/twitter_api.php
index e163d2b3e..a7b971876 100644
--- a/mod/twitter_api/lib/twitter_api.php
+++ b/mod/twitter_api/lib/twitter_api.php
@@ -6,6 +6,27 @@
*/
/**
+ * Get the API wrapper object
+ *
+ * @param string $oauth_token User's OAuth token
+ * @param string $oauth_token_secret User's OAuth secret
+ * @return TwitterOAuth|null
+ */
+function twitter_api_get_api_object($oauth_token = null, $oauth_token_secret = null) {
+ $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
+ $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
+ if (!($consumer_key && $consumer_secret)) {
+ return null;
+ }
+
+ $api = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret);
+ if ($api) {
+ $api->host = "https://api.twitter.com/1.1/";
+ }
+ return $api;
+}
+
+/**
* Tests if the system admin has enabled Sign-On-With-Twitter
*
* @param void
@@ -94,7 +115,7 @@ function twitter_api_login() {
$forward = $login_metadata['forward'];
}
- if (!isset($token['oauth_token']) or !isset($token['oauth_token_secret'])) {
+ if (!isset($token['oauth_token']) || !isset($token['oauth_token_secret'])) {
register_error(elgg_echo('twitter_api:login:error'));
forward();
}
@@ -121,9 +142,7 @@ function twitter_api_login() {
forward();
}
} else {
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
- $api = new TwitterOAuth($consumer_key, $consumer_secret, $token['oauth_token'], $token['oauth_token_secret']);
+ $api = twitter_api_get_api_object($token['oauth_token'], $token['oauth_token_secret']);
$twitter = $api->get('account/verify_credentials');
// backward compatibility for deprecated Twitter Login plugin
@@ -255,7 +274,7 @@ function twitter_api_update_user_avatar($user, $file_location) {
* to establish session request tokens.
*/
function twitter_api_authorize() {
- $token = twitter_api_get_access_token();
+ $token = twitter_api_get_access_token(get_input('oauth_verifier'));
if (!isset($token['oauth_token']) || !isset($token['oauth_token_secret'])) {
register_error(elgg_echo('twitter_api:authorize:error'));
forward('settings/plugins', 'twitter_api');
@@ -314,11 +333,8 @@ function twitter_api_revoke() {
function twitter_api_get_authorize_url($callback = NULL, $login = true) {
global $SESSION;
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
-
// request tokens from Twitter
- $twitter = new TwitterOAuth($consumer_key, $consumer_secret);
+ $twitter = twitter_api_get_api_object();
$token = $twitter->getRequestToken($callback);
// save token in session for use after authorization
@@ -340,16 +356,13 @@ function twitter_api_get_access_token($oauth_verifier = FALSE) {
/* @var ElggSession $SESSION */
global $SESSION;
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
-
// retrieve stored tokens
$oauth_token = $SESSION['twitter_api']['oauth_token'];
$oauth_token_secret = $SESSION['twitter_api']['oauth_token_secret'];
unset($SESSION['twitter_api']);
// fetch an access token
- $api = new TwitterOAuth($consumer_key, $consumer_secret, $oauth_token, $oauth_token_secret);
+ $api = twitter_api_get_api_object($oauth_token, $oauth_token_secret);
return $api->getAccessToken($oauth_verifier);
}
@@ -367,4 +380,4 @@ function twitter_api_allow_new_users_with_twitter() {
}
return false;
-} \ No newline at end of file
+}
diff --git a/mod/twitter_api/manifest.xml b/mod/twitter_api/manifest.xml
index 86bba4b50..3af866bba 100644
--- a/mod/twitter_api/manifest.xml
+++ b/mod/twitter_api/manifest.xml
@@ -2,7 +2,7 @@
<plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
<name>Twitter API</name>
<author>Core developers</author>
- <version>1.8</version>
+ <version>1.8.15</version>
<description>Allows users to authenticate their Elgg account with Twitter.</description>
<category>api</category>
<category>bundled</category>
@@ -14,16 +14,16 @@
<version>1.8</version>
</requires>
<requires>
- <type>plugin</type>
- <name>oauth_api</name>
- </requires>
- <requires>
<type>php_extension</type>
<name>curl</name>
</requires>
<conflicts>
<type>plugin</type>
+ <name>oauth_api</name>
+ </conflicts>
+ <conflicts>
+ <type>plugin</type>
<name>twitterservice</name>
</conflicts>
</plugin_manifest>
diff --git a/mod/twitter_api/pages/twitter_api/interstitial.php b/mod/twitter_api/pages/twitter_api/interstitial.php
index d1f1ac20c..23b5069cb 100644
--- a/mod/twitter_api/pages/twitter_api/interstitial.php
+++ b/mod/twitter_api/pages/twitter_api/interstitial.php
@@ -8,9 +8,7 @@
$title = elgg_echo('twitter_api:interstitial:settings');
-$site = get_config('site');
-$content = elgg_echo('twitter_api:interstitial:description', array($site->name));
-$content .= elgg_view_form('twitter_api/interstitial_settings');
+$content = elgg_view_form('twitter_api/interstitial_settings');
$params = array(
'content' => $content,
diff --git a/mod/twitter_api/start.php b/mod/twitter_api/start.php
index e6221de6b..7318ac55d 100644
--- a/mod/twitter_api/start.php
+++ b/mod/twitter_api/start.php
@@ -35,8 +35,10 @@ function twitter_api_init() {
// register Walled Garden public pages
elgg_register_plugin_hook_handler('public_pages', 'walled_garden', 'twitter_api_public_pages');
- // push status messages to twitter
- elgg_register_plugin_hook_handler('status', 'user', 'twitter_api_tweet');
+ // push wire post messages to twitter
+ if (elgg_get_plugin_setting('wire_posts', 'twitter_api') == 'yes') {
+ elgg_register_plugin_hook_handler('status', 'user', 'twitter_api_tweet');
+ }
$actions = dirname(__FILE__) . '/actions/twitter_api';
elgg_register_action('twitter_api/interstitial_settings', "$actions/interstitial_settings.php", 'logged_in');
@@ -115,13 +117,6 @@ function twitter_api_tweet($hook, $type, $returnvalue, $params) {
// @todo - allow admin to select origins?
- // check admin settings
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
- if (!($consumer_key && $consumer_secret)) {
- return;
- }
-
// check user settings
$user_id = $params['user']->getGUID();
$access_key = elgg_get_plugin_user_setting('access_key', $user_id, 'twitter_api');
@@ -130,8 +125,11 @@ function twitter_api_tweet($hook, $type, $returnvalue, $params) {
return;
}
- // send tweet
- $api = new TwitterOAuth($consumer_key, $consumer_secret, $access_key, $access_secret);
+ $api = twitter_api_get_api_object($access_key, $access_secret);
+ if (!$api) {
+ return;
+ }
+
$api->post('statuses/update', array('status' => $params['message']));
}
@@ -143,12 +141,6 @@ function twitter_api_tweet($hook, $type, $returnvalue, $params) {
* @return array
*/
function twitter_api_fetch_tweets($user_guid, $options = array()) {
- // check admin settings
- $consumer_key = elgg_get_plugin_setting('consumer_key', 'twitter_api');
- $consumer_secret = elgg_get_plugin_setting('consumer_secret', 'twitter_api');
- if (!($consumer_key && $consumer_secret)) {
- return FALSE;
- }
// check user settings
$access_key = elgg_get_plugin_user_setting('access_key', $user_guid, 'twitter_api');
@@ -157,8 +149,11 @@ function twitter_api_fetch_tweets($user_guid, $options = array()) {
return FALSE;
}
- // fetch tweets
- $api = new TwitterOAuth($consumer_key, $consumer_secret, $access_key, $access_secret);
+ $api = twitter_api_get_api_object($access_key, $access_secret);
+ if (!$api) {
+ return FALSE;
+ }
+
return $api->get('statuses/user_timeline', $options);
}
diff --git a/mod/twitter_api/vendors/twitteroauth/OAuth.php b/mod/twitter_api/vendors/twitteroauth/OAuth.php
index e132a5bc8..e76304146 100644
--- a/mod/twitter_api/vendors/twitteroauth/OAuth.php
+++ b/mod/twitter_api/vendors/twitteroauth/OAuth.php
@@ -1,6 +1,12 @@
<?php
// vim: foldmethod=marker
+/* Generic exception class
+ */
+class OAuthException extends Exception {
+ // pass
+}
+
class OAuthConsumer {
public $key;
public $secret;
@@ -46,12 +52,56 @@ class OAuthToken {
}
}
-class twitterOAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod_HMAC_SHA1 {/*{{{*/
- function get_name() {/*{{{*/
+/**
+ * A class for implementing a Signature Method
+ * See section 9 ("Signing Requests") in the spec
+ */
+abstract class OAuthSignatureMethod {
+ /**
+ * Needs to return the name of the Signature Method (ie HMAC-SHA1)
+ * @return string
+ */
+ abstract public function get_name();
+
+ /**
+ * Build up the signature
+ * NOTE: The output of this function MUST NOT be urlencoded.
+ * the encoding is handled in OAuthRequest when the final
+ * request is serialized
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @return string
+ */
+ abstract public function build_signature($request, $consumer, $token);
+
+ /**
+ * Verifies that a given signature is correct
+ * @param OAuthRequest $request
+ * @param OAuthConsumer $consumer
+ * @param OAuthToken $token
+ * @param string $signature
+ * @return bool
+ */
+ public function check_signature($request, $consumer, $token, $signature) {
+ $built = $this->build_signature($request, $consumer, $token);
+ return $built == $signature;
+ }
+}
+
+/**
+ * The HMAC-SHA1 signature method uses the HMAC-SHA1 signature algorithm as defined in [RFC2104]
+ * where the Signature Base String is the text and the key is the concatenated values (each first
+ * encoded per Parameter Encoding) of the Consumer Secret and Token Secret, separated by an '&'
+ * character (ASCII code 38) even if empty.
+ * - Chapter 9.2 ("HMAC-SHA1")
+ */
+class OAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod {
+ function get_name() {
return "HMAC-SHA1";
- }/*}}}*/
+ }
- public function build_signature($request, $consumer, $token) {/*{{{*/
+ public function build_signature($request, $consumer, $token) {
$base_string = $request->get_signature_base_string();
$request->base_string = $base_string;
@@ -63,16 +113,111 @@ class twitterOAuthSignatureMethod_HMAC_SHA1 extends OAuthSignatureMethod_HMAC_SH
$key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
$key = implode('&', $key_parts);
- return base64_encode( hash_hmac('sha1', $base_string, $key, true));
- }/*}}}*/
+ return base64_encode(hash_hmac('sha1', $base_string, $key, true));
+ }
+}
- public function check_signature(&$request, $consumer, $token, $signature) {
- $built = $this->build_signature($request, $consumer, $token);
- return $built == $signature;
+/**
+ * The PLAINTEXT method does not provide any security protection and SHOULD only be used
+ * over a secure channel such as HTTPS. It does not use the Signature Base String.
+ * - Chapter 9.4 ("PLAINTEXT")
+ */
+class OAuthSignatureMethod_PLAINTEXT extends OAuthSignatureMethod {
+ public function get_name() {
+ return "PLAINTEXT";
+ }
+
+ /**
+ * oauth_signature is set to the concatenated encoded values of the Consumer Secret and
+ * Token Secret, separated by a '&' character (ASCII code 38), even if either secret is
+ * empty. The result MUST be encoded again.
+ * - Chapter 9.4.1 ("Generating Signatures")
+ *
+ * Please note that the second encoding MUST NOT happen in the SignatureMethod, as
+ * OAuthRequest handles this!
+ */
+ public function build_signature($request, $consumer, $token) {
+ $key_parts = array(
+ $consumer->secret,
+ ($token) ? $token->secret : ""
+ );
+
+ $key_parts = OAuthUtil::urlencode_rfc3986($key_parts);
+ $key = implode('&', $key_parts);
+ $request->base_string = $key;
+
+ return $key;
}
-}/*}}}*/
+}
+
+/**
+ * The RSA-SHA1 signature method uses the RSASSA-PKCS1-v1_5 signature algorithm as defined in
+ * [RFC3447] section 8.2 (more simply known as PKCS#1), using SHA-1 as the hash function for
+ * EMSA-PKCS1-v1_5. It is assumed that the Consumer has provided its RSA public key in a
+ * verified way to the Service Provider, in a manner which is beyond the scope of this
+ * specification.
+ * - Chapter 9.3 ("RSA-SHA1")
+ */
+abstract class OAuthSignatureMethod_RSA_SHA1 extends OAuthSignatureMethod {
+ public function get_name() {
+ return "RSA-SHA1";
+ }
+
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
+ // (1) do a lookup in a table of trusted certs keyed off of consumer
+ // (2) fetch via http using a url provided by the requester
+ // (3) some sort of specific discovery code based on request
+ //
+ // Either way should return a string representation of the certificate
+ protected abstract function fetch_public_cert(&$request);
+
+ // Up to the SP to implement this lookup of keys. Possible ideas are:
+ // (1) do a lookup in a table of trusted certs keyed off of consumer
+ //
+ // Either way should return a string representation of the certificate
+ protected abstract function fetch_private_cert(&$request);
+
+ public function build_signature($request, $consumer, $token) {
+ $base_string = $request->get_signature_base_string();
+ $request->base_string = $base_string;
+
+ // Fetch the private key cert based on the request
+ $cert = $this->fetch_private_cert($request);
+
+ // Pull the private key ID from the certificate
+ $privatekeyid = openssl_get_privatekey($cert);
+
+ // Sign using the key
+ $ok = openssl_sign($base_string, $signature, $privatekeyid);
+
+ // Release the key resource
+ openssl_free_key($privatekeyid);
+
+ return base64_encode($signature);
+ }
+
+ public function check_signature($request, $consumer, $token, $signature) {
+ $decoded_sig = base64_decode($signature);
+
+ $base_string = $request->get_signature_base_string();
+
+ // Fetch the public key cert based on the request
+ $cert = $this->fetch_public_cert($request);
+
+ // Pull the public key ID from the certificate
+ $publickeyid = openssl_get_publickey($cert);
-class twitterOAuthRequest extends OAuthRequest {
+ // Check the computed signature against the one passed in the query
+ $ok = openssl_verify($base_string, $decoded_sig, $publickeyid);
+
+ // Release the key resource
+ openssl_free_key($publickeyid);
+
+ return $ok == 1;
+ }
+}
+
+class OAuthRequest {
private $parameters;
private $http_method;
private $http_url;
@@ -138,7 +283,7 @@ class twitterOAuthRequest extends OAuthRequest {
}
- return new twitterOAuthRequest($http_method, $http_url, $parameters);
+ return new OAuthRequest($http_method, $http_url, $parameters);
}
/**
@@ -146,16 +291,16 @@ class twitterOAuthRequest extends OAuthRequest {
*/
public static function from_consumer_and_token($consumer, $token, $http_method, $http_url, $parameters=NULL) {
@$parameters or $parameters = array();
- $defaults = array("oauth_version" => twitterOAuthRequest::$version,
- "oauth_nonce" => twitterOAuthRequest::generate_nonce(),
- "oauth_timestamp" => twitterOAuthRequest::generate_timestamp(),
+ $defaults = array("oauth_version" => OAuthRequest::$version,
+ "oauth_nonce" => OAuthRequest::generate_nonce(),
+ "oauth_timestamp" => OAuthRequest::generate_timestamp(),
"oauth_consumer_key" => $consumer->key);
if ($token)
$defaults['oauth_token'] = $token->key;
$parameters = array_merge($defaults, $parameters);
- return new twitterOAuthRequest($http_method, $http_url, $parameters);
+ return new OAuthRequest($http_method, $http_url, $parameters);
}
public function set_parameter($name, $value, $allow_duplicates = true) {
@@ -333,6 +478,217 @@ class twitterOAuthRequest extends OAuthRequest {
}
}
+class OAuthServer {
+ protected $timestamp_threshold = 300; // in seconds, five minutes
+ protected $version = '1.0'; // hi blaine
+ protected $signature_methods = array();
+
+ protected $data_store;
+
+ function __construct($data_store) {
+ $this->data_store = $data_store;
+ }
+
+ public function add_signature_method($signature_method) {
+ $this->signature_methods[$signature_method->get_name()] =
+ $signature_method;
+ }
+
+ // high level functions
+
+ /**
+ * process a request_token request
+ * returns the request token on success
+ */
+ public function fetch_request_token(&$request) {
+ $this->get_version($request);
+
+ $consumer = $this->get_consumer($request);
+
+ // no token required for the initial token request
+ $token = NULL;
+
+ $this->check_signature($request, $consumer, $token);
+
+ // Rev A change
+ $callback = $request->get_parameter('oauth_callback');
+ $new_token = $this->data_store->new_request_token($consumer, $callback);
+
+ return $new_token;
+ }
+
+ /**
+ * process an access_token request
+ * returns the access token on success
+ */
+ public function fetch_access_token(&$request) {
+ $this->get_version($request);
+
+ $consumer = $this->get_consumer($request);
+
+ // requires authorized request token
+ $token = $this->get_token($request, $consumer, "request");
+
+ $this->check_signature($request, $consumer, $token);
+
+ // Rev A change
+ $verifier = $request->get_parameter('oauth_verifier');
+ $new_token = $this->data_store->new_access_token($token, $consumer, $verifier);
+
+ return $new_token;
+ }
+
+ /**
+ * verify an api call, checks all the parameters
+ */
+ public function verify_request(&$request) {
+ $this->get_version($request);
+ $consumer = $this->get_consumer($request);
+ $token = $this->get_token($request, $consumer, "access");
+ $this->check_signature($request, $consumer, $token);
+ return array($consumer, $token);
+ }
+
+ // Internals from here
+ /**
+ * version 1
+ */
+ private function get_version(&$request) {
+ $version = $request->get_parameter("oauth_version");
+ if (!$version) {
+ // Service Providers MUST assume the protocol version to be 1.0 if this parameter is not present.
+ // Chapter 7.0 ("Accessing Protected Ressources")
+ $version = '1.0';
+ }
+ if ($version !== $this->version) {
+ throw new OAuthException("OAuth version '$version' not supported");
+ }
+ return $version;
+ }
+
+ /**
+ * figure out the signature with some defaults
+ */
+ private function get_signature_method(&$request) {
+ $signature_method =
+ @$request->get_parameter("oauth_signature_method");
+
+ if (!$signature_method) {
+ // According to chapter 7 ("Accessing Protected Ressources") the signature-method
+ // parameter is required, and we can't just fallback to PLAINTEXT
+ throw new OAuthException('No signature method parameter. This parameter is required');
+ }
+
+ if (!in_array($signature_method,
+ array_keys($this->signature_methods))) {
+ throw new OAuthException(
+ "Signature method '$signature_method' not supported " .
+ "try one of the following: " .
+ implode(", ", array_keys($this->signature_methods))
+ );
+ }
+ return $this->signature_methods[$signature_method];
+ }
+
+ /**
+ * try to find the consumer for the provided request's consumer key
+ */
+ private function get_consumer(&$request) {
+ $consumer_key = @$request->get_parameter("oauth_consumer_key");
+ if (!$consumer_key) {
+ throw new OAuthException("Invalid consumer key");
+ }
+
+ $consumer = $this->data_store->lookup_consumer($consumer_key);
+ if (!$consumer) {
+ throw new OAuthException("Invalid consumer");
+ }
+
+ return $consumer;
+ }
+
+ /**
+ * try to find the token for the provided request's token key
+ */
+ private function get_token(&$request, $consumer, $token_type="access") {
+ $token_field = @$request->get_parameter('oauth_token');
+ $token = $this->data_store->lookup_token(
+ $consumer, $token_type, $token_field
+ );
+ if (!$token) {
+ throw new OAuthException("Invalid $token_type token: $token_field");
+ }
+ return $token;
+ }
+
+ /**
+ * all-in-one function to check the signature on a request
+ * should guess the signature method appropriately
+ */
+ private function check_signature(&$request, $consumer, $token) {
+ // this should probably be in a different method
+ $timestamp = @$request->get_parameter('oauth_timestamp');
+ $nonce = @$request->get_parameter('oauth_nonce');
+
+ $this->check_timestamp($timestamp);
+ $this->check_nonce($consumer, $token, $nonce, $timestamp);
+
+ $signature_method = $this->get_signature_method($request);
+
+ $signature = $request->get_parameter('oauth_signature');
+ $valid_sig = $signature_method->check_signature(
+ $request,
+ $consumer,
+ $token,
+ $signature
+ );
+
+ if (!$valid_sig) {
+ throw new OAuthException("Invalid signature");
+ }
+ }
+
+ /**
+ * check that the timestamp is new enough
+ */
+ private function check_timestamp($timestamp) {
+ if( ! $timestamp )
+ throw new OAuthException(
+ 'Missing timestamp parameter. The parameter is required'
+ );
+
+ // verify that timestamp is recentish
+ $now = time();
+ if (abs($now - $timestamp) > $this->timestamp_threshold) {
+ throw new OAuthException(
+ "Expired timestamp, yours $timestamp, ours $now"
+ );
+ }
+ }
+
+ /**
+ * check that the nonce is not repeated
+ */
+ private function check_nonce($consumer, $token, $nonce, $timestamp) {
+ if( ! $nonce )
+ throw new OAuthException(
+ 'Missing nonce parameter. The parameter is required'
+ );
+
+ // verify that the nonce is uniqueish
+ $found = $this->data_store->lookup_nonce(
+ $consumer,
+ $token,
+ $nonce,
+ $timestamp
+ );
+ if ($found) {
+ throw new OAuthException("Nonce already used: $nonce");
+ }
+ }
+
+}
+
class OAuthDataStore {
function lookup_consumer($consumer_key) {
// implement me
@@ -514,5 +870,3 @@ class OAuthUtil {
return implode('&', $pairs);
}
}
-
-?>
diff --git a/mod/twitter_api/vendors/twitteroauth/README b/mod/twitter_api/vendors/twitteroauth/README
index 33cb91f21..c9a17ce4b 100644
--- a/mod/twitter_api/vendors/twitteroauth/README
+++ b/mod/twitter_api/vendors/twitteroauth/README
@@ -1,7 +1,114 @@
-Abraham Williams | abraham@poseurte.ch | http://abrah.am | @abraham
+TwitterOAuth
+------------
-The first PHP library for working with Twitter's OAuth API.
+PHP library for working with Twitter's OAuth API.
-Documentation: http://wiki.github.com/abraham/twitteroauth/documentation
-Source: http://github.com/abraham/twitteroauth
-Twitter: http://apiwiki.twitter.com
+Flow Overview
+=============
+
+1. Build TwitterOAuth object using client credentials.
+2. Request temporary credentials from Twitter.
+3. Build authorize URL for Twitter.
+4. Redirect user to authorize URL.
+5. User authorizes access and returns from Twitter.
+6. Rebuild TwitterOAuth object with client credentials and temporary credentials.
+7. Get token credentials from Twitter.
+8. Rebuild TwitterOAuth object with client credentials and token credentials.
+9. Query Twitter API.
+
+Terminology
+===========
+
+The terminology has changed since 0.1.x to better match the draft-hammer-oauth IETF
+RFC. You can read that at http://tools.ietf.org/html/draft-hammer-oauth. Some of the
+terms will differ from those Twitter uses as well.
+
+client credentials - Consumer key/secret you get when registering an app with Twitter.
+temporary credentials - Previously known as the request token.
+token credentials - Previously known as the access token.
+
+Parameters
+==========
+
+There are a number of parameters you can modify after creating a TwitterOAuth object.
+
+Switch an existing TwitterOAuth install to use version 1.1 of the API.
+
+ $connection->$host = "https://api.twitter.com/1.1/";
+
+Custom useragent.
+
+ $connection->useragent = 'Custom useragent string';
+
+Verify Twitters SSL certificate.
+
+ $connection->ssl_verifypeer = TRUE;
+
+There are several more you can find in TwitterOAuth.php.
+
+Extended flow using example code
+================================
+
+To use TwitterOAuth with the Twitter API you need *TwitterOAuth.php*, *OAuth.php* and
+client credentials. You can get client credentials by registering your application at
+[dev.twitter.com/apps](https://dev.twitter.com/apps).
+
+Users start out on connect.php which displays the "Sign in with Twitter" image hyperlinked
+to redirect.php. This button should be displayed on your homepage in your login section. The
+client credentials are saved in config.php as `CONSUMER_KEY` and `CONSUMER_SECRET`. You can
+save a static callback URL in the app settings page, in the config file or use a dynamic
+callback URL later in step 2. In example use https://example.com/callback.php.
+
+1) When a user lands on redirect.php we build a new TwitterOAuth object using the client credentials.
+If you have your own configuration method feel free to use it instead of config.php.
+
+ $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET); // Use config.php client credentials
+ $connection = new TwitterOAuth('abc890', '123xyz');
+
+2) Using the built $connection object you will ask Twitter for temporary credentials. The `oauth_callback` value is required.
+
+ $temporary_credentials = $connection->getRequestToken(OAUTH_CALLBACK); // Use config.php callback URL.
+
+3) Now that we have temporary credentials the user has to go to Twitter and authorize the app
+to access and updates their data. You can also pass a second parameter of FALSE to not use [Sign
+in with Twitter](https://dev.twitter.com/docs/auth/sign-twitter).
+
+ $redirect_url = $connection->getAuthorizeURL($temporary_credentials); // Use Sign in with Twitter
+ $redirect_url = $connection->getAuthorizeURL($temporary_credentials, FALSE);
+
+4) You will now have a Twitter URL that you must send the user to.
+
+ https://api.twitter.com/oauth/authenticate?oauth_token=xyz123
+
+5) The user is now on twitter.com and may have to login. Once authenticated with Twitter they will
+will either have to click on allow/deny, or will be automatically redirected back to the callback.
+
+6) Now that the user has returned to callback.php and allowed access we need to build a new
+TwitterOAuth object using the temporary credentials.
+
+ $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $_SESSION['oauth_token'],
+ $_SESSION['oauth_token_secret']);
+
+7) Now we ask Twitter for long lasting token credentials. These are specific to the application
+and user and will act like password to make future requests. Normally the token credentials would
+get saved in your database but for this example we are just using sessions.
+
+ $token_credentials = $connection->getAccessToken($_REQUEST['oauth_verifier']);
+
+8) With the token credentials we build a new TwitterOAuth object.
+
+ $connection = new TwitterOAuth(CONSUMER_KEY, CONSUMER_SECRET, $token_credentials['oauth_token'],
+ $token_credentials['oauth_token_secret']);
+
+9) And finally we can make requests authenticated as the user. You can GET, POST, and DELETE API
+methods. Directly copy the path from the API documentation and add an array of any parameter
+you wish to include for the API method such as curser or in_reply_to_status_id.
+
+ $account = $connection->get('account/verify_credentials');
+ $status = $connection->post('statuses/update', array('status' => 'Text of status here', 'in_reply_to_status_id' => 123456));
+ $status = $connection->delete('statuses/destroy/12345');
+
+Contributors
+============
+
+* [Abraham Williams](https://twitter.com/abraham) - Main developer, current maintainer.
diff --git a/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php b/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php
index f36e6158d..4c2447c46 100644
--- a/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php
+++ b/mod/twitter_api/vendors/twitteroauth/twitterOAuth.php
@@ -57,7 +57,7 @@ class TwitterOAuth {
* construct TwitterOAuth object
*/
function __construct($consumer_key, $consumer_secret, $oauth_token = NULL, $oauth_token_secret = NULL) {
- $this->sha1_method = new twitterOAuthSignatureMethod_HMAC_SHA1();
+ $this->sha1_method = new OAuthSignatureMethod_HMAC_SHA1();
$this->consumer = new OAuthConsumer($consumer_key, $consumer_secret);
if (!empty($oauth_token) && !empty($oauth_token_secret)) {
$this->token = new OAuthConsumer($oauth_token, $oauth_token_secret);
@@ -72,11 +72,9 @@ class TwitterOAuth {
*
* @returns a key/value array containing oauth_token and oauth_token_secret
*/
- function getRequestToken($oauth_callback = NULL) {
+ function getRequestToken($oauth_callback) {
$parameters = array();
- if (!empty($oauth_callback)) {
- $parameters['oauth_callback'] = $oauth_callback;
- }
+ $parameters['oauth_callback'] = $oauth_callback;
$request = $this->oAuthRequest($this->requestTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
@@ -108,11 +106,9 @@ class TwitterOAuth {
* "user_id" => "9436992",
* "screen_name" => "abraham")
*/
- function getAccessToken($oauth_verifier = FALSE) {
+ function getAccessToken($oauth_verifier) {
$parameters = array();
- if (!empty($oauth_verifier)) {
- $parameters['oauth_verifier'] = $oauth_verifier;
- }
+ $parameters['oauth_verifier'] = $oauth_verifier;
$request = $this->oAuthRequest($this->accessTokenURL(), 'GET', $parameters);
$token = OAuthUtil::parse_parameters($request);
$this->token = new OAuthConsumer($token['oauth_token'], $token['oauth_token_secret']);
@@ -179,7 +175,7 @@ class TwitterOAuth {
if (strrpos($url, 'https://') !== 0 && strrpos($url, 'http://') !== 0) {
$url = "{$this->host}{$url}.{$this->format}";
}
- $request = twitterOAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
+ $request = OAuthRequest::from_consumer_and_token($this->consumer, $this->token, $method, $url, $parameters);
$request->sign_request($this->sha1_method, $this->consumer, $this->token);
switch ($method) {
case 'GET':
diff --git a/mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php b/mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php
index cad2be345..b4882bb7f 100644
--- a/mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php
+++ b/mod/twitter_api/views/default/forms/twitter_api/interstitial_settings.php
@@ -3,6 +3,11 @@
* Make the user set up some alternative ways to login.
*/
+echo '<div>';
+$site = get_config('site');
+echo elgg_echo('twitter_api:interstitial:description', array($site->name));
+echo '</div>';
+
$user = elgg_get_logged_in_user_entity();
if (elgg_is_sticky_form('twitter_api_interstitial')) {
@@ -51,7 +56,7 @@ echo elgg_view_module('info', $title, $body);
// buttons
echo elgg_view('input/submit', array(
- 'text' => elgg_echo('save')
+ 'value' => elgg_echo('save')
));
echo elgg_view('output/url', array(
diff --git a/mod/twitter_api/views/default/plugins/twitter_api/settings.php b/mod/twitter_api/views/default/plugins/twitter_api/settings.php
index 0b9afd4cf..3a3ec93a2 100644
--- a/mod/twitter_api/views/default/plugins/twitter_api/settings.php
+++ b/mod/twitter_api/views/default/plugins/twitter_api/settings.php
@@ -39,12 +39,27 @@ $new_users_with_twitter_view = elgg_view('input/dropdown', array(
'value' => $vars['entity']->new_users ? $vars['entity']->new_users : 'no',
));
+$post_to_twitter = '';
+if (elgg_is_active_plugin('thewire')) {
+ $post_to_twitter_string = elgg_echo('twitter_api:post_to_twitter');
+ $post_to_twitter_view = elgg_view('input/dropdown', array(
+ 'name' => 'params[wire_posts]',
+ 'options_values' => array(
+ 'yes' => elgg_echo('option:yes'),
+ 'no' => elgg_echo('option:no'),
+ ),
+ 'value' => $vars['entity']->wire_posts ? $vars['entity']->wire_posts : 'no',
+ ));
+ $post_to_twitter = "<div>$post_to_twitter_string $post_to_twitter_view</div>";
+}
+
$settings = <<<__HTML
<div class="elgg-content-thin mtm"><p>$instructions</p></div>
<div><label>$consumer_key_string</label><br /> $consumer_key_view</div>
<div><label>$consumer_secret_string</label><br /> $consumer_secret_view</div>
<div>$sign_on_with_twitter_string $sign_on_with_twitter_view</div>
<div>$new_users_with_twitter $new_users_with_twitter_view</div>
+$post_to_twitter
__HTML;
echo $settings;
diff --git a/pages/account/forgotten_password.php b/pages/account/forgotten_password.php
index bf6ef87e0..f464f98c9 100644
--- a/pages/account/forgotten_password.php
+++ b/pages/account/forgotten_password.php
@@ -17,6 +17,11 @@ $content .= elgg_view_form('user/requestnewpassword', array(
'class' => 'elgg-form-account',
));
-$body = elgg_view_layout("one_column", array('content' => $content));
-
-echo elgg_view_page($title, $body);
+if (elgg_get_config('walled_garden')) {
+ elgg_load_css('elgg.walled_garden');
+ $body = elgg_view_layout('walled_garden', array('content' => $content));
+ echo elgg_view_page($title, $body, 'walled_garden');
+} else {
+ $body = elgg_view_layout('one_column', array('content' => $content));
+ echo elgg_view_page($title, $body);
+}
diff --git a/pages/account/login.php b/pages/account/login.php
index 14f65cc3f..6aa3752d0 100644
--- a/pages/account/login.php
+++ b/pages/account/login.php
@@ -15,6 +15,14 @@ if (elgg_is_logged_in()) {
forward('');
}
-$login_box = elgg_view('core/account/login_box');
-$content = elgg_view_layout('one_column', array('content' => $login_box));
-echo elgg_view_page(elgg_echo('login'), $content);
+$title = elgg_echo('login');
+$content = elgg_view('core/account/login_box');
+
+if (elgg_get_config('walled_garden')) {
+ elgg_load_css('elgg.walled_garden');
+ $body = elgg_view_layout('walled_garden', array('content' => $content));
+ echo elgg_view_page($title, $body, 'walled_garden');
+} else {
+ $body = elgg_view_layout('one_column', array('content' => $content));
+ echo elgg_view_page($title, $body);
+}
diff --git a/pages/account/register.php b/pages/account/register.php
index cf18a635b..2fe8b74c0 100644
--- a/pages/account/register.php
+++ b/pages/account/register.php
@@ -48,6 +48,11 @@ $content .= elgg_view_form('register', $form_params, $body_params);
$content .= elgg_view('help/register');
-$body = elgg_view_layout("one_column", array('content' => $content));
-
-echo elgg_view_page($title, $body);
+if (elgg_get_config('walled_garden')) {
+ elgg_load_css('elgg.walled_garden');
+ $body = elgg_view_layout('walled_garden', array('content' => $content));
+ echo elgg_view_page($title, $body, 'walled_garden');
+} else {
+ $body = elgg_view_layout('one_column', array('content' => $content));
+ echo elgg_view_page($title, $body);
+}
diff --git a/pages/account/reset_password.php b/pages/account/reset_password.php
index 6515bfc5d..3ab8ccf3e 100644
--- a/pages/account/reset_password.php
+++ b/pages/account/reset_password.php
@@ -30,6 +30,11 @@ $form = elgg_view_form('user/passwordreset', array('class' => 'elgg-form-account
$title = elgg_echo('resetpassword');
$content = elgg_view_title(elgg_echo('resetpassword')) . $form;
-$body = elgg_view_layout('one_column', array('content' => $content));
-
-echo elgg_view_page($title, $body);
+if (elgg_get_config('walled_garden')) {
+ elgg_load_css('elgg.walled_garden');
+ $body = elgg_view_layout('walled_garden', array('content' => $content));
+ echo elgg_view_page($title, $body, 'walled_garden');
+} else {
+ $body = elgg_view_layout('one_column', array('content' => $content));
+ echo elgg_view_page($title, $body);
+}
diff --git a/pages/avatar/edit.php b/pages/avatar/edit.php
index c71633b8b..56aede887 100644
--- a/pages/avatar/edit.php
+++ b/pages/avatar/edit.php
@@ -11,6 +11,11 @@ elgg_set_context('profile_edit');
$title = elgg_echo('avatar:edit');
$entity = elgg_get_page_owner_entity();
+if (!elgg_instanceof($entity, 'user') || !$entity->canEdit()) {
+ register_error(elgg_echo('avatar:noaccess'));
+ forward(REFERER);
+}
+
$content = elgg_view('core/avatar/upload', array('entity' => $entity));
// only offer the crop view if an avatar has been uploaded
diff --git a/pages/river.php b/pages/river.php
index 0e1511334..801d9f664 100644
--- a/pages/river.php
+++ b/pages/river.php
@@ -49,6 +49,7 @@ $content = elgg_view('core/river/filter', array('selector' => $selector));
$sidebar = elgg_view('core/river/sidebar');
$params = array(
+ 'title' => $title,
'content' => $content . $activity,
'sidebar' => $sidebar,
'filter_context' => $page_filter,
diff --git a/pages/settings/account.php b/pages/settings/account.php
index 1bf71973b..962e1fc37 100644
--- a/pages/settings/account.php
+++ b/pages/settings/account.php
@@ -11,7 +11,8 @@ gatekeeper();
// Make sure we don't open a security hole ...
if ((!elgg_get_page_owner_entity()) || (!elgg_get_page_owner_entity()->canEdit())) {
- elgg_set_page_owner_guid(elgg_get_logged_in_user_guid());
+ register_error(elgg_echo('noaccess'));
+ forward('/');
}
$title = elgg_echo('usersettings:user');
diff --git a/pages/settings/statistics.php b/pages/settings/statistics.php
index 9df71ec5e..9dcc9211d 100644
--- a/pages/settings/statistics.php
+++ b/pages/settings/statistics.php
@@ -11,7 +11,8 @@ gatekeeper();
// Make sure we don't open a security hole ...
if ((!elgg_get_page_owner_entity()) || (!elgg_get_page_owner_entity()->canEdit())) {
- elgg_set_page_owner_guid(elgg_get_logged_in_user_guid());
+ register_error(elgg_echo('noaccess'));
+ forward('/');
}
$title = elgg_echo("usersettings:statistics");
diff --git a/pages/settings/tools.php b/pages/settings/tools.php
index daf381728..ed6b941c0 100644
--- a/pages/settings/tools.php
+++ b/pages/settings/tools.php
@@ -6,12 +6,13 @@
* @subpackage Core
*/
-// Make sure only valid users can see this
+// Only logged in users
gatekeeper();
// Make sure we don't open a security hole ...
if ((!elgg_get_page_owner_entity()) || (!elgg_get_page_owner_entity()->canEdit())) {
- elgg_set_page_owner_guid(elgg_get_logged_in_user_guid());
+ register_error(elgg_echo('noaccess'));
+ forward('/');
}
$title = elgg_echo("usersettings:plugins");
diff --git a/upgrade.php b/upgrade.php
index c5f158c61..d07b2a1da 100644
--- a/upgrade.php
+++ b/upgrade.php
@@ -46,7 +46,7 @@ if (get_input('upgrade') == 'upgrade') {
} else {
// if upgrading from < 1.8.0, check for the core view 'welcome' and bail if it's found.
- // see http://trac.elgg.org/ticket/3064
+ // see https://github.com/elgg/elgg/issues/3064
// we're not checking the view itself because it's likely themes will override this view.
// we're only concerned with core files.
$welcome = dirname(__FILE__) . '/views/default/welcome.php';
diff --git a/version.php b/version.php
index 07a08ab4a..a7a4776a4 100644
--- a/version.php
+++ b/version.php
@@ -5,13 +5,13 @@
* This is compared against the values stored in the database to determine
* whether upgrades should be performed.
*
- * @package Elgg
+ * @package Elgg
* @subpackage Core
*/
// YYYYMMDD = Elgg Date
// XX = Interim incrementer
-$version = 2013012900;
+$version = 2014110100;
// Human-friendly version name
-$release = '1.8.13';
+$release = '1.8.18';
diff --git a/views/default/admin/settings/advanced/site_secret.php b/views/default/admin/settings/advanced/site_secret.php
new file mode 100644
index 000000000..e70ac7ab6
--- /dev/null
+++ b/views/default/admin/settings/advanced/site_secret.php
@@ -0,0 +1,11 @@
+<?php
+/**
+ * Elgg administration site secret settings
+ *
+ * @package Elgg
+ * @subpackage Core
+ */
+
+echo elgg_view_form('admin/site/regenerate_secret', array(), array(
+ 'strength' => _elgg_get_site_secret_strength(),
+));
diff --git a/views/default/css/admin.php b/views/default/css/admin.php
index 059e51dd6..c435621b2 100644
--- a/views/default/css/admin.php
+++ b/views/default/css/admin.php
@@ -446,7 +446,8 @@ input {
.elgg-input-text,
.elgg-input-tags,
.elgg-input-url,
-.elgg-input-plaintext {
+.elgg-input-plaintext,
+.elgg-input-longtext {
width: 98%;
}
textarea {
@@ -624,6 +625,10 @@ a.elgg-button {
text-decoration: none;
color: #4690D6;
}
+.ui-autocomplete a.ui-state-hover {
+ background-color: #eee;
+ display: block;
+}
/* ***************************************
USER PICKER
@@ -999,7 +1004,7 @@ a.elgg-button {
ENTITY MENU
*************************************** */
<?php // height depends on line height/font size ?>
-.elgg-menu-entity, elgg-menu-annotation {
+.elgg-menu-entity, .elgg-menu-annotation {
float: right;
margin-left: 15px;
font-size: 90%;
@@ -1539,6 +1544,26 @@ table.mceLayout {
}
/* ***************************************
+ SITE SECRET
+*************************************** */
+.elgg-form-admin-site-regenerate-secret table {
+ width: 60%;
+ margin: 1em auto;
+}
+td.elgg-strength-strong,
+td.elgg-strength-strong h4 {
+ background: #DFF0D8; color: #468847;
+}
+td.elgg-strength-moderate,
+td.elgg-strength-moderate h4 {
+ background: #FCF8E3; color: #C09853;
+}
+td.elgg-strength-weak,
+td.elgg-strength-weak h4 {
+ background: #F2DEDE; color: #B94A48;
+}
+
+/* ***************************************
HELPERS
*************************************** */
.hidden {
diff --git a/views/default/css/elements/forms.php b/views/default/css/elements/forms.php
index 2fc156447..068cc8fd6 100644
--- a/views/default/css/elements/forms.php
+++ b/views/default/css/elements/forms.php
@@ -69,6 +69,7 @@ input[type="radio"] {
margin:0 3px 0 0;
padding:0;
border:none;
+ border-radius:0;
width:auto;
}
.elgg-input-checkboxes.elgg-horizontal li,
@@ -267,6 +268,10 @@ input[type="radio"] {
text-decoration: none;
color: #4690D6;
}
+.ui-autocomplete a.ui-state-hover {
+ background-color: #eee;
+ display: block;
+}
/* ***************************************
USER PICKER
diff --git a/views/default/css/elements/navigation.php b/views/default/css/elements/navigation.php
index 62f370069..6b29e4c19 100644
--- a/views/default/css/elements/navigation.php
+++ b/views/default/css/elements/navigation.php
@@ -16,7 +16,7 @@
text-align: center;
}
.elgg-pagination li {
- display: inline;
+ display: inline-block;
margin: 0 6px 0 0;
text-align: center;
}
@@ -24,7 +24,8 @@
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
-
+
+ display: block;
padding: 2px 6px;
color: #4690d6;
border: 1px solid #4690d6;
@@ -449,7 +450,7 @@
ENTITY AND ANNOTATION
*************************************** */
<?php // height depends on line height/font size ?>
-.elgg-menu-entity, elgg-menu-annotation {
+.elgg-menu-entity, .elgg-menu-annotation {
float: right;
margin-left: 15px;
font-size: 90%;
diff --git a/views/default/css/ie.php b/views/default/css/ie.php
index 4bddd4d55..34ececa89 100644
--- a/views/default/css/ie.php
+++ b/views/default/css/ie.php
@@ -6,3 +6,11 @@
.elgg-avatar {
display: block;
}
+
+/* ie8 adds space to the top of .elgg-gallery which causes jumpiness if this is display: block; */
+.elgg-gallery .elgg-avatar > a > img {
+ display: inline-block;
+}
+.elgg-gallery .elgg-avatar > .elgg-icon-hover-menu {
+ bottom: 4px;
+}
diff --git a/views/default/css/ie7.php b/views/default/css/ie7.php
index db0125b4b..90274797d 100644
--- a/views/default/css/ie7.php
+++ b/views/default/css/ie7.php
@@ -24,6 +24,7 @@
.elgg-menu-footer > li > a,
.elgg-menu-footer li,
.elgg-menu-general > li > a,
+.elgg-pagination li,
.elgg-menu-general li {
display: inline;
}
@@ -62,4 +63,13 @@
.elgg-module-walledgarden > .elgg-head,
.elgg-module-walledgarden > .elgg-foot {
width: 530px;
-} \ No newline at end of file
+}
+
+input, textarea {
+ width: 98%;
+}
+
+.elgg-tag a {
+ /* IE7 had a weird wrapping issue for tags */
+ word-wrap: normal;
+}
diff --git a/views/default/forms/admin/site/regenerate_secret.php b/views/default/forms/admin/site/regenerate_secret.php
new file mode 100644
index 000000000..af269b801
--- /dev/null
+++ b/views/default/forms/admin/site/regenerate_secret.php
@@ -0,0 +1,24 @@
+<?php
+
+$strength = $vars['strength'];
+
+?>
+<p><?php echo elgg_echo('admin:site:secret:intro'); ?></p>
+
+<table class="elgg-table">
+ <tr>
+ <th><?php echo elgg_echo('site_secret:current_strength'); ?></th>
+ <td class="elgg-strength-<?php echo $strength; ?>">
+ <h4><?php echo elgg_echo("site_secret:strength:$strength"); ?></h4>
+ <div><?php echo elgg_echo("site_secret:strength_msg:$strength"); ?></div>
+ </td>
+ </tr>
+</table>
+
+<div class="elgg-foot">
+ <?php echo elgg_view('input/submit', array(
+ 'value' => elgg_echo('admin:site:secret:regenerate'),
+ 'class' => 'elgg-requires-confirmation elgg-button elgg-button-submit',
+ )); ?>
+ <p class="elgg-text-help mts"><?php echo elgg_echo('admin:site:secret:regenerate:help'); ?></p>
+</div>
diff --git a/views/default/forms/plugins/settings/save.php b/views/default/forms/plugins/settings/save.php
index dc7b2fef7..116529905 100644
--- a/views/default/forms/plugins/settings/save.php
+++ b/views/default/forms/plugins/settings/save.php
@@ -17,11 +17,11 @@ if ($type != 'user') {
$type = '';
}
-if (elgg_view_exists("{$type}settings/$plugin_id/edit")) {
+if (elgg_view_exists("plugins/$plugin_id/{$type}settings")) {
+ echo elgg_view("plugins/$plugin_id/{$type}settings", $vars);
+} elseif (elgg_view_exists("{$type}settings/$plugin_id/edit")) {
elgg_deprecated_notice("{$type}settings/$plugin_id/edit was deprecated in favor of plugins/$plugin_id/{$type}settings", 1.8);
echo elgg_view("{$type}settings/$plugin_id/edit", $vars);
-} else {
- echo elgg_view("plugins/$plugin_id/{$type}settings", $vars);
}
echo '<div class="elgg-foot">';
diff --git a/views/default/forms/profile/edit.php b/views/default/forms/profile/edit.php
index 9538b779e..cb0a37ca4 100644
--- a/views/default/forms/profile/edit.php
+++ b/views/default/forms/profile/edit.php
@@ -13,6 +13,8 @@
</div>
<?php
+$sticky_values = elgg_get_sticky_values('profile:edit');
+
$profile_fields = elgg_get_config('profile_fields');
if (is_array($profile_fields) && count($profile_fields) > 0) {
foreach ($profile_fields as $shortname => $valtype) {
@@ -40,6 +42,14 @@ if (is_array($profile_fields) && count($profile_fields) > 0) {
$access_id = ACCESS_DEFAULT;
}
+ // sticky form values take precedence over saved ones
+ if (isset($sticky_values[$shortname])) {
+ $value = $sticky_values[$shortname];
+ }
+ if (isset($sticky_values['accesslevel'][$shortname])) {
+ $access_id = $sticky_values['accesslevel'][$shortname];
+ }
+
?>
<div>
<label><?php echo elgg_echo("profile:{$shortname}") ?></label>
@@ -59,6 +69,9 @@ if (is_array($profile_fields) && count($profile_fields) > 0) {
<?php
}
}
+
+elgg_clear_sticky_form('profile:edit');
+
?>
<div class="elgg-foot">
<?php
diff --git a/views/default/icon/default.php b/views/default/icon/default.php
index 087c7eae9..7f13a1189 100644
--- a/views/default/icon/default.php
+++ b/views/default/icon/default.php
@@ -37,13 +37,31 @@ if (isset($vars['href'])) {
$icon_sizes = elgg_get_config('icon_sizes');
$size = $vars['size'];
-$img = elgg_view('output/img', array(
+if (!isset($vars['width'])) {
+ $vars['width'] = $size != 'master' ? $icon_sizes[$size]['w'] : null;
+}
+if (!isset($vars['height'])) {
+ $vars['height'] = $size != 'master' ? $icon_sizes[$size]['h'] : null;
+}
+
+$img_params = array(
'src' => $entity->getIconURL($vars['size']),
- 'alt' => $title,
- 'class' => $class,
- 'width' => $size != 'master' ? $icon_sizes[$size]['w'] : NULL,
- 'height' => $size != 'master' ? $icon_sizes[$size]['h'] : NULL,
-));
+ 'alt' => $title,
+);
+
+if (!empty($class)) {
+ $img_params['class'] = $class;
+}
+
+if (!empty($vars['width'])) {
+ $img_params['width'] = $vars['width'];
+}
+
+if (!empty($vars['height'])) {
+ $img_params['height'] = $vars['height'];
+}
+
+$img = elgg_view('output/img', $img_params);
if ($url) {
$params = array(
diff --git a/views/default/input/pulldown.php b/views/default/input/pulldown.php
index 705329691..fc0595300 100644
--- a/views/default/input/pulldown.php
+++ b/views/default/input/pulldown.php
@@ -5,5 +5,5 @@
* @deprecated 1.8
*/
-elgg_deprecated_notice("input/pulldown was deprecated by input/dropdown", 1.8);
+elgg_deprecated_notice("input/pulldown was deprecated by input/dropdown", 1.8, 2);
echo elgg_view('input/dropdown', $vars);
diff --git a/views/default/input/userpicker.php b/views/default/input/userpicker.php
index 91a397e37..8b64d7df5 100644
--- a/views/default/input/userpicker.php
+++ b/views/default/input/userpicker.php
@@ -63,11 +63,13 @@ foreach ($vars['value'] as $user_id) {
?>
<div class="elgg-user-picker">
<input type="text" class="elgg-input-user-picker" size="30"/>
- <input type="checkbox" name="match_on" value="true" />
- <label><?php echo elgg_echo('userpicker:only_friends'); ?></label>
+ <label>
+ <input type="checkbox" name="match_on" value="true" />
+ <?php echo elgg_echo('userpicker:only_friends'); ?>
+ </label>
<ul class="elgg-user-picker-list"><?php echo $user_list; ?></ul>
</div>
<script type="text/javascript">
// @todo grab the values in the init function rather than using inline JS
elgg.userpicker.userList = <?php echo $json_values ?>;
-</script> \ No newline at end of file
+</script>
diff --git a/views/default/js/elgg.php b/views/default/js/elgg.php
index 6fe03484d..c3b56e398 100644
--- a/views/default/js/elgg.php
+++ b/views/default/js/elgg.php
@@ -43,7 +43,7 @@ $libs = array(
foreach ($libs as $file) {
include("{$CONFIG->path}js/lib/$file.js");
- // putting a new line between the files to address http://trac.elgg.org/ticket/3081
+ // putting a new line between the files to address https://github.com/elgg/elgg/issues/3081
echo "\n";
}
diff --git a/views/default/js/languages.php b/views/default/js/languages.php
index c51d7bcb2..fcf903d4b 100644
--- a/views/default/js/languages.php
+++ b/views/default/js/languages.php
@@ -1,15 +1,33 @@
<?php
/**
* @uses $vars['language']
+ * @uses $vars['lc'] if present, client will be sent long expires headers
*/
-global $CONFIG;
$language = $vars['language'];
+$lastcache = elgg_extract('lc', $vars, 0);
-$translations = $CONFIG->translations['en'];
+// @todo add server-side caching
+if ($lastcache) {
+ // we're relying on lastcache changes to predict language changes
+ $etag = '"' . md5("$language|$lastcache") . '"';
+
+ header('Expires: ' . gmdate('D, d M Y H:i:s \G\M\T', strtotime("+6 months")), true);
+ header("Pragma: public", true);
+ header("Cache-Control: public", true);
+ header("ETag: $etag");
+
+ if (isset($_SERVER['HTTP_IF_NONE_MATCH']) && trim($_SERVER['HTTP_IF_NONE_MATCH']) === $etag) {
+ header("HTTP/1.1 304 Not Modified");
+ exit;
+ }
+}
+
+$all_translations = elgg_get_config('translations');
+$translations = $all_translations['en'];
if ($language != 'en') {
- $translations = array_merge($translations, $CONFIG->translations[$language]);
+ $translations = array_merge($translations, $all_translations[$language]);
}
echo json_encode($translations); \ No newline at end of file
diff --git a/views/default/js/walled_garden.php b/views/default/js/walled_garden.php
index 7a482fe23..e228df507 100644
--- a/views/default/js/walled_garden.php
+++ b/views/default/js/walled_garden.php
@@ -5,12 +5,11 @@
* @since 1.8
*/
-// note that this assumes the button view is not using single quotes
$cancel_button = elgg_view('input/button', array(
'value' => elgg_echo('cancel'),
'class' => 'elgg-button-cancel mlm',
));
-$cancel_button = trim($cancel_button);
+$cancel_button = json_encode($cancel_button);
if (0) { ?><script><?php }
?>
@@ -23,10 +22,11 @@ elgg.walled_garden.init = function () {
$('.registration_link').click(elgg.walled_garden.load('register'));
$('input.elgg-button-cancel').live('click', function(event) {
- if ($('.elgg-walledgarden-single').is(':visible')) {
+ var $wgs = $('.elgg-walledgarden-single');
+ if ($wgs.is(':visible')) {
$('.elgg-walledgarden-double').fadeToggle();
- $('.elgg-walledgarden-single').fadeToggle();
- $('.elgg-walledgarden-single').remove();
+ $wgs.fadeToggle();
+ $wgs.remove();
}
event.preventDefault();
});
@@ -42,12 +42,22 @@ elgg.walled_garden.load = function(view) {
return function(event) {
var id = '#elgg-walledgarden-' + view;
id = id.replace('_', '-');
+ //@todo display some visual element that indicates that loading of content is running
elgg.get('walled_garden/' + view, {
'success' : function(data) {
- $('.elgg-body-walledgarden').append(data);
- $(id).find('input.elgg-button-submit').after('<?php echo $cancel_button; ?>');
- $('#elgg-walledgarden-login').fadeToggle();
- $(id).fadeToggle();
+ var $wg = $('.elgg-body-walledgarden');
+ $wg.append(data);
+ $(id).find('input.elgg-button-submit').after(<?php echo $cancel_button; ?>);
+
+ if (view == 'register' && $wg.hasClass('hidden')) {
+ // this was a failed register, display the register form ASAP
+ $('#elgg-walledgarden-login').toggle(false);
+ $(id).toggle();
+ $wg.removeClass('hidden');
+ } else {
+ $('#elgg-walledgarden-login').fadeToggle();
+ $(id).fadeToggle();
+ }
}
});
event.preventDefault();
diff --git a/views/default/object/default.php b/views/default/object/default.php
index 110648304..70e098742 100644
--- a/views/default/object/default.php
+++ b/views/default/object/default.php
@@ -41,7 +41,6 @@ $params = array(
'title' => $title,
'metadata' => $metadata,
'subtitle' => $subtitle,
- 'tags' => $vars['entity']->tags,
);
$params = $params + $vars;
$body = elgg_view('object/elements/summary', $params);
diff --git a/views/default/object/elements/full.php b/views/default/object/elements/full.php
index 9b89f9706..b4634fe7e 100644
--- a/views/default/object/elements/full.php
+++ b/views/default/object/elements/full.php
@@ -22,9 +22,9 @@ $summary = elgg_extract('summary', $vars);
$body = elgg_extract('body', $vars);
$class = elgg_extract('class', $vars);
if ($class) {
- $class = "elgg-content $class";
+ $class = "elgg-content clearfix $class";
} else {
- $class = "elgg-content";
+ $class = "elgg-content clearfix";
}
$header = elgg_view_image_block($icon, $summary);
diff --git a/views/default/object/elements/summary.php b/views/default/object/elements/summary.php
index c0f3ad340..63ab8f816 100644
--- a/views/default/object/elements/summary.php
+++ b/views/default/object/elements/summary.php
@@ -27,7 +27,7 @@ if ($title_link === '') {
$text = $entity->name;
}
$params = array(
- 'text' => $text,
+ 'text' => elgg_get_excerpt($text, 100),
'href' => $entity->getURL(),
'is_trusted' => true,
);
diff --git a/views/default/output/access.php b/views/default/output/access.php
index 91c5c721e..5c8d62c4d 100644
--- a/views/default/output/access.php
+++ b/views/default/output/access.php
@@ -11,7 +11,7 @@ if (isset($vars['entity']) && elgg_instanceof($vars['entity'])) {
$access_id = $vars['entity']->access_id;
$access_class = 'elgg-access';
$access_id_string = get_readable_access_level($access_id);
- $access_id_string = htmlentities($access_id_string, ENT_QUOTES, 'UTF-8');
+ $access_id_string = htmlspecialchars($access_id_string, ENT_QUOTES, 'UTF-8', false);
// if within a group or shared access collection display group name and open/closed membership status
// @todo have a better way to do this instead of checking against subtype / class.
diff --git a/views/default/output/tag.php b/views/default/output/tag.php
index 3c002a31b..6bd9a72a7 100644
--- a/views/default/output/tag.php
+++ b/views/default/output/tag.php
@@ -8,24 +8,25 @@
*
*/
+if (!empty($vars['type'])) {
+ $type = "&type=" . rawurlencode($vars['type']);
+} else {
+ $type = "";
+}
if (!empty($vars['subtype'])) {
- $subtype = "&subtype=" . urlencode($vars['subtype']);
+ $subtype = "&subtype=" . rawurlencode($vars['subtype']);
} else {
$subtype = "";
}
if (!empty($vars['object'])) {
- $object = "&object=" . urlencode($vars['object']);
+ $object = "&object=" . rawurlencode($vars['object']);
} else {
$object = "";
}
if (isset($vars['value'])) {
- if (!empty($vars['type'])) {
- $type = "&type={$vars['type']}";
- } else {
- $type = "";
- }
- $url = elgg_get_site_url() . 'search?q=' . urlencode($vars['value']) . "&search_type=tags{$type}{$subtype}{$object}";
+ $url = elgg_get_site_url() . 'search?q=' . rawurlencode($vars['value']) . "&search_type=tags{$type}{$subtype}{$object}";
+ $vars['value'] = htmlspecialchars($vars['value'], ENT_QUOTES, 'UTF-8', false);
echo elgg_view('output/url', array(
'href' => $url,
'text' => $vars['value'],
diff --git a/views/default/output/tagcloud.php b/views/default/output/tagcloud.php
index a212becd8..2fbf1cd0a 100644
--- a/views/default/output/tagcloud.php
+++ b/views/default/output/tagcloud.php
@@ -39,6 +39,8 @@ if (!empty($vars['tagcloud']) && is_array($vars['tagcloud'])) {
$cloud = '';
foreach ($vars['tagcloud'] as $tag) {
+ $tag->tag = htmlspecialchars($tag->tag, ENT_QUOTES, 'UTF-8', false);
+
if ($cloud != '') {
$cloud .= ', ';
}
diff --git a/views/default/output/tags.php b/views/default/output/tags.php
index 3082dd41e..db096a3be 100644
--- a/views/default/output/tags.php
+++ b/views/default/output/tags.php
@@ -17,13 +17,18 @@ if (isset($vars['entity'])) {
unset($vars['entity']);
}
+if (!empty($vars['type'])) {
+ $type = "&type=" . rawurlencode($vars['type']);
+} else {
+ $type = "";
+}
if (!empty($vars['subtype'])) {
- $subtype = "&subtype=" . urlencode($vars['subtype']);
+ $subtype = "&subtype=" . rawurlencode($vars['subtype']);
} else {
$subtype = "";
}
if (!empty($vars['object'])) {
- $object = "&object=" . urlencode($vars['object']);
+ $object = "&object=" . rawurlencode($vars['object']);
} else {
$object = "";
}
@@ -53,15 +58,11 @@ if (!empty($vars['tags'])) {
$icon_class = elgg_extract('icon_class', $vars);
$list_items = '<li>' . elgg_view_icon('tag', $icon_class) . '</li>';
-
+
foreach($vars['tags'] as $tag) {
- if (!empty($vars['type'])) {
- $type = "&type={$vars['type']}";
- } else {
- $type = "";
- }
- $url = elgg_get_site_url() . 'search?q=' . urlencode($tag) . "&search_type=tags{$type}{$subtype}{$object}";
+ $url = elgg_get_site_url() . 'search?q=' . rawurlencode($tag) . "&search_type=tags{$type}{$subtype}{$object}";
if (is_string($tag)) {
+ $tag = htmlspecialchars($tag, ENT_QUOTES, 'UTF-8', false);
$list_items .= "<li class=\"$item_class\">";
$list_items .= elgg_view('output/url', array('href' => $url, 'text' => $tag, 'rel' => 'tag'));
$list_items .= '</li>';
diff --git a/views/default/page/walled_garden.php b/views/default/page/walled_garden.php
index ff8e317c7..b280cf6b2 100644
--- a/views/default/page/walled_garden.php
+++ b/views/default/page/walled_garden.php
@@ -5,6 +5,12 @@
* Used for the walled garden index page
*/
+$is_sticky_register = elgg_is_sticky_form('register');
+$wg_body_class = 'elgg-body-walledgarden';
+if ($is_sticky_register) {
+ $wg_body_class .= ' hidden';
+}
+
// Set the content type
header("Content-type: text/html; charset=UTF-8");
?>
@@ -18,10 +24,17 @@ header("Content-type: text/html; charset=UTF-8");
<div class="elgg-page-messages">
<?php echo elgg_view('page/elements/messages', array('object' => $vars['sysmessages'])); ?>
</div>
- <div class="elgg-body-walledgarden">
+ <div class="<?php echo $wg_body_class; ?>">
<?php echo $vars['body']; ?>
</div>
</div>
+<?php if ($is_sticky_register): ?>
+<script type="text/javascript">
+elgg.register_hook_handler('init', 'system', function() {
+ $('.registration_link').trigger('click');
+});
+</script>
+<?php endif; ?>
<?php echo elgg_view('page/elements/foot'); ?>
</body>
</html> \ No newline at end of file
diff --git a/views/default/river/elements/summary.php b/views/default/river/elements/summary.php
index 416bc708b..d7bde51dd 100644
--- a/views/default/river/elements/summary.php
+++ b/views/default/river/elements/summary.php
@@ -18,9 +18,10 @@ $subject_link = elgg_view('output/url', array(
'is_trusted' => true,
));
+$object_text = $object->title ? $object->title : $object->name;
$object_link = elgg_view('output/url', array(
'href' => $object->getURL(),
- 'text' => $object->title ? $object->title : $object->name,
+ 'text' => elgg_get_excerpt($object_text, 100),
'class' => 'elgg-river-object',
'is_trusted' => true,
));