diff options
Diffstat (limited to 'mod')
250 files changed, 13662 insertions, 17666 deletions
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 '<', $s, $e, $a, '>'; } 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, '>'); - i = i.replace(/</g, '<'); - i = i.replace(/"/g, '"'); 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, '>'); - i = i.replace(/</g, '<'); - i = i.replace(/"/g, '"'); 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,"&").replace(d,"<").replace(e,">").replace(f,"<span style=\"color: #dcdcdc;\">¬</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 »</span></a> <span class="help" title="tags estimated as half of total > and < 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> </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 » </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 » </span></a> <div id="settingF" style="display: none;">', str_replace(array(' ', "\t", ' '), array(' ', ' ', ' '), 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 »</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 »</span></a> <span class="help" title="tags estimated as half of total > and < 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> </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 » </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 » </span></a> <div id="settingF" style="display: none;">$config: ', str_replace(array(' ', "\t", ' '), array(' ', ' ', ' '), nl2br(htmlspecialchars(print_r($GLOBALS['hlcfg']['config'], true)))), '<br />$spec: ', str_replace(array(' ', "\t", ' '), array(' ', ' ', ' '), 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 »</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 »</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 »</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 »</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 »</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 »</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 »</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 »</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>  About htmLawed</a></span><br /> -  <span class="toc-item"><a href="#s1.1"><span class="item-no">1.1</span>  Example uses</a></span><br /> -  <span class="toc-item"><a href="#s1.2"><span class="item-no">1.2</span>  Features</a></span><br /> -  <span class="toc-item"><a href="#s1.3"><span class="item-no">1.3</span>  History</a></span><br /> -  <span class="toc-item"><a href="#s1.4"><span class="item-no">1.4</span>  License & copyright</a></span><br /> -  <span class="toc-item"><a href="#s1.5"><span class="item-no">1.5</span>  Terms used here</a></span><br /> -<span class="toc-item"><a href="#s2"><span class="item-no">2</span>  Usage</a></span><br /> -  <span class="toc-item"><a href="#s2.1"><span class="item-no">2.1</span>  Simple</a></span><br /> -  <span class="toc-item"><a href="#s2.2"><span class="item-no">2.2</span>  Configuring htmLawed using the <span class="term">$config</span> parameter</a></span><br /> -  <span class="toc-item"><a href="#s2.3"><span class="item-no">2.3</span>  Extra HTML specifications using the <span class="term">$spec</span> parameter</a></span><br /> -  <span class="toc-item"><a href="#s2.4"><span class="item-no">2.4</span>  Performance time & memory usage</a></span><br /> -  <span class="toc-item"><a href="#s2.5"><span class="item-no">2.5</span>  Some security risks to keep in mind</a></span><br /> -  <span class="toc-item"><a href="#s2.6"><span class="item-no">2.6</span>  Use without modifying old <span class="term">kses()</span> code</a></span><br /> -  <span class="toc-item"><a href="#s2.7"><span class="item-no">2.7</span>  Tolerance for ill-written HTML</a></span><br /> -  <span class="toc-item"><a href="#s2.8"><span class="item-no">2.8</span>  Limitations & work-arounds</a></span><br /> -  <span class="toc-item"><a href="#s2.9"><span class="item-no">2.9</span>  Examples of usage</a></span><br /> -<span class="toc-item"><a href="#s3"><span class="item-no">3</span>  Details</a></span><br /> -  <span class="toc-item"><a href="#s3.1"><span class="item-no">3.1</span>  Invalid/dangerous characters</a></span><br /> -  <span class="toc-item"><a href="#s3.2"><span class="item-no">3.2</span>  Character references/entities</a></span><br /> -  <span class="toc-item"><a href="#s3.3"><span class="item-no">3.3</span>  HTML elements</a></span><br /> -    <span class="toc-item"><a href="#s3.3.1"><span class="item-no">3.3.1</span>  HTML comments and <span class="term">CDATA</span> sections</a></span><br /> -    <span class="toc-item"><a href="#s3.3.2"><span class="item-no">3.3.2</span>  Tag-transformation for better XHTML-Strict</a></span><br /> -    <span class="toc-item"><a href="#s3.3.3"><span class="item-no">3.3.3</span>  Tag balancing and proper nesting</a></span><br /> -    <span class="toc-item"><a href="#s3.3.4"><span class="item-no">3.3.4</span>  Elements requiring child elements</a></span><br /> -    <span class="toc-item"><a href="#s3.3.5"><span class="item-no">3.3.5</span>  Beautify or compact HTML</a></span><br /> -  <span class="toc-item"><a href="#s3.4"><span class="item-no">3.4</span>  Attributes</a></span><br /> -    <span class="toc-item"><a href="#s3.4.1"><span class="item-no">3.4.1</span>  Auto-addition of XHTML-required attributes</a></span><br /> -    <span class="toc-item"><a href="#s3.4.2"><span class="item-no">3.4.2</span>  Duplicate/invalid <span class="term">id</span> values</a></span><br /> -    <span class="toc-item"><a href="#s3.4.3"><span class="item-no">3.4.3</span>  URL schemes (protocols) and scripts in attribute values</a></span><br /> -    <span class="toc-item"><a href="#s3.4.4"><span class="item-no">3.4.4</span>  Absolute & relative URLs</a></span><br /> -    <span class="toc-item"><a href="#s3.4.5"><span class="item-no">3.4.5</span>  Lower-cased, standard attribute values</a></span><br /> -    <span class="toc-item"><a href="#s3.4.6"><span class="item-no">3.4.6</span>  Transformation of deprecated attributes</a></span><br /> -    <span class="toc-item"><a href="#s3.4.7"><span class="item-no">3.4.7</span>  Anti-spam & <span class="term">href</span></a></span><br /> -    <span class="toc-item"><a href="#s3.4.8"><span class="item-no">3.4.8</span>  Inline style properties</a></span><br /> -    <span class="toc-item"><a href="#s3.4.9"><span class="item-no">3.4.9</span>  Hook function for tag content</a></span><br /> -  <span class="toc-item"><a href="#s3.5"><span class="item-no">3.5</span>  Simple configuration directive for most valid XHTML</a></span><br /> -  <span class="toc-item"><a href="#s3.6"><span class="item-no">3.6</span>  Simple configuration directive for most <em>safe</em> HTML</a></span><br /> -  <span class="toc-item"><a href="#s3.7"><span class="item-no">3.7</span>  Using a hook function</a></span><br /> -  <span class="toc-item"><a href="#s3.8"><span class="item-no">3.8</span>  Obtaining <em>finalized</em> parameter values</a></span><br /> -  <span class="toc-item"><a href="#s3.9"><span class="item-no">3.9</span>  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>  Other</a></span><br /> -  <span class="toc-item"><a href="#s4.1"><span class="item-no">4.1</span>  Support</a></span><br /> -  <span class="toc-item"><a href="#s4.2"><span class="item-no">4.2</span>  Known issues</a></span><br /> -  <span class="toc-item"><a href="#s4.3"><span class="item-no">4.3</span>  Change-log</a></span><br /> -  <span class="toc-item"><a href="#s4.4"><span class="item-no">4.4</span>  Testing</a></span><br /> -  <span class="toc-item"><a href="#s4.5"><span class="item-no">4.5</span>  Upgrade, & old versions</a></span><br /> -  <span class="toc-item"><a href="#s4.6"><span class="item-no">4.6</span>  Comparison with <span class="term">HTMLPurifier</span></a></span><br /> -  <span class="toc-item"><a href="#s4.7"><span class="item-no">4.7</span>  Use through application plug-ins/modules</a></span><br /> -  <span class="toc-item"><a href="#s4.8"><span class="item-no">4.8</span>  Use in non-PHP applications</a></span><br /> -  <span class="toc-item"><a href="#s4.9"><span class="item-no">4.9</span>  Donate</a></span><br /> -  <span class="toc-item"><a href="#s4.10"><span class="item-no">4.10</span>  Acknowledgements</a></span><br /> -<span class="toc-item"><a href="#s5"><span class="item-no">5</span>  Appendices</a></span><br /> -  <span class="toc-item"><a href="#s5.1"><span class="item-no">5.1</span>  Characters discouraged in HTML</a></span><br /> -  <span class="toc-item"><a href="#s5.2"><span class="item-no">5.2</span>  Valid attribute-element combinations</a></span><br /> -  <span class="toc-item"><a href="#s5.3"><span class="item-no">5.3</span>  CSS 2.1 properties accepting URLs</a></span><br /> -  <span class="toc-item"><a href="#s5.4"><span class="item-no">5.4</span>  Microsoft Windows 1252 character replacements</a></span><br /> -  <span class="toc-item"><a href="#s5.5"><span class="item-no">5.5</span>  URL format</a></span><br /> -  <span class="toc-item"><a href="#s5.6"><span class="item-no">5.6</span>  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 - <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a> </div> -<br /> - -<div class="section"><h2> -<a name="s1" id="s1"></a><span class="item-no">1</span>  About htmLawed -</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> application.<br /> -<br /> -  The <em>lawing in</em> 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>  Example uses -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  *  Filtering of text submitted as comments on blogs to allow only certain HTML elements<br /> -<br /> -  *  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 /> -  *  Text processing for stricter XML standard-compliance: e.g., to have lowercased <span class="term">x</span> 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 /> -  *  Scraping text or data from web-pages<br /> -<br /> -  *  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>  Features -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  Key: <span class="term">*</span> security feature, <span class="term">^</span> standard compliance, <span class="term">~</span> requires setting right options, <span class="term">`</span> different from <span class="term">Kses</span><br /> -<br /> -  *  make input more <strong>secure</strong> and <strong>standard-compliant</strong><br /> -  *  use for HTML 4, XHTML 1.0 or 1.1, or even generic <strong>XML</strong> documents  ^~`<br /> -<br /> -  *  <strong>beautify</strong> or <strong>compact</strong> HTML  ^~`<br /> -<br /> -  *  <strong>restrict elements</strong>  ^~`<br /> -  *  proper closure of empty elements like <span class="term">img</span>  ^`<br /> -  *  <strong>transform deprecated elements</strong> like <span class="term">u</span>  ^~`<br /> -  *  HTML <strong>comments</strong> and <span class="term">CDATA</span> sections can be permitted  ^~`<br /> -  *  elements like <span class="term">script</span>, <span class="term">object</span> and <span class="term">form</span> can be permitted  ~<br /> -<br /> -  *  <strong>restrict attributes</strong>, including <strong>element-specifically</strong>  ^~`<br /> -  *  remove <strong>invalid attributes</strong>  ^`<br /> -  *  element and attribute names are <strong>lower-cased</strong>  ^<br /> -  *  provide <strong>required attributes</strong>, like <span class="term">alt</span> for <span class="term">image</span>  ^`<br /> -  *  <strong>transform deprecated attributes</strong>  ^~`<br /> -  *  attributes <strong>declared only once</strong>  ^`<br /> -<br /> -  *  <strong>restrict attribute values</strong>, including <strong>element-specifically</strong>  ^~`<br /> -  *  a value is declared for <em>empty</em> (<em>minimized</em>) attributes like <span class="term">checked</span>  ^<br /> -  *  check for potentially dangerous attribute values  *~<br /> -  *  ensure <strong>unique</strong> <span class="term">id</span> attribute values  ^~`<br /> -  *  <strong>double-quote</strong> attribute values  ^<br /> -  *  lower-case <strong>standard attribute values</strong> like <span class="term">password</span>  ^`<br /> -<br /> -  *  <strong>attribute-specific URL protocol/scheme restriction</strong>  *~`<br /> -  *  disable <strong>dynamic expressions</strong> in <span class="term">style</span> values  *~`<br /> -<br /> -  *  neutralize invalid named character entities  ^`<br /> -  *  <strong>convert</strong> hexadecimal numeric entities to decimal ones, or vice versa  ^~`<br /> -  *  convert named entities to numeric ones for generic XML use  ^~`<br /> -<br /> -  *  remove <strong>null</strong> characters  *<br /> -  *  neutralize potentially dangerous proprietary Netscape <strong>Javascript entities</strong>  *<br /> -  *  replace potentially dangerous <strong>soft-hyphen</strong> character in URL-accepting attribute values with spaces  *<br /> -<br /> -  *  remove common <strong>invalid characters</strong> not allowed in HTML or XML  ^`<br /> -  *  replace <strong>characters from Microsoft applications</strong> like <span class="term">Word</span> that are discouraged in HTML or XML  ^~`<br /> -  *  neutralize entities for characters invalid or discouraged in HTML or XML  ^`<br /> -  *  appropriately neutralize <span class="term"><</span>, <span class="term">&</span>, <span class="term">"</span>, and <span class="term">></span> characters  ^*`<br /> -<br /> -  *  understands improperly spaced tag content (like, spread over more than a line) and properly spaces them  `<br /> -  *  attempts to <strong>balance tags</strong> for well-formedness  ^~`<br /> -  *  understands when <strong>omitable closing tags</strong> like <span class="term"></p></span> (allowed in HTML 4, transitional, e.g.) are missing  ^~`<br /> -  *  attempts to permit only <strong>validly nested tags</strong>  ^~`<br /> -  *  option to <strong>remove or neutralize bad content</strong> ^~`<br /> -  *  attempts to <strong>rectify common errors of plain-text misplacement</strong> (e.g., directly inside <span class="term">blockquote</span>) ^~`<br /> -<br /> -  *  fast, <strong>non-OOP</strong> code of ~45 kb incurring peak basal memory usage of ~0.5 MB<br /> -  *  <strong>compatible</strong> with pre-existing code using <span class="term">Kses</span> (the filter used by <span class="term">WordPress</span>)<br /> -<br /> -  *  optional <strong>anti-spam</strong> measures such as addition of <span class="term">rel="nofollow"</span> and link-disabling  ~`<br /> -  *  optionally makes <strong>relative URLs absolute</strong>, and vice versa  ~`<br /> -<br /> -  *  optionally mark <span class="term">&</span> to identify the entities for <span class="term">&</span>, <span class="term"><</span> and <span class="term">></span> introduced by htmLawed  ~`<br /> -<br /> -  *  allows deployment of powerful <strong>hook functions</strong> to <strong>inject</strong> HTML, <strong>consolidate</strong> <span class="term">style</span> attributes to <span class="term">class</span>, finely check attribute values, etc.  ~`<br /> -<br /> -  *  <strong>independent of character encoding</strong> of input and does not affect it<br /> -<br /> -  *  <strong>tolerance for ill-written HTML</strong> 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>  History -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> and <span class="term">HTMLPurifier</span> were deemed inadequate, slow, resource-intensive, or dependent on external applications like <span class="term">HTML Tidy</span>.<br /> -<br /> -  htmLawed started as a modification of Ulf Harnhammar's <span class="term">Kses</span> (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>  License & copyright -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> and GPL license version <a href="http://www.gnu.org/licenses/gpl-2.0.txt">2</a> 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>  Terms used here -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  *  <em>administrator</em> - or admin; person setting up the code to pass input through htmLawed; also, <em>user</em><br /> -  *  <em>attributes</em> - name-value pairs like <span class="term">href="http://x.com"</span> in opening tags<br /> -  *  <em>author</em> - <em>writer</em><br /> -  *  <em>character</em> - atomic unit of text; internally represented by a numeric <em>code-point</em> as specified by the <em>encoding</em> or <em>charset</em> in use<br /> -  *  <em>entity</em> - markup like <span class="term">&gt;</span> and <span class="term">&#160;</span> used to refer to a character<br /> -  *  <em>element</em> - HTML element like <span class="term">a</span> and <span class="term">img</span><br /> -  *  <em>element content</em> -  content between the opening and closing tags of an element, like <span class="term">click</span> of <span class="term"><a href="x">click</a></span><br /> -  *  <em>HTML</em> - implies XHTML unless specified otherwise<br /> -  *  <em>input</em> - text string given to htmLawed to process<br /> -  *  <em>processing</em> - involves filtering, correction, etc., of input<br /> -  *  <em>safe</em> - 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 /> -  *  <em>scheme</em> - URL protocol like <span class="term">http</span> and <span class="term">ftp</span><br /> -  *  <em>specs</em> - standard specifications<br /> -  *  <em>style property</em> - terms like <span class="term">border</span> and <span class="term">height</span> for which declarations are made in values for the <span class="term">style</span> attribute of elements<br /> -  *  <em>tag</em> - markers like <span class="term"><a href="x"></span> and <span class="term"></a></span> delineating element content; the opening tag can contain attributes<br /> -  *  <em>tag content</em> - consists of tag markers <span class="term"><</span> and <span class="term">></span>, element names like <span class="term">div</span>, and possibly attributes<br /> -  *  <em>user</em> - administrator<br /> -  *  <em>writer</em> - 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>  Usage -</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  htmLawed should work with PHP 4.4 and higher. Either <span class="term">include()</span> the <span class="term">htmLawed.php</span> file or copy-paste the entire code.<br /> -<br /> -  To easily <strong>test</strong> htmLawed using a form-based interface, use the provided <a href="htmLawedTest.php">demo</a> (<span class="term">htmLawed.php</span> and <span class="term">htmLawedTest.php</span> should be in the same directory on the web-server).<br /> -<br /> -  <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> 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>  Simple -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  The input text to be processed, <span class="term">$text</span>, is passed as an argument of type string; <span class="term">htmLawed()</span> returns the processed string:<br /> -<br /> - -<code class="code">    $processed = htmLawed($text);</code> -<br /> -<br /> -  <strong>Note</strong>: If input is from a <span class="term">$_GET</span> or <span class="term">$_POST</span> value, and <span class="term">magic quotes</span> are enabled on the PHP setup, run <span class="term">stripslashes()</span> on the input before passing to htmLawed.<br /> -<br /> -  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> 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> and <span class="term">$spec</span>:<br /> -<br /> - -<code class="code">    $processed = htmLawed($text, $config, $spec);</code> -<br /> -<br /> -  These extra parameters are detailed below. Some examples are shown in <a href="#s2.9">section 2.9</a>.<br /> -<br /> -  <strong>Note</strong>: For maximum protection against <span class="term">XSS</span> and other scripting attacks (e.g., by disallowing Javascript code), consider using the <span class="term">safe</span> 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>  Configuring htmLawed using the <span class="term">$config</span> parameter -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  <span class="term">$config</span> instructs htmLawed on how to tackle certain tasks. When <span class="term">$config</span> 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> 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">    $config = array('comment'=>0, 'cdata'=>1);</code> -<br /> - -<code class="code">    $processed = htmLawed($text, $config);</code> -<br /> -<br /> -  Or,<br /> -<br /> - -<code class="code">    $processed = htmLawed($text, array('comment'=>0, 'cdata'=>1));</code> -<br /> -<br /> -  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 /> -  Key: <span class="term">*</span> default, <span class="term">^</span> different default when htmLawed is used in the Kses-compatible mode (see <a href="#s2.6">section 2.6</a>), <span class="term">~</span> different default when <span class="term">valid_xhtml</span> is set to <span class="term">1</span> (see <a href="#s3.5">section 3.5</a>), <span class="term">"</span> different default when <span class="term">safe</span> is set to <span class="term">1</span> (see <a href="#s3.6">section 3.6</a>)<br /> -<br /> -  <strong>abs_url</strong><br /> -  Make URLs absolute or relative; <span class="term">$config["base_url"]</span> needs to be set; see <a href="#s3.4.4">section 3.4.4</a><br /> -<br /> -  <span class="term">-1</span> - make relative<br /> -  <span class="term">0</span> - no action  *<br /> -  <span class="term">1</span> - make absolute<br /> -<br /> -  <strong>and_mark</strong><br /> -  Mark <span class="term">&</span> characters in the original input; see <a href="#s3.2">section 3.2</a><br /> -<br /> -  <strong>anti_link_spam</strong><br /> -  Anti-link-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br /> -<br /> -  <span class="term">0</span> - no measure taken  *<br /> -  <span class="term">array("regex1", "regex2")</span> - will ensure a <span class="term">rel</span> attribute with <span class="term">nofollow</span> in its value in case the <span class="term">href</span> attribute value matches the regular expression pattern <span class="term">regex1</span>, and/or will remove <span class="term">href</span> if its value matches the regular expression pattern <span class="term">regex2</span>. E.g., <span class="term">array("/./", "/://\W*(?!(abc\.com|xyz\.org))/")</span>; see <a href="#s3.4.7">section 3.4.7</a> for more.<br /> -<br /> -  <strong>anti_mail_spam</strong><br /> -  Anti-mail-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br /> -<br /> -  <span class="term">0</span> - no measure taken  *<br /> -  <span class="term">word</span> - <span class="term">@</span> in mail address in <span class="term">href</span> attribute value is replaced with specified <span class="term">word</span><br /> -<br /> -  <strong>balance</strong><br /> -  Balance tags for well-formedness and proper nesting; see <a href="#s3.3.3">section 3.3.3</a><br /> -<br /> -  <span class="term">0</span> - no<br /> -  <span class="term">1</span> - yes  *<br /> -<br /> -  <strong>base_url</strong><br /> -  Base URL value that needs to be set if <span class="term">$config["abs_url"]</span> is not <span class="term">0</span>; see <a href="#s3.4.4">section 3.4.4</a><br /> -<br /> -  <strong>cdata</strong><br /> -  Handling of <span class="term">CDATA</span> sections; see <a href="#s3.3.1">section 3.3.1</a><br /> -<br /> -  <span class="term">0</span> - don't consider <span class="term">CDATA</span> sections as markup and proceed as if plain text  ^"<br /> -  <span class="term">1</span> - remove<br /> -  <span class="term">2</span> - allow, but neutralize any <span class="term"><</span>, <span class="term">></span>, and <span class="term">&</span> inside by converting them to named entities<br /> -  <span class="term">3</span> - allow  *<br /> -<br /> -  <strong>clean_ms_char</strong><br /> -  Replace discouraged characters introduced by Microsoft Word, etc.; see <a href="#s3.1">section 3.1</a><br /> -<br /> -  <span class="term">0</span> - no  *<br /> -  <span class="term">1</span> - yes<br /> -  <span class="term">2</span> - yes, but replace special single & double quotes with ordinary ones<br /> -<br /> -  <strong>comment</strong><br /> -  Handling of HTML comments; see <a href="#s3.3.1">section 3.3.1</a><br /> -<br /> -  <span class="term">0</span> - don't consider comments as markup and proceed as if plain text  ^"<br /> -  <span class="term">1</span> - remove<br /> -  <span class="term">2</span> - allow, but neutralize any <span class="term"><</span>, <span class="term">></span>, and <span class="term">&</span> inside by converting to named entities<br /> -  <span class="term">3</span> - allow  *<br /> -<br /> -  <strong>css_expression</strong><br /> -  Allow dynamic CSS expression by not removing the expression from CSS property values in <span class="term">style</span> attributes; see <a href="#s3.4.8">section 3.4.8</a><br /> -<br /> -  <span class="term">0</span> - remove  *<br /> -  <span class="term">1</span> - allow<br /> -<br /> -  <strong>deny_attribute</strong><br /> -  Denied HTML attributes; see <a href="#s3.4">section 3.4</a><br /> -<br /> -  <span class="term">0</span> - none  *<br /> -  <span class="term">string</span> - dictated by values in <span class="term">string</span><br /> -  <span class="term">on*</span> (like <span class="term">onfocus</span>) attributes not allowed - "<br /> -<br /> -  <strong>direct_nest_list</strong><br /> -  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 /> -  <span class="term">0</span> - no  *<br /> -  <span class="term">1</span> - yes<br /> -<br /> -  <strong>elements</strong><br /> -  Allowed HTML elements; see <a href="#s3.3">section 3.3</a><br /> -<br /> -  <span class="term">* -center -dir -font -isindex -menu -s -strike -u</span> -  ~<br /> -  <span class="term">applet, embed, iframe, object, script</span> not allowed - "<br /> -<br /> -  <strong>hexdec_entity</strong><br /> -  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 /> -  <span class="term">0</span> - no<br /> -  <span class="term">1</span> - yes  *<br /> -  <span class="term">2</span> - convert decimal to hexadecimal ones<br /> -<br /> -  <strong>hook</strong><br /> -  Name of an optional hook function to alter the input string, <span class="term">$config</span> or <span class="term">$spec</span> before htmLawed starts its main work; see <a href="#s3.7">section 3.7</a><br /> -<br /> -  <span class="term">0</span> - no hook function  *<br /> -  <span class="term">name</span> - <span class="term">name</span> is name of the hook function (<span class="term">kses_hook</span>  ^)<br /> -<br /> -  <strong>hook_tag</strong><br /> -  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 /> -  <span class="term">0</span> - no hook function  *<br /> -  <span class="term">name</span> - <span class="term">name</span> is name of the hook function<br /> -<br /> -  <strong>keep_bad</strong><br /> -  Neutralize bad tags by converting <span class="term"><</span> and <span class="term">></span> to entities, or remove them; see <a href="#s3.3.3">section 3.3.3</a><br /> -<br /> -  <span class="term">0</span> - remove  ^<br /> -  <span class="term">1</span> - neutralize both tags and element content<br /> -  <span class="term">2</span> - remove tags but neutralize element content<br /> -  <span class="term">3</span> and <span class="term">4</span> - like <span class="term">1</span> and <span class="term">2</span> but remove if text (<span class="term">pcdata</span>) is invalid in parent element<br /> -  <span class="term">5</span> and <span class="term">6</span> * -  like <span class="term">3</span> and <span class="term">4</span> but line-breaks, tabs and spaces are left<br /> -<br /> -  <strong>lc_std_val</strong><br /> -  For XHTML compliance, predefined, standard attribute values, like <span class="term">get</span> for the <span class="term">method</span> attribute of <span class="term">form</span>, must be lowercased; see <a href="#s3.4.5">section 3.4.5</a><br /> -<br /> -  <span class="term">0</span> - no<br /> -  <span class="term">1</span> - yes  *<br /> -<br /> -  <strong>make_tag_strict</strong><br /> -  Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: <span class="term">applet</span> <span class="term">center</span> <span class="term">dir</span> <span class="term">embed</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> <span class="term">u</span>; see <a href="#s3.3.2">section 3.3.2</a><br /> -<br /> -  <span class="term">0</span> - no  ^<br /> -  <span class="term">1</span> - yes, but leave <span class="term">applet</span>, <span class="term">embed</span> and <span class="term">isindex</span> elements that currently can't be transformed  *<br /> -  <span class="term">2</span> - yes, removing <span class="term">applet</span>, <span class="term">embed</span> and <span class="term">isindex</span> elements and their contents (nested elements remain)  ~<br /> -<br /> -  <strong>named_entity</strong><br /> -  Allow non-universal named HTML entities, or convert to numeric ones; see <a href="#s3.2">section 3.2</a><br /> -<br /> -  <span class="term">0</span> - convert<br /> -  <span class="term">1</span> - allow  *<br /> -<br /> -  <strong>no_deprecated_attr</strong><br /> -  Allow deprecated attributes or transform them; see <a href="#s3.4.6">section 3.4.6</a><br /> -<br /> -  <span class="term">0</span> - allow  ^<br /> -  <span class="term">1</span> - transform, but <span class="term">name</span> attributes for <span class="term">a</span> and <span class="term">map</span> are retained  *<br /> -  <span class="term">2</span> - transform<br /> -<br /> -  <strong>parent</strong><br /> -  Name of the parent element, possibly imagined, that will hold the input; see <a href="#s3.3">section 3.3</a><br /> -<br /> -  <strong>safe</strong><br /> -  Magic parameter to make input the most secure against XSS without needing to specify other relevant <span class="term">$config</span> parameters; see <a href="#s3.6">section 3.6</a><br /> -<br /> -  <span class="term">0</span> - no  *<br /> -  <span class="term">1</span> - will auto-adjust other relevant <span class="term">$config</span> parameters (indicated by <span class="term">"</span> in this list)<br /> -<br /> -  <strong>schemes</strong><br /> -  Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or <span class="term">!</span> to <em>deny</em> any URL); <span class="term">*</span> covers all unspecified attributes; see <a href="#s3.4.3">section 3.4.3</a><br /> -<br /> -  <span class="term">href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https</span>  *<br /> -  <span class="term">*: ftp, gopher, http, https, mailto, news, nntp, telnet</span>  ^<br /> -  <span class="term">href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style: !; *:file, http, https</span>  "<br /> -<br /> -  <strong>show_setting</strong><br /> -  Name of a PHP variable to assign the <em>finalized</em> <span class="term">$config</span> and <span class="term">$spec</span> values; see <a href="#s3.8">section 3.8</a><br /> -<br /> -  <strong>style_pass</strong><br /> -  Do not look at <span class="term">style</span> attribute values, letting them through without any alteration<br /> -<br /> -  <span class="term">0</span> - no *<br /> -  <span class="term">1</span> - htmLawed will let through any <span class="term">style</span> value; see <a href="#s3.4.8">section 3.4.8</a><br /> -<br /> -  <strong>tidy</strong><br /> -  Beautify or compact HTML code; see <a href="#s3.3.5">section 3.3.5</a><br /> -<br /> -  <span class="term">-1</span> - compact<br /> -  <span class="term">0</span> - no  *<br /> -  <span class="term">1</span> or <span class="term">string</span> - beautify (custom format specified by <span class="term">string</span>)<br /> -<br /> -  <strong>unique_ids</strong><br /> -  <span class="term">id</span> attribute value checks; see <a href="#s3.4.2">section 3.4.2</a><br /> -<br /> -  <span class="term">0</span> - no  ^<br /> -  <span class="term">1</span> - remove duplicate and/or invalid ones  *<br /> -  <span class="term">word</span> - 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">:</span>.<br /> -<br /> -  <strong>valid_xhtml</strong><br /> -  Magic parameter to make input the most valid XHTML without needing to specify other relevant <span class="term">$config</span> parameters; see <a href="#s3.5">section 3.5</a><br /> -<br /> -  <span class="term">0</span> - no  *<br /> -  <span class="term">1</span> - will auto-adjust other relevant <span class="term">$config</span> parameters (indicated by <span class="term">~</span> in this list)<br /> -<br /> -  <strong>xml:lang</strong><br /> -  Auto-adding <span class="term">xml:lang</span> attribute; see <a href="#s3.4.1">section 3.4.1</a><br /> -<br /> -  <span class="term">0</span> - no  *<br /> -  <span class="term">1</span> - add if <span class="term">lang</span> attribute is present<br /> -  <span class="term">2</span> - add if <span class="term">lang</span> attribute is present, and remove <span class="term">lang</span>  ~<br /> - -</div> -<div class="sub-section"><h3> -<a name="s2.3" id="s2.3"></a><span class="item-no">2.3</span>  Extra HTML specifications using the $spec parameter -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  The <span class="term">$spec</span> 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> 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">    $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';</code> -<br /> - -<code class="code">    $processed = htmLawed($text, $config, $spec);</code> -<br /> -<br /> -  Or,<br /> -<br /> - -<code class="code">    $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');</code> -<br /> -<br /> -  A rule begins with an HTML <strong>element</strong> 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 /> -  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">-*</span> can be used.<br /> -<br /> -  Following shows examples of rule excerpts with rule-element <span class="term">a</span> and the attributes that are being permitted:<br /> -<br /> -  *  <span class="term">a=</span> - all<br /> -  *  <span class="term">a=id</span> - all<br /> -  *  <span class="term">a=href, title, -id, -onclick</span> - all except <span class="term">id</span> and <span class="term">onclick</span><br /> -  *  <span class="term">a=*, id, -id</span> - all except <span class="term">id</span><br /> -  *  <span class="term">a=-*</span> - none<br /> -  *  <span class="term">a=-*, href, title</span> - none except <span class="term">href</span> and <span class="term">title</span><br /> -  *  <span class="term">a=-*, -id, href, title</span> - none except <span class="term">href</span> and <span class="term">title</span><br /> -<br /> -  Rules regarding <strong>attribute values</strong> are optionally specified inside round brackets after attribute names in slash ('/')-separated <em>parameter = value</em> 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 /> -  *  <span class="term">oneof</span> - one or more choices separated by <span class="term">|</span> that the value should match; if only one choice is provided, then the value must match that choice<br /> -<br /> -  *  <span class="term">noneof</span> - one or more choices separated by <span class="term">|</span> that the value should not match<br /> -<br /> -  *  <span class="term">maxlen</span> and <span class="term">minlen</span> - upper and lower limits for the number of characters in the attribute value; specified in numbers<br /> -<br /> -  *  <span class="term">maxval</span> and <span class="term">minval</span> - upper and lower limits for the numerical value specified in the attribute value; specified in numbers<br /> -<br /> -  *  <span class="term">match</span> and <span class="term">nomatch</span> - pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers<br /> -<br /> -  *  <span class="term">default</span> - a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters<br /> -<br /> -  If <span class="term">default</span> 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> 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> to <span class="term">-1</span>).<br /> -<br /> -  Examples with <em>input</em> <span class="term"><input title="WIDTH" value="10em" /><input title="length" value="5" /></span> are shown below.<br /> -<br /> -  <em>Rule</em>: <span class="term">input=title(maxlen=60/minlen=6), value</span><br /> -  <em>Output</em>: <span class="term"><input value="10em" /><input title="length" value="5" /></span><br /> -<br /> -  <em>Rule</em>: <span class="term">input=title(), value(maxval=8/default=6)</span><br /> -  <em>Output</em>: <span class="term"><input title="WIDTH" value="6" /><input title="length" value="5" /></span><br /> -<br /> -  <em>Rule</em>: <span class="term">input=title(nomatch=%w.d%i), value(match=%em%/default=6em)</span><br /> -  <em>Output</em>: <span class="term"><input value="10em" /><input title="length" value="6em" /></span><br /> -<br /> -  <em>Rule</em>: <span class="term">input=title(oneof=height|depth/default=depth), value(noneof=5|6)</span><br /> -  <em>Output</em>: <span class="term"><input title="depth" value="10em" /><input title="depth" /></span><br /> -<br /> -  <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> 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> by enclosing in pairs of double-quotes (<span class="term">"</span>). A back-tick (<span class="term">`</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 `"ID`"")</span>.<br /> -<br /> -  <strong>Note</strong>: To deny an attribute for all elements for which it is legal, <span class="term">$config["deny_attribute"]</span> (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> while being denied globally through <span class="term">$config["deny_attribute"]</span>. The <span class="term">hook_tag</span> parameter (<a href="#s3.4.9">section 3.4.9</a>) can also be used to implement the <span class="term">$spec</span> functionality.<br /> - -</div> -<div class="sub-section"><h3> -<a name="s2.4" id="s2.4"></a><span class="item-no">2.4</span>  Performance time & memory usage -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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 /> -  The htmLawed <a href="htmLawedTest.php">demo</a> 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>  Some security risks to keep in mind -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> HTML code which is meant to steal user-data, deface a website, render a page non-functional, etc.<br /> -<br /> -  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 /> -  *  Allowing <span class="term">script</span>, <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span> or <span class="term">object</span> elements, or certain of their attributes like <span class="term">allowscriptaccess</span><br /> -<br /> -  *  Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., <span class="term"><!--[if gte IE 4]><script>alert("xss");</script><![endif]--></span><br /> -<br /> -  *  Allowing dynamic CSS expressions (a feature of the IE browser)<br /> -<br /> -  *  Allowing the <span class="term">style</span> attribute<br /> -<br /> -  To remove <em>unsecure</em> HTML, code-developers using htmLawed must set <span class="term">$config</span> appropriately. E.g., <span class="term">$config["elements"] = "* -script"</span> to deny the <span class="term">script</span> element (<a href="#s3.3">section 3.3</a>), <span class="term">$config["safe"] = 1</span> to auto-configure ceratin htmLawed parameters for maximizing security (<a href="#s3.6">section 3.6</a>), etc.<br /> -<br /> -  Permitting the <span class="term">*style*</span> attribute brings in risks of <em>click-jacking</em>, <em>phishing</em>, web-page overlays, etc., <em>even</em> when the <span class="term">safe</span> 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> argument, and through the <span class="term">hook_tag</span> parameter (see <a href="#s3.4.8">section 3.4.8</a> for more). Disallowing <span class="term">style</span> completely and relying on CSS classes and stylesheet files is recommended.<br /> -<br /> -  htmLawed does not check or correct the character <strong>encoding</strong> 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> 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>  Use without modifying old <span class="term">kses()</span> code -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  The <span class="term">Kses</span> 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> function declared in the <span class="term">Kses</span> 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> with htmLawed:<br /> -<br /> - -<code class="code">    $comment_filtered = kses($comment_input, array('a'=>array(), 'b'=>array(), 'i'=>array()));</code> -<br /> -<br /> -  For some of the <span class="term">$config</span> parameters, htmLawed will use values other than the default ones. These are indicated by <span class="term">^</span> in <a href="#s2.2">section 2.2</a>. To force htmLawed to use other values, function <span class="term">kses()</span> in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.<br /> -<br /> -  If the application uses a <span class="term">Kses</span> file that has the <span class="term">kses()</span> function declared, then, to have the application use htmLawed instead of <span class="term">Kses</span>, simply rename <span class="term">htmLawed.php</span> (to <span class="term">kses.php</span>, e.g.) and replace the <span class="term">Kses</span> file (or just replace the code in the <span class="term">Kses</span> file with the htmLawed code). If the <span class="term">kses()</span> function in the <span class="term">Kses</span> 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> function in the htmLawed code.<br /> -<br /> -  If the <span class="term">Kses</span> 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> to <span class="term">wp_kses()</span>, and delete the code for the original <span class="term">wp_kses()</span> function.<br /> -<br /> -  If the <span class="term">Kses</span> code has a non-empty hook function (e.g., <span class="term">wp_kses_hook()</span> in case of <span class="term">WordPress</span>), then the code for htmLawed's <span class="term">kses_hook()</span> 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> in case of <span class="term">WordPress</span>:<br /> -<br /> - -<code class="code">    function kses_hook($string, &$cf, &$spec){</code> -<br /> - -<code class="code">    // kses compatibility</code> -<br /> - -<code class="code">    $allowed_html = $spec;</code> -<br /> - -<code class="code">    $allowed_protocols = array();</code> -<br /> - -<code class="code">    foreach($cf['schemes'] as $v){</code> -<br /> - -<code class="code">     foreach($v as $k2=>$v2){</code> -<br /> - -<code class="code">      if(!in_array($k2, $allowed_protocols)){</code> -<br /> - -<code class="code">       $allowed_protocols[] = $k2;</code> -<br /> - -<code class="code">      }</code> -<br /> - -<code class="code">     }</code> -<br /> - -<code class="code">    }</code> -<br /> - -<code class="code">    return wp_kses_hook($string, $allowed_html, $allowed_protocols);</code> -<br /> - -<code class="code">    // eof</code> -<br /> - -<code class="code">    }</code> -<br /> - -</div> -<div class="sub-section"><h3> -<a name="s2.7" id="s2.7"></a><span class="item-no">2.7</span>  Tolerance for ill-written HTML -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be <em>read</em> as HTML, and be considered mere plain text instead. Following statements indicate the degree of <em>looseness</em> that htmLawed can work with, and can be provided in instructions to writers:<br /> -<br /> -  *  Tags must be flanked by <span class="term"><</span> and <span class="term">></span> with no <span class="term">></span> inside -- any needed <span class="term">></span> should be put in as <span class="term">&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">></span>, like <span class="term"><div ></span> and <span class="term"><img / ></span>, but not after the <span class="term"><</span>.<br /> -<br /> -  *  Element and attribute names need not be lower-cased.<br /> -<br /> -  *  Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.<br /> -<br /> -  *  Attribute values may not be double-quoted, or may be single-quoted.<br /> -<br /> -  *  Left-padding of numeric entities (like, <span class="term">&#0160;</span>, <span class="term">&x07ff;</span>) with <span class="term">0</span> is okay as long as the number of characters between between the <span class="term">&</span> and the <span class="term">;</span> does not exceed 8. All entities must end with <span class="term">;</span> though.<br /> -<br /> -  *  Named character entities must be properly cased. E.g., <span class="term">&Lt;</span> or <span class="term">&TILDE;</span> will not be let through without modification.<br /> -<br /> -  *  HTML comments should not be inside element tags (okay between tags), and should begin with <span class="term"><!--</span> and end with <span class="term">--></span>. Characters like <span class="term"><</span>, <span class="term">></span>, and <span class="term">&</span> may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">--></span> inside should be put in as <span class="term">--&gt;</span>. Any <span class="term">--</span> inside will be automatically converted to <span class="term">-</span>, and a space will be added before the comment delimiter <span class="term">--></span>.<br /> -<br /> -  *  <span class="term">CDATA</span> 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"><[CDATA[</span> and end with <span class="term">]]></span>. Characters like <span class="term"><</span>, <span class="term">></span>, and <span class="term">&</span> may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">]]></span> inside should be put in as <span class="term">]]&gt;</span>.<br /> -<br /> -  *  For attribute values, character entities <span class="term">&lt;</span>, <span class="term">&gt;</span> and <span class="term">&amp;</span> should be used instead of characters <span class="term"><</span> and <span class="term">></span>, and <span class="term">&</span> (when <span class="term">&</span> 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 /> -  *  Characters <span class="term"><</span>, <span class="term">></span>, <span class="term">&</span> and <span class="term">"</span> that are part of actual Javascript, etc., code in <span class="term">script</span> elements should be used as such and not be put in as entities like <span class="term">&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> sections.<br /> -<br /> -  *  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 /> -  *  Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.<br /> -<br /> -  *  With <span class="term">$config["unique_ids"]</span> not <span class="term">0</span> and the <span class="term">id</span> attribute being permitted, writers should carefully avoid using duplicate or invalid <span class="term">id</span> 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"><a id="home"></a><input id="home" /><label for="home"></label></span> is processed into<br /> -<span class="term"><a id="home"></a><input id="prefix_home" /><label for="home"></label></span>.<br /> -<br /> -  *  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 /> -  *  For URLs, unless <span class="term">$config["scheme"]</span> is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., <span class="term">htt&#112;</span> (which many browsers will read as the harmless <span class="term">http</span>) may be considered bad by htmLawed.<br /> -<br /> -  *  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> and <span class="term">noscript</span> elements (illegal as per the specs) inside auto-generated <span class="term">div</span> elements.<br /> - -</div> -<div class="sub-section"><h3> -<a name="s2.8" id="s2.8"></a><span class="item-no">2.8</span>  Limitations & work-arounds -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  htmLawed's main objective is to make the input text <em>more</em> 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 /> -  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> elements) are clearly wrong. Regarding security, note that <em>unsafe</em> HTML code is not necessarily legally invalid.<br /> -<br /> -  *  htmLawed is meant for input that goes into the <span class="term">body</span> 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> and <span class="term">noframes</span>.<br /> -<br /> -  *  It cannot transform the non-standard <span class="term">embed</span> elements to the standard-compliant <span class="term">object</span> elements. Yet, it can allow <span class="term">embed</span> elements if permitted (<span class="term">embed</span> is widely used and supported). Admins can certainly use the <span class="term">hook_tag</span> parameter (<a href="#s3.4.9">section 3.4.9</a>) to deploy a custom embed-to-object converter function.<br /> -<br /> -  *  The only non-standard element that may be permitted is <span class="term">embed</span>; others like <span class="term">noembed</span> and <span class="term">nobr</span> cannot be permitted without modifying the htmLawed code.<br /> -<br /> -  *  It cannot handle input that has non-HTML code like <span class="term">SVG</span> 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> parameter (see <a href="#s3.2">section 3.2</a>).<br /> -<br /> -  *  By default, htmLawed won't check many attribute values for standard compliance. E.g., <span class="term">width="20m"</span> with the dimension in non-standard <span class="term">m</span> 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> parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span> to enforce finer checks.<br /> -<br /> -  *  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 /> -  *  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> parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span> for finer checks. Perhaps the best option is to disallow <span class="term">style</span> but allow <span class="term">class</span> attributes with the right <span class="term">oneof</span> or <span class="term">match</span> values for <span class="term">class</span>, and have the various class style properties in <span class="term">.css</span> CSS stylesheet files.<br /> -<br /> -  *  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> elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.<br /> -<br /> -  *  htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate <span class="term">target</span> and <span class="term">onclick</span> 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> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br /> -<br /> -  *  Nesting-based checks are not possible. E.g., one cannot disallow <span class="term">p</span> elements specifically inside <span class="term">td</span> while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br /> -<br /> -  *  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> 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> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br /> -<br /> -  *  Pairs of opening and closing tags that do not enclose any content (like <span class="term"><em></em></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 /> -  *  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> 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> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br /> -<br /> -  *  htmLawed does not check the number of nested elements. E.g., it will allow two <span class="term">caption</span> elements in a <span class="term">table</span> 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> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br /> -<br /> -  *  htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers (<span class="term">/*</span>) in <span class="term">style</span> attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like <span class="term">&#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> parameter (<a href="#s3.4.9">section 3.4.9</a>) to more specifically identify CSS expressions in the <span class="term">style</span> attribute values. Also, using <span class="term">$config["style_pass"]</span>, it is possible to have htmLawed pass <span class="term">style</span> attribute values without even looking at them (<a href="#s3.4.8">section 3.4.8</a>).<br /> -<br /> -  *  htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., <span class="term"><a href="http://x%22+style=%22background-image:xss">x</a></span>). These arise when browsers mis-identify markup in <em>escaped</em> text, defeating the very purpose of escaping text (a bad browser will read the given example as <span class="term"><a href="http://x" style="background-image:xss">x</a></span>).<br /> -<br /> -  *  Because of poor Unicode support in PHP, htmLawed does not remove the <em>high value</em> 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 /> -  *  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> tags, this can permit an exploit (like Google's UTF-7/XSS vulnerability of the past).<br /> -<br /> -  *  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>  Examples of usage -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  Safest, allowing only <em>safe</em> HTML markup --<br /> -<br /> - -<code class="code">    $config = array('safe'=>1);</code> -<br /> - -<code class="code">    $out = htmLawed($in);</code> -<br /> -<br /> -  Simplest, allowing all valid HTML markup except <span class="term">javascript:</span> --<br /> -<br /> - -<code class="code">    $out = htmLawed($in);</code> -<br /> -<br /> -  Allowing all valid HTML markup including <span class="term">javascript:</span> --<br /> -<br /> - -<code class="code">    $config = array('schemes'=>'*:*');</code> -<br /> - -<code class="code">    $out = htmLawed($in, $config);</code> -<br /> -<br /> -  Allowing only <span class="term">safe</span> HTML and the elements <span class="term">a</span>, <span class="term">em</span>, and <span class="term">strong</span> --<br /> -<br /> - -<code class="code">    $config = array('safe'=>1, 'elements'=>'a, em, strong');</code> -<br /> - -<code class="code">    $out = htmLawed($in, $config);</code> -<br /> -<br /> -  Not allowing elements <span class="term">script</span> and <span class="term">object</span> --<br /> -<br /> - -<code class="code">    $config = array('elements'=>'* -script -object');</code> -<br /> - -<code class="code">    $out = htmLawed($in, $config);</code> -<br /> -<br /> -  Not allowing attributes <span class="term">id</span> and <span class="term">style</span> --<br /> -<br /> - -<code class="code">    $config = array('deny_attribute'=>'id, style');</code> -<br /> - -<code class="code">    $out = htmLawed($in, $config);</code> -<br /> -<br /> -  Permitting only attributes <span class="term">title</span> and <span class="term">href</span> --<br /> -<br /> - -<code class="code">    $config = array('deny_attribute'=>'* -title -href');</code> -<br /> - -<code class="code">    $out = htmLawed($in, $config);</code> -<br /> -<br /> -  Remove bad/disallowed tags altogether instead of converting them to entities --<br /> -<br /> - -<code class="code">    $config = array('keep_bad'=>0);</code> -<br /> - -<code class="code">    $out = htmLawed($in, $config);</code> -<br /> -<br /> -  Allowing attribute <span class="term">title</span> only in <span class="term">a</span> and not allowing attributes <span class="term">id</span>, <span class="term">style</span>, or scriptable <em>on*</em> attributes like <span class="term">onclick</span> --<br /> -<br /> - -<code class="code">    $config = array('deny_attribute'=>'title, id, style, on*');</code> -<br /> - -<code class="code">    $spec = 'a=title';</code> -<br /> - -<code class="code">    $out = htmLawed($in, $config, $spec);</code> -<br /> -<br /> -  Some case-studies are presented below.<br /> -<br /> -  <strong>1.</strong> 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> and <span class="term">u</span> in comments, but needs <span class="term">strike</span> and <span class="term">u</span> transformed to <span class="term">span</span> for better XHTML 1-strict compliance, and, he wants the <span class="term">a</span> links to be to <span class="term">http</span> or <span class="term">https</span> resources:<br /> -<br /> - -<code class="code">    $processed = htmLawed($in, array('elements'=>'a, em, strike, strong, u', 'make_tag_strict'=>1, 'safe'=>1, 'schemes'=>'*:http, https'), 'a=href');</code> -<br /> -<br /> -  <strong>2.</strong> 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> 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">    $processed = htmLawed($in, array('css_expression'=>1, 'keep_bad'=>1, 'make_tag_strict'=>1, 'schemes'=>'*:*', 'valid_xhtml'=>1));</code> -<br /> -<br /> -  For the final submission process, <span class="term">keep_bad</span> is set to <span class="term">6</span>. A value of <span class="term">1</span> for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.<br /> -<br /> -  <strong>3.</strong> 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">    $processed = htmLawed($in, array('elements'=>'tr, td', 'tidy'=>-1), 'tr, td =');</code> -<br /> - -</div> -</div> -<div class="section"><h2> -<a name="s3" id="s3"></a><span class="item-no">3</span>  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>  Invalid/dangerous characters -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> to <span class="term">d7ff</span>, and <span class="term">e000</span> to <span class="term">10ffff</span>, except <span class="term">fffe</span> and <span class="term">ffff</span> (decimally, <span class="term">9</span>, <span class="term">10</span>, <span class="term">13</span>, <span class="term">32</span> to <span class="term">55295</span>, and <span class="term">57344</span> to <span class="term">1114111</span>, except <span class="term">65534</span> and <span class="term">65535</span>). htmLawed removes the invalid characters <span class="term">0</span> to <span class="term">8</span>, <span class="term">b</span>, <span class="term">c</span>, and <span class="term">e</span> to <span class="term">1f</span>.<br /> -<br /> -  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 /> -  Characters that are discouraged (see <a href="#s5.1">section 5.1</a>) but not invalid are not removed by htmLawed.<br /> -<br /> -  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"><</span>, <span class="term">></span>, <span class="term">&</span>, and <span class="term">"</span> are converted to entities.<br /> -<br /> -  With <span class="term">$config["clean_ms_char"]</span> set as <span class="term">1</span> or <span class="term">2</span>, many of the discouraged characters (decimal code-points <span class="term">127</span> to <span class="term">159</span> except <span class="term">133</span>) that many Microsoft applications incorrectly use (as per the <span class="term">Windows 1252</span> [<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 /> -  With <span class="term">$config["clean_ms_char"]</span> 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> (for special single-quotes), and <span class="term">84</span>, <span class="term">93</span>, and <span class="term">94</span> (for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.<br /> -<br /> -  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 /> -  The <span class="term">$config["clean_ms_char"]</span> 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> (<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>  Character references/entities -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  Valid character entities take the form <span class="term">&*;</span> where <span class="term">*</span> is <span class="term">#x</span> followed by a hexadecimal number (hexadecimal numeric entity; like <span class="term">&#xA0;</span> for non-breaking space), or alphanumeric like <span class="term">gt</span> (external or named entity; like <span class="term">&nbsp;</span> for non-breaking space), or <span class="term">#</span> followed by a number (decimal numeric entity; like <span class="term">&#160;</span> for non-breaking space). Character entities referring to the soft-hyphen character (the <span class="term">&shy;</span> or <span class="term">\xad</span> character; hexadecimal code-point <span class="term">ad</span> [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 /> -  htmLawed (function <span class="term">hl_ent()</span>):<br /> -<br /> -  *  Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)<br /> -<br /> -  *  Lowercases the <span class="term">X</span> (for XML-compliance) and <span class="term">A-F</span> of hexadecimal numeric entities<br /> -<br /> -  *  Neutralizes entities referring to characters that are HTML-invalid (see <a href="#s3.1">section 3.1</a>)<br /> -<br /> -  *  Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, <span class="term">7f</span> to <span class="term">84</span>, <span class="term">86</span> to <span class="term">9f</span>, and <span class="term">fdd0</span> to <span class="term">fddf</span>, or decimally, <span class="term">127</span> to <span class="term">132</span>, <span class="term">134</span> to <span class="term">159</span>, and <span class="term">64991</span> to <span class="term">64976</span>). Entities referring to the remaining discouraged characters (see <a href="#s5.1">section 5.1</a> for a full list) are let through.<br /> -<br /> -  *  Neutralizes named entities that are not in the specs.<br /> -<br /> -  *  Optionally converts valid HTML-specific named entities except <span class="term">&gt;</span>, <span class="term">&lt;</span>, <span class="term">&quot;</span>, and <span class="term">&amp;</span> 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> should be <span class="term">1</span>.<br /> -<br /> -  *  Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, <span class="term">$config["hexdec_entity"]</span> should be <span class="term">0</span>.<br /> -<br /> -  *  Optionally converts decimal numeric entities to the hexadecimal ones. For this, <span class="term">$config["hexdec_entity"]</span> should be <span class="term">2</span>.<br /> -<br /> -  <em>Neutralization</em> refers to the <em>entitification</em> of <span class="term">&</span> to <span class="term">&amp;</span>.<br /> -<br /> -  <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> <a href="http://www.php.net/html_entity_decode">function</a> for that.<br /> -<br /> -  <strong>Note</strong>: If <span class="term">$config["and_mark"]</span> is set, and set to a value other than <span class="term">0</span>, then the <span class="term">&</span> characters in the original input are replaced with the control character for the hexadecimal code-point <span class="term">6</span> (<span class="term">\x06</span>; <span class="term">&</span> characters introduced by htmLawed, e.g., after converting <span class="term"><</span> to <span class="term">&lt;</span>, are not affected). This allows one to distinguish, say, an <span class="term">&gt;</span> introduced by htmLawed and an <span class="term">&gt;</span> 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(><)o</span> 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> can break documents. Before use in such documents, and preferably before any storage, any remaining <span class="term">\x06</span> should be changed back to <span class="term">&</span>, e.g., with:<br /> -<br /> - -<code class="code">    $final = str_replace("\x06", '&', $prelim);</code> -<br /> -<br /> -  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>  HTML elements -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> (converted to plain text by entitification of <span class="term"><</span> and <span class="term">></span>) or removed.<br /> -<br /> -  E.g., with only <span class="term">em</span> permitted:<br /> -<br /> -  Input:<br /> -<br /> - -<code class="code">      <em>My</em> website is <a href="http://a.com>a.com</a>.</code> -<br /> -<br /> -  Output, with <span class="term">$config["keep_bad"] = 0</span>:<br /> -<br /> - -<code class="code">      <em>My</em> website is a.com.</code> -<br /> -<br /> -  Output, with <span class="term">$config["keep_bad"]</span> not <span class="term">0</span>:<br /> -<br /> - -<code class="code">      <em>My</em> website is &lt;a href=""&gt;a.com&lt;/a&gt;.</code> -<br /> -<br /> -  See <a href="#s3.3.3">section 3.3.3</a> for differences between the various non-zero <span class="term">$config["keep_bad"]</span> values.<br /> -<br /> -  htmLawed by default permits these 86 elements:<br /> -<br /> - -<code class="code">    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 /> -  Except for <span class="term">embed</span> (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 /> -  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> and <span class="term">script</span>; see <a href="#s3.6">section 3.6</a>.<br /> -<br /> -  When <span class="term">$config["elements"]</span>, which specifies allowed elements, is <em>properly</em> defined, and neither empty nor set to <span class="term">0</span> or <span class="term">*</span>, the default set is not used. To have elements added to or removed from the default set, a <span class="term">+/-</span> notation is used. E.g., <span class="term">*-script-object</span> implies that only <span class="term">script</span> and <span class="term">object</span> are disallowed, whereas <span class="term">*+embed</span> means that <span class="term">noembed</span> is also allowed. Elements can also be specified as comma separated names. E.g., <span class="term">a, b, i</span> means only <span class="term">a</span>, <span class="term">b</span> and <span class="term">i</span> are permitted. In this notation, <span class="term">*</span>, <span class="term">+</span> and <span class="term">-</span> have no significance and can actually cause a mis-reading.<br /> -<br /> -  Some more examples of <span class="term">$config["elements"]</span> values indicating permitted elements (note that empty spaces are liberally allowed for clarity):<br /> -<br /> -  *  <span class="term">a, blockquote, code, em, strong</span> -- 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 /> -  *  <span class="term">*-script</span> -- all excluding <span class="term">script</span><br /> -  *  <span class="term">* -center -dir -font -isindex -menu -s -strike -u</span> -- only XHTML-Strict elements<br /> -  *  <span class="term">*+noembed-script</span> -- all including <span class="term">noembed</span> excluding <span class="term">script</span><br /> -<br /> -  Some mis-usages (and the resulting permitted elements) that can be avoided:<br /> -<br /> -  *  <span class="term">-*</span> -- none; instead of htmLawed, one might just use, e.g., the <span class="term">htmlspecialchars()</span> PHP function<br /> -  *  <span class="term">*, -script</span> -- all except <span class="term">script</span>; admin probably meant <span class="term">*-script</span><br /> -  *  <span class="term">-*, a, em, strong</span> -- all; admin probably meant <span class="term">a, em, strong</span><br /> -  *  <span class="term">*</span> -- all; admin need not have set <span class="term">elements</span><br /> -  *  <span class="term">*-form+form</span> -- all; a <span class="term">+</span> will always over-ride any <span class="term">-</span><br /> -  *  <span class="term">*, noembed</span> -- only <span class="term">noembed</span>; admin probably meant <span class="term">*+noembed</span><br /> -  *  <span class="term">a, +b, i</span> -- only <span class="term">a</span> and <span class="term">i</span>; admin probably meant <span class="term">a, b, i</span><br /> -<br /> -  Basically, when using the <span class="term">+/-</span> notation, commas (<span class="term">,</span>) should not be used, and vice versa, and <span class="term">*</span> should be used with the former but not the latter.<br /> -<br /> -  <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> in the last example, it will eventually be removed during tag balancing unless such balancing is turned off (<span class="term">$config["balance"]</span> 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> to accommodate the element and its nesting properties.<br /> -<br /> -  <strong>A possibly second way to specify allowed elements</strong> is to set <span class="term">$config["parent"]</span> to an element name that supposedly will hold the input, and to set <span class="term">$config["balance"]</span> 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> if <span class="term">$config["parent"]</span> is empty, <span class="term">body</span>, or an element not in htmLawed's default set of 86 elements.<br /> -<br /> -  <em>Tag transformation</em> 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>  Handling of comments and CDATA sections -</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  <span class="term">CDATA</span> sections have the format <span class="term"><![CDATA[...anything but not "]]>"...]]></span>, and HTML comments, <span class="term"><!--...anything but not "-->"... --></span>. Neither HTML comments nor <span class="term">CDATA</span> sections can reside inside tags. HTML comments can exist anywhere else, but <span class="term">CDATA</span> sections can exist only where plain text is allowed (e.g., immediately inside <span class="term">td</span> element content but not immediately inside <span class="term">tr</span> element content).<br /> -<br /> -  htmLawed (function <span class="term">hl_cmtcd()</span>) handles HTML comments or <span class="term">CDATA</span> sections depending on the values of <span class="term">$config["comment"]</span> 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"><</span>, <span class="term">></span> and <span class="term">&</span> inside are changed to entities. If <span class="term">3</span>, they are left as such.<br /> -<br /> -  Note that for the last two cases, HTML comments and <span class="term">CDATA</span> sections will always be removed from tag content (function <span class="term">hl_tag()</span>).<br /> -<br /> -  Examples:<br /> -<br /> -  Input:<br /> - -<code class="code">    <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a></code> -<br /> -  Output (<span class="term">$config["comment"] = 0, $config["cdata"] = 2</span>):<br /> - -<code class="code">    &lt;-- home link --&gt;<a href="home.htm"><![CDATA[x=&amp;y]]>Home</a></code> -<br /> -  Output (<span class="term">$config["comment"] = 1, $config["cdata"] = 2</span>):<br /> - -<code class="code">    <a href="home.htm"><![CDATA[x=&amp;y]]>Home</a></code> -<br /> -  Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 2</span>):<br /> - -<code class="code">    <!-- home link --><a href="home.htm"><![CDATA[x=&amp;y]]>Home</a></code> -<br /> -  Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 1</span>):<br /> - -<code class="code">    <!-- home link --><a href="home.htm">Home</a></code> -<br /> -  Output (<span class="term">$config["comment"] = 3, $config["cdata"] = 3</span>):<br /> - -<code class="code">    <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a></code> -<br /> -<br /> -  For standard-compliance, comments are given the form <span class="term"><!--comment --></span>, and any <span class="term">--</span> in the content is made <span class="term">-</span>.<br /> -<br /> -  When <span class="term">$config["safe"] = 1</span>, CDATA sections and comments are considered plain text unless <span class="term">$config["comment"]</span> or <span class="term">$config["cdata"]</span> 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>  Tag-transformation for better XHTML-Strict -</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  If <span class="term">$config["make_tag_strict"]</span> 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 /> -  *  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 /> -  *  center - <span class="term">div style="text-align: center;"</span><br /> -  *  dir - <span class="term">ul</span><br /> -  *  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 /> -  *  font (face, size, color) -    <span class="term">span style="font-family: ; font-size: ; color: ;"</span> (size transformation <a href="http://style.cleverchimp.com/font_size_intervals/altintervals.html">reference</a>)<br /> -  *  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 /> -  *  menu - <span class="term">ul</span><br /> -  *  s - <span class="term">span style="text-decoration: line-through;"</span><br /> -  *  strike - <span class="term">span style="text-decoration: line-through;"</span><br /> -  *  u - <span class="term">span style="text-decoration: underline;"</span><br /> -<br /> -  For an element with a pre-existing <span class="term">style</span> attribute value, the extra style properties are appended.<br /> -<br /> -  Example input:<br /> -<br /> - -<code class="code">    <center></code> -<br /> - -<code class="code">     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>.</code> -<br /> - -<code class="code">    </center></code> -<br /> -<br /> -  The output:<br /> -<br /> - -<code class="code">    <div style="text-align: center;"></code> -<br /> - -<code class="code">     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>.</code> -<br /> - -<code class="code">    </div></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>  Tag balancing and proper nesting -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  If <span class="term">$config["balance"]</span> 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 /> -  Depending on the value of <span class="term">$config["keep_bad"]</span> (see <a href="#s2.2">section 2.2</a> and <a href="#s3.3">section 3.3</a>), illegal content may be removed or neutralized to plain text by converting < and > to entities:<br /> -<br /> -  <span class="term">0</span> - 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 /> -  <span class="term">1</span> - neutralize tags and keep element content<br /> -  <span class="term">2</span> - remove tags but keep element content<br /> -  <span class="term">3</span> and <span class="term">4</span> - like <span class="term">1</span> 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 /> -  <span class="term">5</span> and <span class="term">6</span> -  like <span class="term">3</span> and <span class="term">4</span>, but line-breaks, tabs and spaces are left<br /> -<br /> -  Example input (disallowing the <span class="term">p</span> element):<br /> -<br /> - -<code class="code">    <*> Pseudo-tags <*></code> -<br /> - -<code class="code">    <xml>Non-HTML tag xml</xml></code> -<br /> - -<code class="code">    <p></code> -<br /> - -<code class="code">    Disallowed tag p</code> -<br /> - -<code class="code">    </p></code> -<br /> - -<code class="code">    <ul>Bad<li>OK</li></ul></code> -<br /> -<br /> -  The output with <span class="term">$config["keep_bad"] = 1</span>:<br /> -<br /> - -<code class="code">    &lt;*&gt; Pseudo-tags &lt;*&gt;</code> -<br /> - -<code class="code">    &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;</code> -<br /> - -<code class="code">    &lt;p&gt;</code> -<br /> - -<code class="code">    Disallowed tag p</code> -<br /> - -<code class="code">    &lt;/p&gt;</code> -<br /> - -<code class="code">    <ul>Bad<li>OK</li></ul></code> -<br /> -<br /> -  The output with <span class="term">$config["keep_bad"] = 3</span>:<br /> -<br /> - -<code class="code">    &lt;*&gt; Pseudo-tags &lt;*&gt;</code> -<br /> - -<code class="code">    &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;</code> -<br /> - -<code class="code">    &lt;p&gt;</code> -<br /> - -<code class="code">    Disallowed tag p</code> -<br /> - -<code class="code">    &lt;/p&gt;</code> -<br /> - -<code class="code">    <ul><li>OK</li></ul></code> -<br /> -<br /> -  The output with <span class="term">$config["keep_bad"] = 6</span>:<br /> -<br /> - -<code class="code">    &lt;*&gt; Pseudo-tags &lt;*&gt;</code> -<br /> - -<code class="code">    Non-HTML tag xml</code> -<br /> -<br /> - -<code class="code">    Disallowed tag p</code> -<br /> -<br /> - -<code class="code">    <ul><li>OK</li></ul></code> -<br /> -<br /> -  An option like <span class="term">1</span> is useful, e.g., when a writer previews his submission, whereas one like <span class="term">3</span> is useful before content is finalized and made available to all.<br /> -<br /> -  <strong>Note:</strong> In the example above, unlike <span class="term"><*></span>, <span class="term"><xml></span> 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"><(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?></span> is considered a tag (phrase enclosed by the angled brackets <span class="term"><</span> and <span class="term">></span>, and starting [with an optional slash preceding] with an alphanumeric word that starts with an alphabet...).<br /> -<br /> -  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> 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> is set to <span class="term">1</span>.<br /> -<br /> -  Plain text and/or certain elements nested inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span> and <span class="term">noscript</span> 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:<input type="text" value="b" />C:<input type="text" value="c" /></span> is converted to <span class="term"><div>B:<input type="text" value="b" />C:<input type="text" value="c" /></div></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>  Elements requiring child elements -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  As per specs, the following elements require legal child elements nested inside them:<br /> -<br /> - -<code class="code">    blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul</code> -<br /> -<br /> -  In some cases, the specs stipulate the number and/or the ordering of the child elements. A <span class="term">table</span> 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 /> -  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 /> -  With <span class="term">$config["direct_list_nest"]</span> set to <span class="term">1</span>, htmLawed will allow direct nesting of an <span class="term">ol</span> or <span class="term">ul</span> list within another <span class="term">ol</span> or <span class="term">ul</span> without requiring the child list to be within an <span class="term">li</span> 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> 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>  Beautify or compact HTML -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  By default, htmLawed will neither <em>beautify</em> 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 /> -  As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside <span class="term">pre</span> 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> allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such <em>pretty</em> HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.<br /> -<br /> -  With the <span class="term">$config</span> 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> and <span class="term">textarea</span> elements, CDATA sections, and HTML comments are not subjected to the tidying process.<br /> -<br /> -  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 /> -  To <em>beautify</em>, <span class="term">$config["tidy"]</span> 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> or <span class="term">t</span> 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> and <span class="term">n</span> characters are used to specify line-break characters: <span class="term">n</span> for <span class="term">\n</span> (Unix/Mac OS X line-breaks), <span class="term">rn</span> or <span class="term">nr</span> for <span class="term">\r\n</span> (Windows/DOS line-breaks), or <span class="term">r</span> for <span class="term">\r</span>.<br /> -<br /> -  The <span class="term">$config["tidy"]</span> value of <span class="term">1</span> is equivalent to <span class="term">2s0n</span>. Other <span class="term">$config["tidy"]</span> values are read loosely: a value of <span class="term">4</span> 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 /> -  Input formatting using <span class="term">$config["tidy"]</span> 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>  Attributes -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> element (the non-standard <span class="term">embed</span> element is supported in htmLawed because of its widespread use), and the the <span class="term">xml:space</span> 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 /> -  When <span class="term">$config["deny_attribute"]</span> 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> can be set as a list of comma-separated names of the denied attributes. <span class="term">on*</span> 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> and <span class="term">onsubmit</span>.<br /> -<br /> -  Note that attributes specified in <span class="term">$config["deny_attribute"]</span> are denied globally, for all elements. To deny attributes for only specific elements, <span class="term">$spec</span> (see <a href="#s2.3">section 2.3</a>) can be used. <span class="term">$spec</span> can also be used to element-specifically permit an attribute otherwise denied through <span class="term">$config["deny_attribute"]</span>.<br /> -<br /> -  With <span class="term">$config["safe"] = 1</span> (<a href="#s3.6">section 3.6</a>), the <span class="term">on*</span> attributes are automatically disallowed.<br /> -<br /> -  <strong>Note</strong>: To deny all but a few attributes globally, a simpler way to specify <span class="term">$config["deny_attribute"]</span> would be to use the notation <span class="term">* -attribute1 -attribute2 ...</span>. Thus, a value of <span class="term">* -title -href</span> implies that except <span class="term">href</span> and <span class="term">title</span> (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> (<a href="#s3.6">section 3.6</a>) will have no effect on <span class="term">deny_attribute</span>.<br /> -<br /> -  htmLawed (function <span class="term">hl_tag()</span>) also:<br /> -<br /> -  *  Lower-cases attribute names<br /> -  *  Removes duplicate attributes (last one stays)<br /> -  *  Gives attributes the form <span class="term">name="value"</span> and single-spaces them, removing unnecessary white-spacing<br /> -  *  Provides <em>required</em> attributes (see <a href="#s3.4.1">section 3.4.1</a>)<br /> -  *  Double-quotes values and escapes any <span class="term">"</span> inside them<br /> -  *  Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point <span class="term">ad</span>) in the values with spaces<br /> -  *  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>  Auto-addition of XHTML-required attributes -</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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 /> -  *  area - alt (<span class="term">area</span>)<br /> -  *  area, img - src, alt (<span class="term">image</span>)<br /> -  *  bdo - dir (<span class="term">ltr</span>)<br /> -  *  form - action<br /> -  *  map - name<br /> -  *  optgroup - label<br /> -  *  param - name<br /> -  *  script - type (<span class="term">text/javascript</span>)<br /> -  *  textarea - rows (<span class="term">10</span>), cols (<span class="term">50</span>)<br /> -<br /> -  Additionally, with <span class="term">$config["xml:lang"]</span> set to <span class="term">1</span> or <span class="term">2</span>, if the <span class="term">lang</span> but not the <span class="term">xml:lang</span> 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:lang"]</span> set to <span class="term">2</span>, the <span class="term">lang</span> attribute is removed (XHTML 1.1 specs).<br /> -<br /> -  Note that the <span class="term">name</span> 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>  Duplicate/invalid <span class="term">id</span> values -</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  If <span class="term">$config["unique_ids"]</span> is <span class="term">1</span>, htmLawed (function <span class="term">hl_tag()</span>) removes <span class="term">id</span> attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, <span class="term">:</span>, <span class="term">.</span>, <span class="term">-</span> and <span class="term">_</span>) or duplicate. If <span class="term">$config["unique_ids"]</span> 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">:</span>, <span class="term">.</span>, <span class="term">_</span> and <span class="term">-</span>.<br /> -<br /> -  Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of <span class="term">id</span> values as it uses a global variable (<span class="term">$GLOBALS["hl_Ids"]</span> array). Further, an admin can restrict the use of certain <span class="term">id</span> values by presetting this variable before htmLawed is called into use. E.g.:<br /> -<br /> - -<code class="code">    $GLOBALS['hl_Ids'] = array('top'=>1, 'bottom'=>1, 'myform'=>1); // id values not allowed in input</code> -<br /> - -<code class="code">    $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>  URL schemes (protocols) and scripts in attribute values -</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> scheme is not permitted, then <span class="term"><a href="afp://domain.org"></span> becomes <span class="term"><a href="denied:afp://domain.org"></span>, and if Javascript is not permitted <span class="term"><a onclick="javascript:xss();"></span> becomes <span class="term"><a onclick="denied:javascript:xss();"></span>.<br /> -<br /> -  By default htmLawed permits these schemes in URLs for the <span class="term">href</span> attribute:<br /> -<br /> - -<code class="code">    aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet</code> -<br /> -<br /> -  Also, only <span class="term">file</span>, <span class="term">http</span> and <span class="term">https</span> are permitted in attributes whose names start with <span class="term">o</span> (like <span class="term">onmouseover</span>), and in these attributes that accept URLs:<br /> -<br /> - -<code class="code">    action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap</code> -<br /> -<br /> -  These default sets are used when <span class="term">$config["schemes"]</span> is not set (see <a href="#s2.2">section 2.2</a>). To over-ride the defaults, <span class="term">$config["schemes"]</span> is defined as a string of semi-colon-separated sub-strings of type <span class="term">attribute: comma-separated schemes</span>. E.g., <span class="term">href: mailto, http, https; onclick: javascript; src: http, https</span>. For unspecified attributes, <span class="term">file</span>, <span class="term">http</span> and <span class="term">https</span> are permitted. This can be changed by passing schemes for <span class="term">*</span> in <span class="term">$config["schemes"]</span>. E.g., <span class="term">href: mailto, http, https; *: https, https</span>.<br /> -<br /> -  <span class="term">*</span> can be put in the list of schemes to permit all protocols. E.g., <span class="term">style: *; img: http, https</span> results in protocols not being checked in <span class="term">style</span> 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 /> -  Thus, <em>to allow Javascript</em>, one can set <span class="term">$config["schemes"]</span> as <span class="term">href: mailto, http, https; *: http, https, javascript</span>, or <span class="term">href: mailto, http, https, javascript; *: http, https, javascript</span>, or <span class="term">*: *</span>, and so on.<br /> -<br /> -  As a side-note, one may find <span class="term">style: *</span> useful as URLs in <span class="term">style</span> 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 /> -  <span class="term">!</span> can be put in the list of schemes to disallow all protocols as well as <em>local</em> URLs. Thus, with <span class="term">href: http, style: !</span>, '<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>'.<br /> -<br /> -  <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> (e.g., <span class="term">dynsrc</span>) or starts with <span class="term">o</span> (e.g., <span class="term">onbeforecopy</span>).<br /> -<br /> -  With <span class="term">$config["safe"] = 1</span>, all URLs are disallowed in the <span class="term">style</span> 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>  Absolute & relative URLs in attribute values -</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  htmLawed can make absolute URLs in attributes like <span class="term">href</span> relative (<span class="term">$config["abs_url"]</span> is <span class="term">-1</span>), and vice versa (<span class="term">$config["abs_url"]</span> 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> (fragment), <span class="term">?name=Tim#show</span> (starting with query string), and <span class="term">;var=1?name=Tim#show</span> (starting with parameters). Further, this requires that <span class="term">$config["base_url"]</span> be set properly, with the <span class="term">://</span> and a trailing slash (<span class="term">/</span>), with no query string, etc. E.g., <span class="term">file:///D:/page/</span>, <span class="term">https://abc.com/x/y/</span>, or <span class="term">http://localhost/demo/</span> are okay, but <span class="term">file:///D:/page/?help=1</span>, <span class="term">abc.com/x/y/</span> and <span class="term">http://localhost/demo/index.htm</span> are not.<br /> -<br /> -  For making absolute URLs relative, only those URLs that have the <span class="term">$config["base_url"]</span> string at the beginning are converted. E.g., with <span class="term">$config["base_url"] = "https://abc.com/x/y/"</span>, <span class="term">https://abc.com/x/y/a.gif</span> and <span class="term">https://abc.com/x/y/z/b.gif</span> become <span class="term">a.gif</span> and <span class="term">z/b.gif</span> respectively, while <span class="term">https://abc.com/x/c.gif</span> is not changed.<br /> -<br /> -  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> 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>  Lower-cased, standard attribute values -</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> instead of <span class="term">input type="Password"</span>, if <span class="term">$config["lc_std_val"]</span> 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> attribute of <span class="term">button</span> or <span class="term">input</span>) are lower-cased:<br /> -<br /> - -<code class="code">    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">    a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml:space</code> -<br /> -<br /> -  The following <em>empty</em> (<em>minimized</em>) attributes are always assigned lower-cased values (same as the names):<br /> -<br /> - -<code class="code">    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>  Transformation of deprecated attributes -</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  If <span class="term">$config["no_deprecated_attr"]</span> 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> attributes (function <span class="term">hl_tag()</span>). Except for <span class="term">bordercolor</span> for <span class="term">table</span>, <span class="term">tr</span> 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 /> -  <strong>Note</strong>: The attribute <span class="term">target</span> for <span class="term">a</span> 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 /> -  *  align - for <span class="term">img</span> with value of <span class="term">left</span> or <span class="term">right</span>, becomes, e.g., <span class="term">float: left</span>; for <span class="term">div</span> and <span class="term">table</span> with value <span class="term">center</span>, becomes <span class="term">margin: auto</span>; all others become, e.g., <span class="term">text-align: right</span><br /> -<br /> -  *  bgcolor - E.g., <span class="term">bgcolor="#ffffff"</span> becomes <span class="term">background-color: #ffffff</span><br /> -  *  border - E.g., <span class="term">height= "10"</span> becomes <span class="term">height: 10px</span><br /> -  *  bordercolor - E.g., <span class="term">bordercolor=#999999</span> becomes <span class="term">border-color: #999999;</span><br /> -  *  compact - <span class="term">font-size: 85%</span><br /> -  *  clear - E.g., 'clear="all" becomes <span class="term">clear: both</span><br /> -<br /> -  *  height - E.g., <span class="term">height= "10"</span> becomes <span class="term">height: 10px</span> and <span class="term">height="*"</span> becomes <span class="term">height: auto</span><br /> -<br /> -  *  hspace - E.g., <span class="term">hspace="10"</span> becomes <span class="term">margin-left: 10px; margin-right: 10px</span><br /> -  *  language - <span class="term">language="VBScript"</span> becomes <span class="term">type="text/vbscript"</span><br /> -  *  name - E.g., <span class="term">name="xx"</span> becomes <span class="term">id="xx"</span><br /> -  *  noshade - <span class="term">border-style: none; border: 0; background-color: gray; color: gray</span><br /> -  *  nowrap - <span class="term">white-space: nowrap</span><br /> -  *  size - E.g., <span class="term">size="10"</span> becomes <span class="term">height: 10px</span><br /> -  *  start - removed<br /> -  *  type - E.g., <span class="term">type="i"</span> becomes <span class="term">list-style-type: lower-roman</span><br /> -  *  value - removed<br /> -  *  vspace - E.g., <span class="term">vspace="10"</span> becomes <span class="term">margin-top: 10px; margin-bottom: 10px</span><br /> -  *  width - like <span class="term">height</span><br /> -<br /> -  Example input:<br /> -<br /> - -<code class="code">    <img src="j.gif" alt="image" name="dad's" /><img src="k.gif" alt="image" id="dad_off" name="dad" /></code> -<br /> - -<code class="code">    <br clear="left" /></code> -<br /> - -<code class="code">    <hr noshade size="1" /></code> -<br /> - -<code class="code">    <img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding:5px;" /></code> -<br /> - -<code class="code">    <table width="50em" align="center" bgcolor="red"></code> -<br /> - -<code class="code">     <tr></code> -<br /> - -<code class="code">      <td width="20%"></code> -<br /> - -<code class="code">       <div align="center"></code> -<br /> - -<code class="code">        <h3 align="right">Section</h3></code> -<br /> - -<code class="code">        <p align="right">Para</p></code> -<br /> - -<code class="code">        <ol type="a" start="e"><li value="x">First item</li></ol></code> -<br /> - -<code class="code">       </div></code> -<br /> - -<code class="code">      </td></code> -<br /> - -<code class="code">      <td width="*"></code> -<br /> - -<code class="code">       <ol type="1"><li>First item</li></ol></code> -<br /> - -<code class="code">      </td></code> -<br /> - -<code class="code">     </tr></code> -<br /> - -<code class="code">    </table></code> -<br /> - -<code class="code">    <br clear="all" /></code> -<br /> -<br /> -  And the output with <span class="term">$config["no_deprecated_attr"] = 1</span>:<br /> -<br /> - -<code class="code">    <img src="j.gif" alt="image" /><img src="k.gif" alt="image" id="dad_off" /></code> -<br /> - -<code class="code">    <br style="clear: left;" /></code> -<br /> - -<code class="code">    <hr style="border-style: none; border: 0; background-color: gray; color: gray; size: 1px;" /></code> -<br /> - -<code class="code">    <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" /></code> -<br /> - -<code class="code">    <table width="50em" style="margin: auto; background-color: red;"></code> -<br /> - -<code class="code">     <tr></code> -<br /> - -<code class="code">      <td style="width: 20%;"></code> -<br /> - -<code class="code">       <div style="margin: auto;"></code> -<br /> - -<code class="code">        <h3 style="text-align: right;">Section</h3></code> -<br /> - -<code class="code">        <p style="text-align: right;">Para</p></code> -<br /> - -<code class="code">        <ol style="list-style-type: lower-latin;"><li>First item</li></ol></code> -<br /> - -<code class="code">       </div></code> -<br /> - -<code class="code">      </td></code> -<br /> - -<code class="code">      <td style="width: auto;"></code> -<br /> - -<code class="code">       <ol style="list-style-type: decimal;"><li>First item</li></ol></code> -<br /> - -<code class="code">      </td></code> -<br /> - -<code class="code">     </tr></code> -<br /> - -<code class="code">    </table></code> -<br /> - -<code class="code">    <br style="clear: both;" /></code> -<br /> -<br /> -  For <span class="term">lang</span>, deprecated in XHTML 1.1, transformation is taken care of through <span class="term">$config["xml:lang"]</span>; see <a href="#s3.4.1">section 3.4.1</a>.<br /> -<br /> -  The attribute <span class="term">name</span> 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> if an <span class="term">id</span> attribute doesn't exist and if the <span class="term">name</span> value is appropriate for <span class="term">id</span>. For such replacements for <span class="term">a</span> and <span class="term">map</span>, for which the <span class="term">name</span> attribute is deprecated in XHTML 1.1, <span class="term">$config["no_deprecated_attr"]</span> should be set to <span class="term">2</span> (when set to <span class="term">1</span>, for these two elements, the <span class="term">name</span> 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>  Anti-spam & <span class="term">href</span> -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  htmLawed (function <span class="term">hl_tag()</span>) can check the <span class="term">href</span> attribute values (link addresses) as an anti-spam (email or link spam) measure.<br /> -<br /> -  If <span class="term">$config["anti_mail_spam"]</span> is not <span class="term">0</span>, the <span class="term">@</span> of email addresses in <span class="term">href</span> values like <span class="term">mailto:a@b.com</span> 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"><remove_this_antispam>@</span> (makes the example address <span class="term">a<remove_this_antispam>@b.com</span>).<br /> -<br /> -  For regular links, one can choose to have a <span class="term">rel</span> attribute with <span class="term">nofollow</span> 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> value altogether (disable the link).<br /> -<br /> -  For use of these options, <span class="term">$config["anti_link_spam"]</span> should be set as an array with values <span class="term">regex1</span> 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> or <span class="term">regex2</span> should be PHP- and PCRE-compatible regular expression patterns: <span class="term">href</span> values will be matched against them and those matching the pattern will accordingly be treated.<br /> -<br /> -  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 /> -  An example, to have a <span class="term">rel</span> attribute with <span class="term">nofollow</span> for all links, and to disable links that do not point to domains <span class="term">abc.com</span> and <span class="term">xyz.org</span>:<br /> -<br /> - -<code class="code">    $config["anti_link_spam"] = array('`.`', '`://\W*(?!(abc\.com|xyz\.org))`');</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>  Inline style properties -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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> attributes. (CSS properties like <span class="term">background-image</span> 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> to <span class="term">1</span> (default setting). Note that when <span class="term">$config["css_expression"]</span> is set to <span class="term">1</span>, htmLawed will remove <span class="term">/*</span> from the <span class="term">style</span> values.<br /> -<br /> -  <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> 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:*..."</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> values through the <span class="term">hook_tag</span> parameter (see <a href="#s3.4.9">section 3.4.9</a>).<br /> -<br /> -  It is also possible to have htmLawed let through any <span class="term">style</span> value by setting <span class="term">$config["style_pass"]</span> to <span class="term">1</span>.<br /> -<br /> -  As such, it is better to set up a CSS file with class declarations, disallow the <span class="term">style</span> attribute, set a <span class="term">$spec</span> rule (see <a href="#s2.3">section 2.3</a>) for <span class="term">class</span> for the <span class="term">oneof</span> or <span class="term">match</span> parameter, and ask writers to make use of the <span class="term">class</span> 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>  Hook function for tag content -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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 /> -  When <span class="term">$config</span> parameter <span class="term">hook_tag</span> 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> 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"><element_name attribute_1_name="attribute_1_value"...></span> (for empty elements like <span class="term">img</span> and <span class="term">input</span>, the element-closing slash <span class="term">/</span> should also be included), etc.<br /> -<br /> -  Any <span class="term">hook_tag</span> function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as <span class="term">a</span> in the closing <span class="term"></a></span> tag of the element <span class="term"><a href="http://cnn.com">CNN</a></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"></a></span>).<br /> -<br /> -  This is a <strong>powerful functionality</strong> that can be exploited for various objectives: consolidate-and-convert inline <span class="term">style</span> attributes to <span class="term">class</span>, convert <span class="term">embed</span> elements to <span class="term">object</span>, permit only one <span class="term">caption</span> element in a <span class="term">table</span> element, disallow embedding of certain types of media, <strong>inject HTML</strong>, use <a href="http://csstidy.sourceforge.net">CSSTidy</a> to sanitize <span class="term">style</span> attribute values, etc.<br /> -<br /> -  As an example, the custom hook code below can be used to force a series of specifically ordered <span class="term">id</span> attributes on all elements, and a specific <span class="term">param</span> element inside all <span class="term">object</span> elements:<br /> -<br /> - -<code class="code">    function my_tag_function($element, $attribute_array=0){</code> -<br /> -<br /> - -<code class="code">      // If second argument is not received, it means a closing tag is being handled</code> -<br /> - -<code class="code">      if(is_numeric($attribute_array)){</code> -<br /> - -<code class="code">        return "</$element>";</code> -<br /> - -<code class="code">      }</code> -<br /> -<br /> - -<code class="code">      static $id = 0;</code> -<br /> - -<code class="code">      // Remove any duplicate element</code> -<br /> - -<code class="code">      if($element == 'param' && isset($attribute_array['allowscriptaccess'])){</code> -<br /> - -<code class="code">        return '';</code> -<br /> - -<code class="code">      }</code> -<br /> -<br /> - -<code class="code">      $new_element = '';</code> -<br /> -<br /> - -<code class="code">      // Force a serialized ID number</code> -<br /> - -<code class="code">      $attribute_array['id'] = 'my_'. $id;</code> -<br /> - -<code class="code">      ++$id;</code> -<br /> -<br /> - -<code class="code">      // Inject param for allowscriptaccess</code> -<br /> - -<code class="code">      if($element == 'object'){</code> -<br /> - -<code class="code">        $new_element = '<param id='my_'. $id; allowscriptaccess="never" />';</code> -<br /> - -<code class="code">        ++$id;</code> -<br /> - -<code class="code">      }</code> -<br /> -<br /> - -<code class="code">      $string = '';</code> -<br /> - -<code class="code">      foreach($attribute_array as $k=>$v){</code> -<br /> - -<code class="code">        $string .= " {$k}=\"{$v}\"";</code> -<br /> - -<code class="code">      }</code> -<br /> -<br /> - -<code class="code">      static $empty_elements = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1);</code> -<br /> -<br /> - -<code class="code">      return "<{$element}{$string}". (isset($in_array($element, $empty_elements) ? ' /' : ''). '>'. $new_element;</code> -<br /> - -<code class="code">    }</code> -<br /> -<br /> -  The <span class="term">hook_tag</span> parameter is different from the <span class="term">hook</span> parameter (<a href="#s3.7">section 3.7</a>).<br /> -<br /> -  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> website.<br /> - -</div> -</div> -<div class="sub-section"><h3> -<a name="s3.5" id="s3.5"></a><span class="item-no">3.5</span>  Simple configuration directive for most valid XHTML -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  If <span class="term">$config["valid_xhtml"]</span> is set to <span class="term">1</span>, some relevant <span class="term">$config</span> parameters (indicated by <span class="term">~</span> in <a href="#s2.2">section 2.2</a>) are auto-adjusted. This allows one to pass the <span class="term">$config</span> argument with a simpler value. If a value for a parameter auto-set through <span class="term">valid_xhtml</span> 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>  Simple configuration directive for most <em>safe</em> HTML -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  <em>Safe</em> 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> and <span class="term">object</span>, and attributes such as <span class="term">onmouseover</span> and <span class="term">style</span> are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered <span class="term">safe</span> depends on the nature of the web application and the trust-level accorded to its users.<br /> -<br /> -  htmLawed allows an admin to use <span class="term">$config["safe"]</span> to auto-adjust multiple <span class="term">$config</span> parameters (such as <span class="term">elements</span> which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by <span class="term">"</span> in <a href="#s2.2">section 2.2</a>). Thus, one can pass the <span class="term">$config</span> argument with a simpler value.<br /> -<br /> -  With the value of <span class="term">1</span>, htmLawed considers <span class="term">CDATA</span> 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> and <span class="term">script</span> elements, and the <span class="term">on*</span> attributes like <span class="term">onclick</span>. ( There are <span class="term">$config</span> parameters like <span class="term">css_expression</span> that are not affected by the value set for <span class="term">safe</span> but whose default values still contribute towards a more <em>safe</em> 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:url(http://danger)"</span> becomes <span class="term">style="moz-binding:url(denied:http://danger)"</span>.<br /> -<br /> -  Admins, however, may still want to completely deny the <span class="term">style</span> attribute, e.g., with code like<br /> -<br /> - -<code class="code">    $processed = htmLawed($text, array('safe'=>1, 'deny_attribute'=>'style'));</code> -<br /> -<br /> -  Permitting the <span class="term">style</span> 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> values. It does provide ways for the code-developer implementing htmLawed to do such checks through the <span class="term">$spec</span> argument, and through the <span class="term">hook_tag</span> parameter (see <a href="#s3.4.8">section 3.4.8</a> for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.<br /> -<br /> -  If a value for a parameter auto-set through <span class="term">safe</span> is still manually provided, then that value can over-ride the auto-set value. E.g., with <span class="term">$config["safe"] = 1</span> and <span class="term">$config["elements"] = "*+script"</span>, <span class="term">script</span>, but not <span class="term">applet</span>, is allowed.<br /> -<br /> -  A page illustrating the efficacy of htmLawed's anti-XSS abilities with <span class="term">safe</span> set to <span class="term">1</span> against XSS vectors listed by <a href="http://ha.ckers.org/xss.html">RSnake</a> 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>  Using a hook function -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  If <span class="term">$config["hook"]</span> 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> before starting the main work (but after handling of characters, entities, HTML comments and <span class="term">CDATA</span> sections -- see code for function <span class="term">htmLawed()</span>).<br /> -<br /> -  The hook function also allows one to alter the <em>finalized</em> values of <span class="term">$config</span> and <span class="term">$spec</span>.<br /> -<br /> -  Note that the <span class="term">hook</span> parameter is different from the <span class="term">hook_tag</span> parameter (<a href="#s3.4.9">section 3.4.9</a>).<br /> -<br /> -  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> website.<br /> - -</div> -<div class="sub-section"><h3> -<a name="s3.8" id="s3.8"></a><span class="item-no">3.8</span>  Obtaining <em>finalized</em> parameter values -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  htmLawed can assign the <em>finalized</em> <span class="term">$config</span> and <span class="term">$spec</span> 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> value, <span class="term">spec</span>, with the <span class="term">$spec</span> 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> 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 /> -  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>  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 /> -  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"><</span>, <span class="term">></span> and <span class="term">&</span> 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 /> -  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"><</span>, <span class="term">></span> and <span class="term">&</span> 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 /> -  An example (mixed HTML and PHP code in input text):<br /> -<br /> - -<code class="code">    $text = preg_replace('`<\?php(.+?)\?>`sm', "\x83?php\\1?\x84", $text);</code> -<br /> - -<code class="code">    $processed = htmLawed($text);</code> -<br /> - -<code class="code">    $processed = preg_replace('`\x83\?php(.+?)\?\x84`sm', '<?php$1?>', $processed);</code> -<br /> -<br /> -  This code will not work if <span class="term">$config["clean_ms_char"]</span> is set to <span class="term">1</span> (<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> to <span class="term">7</span>, and use of these characters as markers in the logic of hook functions may cause issues.)<br /> -<br /> -  Admins may also be able to use <span class="term">$config["and_mark"]</span> 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>  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>  Support -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  A careful re-reading of this documentation will very likely answer your questions.<br /> -<br /> -  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>  Known issues -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  See <a href="#s2.8">section 2.8</a>.<br /> -<br /> -  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>  Change-log -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the <span class="term">htmLawed.php</span> file may be updated independently if the secondary files are revised.)<br /> -<br /> -  <em>Version number - Release date. Notes</em><br /> -<br /> -  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 /> -  1.1.10 - 22 October 2011. Fix for a bug in the <span class="term">tidy</span> functionality that caused the entire input to be replaced with a single space; new parameter, <span class="term">$config["direct_list_nest"]</span> to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)<br /> -<br /> -  1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of <span class="term">li</span> within <span class="term">dir</span><br /> -<br /> -  1.1.9.4 - 3 July 2010. Parameter <span class="term">schemes</span> now accepts <span class="term">!</span> 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> properties was not checked was fixed.<br /> -<br /> -  1.1.9.3 - 17 May 2010. Checks for correct nesting of <span class="term">param</span><br /> -<br /> -  1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes<br /> -<br /> -  1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for <span class="term">flashvars</span> attribute for <span class="term">embed</span><br /> -<br /> -  1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values<br /> -<br /> -  1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice<br /> -<br /> -  1.1.8 - 23 April 2009. Parameter <span class="term">deny_attribute</span> now accepts the wild-card <span class="term">*</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 /> -  1.1.7 - 11-12 March 2009. Attributes globally denied through <span class="term">deny_attribute</span> can be allowed element-specifically through <span class="term">$spec</span>; <span class="term">$config["style_pass"]</span> allowing letting through any <span class="term">style</span> value introduced; altered logic to catch certain types of dynamic crafted CSS expressions<br /> -<br /> -  1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions<br /> -<br /> -  1.1.2 - 22 January 2009. Fixed bug in parsing of <span class="term">font</span> attributes during tag transformation<br /> -<br /> -  1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent<br /> -<br /> -  1.1 - 29 June 2008. <span class="term">$config["hook_tag"]</span> and <span class="term">$config["format"]</span> introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug<br /> -<br /> -  1.0.9 - 11 June 2008. Fixed bug in invalid HTML code-point entity check<br /> -<br /> -  1.0.8 - 15 May 2008. <span class="term">bordercolor</span> attribute for <span class="term">table</span>, <span class="term">td</span> and <span class="term">tr</span><br /> -<br /> -  1.0.7 - 1 May 2008. Support for <span class="term">wmode</span> attribute for <span class="term">embed</span>; <span class="term">$config["show_setting"]</span> introduced; improved <span class="term">$config["elements"]</span> evaluation<br /> -<br /> -  1.0.6 - 20 April 2008. <span class="term">$config["and_mark"]</span> introduced<br /> -<br /> -  1.0.5 - 12 March 2008. <span class="term">style</span> URL schemes essentially disallowed when $config <span class="term">safe</span> is on; improved regex for CSS expression search<br /> -<br /> -  1.0.4 - 10 March 2008. Improved corrections for <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span> and <span class="term">noscript</span><br /> -<br /> -  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> directly inside <span class="term">table</span> fixed; <span class="term">safe</span> <span class="term">$config</span> parameter added<br /> -<br /> -  1.0.2 - 13 February 2008. Improved implementation of <span class="term">$config["keep_bad"]</span><br /> -<br /> -  1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions (<span class="term">hl_tag()</span> and <span class="term">hl_prot()</span>); no error display with <span class="term">hl_regex()</span><br /> -<br /> -  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>  Testing -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  To test htmLawed using a form interface, a <a href="htmLawedTest.php">demo</a> web-page is provided with the htmLawed distribution (<span class="term">htmLawed.php</span> and <span class="term">htmLawedTest.php</span> should be in the same directory on the web-server). A file with <a href="htmLawed_TESTCASE.txt">test-cases</a> 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>  Upgrade, & old versions -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  Upgrading is as simple as replacing the previous version of <span class="term">htmLawed.php</span> (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 /> -  <strong>Important</strong>  The following upgrades may affect the functionality of a specific htmLawed as indicated by their corresponding notes:<br /> -<br /> -  (1) From version 1.1-1.1.10 to 1.1.11, if a <span class="term">hook_tag</span> 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> function receives only the element name. The <span class="term">hook_tag</span> function therefore may have to be edited. See <a href="#s3.4.9">section 3.4.9</a>.<br /> -<br /> -  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>  Comparison with <span class="term">HTMLPurifier</span> -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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 /> -  *  does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)<br /> -<br /> -  *  is 15-20 times bigger (scores of files totalling more than 750 kb)<br /> -<br /> -  *  consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)<br /> -<br /> -  *  is expectedly slower<br /> -<br /> -  *  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> illegal)<br /> -<br /> -  *  lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)<br /> -<br /> -  *  has poor documentation<br /> -<br /> -  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> 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>  Use through application plug-ins/modules -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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>  Use in non-PHP applications -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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>  Donate -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  A donation in any currency and amount to appreciate or support this software can be sent by <a href="http://paypal.com">PayPal</a> 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>  Acknowledgements -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  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 /> -  Thank you!<br /> - -</div> -</div> -<div class="section"><h2> -<a name="s5" id="s5"></a><span class="item-no">5</span>  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>  Characters discouraged in XHTML -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  Characters represented by the following hexadecimal code-points are <em>not</em> invalid, even though some validators may issue messages stating otherwise.<br /> -<br /> -  <span class="term">7f</span> to <span class="term">84</span>, <span class="term">86</span> to <span class="term">9f</span>, <span class="term">fdd0</span> 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> 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>  Valid attribute-element combinations -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  Valid attribute-element combinations as per W3C specs.<br /> -<br /> -  *  includes deprecated attributes (marked <span class="term">^</span>), attributes for the non-standard <span class="term">embed</span> element (marked <span class="term">*</span>), and the proprietary <span class="term">bordercolor</span> (marked <span class="term">~</span>)<br /> -  *  only non-frameset, HTML body elements<br /> -  *  <span class="term">name</span> for <span class="term">a</span> and <span class="term">map</span>, and <span class="term">lang</span> are invalid in XHTML 1.1<br /> -  *  <span class="term">target</span> is valid for <span class="term">a</span> in XHTML 1.1 and higher<br /> -  *  <span class="term">xml:space</span> is only for XHTML 1.1<br /> -<br /> -  abbr - td, th<br /> -  accept - form, input<br /> -  accept-charset - form<br /> -  accesskey - a, area, button, input, label, legend, textarea<br /> -  action - form<br /> -  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 /> -  alt - applet, area, img, input<br /> -  archive - applet, object<br /> -  axis - td, th<br /> -  bgcolor - embed, table^, tr^, td^, th^<br /> -  border - table, img^, object^<br /> -  bordercolor~ - table, td, tr<br /> -  cellpadding - table<br /> -  cellspacing - table<br /> -  char - col, colgroup, tbody, td, tfoot, th, thead, tr<br /> -  charoff - col, colgroup, tbody, td, tfoot, th, thead, tr<br /> -  charset - a, script<br /> -  checked - input<br /> -  cite - blockquote, q, del, ins<br /> -  classid - object<br /> -  clear - br^<br /> -  code - applet<br /> -  codebase - object, applet<br /> -  codetype - object<br /> -  color - font<br /> -  cols - textarea<br /> -  colspan - td, th<br /> -  compact - dir, dl^, menu, ol^, ul^<br /> -  coords - area, a<br /> -  data - object<br /> -  datetime - del, ins<br /> -  declare - object<br /> -  defer - script<br /> -  dir - bdo<br /> -  disabled - button, input, optgroup, option, select, textarea<br /> -  enctype - form<br /> -  face - font<br /> -  flashvars* - embed<br /> -  for - label<br /> -  frame - table<br /> -  frameborder - iframe<br /> -  headers - td, th<br /> -  height - embed, iframe, td^, th^, img, object, applet<br /> -  href - a, area<br /> -  hreflang - a<br /> -  hspace - applet, img^, object^<br /> -  ismap - img, input<br /> -  label - option, optgroup<br /> -  language - script^<br /> -  longdesc - img, iframe<br /> -  marginheight - iframe<br /> -  marginwidth - iframe<br /> -  maxlength - input<br /> -  method - form<br /> -  model* - embed<br /> -  multiple - select<br /> -  name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param<br /> -  nohref - area<br /> -  noshade - hr^<br /> -  nowrap - td^, th^<br /> -  object - applet<br /> -  onblur - a, area, button, input, label, select, textarea<br /> -  onchange - input, select, textarea<br /> -  onfocus - a, area, button, input, label, select, textarea<br /> -  onreset - form<br /> -  onselect - input, textarea<br /> -  onsubmit - form<br /> -  pluginspage* - embed<br /> -  pluginurl* - embed<br /> -  prompt - isindex<br /> -  readonly - textarea, input<br /> -  rel - a<br /> -  rev - a<br /> -  rows - textarea<br /> -  rowspan - td, th<br /> -  rules - table<br /> -  scope - td, th<br /> -  scrolling - iframe<br /> -  selected - option<br /> -  shape - area, a<br /> -  size - hr^, font, input, select<br /> -  span - col, colgroup<br /> -  src - embed, script, input, iframe, img<br /> -  standby - object<br /> -  start - ol^<br /> -  summary - table<br /> -  tabindex - a, area, button, input, object, select, textarea<br /> -  target - a^, area, form<br /> -  type - a, embed, object, param, script, input, li^, ol^, ul^, button<br /> -  usemap - img, input, object<br /> -  valign - col, colgroup, tbody, td, tfoot, th, thead, tr<br /> -  value - input, option, param, button, li^<br /> -  valuetype - param<br /> -  vspace - applet, img^, object^<br /> -  width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^<br /> -  wmode - embed<br /> -  xml:space - pre, script, style<br /> -<br /> -  These are allowed in all but the shown elements:<br /> -<br /> -  class - param, script<br /> -  dir - applet, bdo, br, iframe, param, script<br /> -  id - script<br /> -  lang - applet, br, iframe, param, script<br /> -  onclick - applet, bdo, br, font, iframe, isindex, param, script<br /> -  ondblclick - applet, bdo, br, font, iframe, isindex, param, script<br /> -  onkeydown - applet, bdo, br, font, iframe, isindex, param, script<br /> -  onkeypress - applet, bdo, br, font, iframe, isindex, param, script<br /> -  onkeyup - applet, bdo, br, font, iframe, isindex, param, script<br /> -  onmousedown - applet, bdo, br, font, iframe, isindex, param, script<br /> -  onmousemove - applet, bdo, br, font, iframe, isindex, param, script<br /> -  onmouseout - applet, bdo, br, font, iframe, isindex, param, script<br /> -  onmouseover - applet, bdo, br, font, iframe, isindex, param, script<br /> -  onmouseup - applet, bdo, br, font, iframe, isindex, param, script<br /> -  style - param, script<br /> -  title - param, script<br /> -  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>  CSS 2.1 properties accepting URLs -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  background<br /> -  background-image<br /> -  content<br /> -  cue-after<br /> -  cue-before<br /> -  cursor<br /> -  list-style<br /> -  list-style-image<br /> -  play-during<br /> - -</div> -<div class="sub-section"><h3> -<a name="s5.4" id="s5.4"></a><span class="item-no">5.4</span>  Microsoft Windows 1252 character replacements -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  Key: <span class="term">d</span> double, <span class="term">l</span> left, <span class="term">q</span> quote, <span class="term">r</span> right, <span class="term">s.</span> single<br /> -<br /> -  Code-point (decimal) - hexadecimal value - replacement entity - represented character<br /> -<br /> -  127 - 7f - (removed) - (not used)<br /> -  128 - 80 - &#8364; - euro<br /> -  129 - 81 - (removed) - (not used)<br /> -  130 - 82 - &#8218; - baseline s. q<br /> -  131 - 83 - &#402; - florin<br /> -  132 - 84 - &#8222; - baseline d q<br /> -  133 - 85 - &#8230; - ellipsis<br /> -  134 - 86 - &#8224; - dagger<br /> -  135 - 87 - &#8225; - d dagger<br /> -  136 - 88 - &#710; - circumflex accent<br /> -  137 - 89 - &#8240; - permile<br /> -  138 - 8a - &#352; - S Hacek<br /> -  139 - 8b - &#8249; - l s. guillemet<br /> -  140 - 8c - &#338; - OE ligature<br /> -  141 - 8d - (removed) - (not used)<br /> -  142 - 8e - &#381; - Z dieresis<br /> -  143 - 8f - (removed) - (not used)<br /> -  144 - 90 - (removed) - (not used)<br /> -  145 - 91 - &#8216; - l s. q<br /> -  146 - 92 - &#8217; - r s. q<br /> -  147 - 93 - &#8220; - l d q<br /> -  148 - 94 - &#8221; - r d q<br /> -  149 - 95 - &#8226; - bullet<br /> -  150 - 96 - &#8211; - en dash<br /> -  151 - 97 - &#8212; - em dash<br /> -  152 - 98 - &#732; - tilde accent<br /> -  153 - 99 - &#8482; - trademark<br /> -  154 - 9a - &#353; - s Hacek<br /> -  155 - 9b - &#8250; - r s. guillemet<br /> -  156 - 9c - &#339; - oe ligature<br /> -  157 - 9d - (removed) - (not used)<br /> -  158 - 9e - &#382; - z dieresis<br /> -  159 - 9f - &#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>  URL format -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  An <em>absolute</em> URL has a <span class="term">protocol</span> or <span class="term">scheme</span>, a <span class="term">network location</span> or <span class="term">hostname</span>, and, optional <span class="term">path</span>, <span class="term">parameters</span>, <span class="term">query</span> and <span class="term">fragment</span> segments. Thus, an absolute URL has this generic structure:<br /> -<br /> - -<code class="code">    (scheme) : (//network location) /(path) ;(parameters) ?(query) #(fragment)</code> -<br /> -<br /> -  The schemes can only contain letters, digits, <span class="term">+</span>, <span class="term">.</span> and <span class="term">-</span>. Hostname is the portion after the <span class="term">//</span> and up to the first <span class="term">/</span> (if any; else, up to the end) when <span class="term">:</span> is followed by a <span class="term">//</span> (e.g., <span class="term">abc.com</span> in <span class="term">ftp://abc.com/def</span>); otherwise, it consists of everything after the <span class="term">:</span> (e.g., <span class="term">def@abc.com</span> in mailto:def@abc.com').<br /> -<br /> -  <em>Relative</em> URLs do not have explicit schemes and network locations; such values are inherited from a <em>base</em> URL.<br /> - -</div> -<div class="sub-section"><h3> -<a name="s5.6" id="s5.6"></a><span class="item-no">5.6</span>  Brief on htmLawed code -</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" /> -<br /> -  Much of the code's logic and reasoning can be understood from the documentation above.<br /> -<br /> -  The <strong>output</strong> of htmLawed is a text string containing the processed input. There is no custom error tracking.<br /> -<br /> -  <strong>Function arguments</strong> for htmLawed are:<br /> -<br /> -  *  <span class="term">$in</span> - 1st argument; a text string; the <strong>input text</strong> to be processed. Any extraneous slashes added by PHP when <em>magic quotes</em> are enabled should be removed beforehand using PHP's <span class="term">stripslashes()</span> function.<br /> -<br /> -  *  <span class="term">$config</span> - 2nd argument; an associative array; optional (named <span class="term">$C</span> in htmLawed code). The array has keys with names like <span class="term">balance</span> 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> (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> <span class="term">$config</span> is thus a filtered and possibly larger array.<br /> -<br /> -  *  <span class="term">$spec</span> - 3rd argument; a text string; optional. The string has rules, written in an htmLawed-designated format, <strong>specifying</strong> element-specific attribute and attribute value restrictions. Function <span class="term">hl_spec()</span> is used to convert the string to an associative-array for internal use. <em>Finalized</em> <span class="term">$spec</span> is thus an array.<br /> -<br /> -  <em>Finalized</em> <span class="term">$config</span> and <span class="term">$spec</span> are made <strong>global variables</strong> 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> values, the <span class="term">show_settings</span> parameter of <span class="term">$config</span> 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> attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.<br /> -<br /> -  Except for the main function <span class="term">htmLawed()</span> and the functions <span class="term">kses()</span> and <span class="term">kses_hook()</span>, htmLawed's functions are <strong>name-spaced</strong> using the <span class="term">hl_</span> prefix. The <strong>functions</strong> and their roles are:<br /> -<br /> -  *  <span class="term">hl_attrval</span> - checking attribute values against $spec<br /> -  *  <span class="term">hl_bal</span> - tag balancing<br /> -  *  <span class="term">hl_cmtcd</span> - handling CDATA sections and HTML comments<br /> -  *  <span class="term">hl_ent</span> - entity handling<br /> -  *  <span class="term">hl_prot</span> - checking a URL scheme/protocol<br /> -  *  <span class="term">hl_regex</span> - checking syntax of a regular expression<br /> -  *  <span class="term">hl_spec</span> - converting user-supplied $spec value to one used by htmLawed internally<br /> -  *  <span class="term">hl_tag</span> - handling tags<br /> -  *  <span class="term">hl_tag2</span> - transforming tags<br /> -  *  <span class="term">hl_tidy</span> - compact/beautify HTML<br /> -  *  <span class="term">hl_version</span> - reporting htmLawed version<br /> -  *  <span class="term">htmLawed</span> - main function<br /> -  *  <span class="term">kses</span> - main function of <span class="term">kses</span><br /> -  *  <span class="term">kses_hook</span> - hook function of <span class="term">kses</span><br /> -<br /> -  The last two are for compatibility with pre-existing code using the <span class="term">kses</span> script. htmLawed's <span class="term">kses()</span> basically passes on the filtering task to <span class="term">htmLawed()</span> function after deciphering <span class="term">$config</span> and <span class="term">$spec</span> from the argument values supplied to it. <span class="term">kses_hook()</span> is an empty function and is meant for being filled with custom code if the <span class="term">kses</span> script users were using one.<br /> -<br /> -  <span class="term">htmLawed()</span> finalizes <span class="term">$spec</span> (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> involves setting default values if an inappropriate or invalid one is supplied. This includes calling <span class="term">hl_regex()</span> 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> then removes invalid characters like nulls and <span class="term">x01</span> 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> with the help of <span class="term">hl_cmtcd()</span>. When retained, the <span class="term"><</span> and <span class="term">></span> characters identifying them, and the <span class="term"><</span>, <span class="term">></span> and <span class="term">&</span> characters inside them, are replaced with control characters (code-points <span class="term">1</span> to <span class="term">5</span>) till any tag balancing is completed.<br /> -<br /> -  After this <em>initial processing</em> <span class="term">htmLawed()</span> identifies tags using regex and processes them with the help of <span class="term">hl_tag()</span> --  a large function that analyzes tag content, filtering it as per HTML standards, <span class="term">$config</span> and <span class="term">$spec</span>. Among other things, <span class="term">hl_tag()</span> 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> rules using <span class="term">hl_attrval()</span>, and checks URL protocols using <span class="term">hl_prot()</span>. <span class="term">htmLawed()</span> 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"><</span>, <span class="term">></span> and <span class="term">&</span> characters inside <span class="term">pre</span>, <span class="term">script</span> and <span class="term">textarea</span> elements, and HTML comments and CDATA sections with control characters (code-points <span class="term">1</span> to <span class="term">5</span>, and <span class="term">7</span>).<br /> -<br /> -  htmLawed permits the use of custom code or <strong>hook functions</strong> 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> once the tag content is finalized (see <a href="#s3.4.9">section 3.4.9</a>).<br /> -<br /> -  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>  About htmLawed</a></span><br />
+  <span class="toc-item"><a href="#s1.1"><span class="item-no">1.1</span>  Example uses</a></span><br />
+  <span class="toc-item"><a href="#s1.2"><span class="item-no">1.2</span>  Features</a></span><br />
+  <span class="toc-item"><a href="#s1.3"><span class="item-no">1.3</span>  History</a></span><br />
+  <span class="toc-item"><a href="#s1.4"><span class="item-no">1.4</span>  License & copyright</a></span><br />
+  <span class="toc-item"><a href="#s1.5"><span class="item-no">1.5</span>  Terms used here</a></span><br />
+<span class="toc-item"><a href="#s2"><span class="item-no">2</span>  Usage</a></span><br />
+  <span class="toc-item"><a href="#s2.1"><span class="item-no">2.1</span>  Simple</a></span><br />
+  <span class="toc-item"><a href="#s2.2"><span class="item-no">2.2</span>  Configuring htmLawed using the <span class="term">$config</span> parameter</a></span><br />
+  <span class="toc-item"><a href="#s2.3"><span class="item-no">2.3</span>  Extra HTML specifications using the <span class="term">$spec</span> parameter</a></span><br />
+  <span class="toc-item"><a href="#s2.4"><span class="item-no">2.4</span>  Performance time & memory usage</a></span><br />
+  <span class="toc-item"><a href="#s2.5"><span class="item-no">2.5</span>  Some security risks to keep in mind</a></span><br />
+  <span class="toc-item"><a href="#s2.6"><span class="item-no">2.6</span>  Use without modifying old <span class="term">kses()</span> code</a></span><br />
+  <span class="toc-item"><a href="#s2.7"><span class="item-no">2.7</span>  Tolerance for ill-written HTML</a></span><br />
+  <span class="toc-item"><a href="#s2.8"><span class="item-no">2.8</span>  Limitations & work-arounds</a></span><br />
+  <span class="toc-item"><a href="#s2.9"><span class="item-no">2.9</span>  Examples of usage</a></span><br />
+<span class="toc-item"><a href="#s3"><span class="item-no">3</span>  Details</a></span><br />
+  <span class="toc-item"><a href="#s3.1"><span class="item-no">3.1</span>  Invalid/dangerous characters</a></span><br />
+  <span class="toc-item"><a href="#s3.2"><span class="item-no">3.2</span>  Character references/entities</a></span><br />
+  <span class="toc-item"><a href="#s3.3"><span class="item-no">3.3</span>  HTML elements</a></span><br />
+    <span class="toc-item"><a href="#s3.3.1"><span class="item-no">3.3.1</span>  HTML comments and <span class="term">CDATA</span> sections</a></span><br />
+    <span class="toc-item"><a href="#s3.3.2"><span class="item-no">3.3.2</span>  Tag-transformation for better XHTML-Strict</a></span><br />
+    <span class="toc-item"><a href="#s3.3.3"><span class="item-no">3.3.3</span>  Tag balancing and proper nesting</a></span><br />
+    <span class="toc-item"><a href="#s3.3.4"><span class="item-no">3.3.4</span>  Elements requiring child elements</a></span><br />
+    <span class="toc-item"><a href="#s3.3.5"><span class="item-no">3.3.5</span>  Beautify or compact HTML</a></span><br />
+  <span class="toc-item"><a href="#s3.4"><span class="item-no">3.4</span>  Attributes</a></span><br />
+    <span class="toc-item"><a href="#s3.4.1"><span class="item-no">3.4.1</span>  Auto-addition of XHTML-required attributes</a></span><br />
+    <span class="toc-item"><a href="#s3.4.2"><span class="item-no">3.4.2</span>  Duplicate/invalid <span class="term">id</span> values</a></span><br />
+    <span class="toc-item"><a href="#s3.4.3"><span class="item-no">3.4.3</span>  URL schemes (protocols) and scripts in attribute values</a></span><br />
+    <span class="toc-item"><a href="#s3.4.4"><span class="item-no">3.4.4</span>  Absolute & relative URLs</a></span><br />
+    <span class="toc-item"><a href="#s3.4.5"><span class="item-no">3.4.5</span>  Lower-cased, standard attribute values</a></span><br />
+    <span class="toc-item"><a href="#s3.4.6"><span class="item-no">3.4.6</span>  Transformation of deprecated attributes</a></span><br />
+    <span class="toc-item"><a href="#s3.4.7"><span class="item-no">3.4.7</span>  Anti-spam & <span class="term">href</span></a></span><br />
+    <span class="toc-item"><a href="#s3.4.8"><span class="item-no">3.4.8</span>  Inline style properties</a></span><br />
+    <span class="toc-item"><a href="#s3.4.9"><span class="item-no">3.4.9</span>  Hook function for tag content</a></span><br />
+  <span class="toc-item"><a href="#s3.5"><span class="item-no">3.5</span>  Simple configuration directive for most valid XHTML</a></span><br />
+  <span class="toc-item"><a href="#s3.6"><span class="item-no">3.6</span>  Simple configuration directive for most <em>safe</em> HTML</a></span><br />
+  <span class="toc-item"><a href="#s3.7"><span class="item-no">3.7</span>  Using a hook function</a></span><br />
+  <span class="toc-item"><a href="#s3.8"><span class="item-no">3.8</span>  Obtaining <em>finalized</em> parameter values</a></span><br />
+  <span class="toc-item"><a href="#s3.9"><span class="item-no">3.9</span>  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>  Other</a></span><br />
+  <span class="toc-item"><a href="#s4.1"><span class="item-no">4.1</span>  Support</a></span><br />
+  <span class="toc-item"><a href="#s4.2"><span class="item-no">4.2</span>  Known issues</a></span><br />
+  <span class="toc-item"><a href="#s4.3"><span class="item-no">4.3</span>  Change-log</a></span><br />
+  <span class="toc-item"><a href="#s4.4"><span class="item-no">4.4</span>  Testing</a></span><br />
+  <span class="toc-item"><a href="#s4.5"><span class="item-no">4.5</span>  Upgrade, & old versions</a></span><br />
+  <span class="toc-item"><a href="#s4.6"><span class="item-no">4.6</span>  Comparison with <span class="term">HTMLPurifier</span></a></span><br />
+  <span class="toc-item"><a href="#s4.7"><span class="item-no">4.7</span>  Use through application plug-ins/modules</a></span><br />
+  <span class="toc-item"><a href="#s4.8"><span class="item-no">4.8</span>  Use in non-PHP applications</a></span><br />
+  <span class="toc-item"><a href="#s4.9"><span class="item-no">4.9</span>  Donate</a></span><br />
+  <span class="toc-item"><a href="#s4.10"><span class="item-no">4.10</span>  Acknowledgements</a></span><br />
+<span class="toc-item"><a href="#s5"><span class="item-no">5</span>  Appendices</a></span><br />
+  <span class="toc-item"><a href="#s5.1"><span class="item-no">5.1</span>  Characters discouraged in HTML</a></span><br />
+  <span class="toc-item"><a href="#s5.2"><span class="item-no">5.2</span>  Valid attribute-element combinations</a></span><br />
+  <span class="toc-item"><a href="#s5.3"><span class="item-no">5.3</span>  CSS 2.1 properties accepting URLs</a></span><br />
+  <span class="toc-item"><a href="#s5.4"><span class="item-no">5.4</span>  Microsoft Windows 1252 character replacements</a></span><br />
+  <span class="toc-item"><a href="#s5.5"><span class="item-no">5.5</span>  URL format</a></span><br />
+  <span class="toc-item"><a href="#s5.6"><span class="item-no">5.6</span>  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 - <a href="http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed">http://www.bioinformatics.org/phplabware/internal_utilities/htmLawed</a> </div>
+<br />
+
+<div class="section"><h2>
+<a name="s1" id="s1"></a><span class="item-no">1</span>  About htmLawed
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> 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 />
+  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> application.<br />
+
+<div class="sub-section"><h3>
+<a name="s1.1" id="s1.1"></a><span class="item-no">1.1</span>  Example uses
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  *  Filtering of text submitted as comments on blogs to allow only certain HTML elements<br />
+<br />
+  *  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 />
+  *  Text processing for stricter XML standard-compliance: e.g., to have lowercased <span class="term">x</span> 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 />
+  *  Scraping text or data from web-pages<br />
+<br />
+  *  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>  Features
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  Key: <span class="term">*</span> security feature, <span class="term">^</span> standard compliance, <span class="term">~</span> requires setting right options, <span class="term">`</span> different from <span class="term">Kses</span><br />
+<br />
+  *  make input more <strong>secure</strong> and <strong>standard-compliant</strong><br />
+  *  use for HTML 4, XHTML 1.0 or 1.1, or even generic <strong>XML</strong> documents  ^~`<br />
+<br />
+  *  <strong>beautify</strong> or <strong>compact</strong> HTML  ^~`<br />
+<br />
+  *  can <strong>restrict elements</strong>  ^~`<br />
+  *  ensures proper closure of empty elements like <span class="term">img</span>  ^`<br />
+  *  <strong>transform deprecated elements</strong> like <span class="term">u</span>  ^~`<br />
+  *  HTML <strong>comments</strong> and <span class="term">CDATA</span> sections can be permitted  ^~`<br />
+  *  elements like <span class="term">script</span>, <span class="term">object</span> and <span class="term">form</span> can be permitted  ~<br />
+<br />
+  *  <strong>restrict attributes</strong>, including <strong>element-specifically</strong>  ^~`<br />
+  *  remove <strong>invalid attributes</strong>  ^`<br />
+  *  element and attribute names are <strong>lower-cased</strong>  ^<br />
+  *  provide <strong>required attributes</strong>, like <span class="term">alt</span> for <span class="term">image</span>  ^`<br />
+  *  <strong>transforms deprecated attributes</strong>  ^~`<br />
+  *  attributes <strong>declared only once</strong>  ^`<br />
+<br />
+  *  <strong>restrict attribute values</strong>, including <strong>element-specifically</strong>  ^~`<br />
+  *  a value is declared for <em>empty</em> (<em>minimized</em>) attributes like <span class="term">checked</span>  ^<br />
+  *  check for potentially dangerous attribute values  *~<br />
+  *  ensure <strong>unique</strong> <span class="term">id</span> attribute values  ^~`<br />
+  *  <strong>double-quote</strong> attribute values  ^<br />
+  *  lower-case <strong>standard attribute values</strong> like <span class="term">password</span>  ^`<br />
+  *  permit custom, non-standard attributes as well as custom rules for standard attributes  ~`<br />
+<br />
+  *  <strong>attribute-specific URL protocol/scheme restriction</strong>  *~`<br />
+  *  disable <strong>dynamic expressions</strong> in <span class="term">style</span> values  *~`<br />
+<br />
+  *  neutralize invalid named character entities  ^`<br />
+  *  <strong>convert</strong> hexadecimal numeric entities to decimal ones, or vice versa  ^~`<br />
+  *  convert named entities to numeric ones for generic XML use  ^~`<br />
+<br />
+  *  remove <strong>null</strong> characters  *<br />
+  *  neutralize potentially dangerous proprietary Netscape <strong>Javascript entities</strong>  *<br />
+  *  replace potentially dangerous <strong>soft-hyphen</strong> character in URL-accepting attribute values with spaces  *<br />
+<br />
+  *  remove common <strong>invalid characters</strong> not allowed in HTML or XML  ^`<br />
+  *  replace <strong>characters from Microsoft applications</strong> like <span class="term">Word</span> that are discouraged in HTML or XML  ^~`<br />
+  *  neutralize entities for characters invalid or discouraged in HTML or XML  ^`<br />
+  *  appropriately neutralize <span class="term"><</span>, <span class="term">&</span>, <span class="term">"</span>, and <span class="term">></span> characters  ^*`<br />
+<br />
+  *  understands improperly spaced tag content (like, spread over more than a line) and properly spaces them  `<br />
+  *  attempts to <strong>balance tags</strong> for well-formedness  ^~`<br />
+  *  understands when <strong>omitable closing tags</strong> like <span class="term"></p></span> (allowed in HTML 4, transitional, e.g.) are missing  ^~`<br />
+  *  attempts to permit only <strong>validly nested tags</strong>  ^~`<br />
+  *  option to <strong>remove or neutralize bad content</strong> ^~`<br />
+  *  attempts to <strong>rectify common errors of plain-text misplacement</strong> (e.g., directly inside <span class="term">blockquote</span>) ^~`<br />
+<br />
+  *  fast, <strong>non-OOP</strong> code of ~45 kb incurring peak basal memory usage of ~0.5 MB<br />
+  *  <strong>compatible</strong> with pre-existing code using <span class="term">Kses</span> (the filter used by <span class="term">WordPress</span>)<br />
+<br />
+  *  optional <strong>anti-spam</strong> measures such as addition of <span class="term">rel="nofollow"</span> and link-disabling  ~`<br />
+  *  optionally makes <strong>relative URLs absolute</strong>, and vice versa  ~`<br />
+<br />
+  *  optionally mark <span class="term">&</span> to identify the entities for <span class="term">&</span>, <span class="term"><</span> and <span class="term">></span> introduced by htmLawed  ~`<br />
+<br />
+  *  allows deployment of powerful <strong>hook functions</strong> to <strong>inject</strong> HTML, <strong>consolidate</strong> <span class="term">style</span> attributes to <span class="term">class</span>, finely check attribute values, etc.  ~`<br />
+<br />
+  *  <strong>independent of character encoding</strong> of input and does not affect it<br />
+<br />
+  *  <strong>tolerance for ill-written HTML</strong> 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>  History
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> and <span class="term">HTMLPurifier</span> 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> (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 />
+  See <a href="#s4.3">section 4.3</a> for a detailed log of changes in htmLawed over the years, and <a href="#s4.10">section 4.10</a> for acknowledgements.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.4" id="s1.4"></a><span class="item-no">1.4</span>  License & copyright
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> (or later).<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s1.5" id="s1.5"></a><span class="item-no">1.5</span>  Terms used here
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> and <span class="term">noframes</span>, and these elements are ignored here.<br />
+<br />
+  *  <em>administrator</em> - or admin; person setting up the code that utilizes htmLawed; also, <em>user</em><br />
+  *  <em>attributes</em> - name-value pairs like <span class="term">href="http://x.com"</span> in opening tags<br />
+  *  <em>author</em> - see <em>writer</em><br />
+  *  <em>character</em> - atomic unit of text; internally represented by a numeric <em>code-point</em> as specified by the <em>encoding</em> or <em>charset</em> in use<br />
+  *  <em>entity</em> - markup like <span class="term">&gt;</span> and <span class="term">&#160;</span> used to refer to a character<br />
+  *  <em>element</em> - HTML element like <span class="term">a</span> and <span class="term">img</span><br />
+  *  <em>element content</em> -  content between the opening and closing tags of an element, like <span class="term">click</span> of the <span class="term"><a href="x">click</a></span> element<br />
+  *  <em>HTML</em> - implies XHTML unless specified otherwise<br />
+  *  <em>HTML body</em> - Complete HTML documents typically have a <em>head</em> and a <em>body</em> container. Information in <em>head</em> 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> and <span class="term">noframes</span> that htmLawed is concerned with<br />
+  *  <em>input</em> - text given to htmLawed to process<br />
+  *  <em>processing</em> - involves filtering, correction, etc., of input<br />
+  *  <em>safe</em> - 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 />
+  *  <em>scheme</em> - a URL protocol like <span class="term">http</span> and <span class="term">ftp</span><br />
+  *  <em>specifications</em> - standard specifications, for HTML4, HTML5, Ruby, etc.<br />
+  *  <em>style property</em> - terms like <span class="term">border</span> and <span class="term">height</span> for which declarations are made in values for the <span class="term">style</span> attribute of elements<br />
+  *  <em>tag</em> - markers like <span class="term"><a href="x"></span> and <span class="term"></a></span> delineating element content; the opening tag can contain attributes<br />
+  *  <em>tag content</em> - consists of tag markers <span class="term"><</span> and <span class="term">></span>, element names like <span class="term">div</span>, and possibly attributes<br />
+  *  <em>user</em> - administrator<br />
+  *  <em>writer</em> - 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>  Availability
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> file, the download has the htmLawed documentation (this document) in plain <a href="htmLawed_README.txt">text</a> and <a href="htmLawed_README.htm">HTML</a> 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>  Usage
+</h2><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  htmLawed works in PHP version 4.4 or higher. Either <span class="term">include()</span> the <span class="term">htmLawed.php</span> file, or copy-paste the entire code. To use with PHP 4.3, have the following code included:<br />
+<br />
+
+<code class="code">    if(!function_exists('ctype_digit')){</code>
+<br />
+
+<code class="code">     function ctype_digit($var){</code>
+<br />
+
+<code class="code">      return ((int) $var == $var);</code>
+<br />
+
+<code class="code">     }</code>
+<br />
+
+<code class="code">    }</code>
+<br />
+
+<div class="sub-section"><h3>
+<a name="s2.1" id="s2.1"></a><span class="item-no">2.1</span>  Simple
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  The input text to be processed, <span class="term">$text</span>, is passed as an argument of type string; <span class="term">htmLawed()</span> returns the processed string:<br />
+<br />
+
+<code class="code">    $processed = htmLawed($text);</code>
+<br />
+<br />
+  With the <span class="term">htmLawed class</span> (<a href="#s1.6">section 1.6</a>), usage is:<br />
+<br />
+
+<code class="code">    $processed = htmLawed::hl($text);</code>
+<br />
+<br />
+  <strong>Notes</strong>: (1) If input is from a <span class="term">$_GET</span> or <span class="term">$_POST</span> value, and <span class="term">magic quotes</span> are enabled on the PHP setup, run <span class="term">stripslashes()</span> 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> and <span class="term">noframes</span>.<br />
+<br />
+  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> 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> and <span class="term">$spec</span>:<br />
+<br />
+
+<code class="code">    $processed = htmLawed($text, $config, $spec);</code>
+<br />
+<br />
+  The <span class="term">$config</span> and <span class="term">$spec</span> 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> and other scripting attacks (e.g., by disallowing Javascript code), consider using the <span class="term">safe</span> 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>  Configuring htmLawed using the <span class="term">$config</span> parameter
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  <span class="term">$config</span> instructs htmLawed on how to tackle certain tasks. When <span class="term">$config</span> 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> 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">    $config = array('comment'=>0, 'cdata'=>1);</code>
+<br />
+
+<code class="code">    $processed = htmLawed($text, $config);</code>
+<br />
+<br />
+  Or,<br />
+<br />
+
+<code class="code">    $processed = htmLawed($text, array('comment'=>0, 'cdata'=>1));</code>
+<br />
+<br />
+  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 />
+  Key: <span class="term">*</span> default, <span class="term">^</span> different default when htmLawed is used in the Kses-compatible mode (see <a href="#s2.6">section 2.6</a>), <span class="term">~</span> different default when <span class="term">valid_xhtml</span> is set to <span class="term">1</span> (see <a href="#s3.5">section 3.5</a>), <span class="term">"</span> different default when <span class="term">safe</span> is set to <span class="term">1</span> (see <a href="#s3.6">section 3.6</a>)<br />
+<br />
+  <strong>abs_url</strong><br />
+  Make URLs absolute or relative; <span class="term">$config["base_url"]</span> needs to be set; see <a href="#s3.4.4">section 3.4.4</a><br />
+<br />
+  <span class="term">-1</span> - make relative<br />
+  <span class="term">0</span> - no action  *<br />
+  <span class="term">1</span> - make absolute<br />
+<br />
+  <strong>and_mark</strong><br />
+  Mark <span class="term">&</span> characters in the original input; see <a href="#s3.2">section 3.2</a><br />
+<br />
+  <strong>anti_link_spam</strong><br />
+  Anti-link-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
+<br />
+  <span class="term">0</span> - no measure taken  *<br />
+  <em>array("regex1", "regex2")</em> - will ensure a <span class="term">rel</span> attribute with <span class="term">nofollow</span> in its value in case the <span class="term">href</span> attribute value matches the regular expression pattern <span class="term">regex1</span>, and/or will remove <span class="term">href</span> if its value matches the regular expression pattern <span class="term">regex2</span>. E.g., <span class="term">array("/./", "/://\W*(?!(abc\.com|xyz\.org))/")</span>; see <a href="#s3.4.7">section 3.4.7</a> for more.<br />
+<br />
+  <strong>anti_mail_spam</strong><br />
+  Anti-mail-spam measure; see <a href="#s3.4.7">section 3.4.7</a><br />
+<br />
+  <span class="term">0</span> - no measure taken  *<br />
+  <em>word</em> - <span class="term">@</span> in mail address in <span class="term">href</span> attribute value is replaced with specified <em>word</em><br />
+<br />
+  <strong>balance</strong><br />
+  Balance tags for well-formedness and proper nesting; see <a href="#s3.3.3">section 3.3.3</a><br />
+<br />
+  <span class="term">0</span> - no<br />
+  <span class="term">1</span> - yes  *<br />
+<br />
+  <strong>base_url</strong><br />
+  Base URL value that needs to be set if <span class="term">$config["abs_url"]</span> is not <span class="term">0</span>; see <a href="#s3.4.4">section 3.4.4</a><br />
+<br />
+  <strong>cdata</strong><br />
+  Handling of <span class="term">CDATA</span> sections; see <a href="#s3.3.1">section 3.3.1</a><br />
+<br />
+  <span class="term">0</span> - don't consider <span class="term">CDATA</span> sections as markup and proceed as if plain text  ^"<br />
+  <span class="term">1</span> - remove<br />
+  <span class="term">2</span> - allow, but neutralize any <span class="term"><</span>, <span class="term">></span>, and <span class="term">&</span> inside by converting them to named entities<br />
+  <span class="term">3</span> - allow  *<br />
+<br />
+  <strong>clean_ms_char</strong><br />
+  Replace discouraged characters introduced by Microsoft Word, etc.; see <a href="#s3.1">section 3.1</a><br />
+<br />
+  <span class="term">0</span> - no  *<br />
+  <span class="term">1</span> - yes<br />
+  <span class="term">2</span> - yes, but replace special single & double quotes with ordinary ones<br />
+<br />
+  <strong>comment</strong><br />
+  Handling of HTML comments; see <a href="#s3.3.1">section 3.3.1</a><br />
+<br />
+  <span class="term">0</span> - don't consider comments as markup and proceed as if plain text  ^"<br />
+  <span class="term">1</span> - remove<br />
+  <span class="term">2</span> - allow, but neutralize any <span class="term"><</span>, <span class="term">></span>, and <span class="term">&</span> inside by converting to named entities<br />
+  <span class="term">3</span> - allow  *<br />
+<br />
+  <strong>css_expression</strong><br />
+  Allow dynamic CSS expression by not removing the expression from CSS property values in <span class="term">style</span> attributes; see <a href="#s3.4.8">section 3.4.8</a><br />
+<br />
+  <span class="term">0</span> - remove  *<br />
+  <span class="term">1</span> - allow<br />
+<br />
+  <strong>deny_attribute</strong><br />
+  Denied HTML attributes; see <a href="#s3.4">section 3.4</a><br />
+<br />
+  <span class="term">0</span> - none  *<br />
+  <em>string</em> - dictated by values in <em>string</em><br />
+  <span class="term">on*</span> (like <span class="term">onfocus</span>) attributes not allowed - "<br />
+<br />
+  <strong>direct_nest_list</strong><br />
+  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 />
+  <span class="term">0</span> - no  *<br />
+  <span class="term">1</span> - yes<br />
+<br />
+  <strong>elements</strong><br />
+  Allowed HTML elements; see <a href="#s3.3">section 3.3</a><br />
+<br />
+  <span class="term">* -center -dir -font -isindex -menu -s -strike -u</span> -  ~<br />
+  <span class="term">applet, embed, iframe, object, script</span> not allowed - "<br />
+<br />
+  <strong>hexdec_entity</strong><br />
+  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 />
+  <span class="term">0</span> - no<br />
+  <span class="term">1</span> - yes  *<br />
+  <span class="term">2</span> - convert decimal to hexadecimal ones<br />
+<br />
+  <strong>hook</strong><br />
+  Name of an optional hook function to alter the input string, <span class="term">$config</span> or <span class="term">$spec</span> before htmLawed starts its main work; see <a href="#s3.7">section 3.7</a><br />
+<br />
+  <span class="term">0</span> - no hook function  *<br />
+  <em>name</em> - <em>name</em> is name of the hook function (<span class="term">kses_hook</span>  ^)<br />
+<br />
+  <strong>hook_tag</strong><br />
+  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 />
+  <span class="term">0</span> - no hook function  *<br />
+  <em>name</em> - <em>name</em> is name of the hook function<br />
+<br />
+  <strong>keep_bad</strong><br />
+  Neutralize bad tags by converting <span class="term"><</span> and <span class="term">></span> to entities, or remove them; see <a href="#s3.3.3">section 3.3.3</a><br />
+<br />
+  <span class="term">0</span> - remove  ^<br />
+  <span class="term">1</span> - neutralize both tags and element content<br />
+  <span class="term">2</span> - remove tags but neutralize element content<br />
+  <span class="term">3</span> and <span class="term">4</span> - like <span class="term">1</span> and <span class="term">2</span> but remove if text (<span class="term">pcdata</span>) is invalid in parent element<br />
+  <span class="term">5</span> and <span class="term">6</span> * -  like <span class="term">3</span> and <span class="term">4</span> but line-breaks, tabs and spaces are left<br />
+<br />
+  <strong>lc_std_val</strong><br />
+  For XHTML compliance, predefined, standard attribute values, like <span class="term">get</span> for the <span class="term">method</span> attribute of <span class="term">form</span>, must be lowercased; see <a href="#s3.4.5">section 3.4.5</a><br />
+<br />
+  <span class="term">0</span> - no<br />
+  <span class="term">1</span> - yes  *<br />
+<br />
+  <strong>make_tag_strict</strong><br />
+  Transform/remove these non-strict XHTML elements, even if they are allowed by the admin: <span class="term">applet</span> <span class="term">center</span> <span class="term">dir</span> <span class="term">embed</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> <span class="term">u</span>; see <a href="#s3.3.2">section 3.3.2</a><br />
+<br />
+  <span class="term">0</span> - no  ^<br />
+  <span class="term">1</span> - yes, but leave <span class="term">applet</span>, <span class="term">embed</span> and <span class="term">isindex</span> elements that currently can't be transformed  *<br />
+  <span class="term">2</span> - yes, removing <span class="term">applet</span>, <span class="term">embed</span> and <span class="term">isindex</span> elements and their contents (nested elements remain)  ~<br />
+<br />
+  <strong>named_entity</strong><br />
+  Allow non-universal named HTML entities, or convert to numeric ones; see <a href="#s3.2">section 3.2</a><br />
+<br />
+  <span class="term">0</span> - convert<br />
+  <span class="term">1</span> - allow  *<br />
+<br />
+  <strong>no_deprecated_attr</strong><br />
+  Allow deprecated attributes or transform them; see <a href="#s3.4.6">section 3.4.6</a><br />
+<br />
+  <span class="term">0</span> - allow  ^<br />
+  <span class="term">1</span> - transform, but <span class="term">name</span> attributes for <span class="term">a</span> and <span class="term">map</span> are retained  *<br />
+  <span class="term">2</span> - transform<br />
+<br />
+  <strong>parent</strong><br />
+  Name of the parent element, possibly imagined, that will hold the input; see <a href="#s3.3">section 3.3</a><br />
+<br />
+  <strong>safe</strong><br />
+  Magic parameter to make input the most secure against XSS without needing to specify other relevant <span class="term">$config</span> parameters; see <a href="#s3.6">section 3.6</a><br />
+<br />
+  <span class="term">0</span> - no  *<br />
+  <span class="term">1</span> - will auto-adjust other relevant <span class="term">$config</span> parameters (indicated by <span class="term">"</span> in this list)<br />
+<br />
+  <strong>schemes</strong><br />
+  Array of attribute-specific, comma-separated, lower-cased list of schemes (protocols) allowed in attributes accepting URLs (or <span class="term">!</span> to <em>deny</em> any URL); <span class="term">*</span> covers all unspecified attributes; see <a href="#s3.4.3">section 3.4.3</a><br />
+<br />
+  <span class="term">href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; *:file, http, https</span>  *<br />
+  <span class="term">*: ftp, gopher, http, https, mailto, news, nntp, telnet</span>  ^<br />
+  <span class="term">href: aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet; style: !; *:file, http, https</span>  "<br />
+<br />
+  <strong>show_setting</strong><br />
+  Name of a PHP variable to assign the <em>finalized</em> <span class="term">$config</span> and <span class="term">$spec</span> values; see <a href="#s3.8">section 3.8</a><br />
+<br />
+  <strong>style_pass</strong><br />
+  Do not look at <span class="term">style</span> attribute values, letting them through without any alteration<br />
+<br />
+  <span class="term">0</span> - no *<br />
+  <span class="term">1</span> - htmLawed will let through any <span class="term">style</span> value; see <a href="#s3.4.8">section 3.4.8</a><br />
+<br />
+  <strong>tidy</strong><br />
+  Beautify or compact HTML code; see <a href="#s3.3.5">section 3.3.5</a><br />
+<br />
+  <span class="term">-1</span> - compact<br />
+  <span class="term">0</span> - no  *<br />
+  <span class="term">1</span> or <span class="term">string</span> - beautify (custom format specified by <span class="term">string</span>)<br />
+<br />
+  <strong>unique_ids</strong><br />
+  <span class="term">id</span> attribute value checks; see <a href="#s3.4.2">section 3.4.2</a><br />
+<br />
+  <span class="term">0</span> - no  ^<br />
+  <span class="term">1</span> - remove duplicate and/or invalid ones  *<br />
+  <em>word</em> - 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">:</span>.<br />
+<br />
+  <strong>valid_xhtml</strong><br />
+  Magic parameter to make input the most valid XHTML without needing to specify other relevant <span class="term">$config</span> parameters; see <a href="#s3.5">section 3.5</a><br />
+<br />
+  <span class="term">0</span> - no  *<br />
+  <span class="term">1</span> - will auto-adjust other relevant <span class="term">$config</span> parameters (indicated by <span class="term">~</span> in this list)<br />
+<br />
+  <strong>xml:lang</strong><br />
+  Auto-adding <span class="term">xml:lang</span> attribute; see <a href="#s3.4.1">section 3.4.1</a><br />
+<br />
+  <span class="term">0</span> - no  *<br />
+  <span class="term">1</span> - add if <span class="term">lang</span> attribute is present<br />
+  <span class="term">2</span> - add if <span class="term">lang</span> attribute is present, and remove <span class="term">lang</span>  ~<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.3" id="s2.3"></a><span class="item-no">2.3</span>  Extra HTML specifications using the $spec parameter
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  The <span class="term">$spec</span> 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> 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">    $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';</code>
+<br />
+
+<code class="code">    $processed = htmLawed($text, $config, $spec);</code>
+<br />
+<br />
+  Or,<br />
+<br />
+
+<code class="code">    $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');</code>
+<br />
+<br />
+  A rule begins with an HTML <strong>element</strong> 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 />
+  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">-*</span> can be used.<br />
+<br />
+  Following shows examples of rule excerpts with rule-element <span class="term">a</span> and the attributes that are being permitted:<br />
+<br />
+  *  <span class="term">a=</span> - all<br />
+  *  <span class="term">a=id</span> - all<br />
+  *  <span class="term">a=href, title, -id, -onclick</span> - all except <span class="term">id</span> and <span class="term">onclick</span><br />
+  *  <span class="term">a=*, id, -id</span> - all except <span class="term">id</span><br />
+  *  <span class="term">a=-*</span> - none<br />
+  *  <span class="term">a=-*, href, title</span> - none except <span class="term">href</span> and <span class="term">title</span><br />
+  *  <span class="term">a=-*, -id, href, title</span> - none except <span class="term">href</span> and <span class="term">title</span><br />
+<br />
+  Rules regarding <strong>attribute values</strong> are optionally specified inside round brackets after attribute names in slash ('/')-separated <em>parameter = value</em> 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 />
+  *  <span class="term">oneof</span> - one or more choices separated by <span class="term">|</span> that the value should match; if only one choice is provided, then the value must match that choice<br />
+<br />
+  *  <span class="term">noneof</span> - one or more choices separated by <span class="term">|</span> that the value should not match<br />
+<br />
+  *  <span class="term">maxlen</span> and <span class="term">minlen</span> - upper and lower limits for the number of characters in the attribute value; specified in numbers<br />
+<br />
+  *  <span class="term">maxval</span> and <span class="term">minval</span> - upper and lower limits for the numerical value specified in the attribute value; specified in numbers<br />
+<br />
+  *  <span class="term">match</span> and <span class="term">nomatch</span> - pattern that the attribute value should or should not match; specified as PHP/PCRE-compatible regular expressions with delimiters and possibly modifiers<br />
+<br />
+  *  <span class="term">default</span> - a value to force on the attribute if the value provided by the writer does not fit any of the specified parameters<br />
+<br />
+  If <span class="term">default</span> 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> 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> to <span class="term">-1</span>).<br />
+<br />
+  Examples with <em>input</em> <span class="term"><input title="WIDTH" value="10em" /><input title="length" value="5" /></span> are shown below.<br />
+<br />
+  <em>Rule</em>: <span class="term">input=title(maxlen=60/minlen=6), value</span><br />
+  <em>Output</em>: <span class="term"><input value="10em" /><input title="length" value="5" /></span><br />
+<br />
+  <em>Rule</em>: <span class="term">input=title(), value(maxval=8/default=6)</span><br />
+  <em>Output</em>: <span class="term"><input title="WIDTH" value="6" /><input title="length" value="5" /></span><br />
+<br />
+  <em>Rule</em>: <span class="term">input=title(nomatch=%w.d%i), value(match=%em%/default=6em)</span><br />
+  <em>Output</em>: <span class="term"><input value="10em" /><input title="length" value="6em" /></span><br />
+<br />
+  <em>Rule</em>: <span class="term">input=title(oneof=height|depth/default=depth), value(noneof=5|6)</span><br />
+  <em>Output</em>: <span class="term"><input title="depth" value="10em" /><input title="depth" /></span><br />
+<br />
+  <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> 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> by enclosing in pairs of double-quotes (<span class="term">"</span>). A back-tick (<span class="term">`</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 `"ID`"")</span>.<br />
+<br />
+  <strong>Note</strong>: To deny an attribute for all elements for which it is legal, <span class="term">$config["deny_attribute"]</span> (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> while being denied globally through <span class="term">$config["deny_attribute"]</span>. The <span class="term">hook_tag</span> 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> functionality.<br />
+<br />
+  <span class="term">$spec</span> 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> will permit the custom uses of the standard <span class="term">rel</span> attribute in <span class="term">input</span> (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">    $spec = 'img=vFlag; input=rel'</code>
+<br />
+<br />
+  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>  Performance time & memory usage
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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 />
+  The htmLawed <a href="htmLawedTest.php">demo</a> 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>  Some security risks to keep in mind
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> 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 />
+  *  Allowing <span class="term">script</span>, <span class="term">applet</span>, <span class="term">embed</span>, <span class="term">iframe</span> or <span class="term">object</span> elements, or certain of their attributes like <span class="term">allowscriptaccess</span><br />
+<br />
+  *  Allowing HTML comments (some Internet Explorer versions are vulnerable with, e.g., <span class="term"><!--[if gte IE 4]><script>alert("xss");</script><![endif]--></span><br />
+<br />
+  *  Allowing dynamic CSS expressions (some Internet Explorer versions are vulnerable)<br />
+<br />
+  *  Allowing the <span class="term">style</span> attribute<br />
+<br />
+  To remove <em>unsecure</em> HTML, code-developers using htmLawed must set <span class="term">$config</span> appropriately. E.g., <span class="term">$config["elements"] = "* -script"</span> to deny the <span class="term">script</span> element (<a href="#s3.3">section 3.3</a>), <span class="term">$config["safe"] = 1</span> to auto-configure ceratin htmLawed parameters for maximizing security (<a href="#s3.6">section 3.6</a>), etc.<br />
+<br />
+  Permitting the <span class="term">*style*</span> attribute brings in risks of <em>click-jacking</em>, <em>phishing</em>, web-page overlays, etc., <em>even</em> when the <span class="term">safe</span> 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> argument, and through the <span class="term">hook_tag</span> parameter (see <a href="#s3.4.8">section 3.4.8</a> for more). Disallowing <span class="term">style</span> completely and relying on CSS classes and stylesheet files is recommended.<br />
+<br />
+  htmLawed does not check or correct the character <strong>encoding</strong> 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> tags, this can allow for an exploit (like Google's <em>UTF-7/XSS</em> 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>  Use without modifying old <span class="term">kses()</span> code
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  The <span class="term">Kses</span> 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> function declared in the <span class="term">Kses</span> 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> with htmLawed:<br />
+<br />
+
+<code class="code">    $comment_filtered = kses($comment_input, array('a'=>array(), 'b'=>array(), 'i'=>array()));</code>
+<br />
+<br />
+  For some of the <span class="term">$config</span> parameters, htmLawed will use values other than the default ones. These are indicated by <span class="term">^</span> in <a href="#s2.2">section 2.2</a>. To force htmLawed to use other values, function <span class="term">kses()</span> in the htmLawed code should be edited -- a few configurable parameters/variables need to be changed.<br />
+<br />
+  If the application uses a <span class="term">Kses</span> file that has the <span class="term">kses()</span> function declared, then, to have the application use htmLawed instead of <span class="term">Kses</span>, simply rename <span class="term">htmLawed.php</span> (to <span class="term">kses.php</span>, e.g.) and replace the <span class="term">Kses</span> file (or just replace the code in the <span class="term">Kses</span> file with the htmLawed code). If the <span class="term">kses()</span> function in the <span class="term">Kses</span> 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> function in the htmLawed code.<br />
+<br />
+  If the <span class="term">Kses</span> 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> to <span class="term">wp_kses()</span>, and delete the code for the original <span class="term">wp_kses()</span> function.<br />
+<br />
+  If the <span class="term">Kses</span> code has a non-empty hook function (e.g., <span class="term">wp_kses_hook()</span> in case of <span class="term">WordPress</span>), then the code for htmLawed's <span class="term">kses_hook()</span> 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> in case of <span class="term">WordPress</span>:<br />
+<br />
+
+<code class="code">    function kses_hook($string, &$cf, &$spec){</code>
+<br />
+
+<code class="code">    // kses compatibility</code>
+<br />
+
+<code class="code">    $allowed_html = $spec;</code>
+<br />
+
+<code class="code">    $allowed_protocols = array();</code>
+<br />
+
+<code class="code">    foreach($cf['schemes'] as $v){</code>
+<br />
+
+<code class="code">     foreach($v as $k2=>$v2){</code>
+<br />
+
+<code class="code">      if(!in_array($k2, $allowed_protocols)){</code>
+<br />
+
+<code class="code">       $allowed_protocols[] = $k2;</code>
+<br />
+
+<code class="code">      }</code>
+<br />
+
+<code class="code">     }</code>
+<br />
+
+<code class="code">    }</code>
+<br />
+
+<code class="code">    return wp_kses_hook($string, $allowed_html, $allowed_protocols);</code>
+<br />
+
+<code class="code">    // eof</code>
+<br />
+
+<code class="code">    }</code>
+<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.7" id="s2.7"></a><span class="item-no">2.7</span>  Tolerance for ill-written HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  htmLawed can work with ill-written HTML code in the input. However, HTML that is too ill-written may not be <em>read</em> as HTML, and may therefore get identified as mere plain text. Following statements indicate the degree of <em>looseness</em> that htmLawed can work with, and can be provided in instructions to writers:<br />
+<br />
+  *  Tags must be flanked by <span class="term"><</span> and <span class="term">></span> with no <span class="term">></span> inside -- any needed <span class="term">></span> should be put in as <span class="term">&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">></span>, like <span class="term"><div ></span> and <span class="term"><img / ></span>, but not after the <span class="term"><</span>.<br />
+<br />
+  *  Element and attribute names need not be lower-cased.<br />
+<br />
+  *  Attribute string of elements may be liberally spaced with tabs, line-breaks, etc.<br />
+<br />
+  *  Attribute values may be single- and not double-quoted.<br />
+<br />
+  *  Left-padding of numeric entities (like, <span class="term">&#0160;</span>, <span class="term">&x07ff;</span>) with <span class="term">0</span> is okay as long as the number of characters between between the <span class="term">&</span> and the <span class="term">;</span> does not exceed 8. All entities must end with <span class="term">;</span> though.<br />
+<br />
+  *  Named character entities must be properly cased. Thus, <span class="term">&Lt;</span> or <span class="term">&TILDE;</span> will not be recognized as entities and will be <em>neutralized</em>.<br />
+<br />
+  *  HTML comments should not be inside element tags (they can be between tags), and should begin with <span class="term"><!--</span> and end with <span class="term">--></span>. Characters like <span class="term"><</span>, <span class="term">></span>, and <span class="term">&</span> may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">--></span> inside should be put in as <span class="term">--&gt;</span>. Any <span class="term">--</span> inside will be automatically converted to <span class="term">-</span>, and a space will be added before the comment delimiter <span class="term">--></span>.<br />
+<br />
+  *  <span class="term">CDATA</span> 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"><[CDATA[</span> and end with <span class="term">]]></span>. Characters like <span class="term"><</span>, <span class="term">></span>, and <span class="term">&</span> may be allowed inside depending on <span class="term">$config</span>, but any <span class="term">]]></span> inside should be put in as <span class="term">]]&gt;</span>.<br />
+<br />
+  *  For attribute values, character entities <span class="term">&lt;</span>, <span class="term">&gt;</span> and <span class="term">&amp;</span> should be used instead of characters <span class="term"><</span> and <span class="term">></span>, and <span class="term">&</span> (when <span class="term">&</span> 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 />
+  *  Characters <span class="term"><</span>, <span class="term">></span>, <span class="term">&</span> and <span class="term">"</span> that are part of actual Javascript, etc., code in <span class="term">script</span> elements should be used as such and not be put in as entities like <span class="term">&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> sections.<br />
+<br />
+  *  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 />
+  *  Input authors should be notified of admin-specified allowed elements, attributes, configuration values (like conversion of named entities to numeric ones), etc.<br />
+<br />
+  *  With <span class="term">$config["unique_ids"]</span> not <span class="term">0</span> and the <span class="term">id</span> attribute being permitted, writers should carefully avoid using duplicate or invalid <span class="term">id</span> 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"><a id="home"></a><input id="home" /><label for="home"></label></span> is processed into<br />
+<span class="term"><a id="home"></a><input id="prefix_home" /><label for="home"></label></span>.<br />
+<br />
+  *  Even if intended HTML is lost from an ill-written input, the processed output will be more secure and standard-compliant.<br />
+<br />
+  *  For URLs, unless <span class="term">$config["scheme"]</span> is appropriately set, writers should avoid using escape characters or entities in schemes. E.g., <span class="term">htt&#112;</span> (which many browsers will read as the harmless <span class="term">http</span>) may be considered bad by htmLawed.<br />
+<br />
+  *  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> and <span class="term">noscript</span> elements (illegal as per the specifications) inside auto-generated <span class="term">div</span> elements.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s2.8" id="s2.8"></a><span class="item-no">2.8</span>  Limitations & work-arounds
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  htmLawed's main objective is to make the input text <em>more</em> 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 />
+  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> elements) are clearly wrong. Regarding security, note that <em>unsafe</em> HTML code is not legally invalid per se.<br />
+<br />
+  *  htmLawed is meant for input that goes into the <span class="term">body</span> 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> and <span class="term">noframes</span>. Content of the latter elements can, however, be individually filtered through htmLawed.<br />
+<br />
+  *  It cannot transform the non-standard <span class="term">embed</span> elements to the standard-compliant <span class="term">object</span> elements. Yet, it can allow <span class="term">embed</span> elements if permitted (<span class="term">embed</span> is widely used and supported). Admins can certainly use the <span class="term">hook_tag</span> parameter (<a href="#s3.4.9">section 3.4.9</a>) to deploy a custom embed-to-object converter function.<br />
+<br />
+  *  The only non-standard element that may be permitted is <span class="term">embed</span>; others like <span class="term">noembed</span> and <span class="term">nobr</span> cannot be permitted without modifying the htmLawed code.<br />
+<br />
+  *  It cannot handle input that has non-HTML code like <span class="term">SVG</span> 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> parameter (see <a href="#s3.2">section 3.2</a>).<br />
+<br />
+  *  By default, htmLawed won't check many attribute values for standard compliance. E.g., <span class="term">width="20m"</span> with the dimension in non-standard <span class="term">m</span> 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> parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span> to enforce finer checks.<br />
+<br />
+  *  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 />
+  *  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> parameter (<a href="#s3.4.9">section 3.4.9</a>) or <span class="term">$spec</span> for finer checks. Perhaps the best option is to disallow <span class="term">style</span> but allow <span class="term">class</span> attributes with the right <span class="term">oneof</span> or <span class="term">match</span> values for <span class="term">class</span>, and have the various class style properties in <span class="term">.css</span> CSS stylesheet files.<br />
+<br />
+  *  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> elements. Such functions are beyond its purview. Admins should use other code to pre- or post-process the input for such purposes.<br />
+<br />
+  *  htmLawed cannot be used to have links force-opened in new windows (by auto-adding appropriate <span class="term">target</span> and <span class="term">onclick</span> 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> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+  *  Nesting-based checks are not possible. E.g., one cannot disallow <span class="term">p</span> elements specifically inside <span class="term">td</span> while permitting it elsewhere. Admins may be able to use a custom hook function to enforce such checks (<span class="term">hook_tag</span> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+  *  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> 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> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+  *  Pairs of opening and closing tags that do not enclose any content (like <span class="term"><em></em></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 />
+  *  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> 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> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+  *  htmLawed does not check the number of nested elements. E.g., it will allow two <span class="term">caption</span> elements in a <span class="term">table</span> 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> parameter; see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+  *  htmLawed might convert certain entities to actual characters and remove backslashes and CSS comment-markers (<span class="term">/*</span>) in <span class="term">style</span> attribute values in order to detect malicious HTML like crafted IE-specific dynamic expressions like <span class="term">&#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> parameter (<a href="#s3.4.9">section 3.4.9</a>) to more specifically identify CSS expressions in the <span class="term">style</span> attribute values. Also, using <span class="term">$config["style_pass"]</span>, it is possible to have htmLawed pass <span class="term">style</span> attribute values without even looking at them (<a href="#s3.4.8">section 3.4.8</a>).<br />
+<br />
+  *  htmLawed does not correct certain possible attribute-based security vulnerabilities (e.g., <span class="term"><a href="http://x%22+style=%22background-image:xss">x</a></span>). These arise when browsers mis-identify markup in <em>escaped</em> text, defeating the very purpose of escaping text (a bad browser will read the given example as <span class="term"><a href="http://x" style="background-image:xss">x</a></span>).<br />
+<br />
+  *  Because of poor Unicode support in PHP, htmLawed does not remove the <em>high value</em> 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 />
+  *  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> tags, this can permit an exploit (like Google's <em>UTF-7/XSS</em> 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 />
+  *  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> functions, or some other mean, to convert text to UTF-8 before passing it to htmLawed.<br />
+<br />
+  *  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>  Examples of usage
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  Safest, allowing only <em>safe</em> HTML markup --<br />
+<br />
+
+<code class="code">    $config = array('safe'=>1);</code>
+<br />
+
+<code class="code">    $out = htmLawed($in);</code>
+<br />
+<br />
+  Simplest, allowing all valid HTML markup except <span class="term">javascript:</span> --<br />
+<br />
+
+<code class="code">    $out = htmLawed($in);</code>
+<br />
+<br />
+  Allowing all valid HTML markup including <span class="term">javascript:</span> --<br />
+<br />
+
+<code class="code">    $config = array('schemes'=>'*:*');</code>
+<br />
+
+<code class="code">    $out = htmLawed($in, $config);</code>
+<br />
+<br />
+  Allowing only <span class="term">safe</span> HTML and the elements <span class="term">a</span>, <span class="term">em</span>, and <span class="term">strong</span> --<br />
+<br />
+
+<code class="code">    $config = array('safe'=>1, 'elements'=>'a, em, strong');</code>
+<br />
+
+<code class="code">    $out = htmLawed($in, $config);</code>
+<br />
+<br />
+  Not allowing elements <span class="term">script</span> and <span class="term">object</span> --<br />
+<br />
+
+<code class="code">    $config = array('elements'=>'* -script -object');</code>
+<br />
+
+<code class="code">    $out = htmLawed($in, $config);</code>
+<br />
+<br />
+  Not allowing attributes <span class="term">id</span> and <span class="term">style</span> --<br />
+<br />
+
+<code class="code">    $config = array('deny_attribute'=>'id, style');</code>
+<br />
+
+<code class="code">    $out = htmLawed($in, $config);</code>
+<br />
+<br />
+  Permitting only attributes <span class="term">title</span> and <span class="term">href</span> --<br />
+<br />
+
+<code class="code">    $config = array('deny_attribute'=>'* -title -href');</code>
+<br />
+
+<code class="code">    $out = htmLawed($in, $config);</code>
+<br />
+<br />
+  Remove bad/disallowed tags altogether instead of converting them to entities --<br />
+<br />
+
+<code class="code">    $config = array('keep_bad'=>0);</code>
+<br />
+
+<code class="code">    $out = htmLawed($in, $config);</code>
+<br />
+<br />
+  Allowing attribute <span class="term">title</span> only in <span class="term">a</span> and not allowing attributes <span class="term">id</span>, <span class="term">style</span>, or scriptable <em>on*</em> attributes like <span class="term">onclick</span> --<br />
+<br />
+
+<code class="code">    $config = array('deny_attribute'=>'title, id, style, on*');</code>
+<br />
+
+<code class="code">    $spec = 'a=title';</code>
+<br />
+
+<code class="code">    $out = htmLawed($in, $config, $spec);</code>
+<br />
+<br />
+  Allowing a custom attribute, <span class="term">vFlag</span>, in <span class="term">img</span> and permitting custom use of the standard attribute, <span class="term">rel</span>, in <span class="term">input</span> --<br />
+<br />
+
+<code class="code">    $spec = 'img=vFlag; input=rel';</code>
+<br />
+
+<code class="code">    $out = htmLawed($in, $config, $spec);</code>
+<br />
+<br />
+  Some case-studies are presented below.<br />
+<br />
+  <strong>1.</strong> 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> and <span class="term">u</span> in comments, but needs <span class="term">strike</span> and <span class="term">u</span> transformed to <span class="term">span</span> for better XHTML 1-strict compliance, and, he wants the <span class="term">a</span> links to point only to <span class="term">http</span> or <span class="term">https</span> resources:<br />
+<br />
+
+<code class="code">    $processed = htmLawed($in, array('elements'=>'a, em, strike, strong, u', 'make_tag_strict'=>1, 'safe'=>1, 'schemes'=>'*:http, https'), 'a=href');</code>
+<br />
+<br />
+  <strong>2.</strong> 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> 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">    $processed = htmLawed($in, array('css_expression'=>1, 'keep_bad'=>1, 'make_tag_strict'=>1, 'schemes'=>'*:*', 'valid_xhtml'=>1));</code>
+<br />
+<br />
+  For the final submission process, <span class="term">keep_bad</span> is set to <span class="term">6</span>. A value of <span class="term">1</span> for the preview process allows the author to note and correct any HTML mistake without losing any of the typed text.<br />
+<br />
+  <strong>3.</strong> 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">    $processed = htmLawed($in, array('elements'=>'tr, td', 'tidy'=>-1), 'tr, td =');</code>
+<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s3" id="s3"></a><span class="item-no">3</span>  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>  Invalid/dangerous characters
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> to <span class="term">d7ff</span>, and <span class="term">e000</span> to <span class="term">10ffff</span>, except <span class="term">fffe</span> and <span class="term">ffff</span> (decimally, <span class="term">9</span>, <span class="term">10</span>, <span class="term">13</span>, <span class="term">32</span> to <span class="term">55295</span>, and <span class="term">57344</span> to <span class="term">1114111</span>, except <span class="term">65534</span> and <span class="term">65535</span>). htmLawed removes the invalid characters <span class="term">0</span> to <span class="term">8</span>, <span class="term">b</span>, <span class="term">c</span>, and <span class="term">e</span> to <span class="term">1f</span>.<br />
+<br />
+  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 />
+  Characters that are discouraged (see <a href="#s5.1">section 5.1</a>) but not invalid are not removed by htmLawed.<br />
+<br />
+  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"><</span>, <span class="term">></span>, <span class="term">&</span>, and <span class="term">"</span> are converted to entities.<br />
+<br />
+  With <span class="term">$config["clean_ms_char"]</span> set as <span class="term">1</span> or <span class="term">2</span>, many of the discouraged characters (decimal code-points <span class="term">127</span> to <span class="term">159</span> except <span class="term">133</span>) that many Microsoft applications incorrectly use (as per the <span class="term">Windows 1252</span> [<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 />
+  With <span class="term">$config["clean_ms_char"]</span> 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> (for special single-quotes), and <span class="term">84</span>, <span class="term">93</span>, and <span class="term">94</span> (for special double-quotes) are converted to ordinary single and double quotes respectively and not to entities.<br />
+<br />
+  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 />
+  The <span class="term">$config["clean_ms_char"]</span> 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> (<span class="term">Cp-1252</span>) or a similar encoding like <span class="term">Cp-1251</span> (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>  Character references/entities
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  Valid character entities take the form <span class="term">&*;</span> where <span class="term">*</span> is <span class="term">#x</span> followed by a hexadecimal number (hexadecimal numeric entity; like <span class="term">&#xA0;</span> for non-breaking space), or alphanumeric like <span class="term">gt</span> (external or named entity; like <span class="term">&nbsp;</span> for non-breaking space), or <span class="term">#</span> followed by a number (decimal numeric entity; like <span class="term">&#160;</span> for non-breaking space). Character entities referring to the soft-hyphen character (the <span class="term">&shy;</span> or <span class="term">\xad</span> character; hexadecimal code-point <span class="term">ad</span> [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 />
+  htmLawed (function <span class="term">hl_ent()</span>):<br />
+<br />
+  *  Neutralizes entities with multiple leading zeroes or missing semi-colons (potentially dangerous)<br />
+<br />
+  *  Lowercases the <span class="term">X</span> (for XML-compliance) and <span class="term">A-F</span> of hexadecimal numeric entities<br />
+<br />
+  *  Neutralizes entities referring to characters that are HTML-invalid (see <a href="#s3.1">section 3.1</a>)<br />
+<br />
+  *  Neutralizes entities referring to characters that are HTML-discouraged (code-points, hexadecimally, <span class="term">7f</span> to <span class="term">84</span>, <span class="term">86</span> to <span class="term">9f</span>, and <span class="term">fdd0</span> to <span class="term">fddf</span>, or decimally, <span class="term">127</span> to <span class="term">132</span>, <span class="term">134</span> to <span class="term">159</span>, and <span class="term">64991</span> to <span class="term">64976</span>). Entities referring to the remaining discouraged characters (see <a href="#s5.1">section 5.1</a> for a full list) are let through.<br />
+<br />
+  *  Neutralizes named entities that are not in the specs.<br />
+<br />
+  *  Optionally converts valid HTML-specific named entities except <span class="term">&gt;</span>, <span class="term">&lt;</span>, <span class="term">&quot;</span>, and <span class="term">&amp;</span> 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> should be <span class="term">1</span>.<br />
+<br />
+  *  Optionally converts hexadecimal numeric entities to the more widely supported decimal ones. For this, <span class="term">$config["hexdec_entity"]</span> should be <span class="term">0</span>.<br />
+<br />
+  *  Optionally converts decimal numeric entities to the hexadecimal ones. For this, <span class="term">$config["hexdec_entity"]</span> should be <span class="term">2</span>.<br />
+<br />
+  <em>Neutralization</em> refers to the <em>entitification</em> of <span class="term">&</span> to <span class="term">&amp;</span>.<br />
+<br />
+  <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> <a href="http://www.php.net/html_entity_decode">function</a> for that.<br />
+<br />
+  <strong>Note</strong>: If <span class="term">$config["and_mark"]</span> is set, and set to a value other than <span class="term">0</span>, then the <span class="term">&</span> characters in the original input are replaced with the control character for the hexadecimal code-point <span class="term">6</span> (<span class="term">\x06</span>; <span class="term">&</span> characters introduced by htmLawed, e.g., after converting <span class="term"><</span> to <span class="term">&lt;</span>, are not affected). This allows one to distinguish, say, an <span class="term">&gt;</span> introduced by htmLawed and an <span class="term">&gt;</span> 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(><)o</span> 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> can break documents. Before use in such documents, and preferably before any storage, any remaining <span class="term">\x06</span> should be changed back to <span class="term">&</span>, e.g., with:<br />
+<br />
+
+<code class="code">    $final = str_replace("\x06", '&', $prelim);</code>
+<br />
+<br />
+  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>  HTML elements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> (converted to plain text by entitification of <span class="term"><</span> and <span class="term">></span>) or removed.<br />
+<br />
+  E.g., with only <span class="term">em</span> permitted:<br />
+<br />
+  Input:<br />
+<br />
+
+<code class="code">      <em>My</em> website is <a href="http://a.com>a.com</a>.</code>
+<br />
+<br />
+  Output, with <span class="term">$config["keep_bad"] = 0</span>:<br />
+<br />
+
+<code class="code">      <em>My</em> website is a.com.</code>
+<br />
+<br />
+  Output, with <span class="term">$config["keep_bad"]</span> not <span class="term">0</span>:<br />
+<br />
+
+<code class="code">      <em>My</em> website is &lt;a href=""&gt;a.com&lt;/a&gt;.</code>
+<br />
+<br />
+  See <a href="#s3.3.3">section 3.3.3</a> for differences between the various non-zero <span class="term">$config["keep_bad"]</span> values.<br />
+<br />
+  htmLawed by default permits these 86 elements:<br />
+<br />
+
+<code class="code">    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 />
+  Except for <span class="term">embed</span> (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 />
+  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> and <span class="term">script</span>; see <a href="#s3.6">section 3.6</a>.<br />
+<br />
+  When <span class="term">$config["elements"]</span>, which specifies allowed elements, is <em>properly</em> defined, and neither empty nor set to <span class="term">0</span> or <span class="term">*</span>, the default set is not used. To have elements added to or removed from the default set, a <span class="term">+/-</span> notation is used. E.g., <span class="term">*-script-object</span> implies that only <span class="term">script</span> and <span class="term">object</span> are disallowed, whereas <span class="term">*+embed</span> means that <span class="term">noembed</span> is also allowed. Elements can also be specified as comma separated names. E.g., <span class="term">a, b, i</span> means only <span class="term">a</span>, <span class="term">b</span> and <span class="term">i</span> are permitted. In this notation, <span class="term">*</span>, <span class="term">+</span> and <span class="term">-</span> have no significance and can actually cause a mis-reading.<br />
+<br />
+  Some more examples of <span class="term">$config["elements"]</span> values indicating permitted elements (note that empty spaces are liberally allowed for clarity):<br />
+<br />
+  *  <span class="term">a, blockquote, code, em, strong</span> -- 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 />
+  *  <span class="term">*-script</span> -- all excluding <span class="term">script</span><br />
+  *  <span class="term">* -center -dir -font -isindex -menu -s -strike -u</span> -- only XHTML-Strict elements<br />
+  *  <span class="term">*+noembed-script</span> -- all including <span class="term">noembed</span> excluding <span class="term">script</span><br />
+<br />
+  Some mis-usages (and the resulting permitted elements) that can be avoided:<br />
+<br />
+  *  <span class="term">-*</span> -- none; instead of htmLawed, one might just use, e.g., the <span class="term">htmlspecialchars()</span> PHP function<br />
+  *  <span class="term">*, -script</span> -- all except <span class="term">script</span>; admin probably meant <span class="term">*-script</span><br />
+  *  <span class="term">-*, a, em, strong</span> -- all; admin probably meant <span class="term">a, em, strong</span><br />
+  *  <span class="term">*</span> -- all; admin need not have set <span class="term">elements</span><br />
+  *  <span class="term">*-form+form</span> -- all; a <span class="term">+</span> will always over-ride any <span class="term">-</span><br />
+  *  <span class="term">*, noembed</span> -- only <span class="term">noembed</span>; admin probably meant <span class="term">*+noembed</span><br />
+  *  <span class="term">a, +b, i</span> -- only <span class="term">a</span> and <span class="term">i</span>; admin probably meant <span class="term">a, b, i</span><br />
+<br />
+  Basically, when using the <span class="term">+/-</span> notation, commas (<span class="term">,</span>) should not be used, and vice versa, and <span class="term">*</span> should be used with the former but not the latter.<br />
+<br />
+  <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> in the last example, it will eventually be removed during tag balancing unless such balancing is turned off (<span class="term">$config["balance"]</span> 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> to accommodate the element and its nesting properties.<br />
+<br />
+  <strong>A possibly second way to specify allowed elements</strong> is to set <span class="term">$config["parent"]</span> to an element name that supposedly will hold the input, and to set <span class="term">$config["balance"]</span> 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> if <span class="term">$config["parent"]</span> is empty, <span class="term">body</span>, or an element not in htmLawed's default set of 86 elements.<br />
+<br />
+  <em>Tag transformation</em> 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>  Handling of comments and CDATA sections
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  <span class="term">CDATA</span> sections have the format <span class="term"><![CDATA[...anything but not "]]>"...]]></span>, and HTML comments, <span class="term"><!--...anything but not "-->"... --></span>. Neither HTML comments nor <span class="term">CDATA</span> sections can reside inside tags. HTML comments can exist anywhere else, but <span class="term">CDATA</span> sections can exist only where plain text is allowed (e.g., immediately inside <span class="term">td</span> element content but not immediately inside <span class="term">tr</span> element content).<br />
+<br />
+  htmLawed (function <span class="term">hl_cmtcd()</span>) handles HTML comments or <span class="term">CDATA</span> sections depending on the values of <span class="term">$config["comment"]</span> 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"><</span>, <span class="term">></span> and <span class="term">&</span> inside are changed to entities. If <span class="term">3</span>, they are left as such.<br />
+<br />
+  Note that for the last two cases, HTML comments and <span class="term">CDATA</span> sections will always be removed from tag content (function <span class="term">hl_tag()</span>).<br />
+<br />
+  Examples:<br />
+<br />
+  Input:<br />
+
+<code class="code">    <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a></code>
+<br />
+  Output (<span class="term">$config["comment"] = 0, $config["cdata"] = 2</span>):<br />
+
+<code class="code">    &lt;-- home link --&gt;<a href="home.htm"><![CDATA[x=&amp;y]]>Home</a></code>
+<br />
+  Output (<span class="term">$config["comment"] = 1, $config["cdata"] = 2</span>):<br />
+
+<code class="code">    <a href="home.htm"><![CDATA[x=&amp;y]]>Home</a></code>
+<br />
+  Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 2</span>):<br />
+
+<code class="code">    <!-- home link --><a href="home.htm"><![CDATA[x=&amp;y]]>Home</a></code>
+<br />
+  Output (<span class="term">$config["comment"] = 2, $config["cdata"] = 1</span>):<br />
+
+<code class="code">    <!-- home link --><a href="home.htm">Home</a></code>
+<br />
+  Output (<span class="term">$config["comment"] = 3, $config["cdata"] = 3</span>):<br />
+
+<code class="code">    <!-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a></code>
+<br />
+<br />
+  For standard-compliance, comments are given the form <span class="term"><!--comment --></span>, and any <span class="term">--</span> in the content is made <span class="term">-</span>.<br />
+<br />
+  When <span class="term">$config["safe"] = 1</span>, CDATA sections and comments are considered plain text unless <span class="term">$config["comment"]</span> or <span class="term">$config["cdata"]</span> 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>  Tag-transformation for better XHTML-Strict
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  If <span class="term">$config["make_tag_strict"]</span> 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 />
+  *  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 />
+  *  center - <span class="term">div style="text-align: center;"</span><br />
+  *  dir - <span class="term">ul</span><br />
+  *  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 />
+  *  font (face, size, color) -    <span class="term">span style="font-family: ; font-size: ; color: ;"</span> (size transformation <a href="http://style.cleverchimp.com/font_size_intervals/altintervals.html">reference</a>)<br />
+  *  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 />
+  *  menu - <span class="term">ul</span><br />
+  *  s - <span class="term">span style="text-decoration: line-through;"</span><br />
+  *  strike - <span class="term">span style="text-decoration: line-through;"</span><br />
+  *  u - <span class="term">span style="text-decoration: underline;"</span><br />
+<br />
+  For an element with a pre-existing <span class="term">style</span> attribute value, the extra style properties are appended.<br />
+<br />
+  Example input:<br />
+<br />
+
+<code class="code">    <center></code>
+<br />
+
+<code class="code">     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>.</code>
+<br />
+
+<code class="code">    </center></code>
+<br />
+<br />
+  The output:<br />
+<br />
+
+<code class="code">    <div style="text-align: center;"></code>
+<br />
+
+<code class="code">     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>.</code>
+<br />
+
+<code class="code">    </div></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>  Tag balancing and proper nesting
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  If <span class="term">$config["balance"]</span> 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 />
+  Depending on the value of <span class="term">$config["keep_bad"]</span> (see <a href="#s2.2">section 2.2</a> and <a href="#s3.3">section 3.3</a>), illegal content may be removed or neutralized to plain text by converting < and > to entities:<br />
+<br />
+  <span class="term">0</span> - 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 />
+  <span class="term">1</span> - neutralize tags and keep element content<br />
+  <span class="term">2</span> - remove tags but keep element content<br />
+  <span class="term">3</span> and <span class="term">4</span> - like <span class="term">1</span> 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 />
+  <span class="term">5</span> and <span class="term">6</span> -  like <span class="term">3</span> and <span class="term">4</span>, but line-breaks, tabs and spaces are left<br />
+<br />
+  Example input (disallowing the <span class="term">p</span> element):<br />
+<br />
+
+<code class="code">    <*> Pseudo-tags <*></code>
+<br />
+
+<code class="code">    <xml>Non-HTML tag xml</xml></code>
+<br />
+
+<code class="code">    <p></code>
+<br />
+
+<code class="code">    Disallowed tag p</code>
+<br />
+
+<code class="code">    </p></code>
+<br />
+
+<code class="code">    <ul>Bad<li>OK</li></ul></code>
+<br />
+<br />
+  The output with <span class="term">$config["keep_bad"] = 1</span>:<br />
+<br />
+
+<code class="code">    &lt;*&gt; Pseudo-tags &lt;*&gt;</code>
+<br />
+
+<code class="code">    &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;</code>
+<br />
+
+<code class="code">    &lt;p&gt;</code>
+<br />
+
+<code class="code">    Disallowed tag p</code>
+<br />
+
+<code class="code">    &lt;/p&gt;</code>
+<br />
+
+<code class="code">    <ul>Bad<li>OK</li></ul></code>
+<br />
+<br />
+  The output with <span class="term">$config["keep_bad"] = 3</span>:<br />
+<br />
+
+<code class="code">    &lt;*&gt; Pseudo-tags &lt;*&gt;</code>
+<br />
+
+<code class="code">    &lt;xml&gt;Non-HTML tag xml&lt;/xml&gt;</code>
+<br />
+
+<code class="code">    &lt;p&gt;</code>
+<br />
+
+<code class="code">    Disallowed tag p</code>
+<br />
+
+<code class="code">    &lt;/p&gt;</code>
+<br />
+
+<code class="code">    <ul><li>OK</li></ul></code>
+<br />
+<br />
+  The output with <span class="term">$config["keep_bad"] = 6</span>:<br />
+<br />
+
+<code class="code">    &lt;*&gt; Pseudo-tags &lt;*&gt;</code>
+<br />
+
+<code class="code">    Non-HTML tag xml</code>
+<br />
+<br />
+
+<code class="code">    Disallowed tag p</code>
+<br />
+<br />
+
+<code class="code">    <ul><li>OK</li></ul></code>
+<br />
+<br />
+  An option like <span class="term">1</span> is useful, e.g., when a writer previews his submission, whereas one like <span class="term">3</span> is useful before content is finalized and made available to all.<br />
+<br />
+  <strong>Note:</strong> In the example above, unlike <span class="term"><*></span>, <span class="term"><xml></span> 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> parameter's value affects <span class="term"><xml></span> but not <span class="term"><*></span>. In general, text matching the regular expression pattern <span class="term"><(/?)([a-zA-Z][a-zA-Z1-6]*)([^>]*?)\s?></span> is considered a tag (phrase enclosed by the angled brackets <span class="term"><</span> and <span class="term">></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> value.<br />
+<br />
+  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> 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> is set to <span class="term">1</span>.<br />
+<br />
+  Plain text and/or certain elements nested inside <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span> and <span class="term">noscript</span> 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:<input type="text" value="b" />C:<input type="text" value="c" /></span> is converted to <span class="term"><div>B:<input type="text" value="b" />C:<input type="text" value="c" /></div></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>  Elements requiring child elements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  As per specs, the following elements require legal child elements nested inside them:<br />
+<br />
+
+<code class="code">    blockquote, dir, dl, form, map, menu, noscript, ol, optgroup, rbc, rtc, ruby, select, table, tbody, tfoot, thead, tr, ul</code>
+<br />
+<br />
+  In some cases, the specs stipulate the number and/or the ordering of the child elements. A <span class="term">table</span> 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 />
+  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 />
+  With <span class="term">$config["direct_list_nest"]</span> set to <span class="term">1</span>, htmLawed will allow direct nesting of an <span class="term">ol</span> or <span class="term">ul</span> list within another <span class="term">ol</span> or <span class="term">ul</span> without requiring the child list to be within an <span class="term">li</span> 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> 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>  Beautify or compact HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  By default, htmLawed will neither <em>beautify</em> 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 />
+  As per the HTML standards, spaces, tabs and line-breaks in web-pages (except those inside <span class="term">pre</span> 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> allows the use of text/code beautifully formatted with indentations and line-spacings for readability. Such <em>pretty</em> HTML can, however, increase the size of web-pages, or make the extraction or scraping of plain text cumbersome.<br />
+<br />
+  With the <span class="term">$config</span> 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> and <span class="term">textarea</span> elements, CDATA sections, and HTML comments are not subjected to the tidying process.<br />
+<br />
+  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 />
+  To <em>beautify</em>, <span class="term">$config["tidy"]</span> 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> or <span class="term">t</span> 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> and <span class="term">n</span> characters are used to specify line-break characters: <span class="term">n</span> for <span class="term">\n</span> (Unix/Mac OS X line-breaks), <span class="term">rn</span> or <span class="term">nr</span> for <span class="term">\r\n</span> (Windows/DOS line-breaks), or <span class="term">r</span> for <span class="term">\r</span>.<br />
+<br />
+  The <span class="term">$config["tidy"]</span> value of <span class="term">1</span> is equivalent to <span class="term">2s0n</span>. Other <span class="term">$config["tidy"]</span> values are read loosely: a value of <span class="term">4</span> 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 />
+  Input formatting using <span class="term">$config["tidy"]</span> 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>  Attributes
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> element (the non-standard <span class="term">embed</span> element is supported in htmLawed because of its widespread use), and the the <span class="term">xml:space</span> 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> 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 />
+  When <span class="term">$config["deny_attribute"]</span> 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> can be set as a list of comma-separated names of the denied attributes. <span class="term">on*</span> 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> and <span class="term">onsubmit</span>.<br />
+<br />
+  Note that attributes specified in <span class="term">$config["deny_attribute"]</span> are denied globally, for all elements. To deny attributes for only specific elements, <span class="term">$spec</span> (see <a href="#s2.3">section 2.3</a>) can be used. <span class="term">$spec</span> can also be used to element-specifically permit an attribute otherwise denied through <span class="term">$config["deny_attribute"]</span>.<br />
+<br />
+  With <span class="term">$config["safe"] = 1</span> (<a href="#s3.6">section 3.6</a>), the <span class="term">on*</span> attributes are automatically disallowed.<br />
+<br />
+  <strong>Note</strong>: To deny all but a few attributes globally, a simpler way to specify <span class="term">$config["deny_attribute"]</span> would be to use the notation <span class="term">* -attribute1 -attribute2 ...</span>. Thus, a value of <span class="term">* -title -href</span> implies that except <span class="term">href</span> and <span class="term">title</span> (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> (<a href="#s3.6">section 3.6</a>) will have no effect on <span class="term">deny_attribute</span>.<br />
+<br />
+  htmLawed (function <span class="term">hl_tag()</span>) also:<br />
+<br />
+  *  Lower-cases attribute names<br />
+  *  Removes duplicate attributes (last one stays)<br />
+  *  Gives attributes the form <span class="term">name="value"</span> and single-spaces them, removing unnecessary white-spacing<br />
+  *  Provides <em>required</em> attributes (see <a href="#s3.4.1">section 3.4.1</a>)<br />
+  *  Double-quotes values and escapes any <span class="term">"</span> inside them<br />
+  *  Replaces the possibly dangerous soft-hyphen characters (hexadecimal code-point <span class="term">ad</span>) in the values with spaces<br />
+  *  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>  Auto-addition of XHTML-required attributes
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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 />
+  *  area - alt (<span class="term">area</span>)<br />
+  *  area, img - src, alt (<span class="term">image</span>)<br />
+  *  bdo - dir (<span class="term">ltr</span>)<br />
+  *  form - action<br />
+  *  map - name<br />
+  *  optgroup - label<br />
+  *  param - name<br />
+  *  script - type (<span class="term">text/javascript</span>)<br />
+  *  textarea - rows (<span class="term">10</span>), cols (<span class="term">50</span>)<br />
+<br />
+  Additionally, with <span class="term">$config["xml:lang"]</span> set to <span class="term">1</span> or <span class="term">2</span>, if the <span class="term">lang</span> but not the <span class="term">xml:lang</span> 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:lang"]</span> set to <span class="term">2</span>, the <span class="term">lang</span> attribute is removed (XHTML 1.1 specs).<br />
+<br />
+  Note that the <span class="term">name</span> 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>  Duplicate/invalid <span class="term">id</span> values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  If <span class="term">$config["unique_ids"]</span> is <span class="term">1</span>, htmLawed (function <span class="term">hl_tag()</span>) removes <span class="term">id</span> attributes with values that are not XHTML-compliant (must begin with a letter and can contain letters, digits, <span class="term">:</span>, <span class="term">.</span>, <span class="term">-</span> and <span class="term">_</span>) or duplicate. If <span class="term">$config["unique_ids"]</span> 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">:</span>, <span class="term">.</span>, <span class="term">_</span> and <span class="term">-</span>.<br />
+<br />
+  Even if multiple inputs need to be filtered (through multiple calls to htmLawed), htmLawed ensures uniqueness of <span class="term">id</span> values as it uses a global variable (<span class="term">$GLOBALS["hl_Ids"]</span> array). Further, an admin can restrict the use of certain <span class="term">id</span> values by presetting this variable before htmLawed is called into use. E.g.:<br />
+<br />
+
+<code class="code">    $GLOBALS['hl_Ids'] = array('top'=>1, 'bottom'=>1, 'myform'=>1); // id values not allowed in input</code>
+<br />
+
+<code class="code">    $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>  URL schemes (protocols) and scripts in attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> scheme is not permitted, then <span class="term"><a href="afp://domain.org"></span> becomes <span class="term"><a href="denied:afp://domain.org"></span>, and if Javascript is not permitted <span class="term"><a onclick="javascript:xss();"></span> becomes <span class="term"><a onclick="denied:javascript:xss();"></span>.<br />
+<br />
+  By default htmLawed permits these schemes in URLs for the <span class="term">href</span> attribute:<br />
+<br />
+
+<code class="code">    aim, feed, file, ftp, gopher, http, https, irc, mailto, news, nntp, sftp, ssh, telnet</code>
+<br />
+<br />
+  Also, only <span class="term">file</span>, <span class="term">http</span> and <span class="term">https</span> are permitted in attributes whose names start with <span class="term">o</span> (like <span class="term">onmouseover</span>), and in these attributes that accept URLs:<br />
+<br />
+
+<code class="code">    action, cite, classid, codebase, data, href, longdesc, model, pluginspage, pluginurl, src, style, usemap</code>
+<br />
+<br />
+  These default sets are used when <span class="term">$config["schemes"]</span> is not set (see <a href="#s2.2">section 2.2</a>). To over-ride the defaults, <span class="term">$config["schemes"]</span> is defined as a string of semi-colon-separated sub-strings of type <span class="term">attribute: comma-separated schemes</span>. E.g., <span class="term">href: mailto, http, https; onclick: javascript; src: http, https</span>. For unspecified attributes, <span class="term">file</span>, <span class="term">http</span> and <span class="term">https</span> are permitted. This can be changed by passing schemes for <span class="term">*</span> in <span class="term">$config["schemes"]</span>. E.g., <span class="term">href: mailto, http, https; *: https, https</span>.<br />
+<br />
+  <span class="term">*</span> can be put in the list of schemes to permit all protocols. E.g., <span class="term">style: *; img: http, https</span> results in protocols not being checked in <span class="term">style</span> 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 />
+  Thus, <em>to allow Javascript</em>, one can set <span class="term">$config["schemes"]</span> as <span class="term">href: mailto, http, https; *: http, https, javascript</span>, or <span class="term">href: mailto, http, https, javascript; *: http, https, javascript</span>, or <span class="term">*: *</span>, and so on.<br />
+<br />
+  As a side-note, one may find <span class="term">style: *</span> useful as URLs in <span class="term">style</span> 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 />
+  <span class="term">!</span> can be put in the list of schemes to disallow all protocols as well as <em>local</em> URLs. Thus, with <span class="term">href: http, style: !</span>, '<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>'.<br />
+<br />
+  <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> (e.g., <span class="term">dynsrc</span>) or starts with <span class="term">o</span> (e.g., <span class="term">onbeforecopy</span>).<br />
+<br />
+  With <span class="term">$config["safe"] = 1</span>, all URLs are disallowed in the <span class="term">style</span> 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>  Absolute & relative URLs in attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  htmLawed can make absolute URLs in attributes like <span class="term">href</span> relative (<span class="term">$config["abs_url"]</span> is <span class="term">-1</span>), and vice versa (<span class="term">$config["abs_url"]</span> 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> (fragment), <span class="term">?name=Tim#show</span> (starting with query string), and <span class="term">;var=1?name=Tim#show</span> (starting with parameters). Further, this requires that <span class="term">$config["base_url"]</span> be set properly, with the <span class="term">://</span> and a trailing slash (<span class="term">/</span>), with no query string, etc. E.g., <span class="term">file:///D:/page/</span>, <span class="term">https://abc.com/x/y/</span>, or <span class="term">http://localhost/demo/</span> are okay, but <span class="term">file:///D:/page/?help=1</span>, <span class="term">abc.com/x/y/</span> and <span class="term">http://localhost/demo/index.htm</span> are not.<br />
+<br />
+  For making absolute URLs relative, only those URLs that have the <span class="term">$config["base_url"]</span> string at the beginning are converted. E.g., with <span class="term">$config["base_url"] = "https://abc.com/x/y/"</span>, <span class="term">https://abc.com/x/y/a.gif</span> and <span class="term">https://abc.com/x/y/z/b.gif</span> become <span class="term">a.gif</span> and <span class="term">z/b.gif</span> respectively, while <span class="term">https://abc.com/x/c.gif</span> is not changed.<br />
+<br />
+  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> 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>  Lower-cased, standard attribute values
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> instead of <span class="term">input type="Password"</span>, if <span class="term">$config["lc_std_val"]</span> 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> attribute of <span class="term">button</span> or <span class="term">input</span>) are lower-cased:<br />
+<br />
+
+<code class="code">    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">    a, area, bdo, button, col, form, img, input, object, option, optgroup, param, script, select, table, td, tfoot, th, thead, tr, xml:space</code>
+<br />
+<br />
+  The following <em>empty</em> (<em>minimized</em>) attributes are always assigned lower-cased values (same as the names):<br />
+<br />
+
+<code class="code">    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>  Transformation of deprecated attributes
+</h4><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  If <span class="term">$config["no_deprecated_attr"]</span> 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> attributes (function <span class="term">hl_tag()</span>). Except for <span class="term">bordercolor</span> for <span class="term">table</span>, <span class="term">tr</span> 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 />
+  <strong>Note</strong>: The attribute <span class="term">target</span> for <span class="term">a</span> 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 />
+  *  align - for <span class="term">img</span> with value of <span class="term">left</span> or <span class="term">right</span>, becomes, e.g., <span class="term">float: left</span>; for <span class="term">div</span> and <span class="term">table</span> with value <span class="term">center</span>, becomes <span class="term">margin: auto</span>; all others become, e.g., <span class="term">text-align: right</span><br />
+<br />
+  *  bgcolor - E.g., <span class="term">bgcolor="#ffffff"</span> becomes <span class="term">background-color: #ffffff</span><br />
+  *  border - E.g., <span class="term">height= "10"</span> becomes <span class="term">height: 10px</span><br />
+  *  bordercolor - E.g., <span class="term">bordercolor=#999999</span> becomes <span class="term">border-color: #999999;</span><br />
+  *  compact - <span class="term">font-size: 85%</span><br />
+  *  clear - E.g., 'clear="all" becomes <span class="term">clear: both</span><br />
+<br />
+  *  height - E.g., <span class="term">height= "10"</span> becomes <span class="term">height: 10px</span> and <span class="term">height="*"</span> becomes <span class="term">height: auto</span><br />
+<br />
+  *  hspace - E.g., <span class="term">hspace="10"</span> becomes <span class="term">margin-left: 10px; margin-right: 10px</span><br />
+  *  language - <span class="term">language="VBScript"</span> becomes <span class="term">type="text/vbscript"</span><br />
+  *  name - E.g., <span class="term">name="xx"</span> becomes <span class="term">id="xx"</span><br />
+  *  noshade - <span class="term">border-style: none; border: 0; background-color: gray; color: gray</span><br />
+  *  nowrap - <span class="term">white-space: nowrap</span><br />
+  *  size - E.g., <span class="term">size="10"</span> becomes <span class="term">height: 10px</span><br />
+  *  start - removed<br />
+  *  type - E.g., <span class="term">type="i"</span> becomes <span class="term">list-style-type: lower-roman</span><br />
+  *  value - removed<br />
+  *  vspace - E.g., <span class="term">vspace="10"</span> becomes <span class="term">margin-top: 10px; margin-bottom: 10px</span><br />
+  *  width - like <span class="term">height</span><br />
+<br />
+  Example input:<br />
+<br />
+
+<code class="code">    <img src="j.gif" alt="image" name="dad's" /><img src="k.gif" alt="image" id="dad_off" name="dad" /></code>
+<br />
+
+<code class="code">    <br clear="left" /></code>
+<br />
+
+<code class="code">    <hr noshade size="1" /></code>
+<br />
+
+<code class="code">    <img name="img" src="i.gif" align="left" alt="image" hspace="10" vspace="10" width="10em" height="20" border="1" style="padding:5px;" /></code>
+<br />
+
+<code class="code">    <table width="50em" align="center" bgcolor="red"></code>
+<br />
+
+<code class="code">     <tr></code>
+<br />
+
+<code class="code">      <td width="20%"></code>
+<br />
+
+<code class="code">       <div align="center"></code>
+<br />
+
+<code class="code">        <h3 align="right">Section</h3></code>
+<br />
+
+<code class="code">        <p align="right">Para</p></code>
+<br />
+
+<code class="code">        <ol type="a" start="e"><li value="x">First item</li></ol></code>
+<br />
+
+<code class="code">       </div></code>
+<br />
+
+<code class="code">      </td></code>
+<br />
+
+<code class="code">      <td width="*"></code>
+<br />
+
+<code class="code">       <ol type="1"><li>First item</li></ol></code>
+<br />
+
+<code class="code">      </td></code>
+<br />
+
+<code class="code">     </tr></code>
+<br />
+
+<code class="code">    </table></code>
+<br />
+
+<code class="code">    <br clear="all" /></code>
+<br />
+<br />
+  And the output with <span class="term">$config["no_deprecated_attr"] = 1</span>:<br />
+<br />
+
+<code class="code">    <img src="j.gif" alt="image" /><img src="k.gif" alt="image" id="dad_off" /></code>
+<br />
+
+<code class="code">    <br style="clear: left;" /></code>
+<br />
+
+<code class="code">    <hr style="border-style: none; border: 0; background-color: gray; color: gray; size: 1px;" /></code>
+<br />
+
+<code class="code">    <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" /></code>
+<br />
+
+<code class="code">    <table width="50em" style="margin: auto; background-color: red;"></code>
+<br />
+
+<code class="code">     <tr></code>
+<br />
+
+<code class="code">      <td style="width: 20%;"></code>
+<br />
+
+<code class="code">       <div style="margin: auto;"></code>
+<br />
+
+<code class="code">        <h3 style="text-align: right;">Section</h3></code>
+<br />
+
+<code class="code">        <p style="text-align: right;">Para</p></code>
+<br />
+
+<code class="code">        <ol style="list-style-type: lower-latin;"><li>First item</li></ol></code>
+<br />
+
+<code class="code">       </div></code>
+<br />
+
+<code class="code">      </td></code>
+<br />
+
+<code class="code">      <td style="width: auto;"></code>
+<br />
+
+<code class="code">       <ol style="list-style-type: decimal;"><li>First item</li></ol></code>
+<br />
+
+<code class="code">      </td></code>
+<br />
+
+<code class="code">     </tr></code>
+<br />
+
+<code class="code">    </table></code>
+<br />
+
+<code class="code">    <br style="clear: both;" /></code>
+<br />
+<br />
+  For <span class="term">lang</span>, deprecated in XHTML 1.1, transformation is taken care of through <span class="term">$config["xml:lang"]</span>; see <a href="#s3.4.1">section 3.4.1</a>.<br />
+<br />
+  The attribute <span class="term">name</span> 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> if an <span class="term">id</span> attribute doesn't exist and if the <span class="term">name</span> value is appropriate for <span class="term">id</span>. For such replacements for <span class="term">a</span> and <span class="term">map</span>, for which the <span class="term">name</span> attribute is deprecated in XHTML 1.1, <span class="term">$config["no_deprecated_attr"]</span> should be set to <span class="term">2</span> (when set to <span class="term">1</span>, for these two elements, the <span class="term">name</span> 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>  Anti-spam & <span class="term">href</span>
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  htmLawed (function <span class="term">hl_tag()</span>) can check the <span class="term">href</span> attribute values (link addresses) as an anti-spam (email or link spam) measure.<br />
+<br />
+  If <span class="term">$config["anti_mail_spam"]</span> is not <span class="term">0</span>, the <span class="term">@</span> of email addresses in <span class="term">href</span> values like <span class="term">mailto:a@b.com</span> 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"><remove_this_antispam>@</span> (makes the example address <span class="term">a<remove_this_antispam>@b.com</span>).<br />
+<br />
+  For regular links, one can choose to have a <span class="term">rel</span> attribute with <span class="term">nofollow</span> 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> value altogether (disable the link).<br />
+<br />
+  For use of these options, <span class="term">$config["anti_link_spam"]</span> should be set as an array with values <span class="term">regex1</span> 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> or <span class="term">regex2</span> should be PHP- and PCRE-compatible regular expression patterns: <span class="term">href</span> values will be matched against them and those matching the pattern will accordingly be treated.<br />
+<br />
+  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 />
+  An example, to have a <span class="term">rel</span> attribute with <span class="term">nofollow</span> for all links, and to disable links that do not point to domains <span class="term">abc.com</span> and <span class="term">xyz.org</span>:<br />
+<br />
+
+<code class="code">    $config["anti_link_spam"] = array('`.`', '`://\W*(?!(abc\.com|xyz\.org))`');</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>  Inline style properties
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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> attributes. (CSS properties like <span class="term">background-image</span> 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> to <span class="term">1</span> (default setting). Note that when <span class="term">$config["css_expression"]</span> is set to <span class="term">1</span>, htmLawed will remove <span class="term">/*</span> from the <span class="term">style</span> values.<br />
+<br />
+  <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> 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:*..."</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> values through the <span class="term">hook_tag</span> parameter (see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+  It is also possible to have htmLawed let through any <span class="term">style</span> value by setting <span class="term">$config["style_pass"]</span> to <span class="term">1</span>.<br />
+<br />
+  As such, it is better to set up a CSS file with class declarations, disallow the <span class="term">style</span> attribute, set a <span class="term">$spec</span> rule (see <a href="#s2.3">section 2.3</a>) for <span class="term">class</span> for the <span class="term">oneof</span> or <span class="term">match</span> parameter, and ask writers to make use of the <span class="term">class</span> 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>  Hook function for tag content
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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 />
+  When <span class="term">$config</span> parameter <span class="term">hook_tag</span> 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> 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"><element_name attribute_1_name="attribute_1_value"...></span> (for empty elements like <span class="term">img</span> and <span class="term">input</span>, the element-closing slash <span class="term">/</span> should also be included), etc.<br />
+<br />
+  Any <span class="term">hook_tag</span> function, since htmLawed version 1.1.11, also receives names of elements in closing tags, such as <span class="term">a</span> in the closing <span class="term"></a></span> tag of the element <span class="term"><a href="http://cnn.com">CNN</a></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"></a></span>).<br />
+<br />
+  This is a <strong>powerful functionality</strong> that can be exploited for various objectives: consolidate-and-convert inline <span class="term">style</span> attributes to <span class="term">class</span>, convert <span class="term">embed</span> elements to <span class="term">object</span>, permit only one <span class="term">caption</span> element in a <span class="term">table</span> element, disallow embedding of certain types of media, <strong>inject HTML</strong>, use <a href="http://csstidy.sourceforge.net">CSSTidy</a> to sanitize <span class="term">style</span> attribute values, etc.<br />
+<br />
+  As an example, the custom hook code below can be used to force a series of specifically ordered <span class="term">id</span> attributes on all elements, and a specific <span class="term">param</span> element inside all <span class="term">object</span> elements:<br />
+<br />
+
+<code class="code">    function my_tag_function($element, $attribute_array=0){</code>
+<br />
+<br />
+
+<code class="code">      // If second argument is not received, it means a closing tag is being handled</code>
+<br />
+
+<code class="code">      if(is_numeric($attribute_array)){</code>
+<br />
+
+<code class="code">        return "</$element>";</code>
+<br />
+
+<code class="code">      }</code>
+<br />
+<br />
+
+<code class="code">      static $id = 0;</code>
+<br />
+
+<code class="code">      // Remove any duplicate element</code>
+<br />
+
+<code class="code">      if($element == 'param' && isset($attribute_array['allowscriptaccess'])){</code>
+<br />
+
+<code class="code">        return '';</code>
+<br />
+
+<code class="code">      }</code>
+<br />
+<br />
+
+<code class="code">      $new_element = '';</code>
+<br />
+<br />
+
+<code class="code">      // Force a serialized ID number</code>
+<br />
+
+<code class="code">      $attribute_array['id'] = 'my_'. $id;</code>
+<br />
+
+<code class="code">      ++$id;</code>
+<br />
+<br />
+
+<code class="code">      // Inject param for allowscriptaccess</code>
+<br />
+
+<code class="code">      if($element == 'object'){</code>
+<br />
+
+<code class="code">        $new_element = '<param id='my_'. $id; allowscriptaccess="never" />';</code>
+<br />
+
+<code class="code">        ++$id;</code>
+<br />
+
+<code class="code">      }</code>
+<br />
+<br />
+
+<code class="code">      $string = '';</code>
+<br />
+
+<code class="code">      foreach($attribute_array as $k=>$v){</code>
+<br />
+
+<code class="code">        $string .= " {$k}=\"{$v}\"";</code>
+<br />
+
+<code class="code">      }</code>
+<br />
+<br />
+
+<code class="code">      static $empty_elements = array('area'=>1, 'br'=>1, 'col'=>1, 'embed'=>1, 'hr'=>1, 'img'=>1, 'input'=>1, 'isindex'=>1, 'param'=>1);</code>
+<br />
+<br />
+
+<code class="code">      return "<{$element}{$string}". (isset($in_array($element, $empty_elements) ? ' /' : ''). '>'. $new_element;</code>
+<br />
+
+<code class="code">    }</code>
+<br />
+<br />
+  The <span class="term">hook_tag</span> parameter is different from the <span class="term">hook</span> parameter (<a href="#s3.7">section 3.7</a>).<br />
+<br />
+  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> website.<br />
+
+</div>
+</div>
+<div class="sub-section"><h3>
+<a name="s3.5" id="s3.5"></a><span class="item-no">3.5</span>  Simple configuration directive for most valid XHTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  If <span class="term">$config["valid_xhtml"]</span> is set to <span class="term">1</span>, some relevant <span class="term">$config</span> parameters (indicated by <span class="term">~</span> in <a href="#s2.2">section 2.2</a>) are auto-adjusted. This allows one to pass the <span class="term">$config</span> argument with a simpler value. If a value for a parameter auto-set through <span class="term">valid_xhtml</span> 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>  Simple configuration directive for most <em>safe</em> HTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  <em>Safe</em> 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> and <span class="term">object</span>, and attributes such as <span class="term">onmouseover</span> and <span class="term">style</span> are allowed in the input text, an input writer can introduce malevolent HTML code. Note that what is considered <span class="term">safe</span> depends on the nature of the web application and the trust-level accorded to its users.<br />
+<br />
+  htmLawed allows an admin to use <span class="term">$config["safe"]</span> to auto-adjust multiple <span class="term">$config</span> parameters (such as <span class="term">elements</span> which declares the allowed element-set), which otherwise would have to be manually set. The relevant parameters are indicated by <span class="term">"</span> in <a href="#s2.2">section 2.2</a>). Thus, one can pass the <span class="term">$config</span> argument with a simpler value.<br />
+<br />
+  With the value of <span class="term">1</span>, htmLawed considers <span class="term">CDATA</span> 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> and <span class="term">script</span> elements, and the <span class="term">on*</span> attributes like <span class="term">onclick</span>. ( There are <span class="term">$config</span> parameters like <span class="term">css_expression</span> that are not affected by the value set for <span class="term">safe</span> but whose default values still contribute towards a more <em>safe</em> 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:url(http://danger)"</span> becomes <span class="term">style="moz-binding:url(denied:http://danger)"</span>.<br />
+<br />
+  Admins, however, may still want to completely deny the <span class="term">style</span> attribute, e.g., with code like<br />
+<br />
+
+<code class="code">    $processed = htmLawed($text, array('safe'=>1, 'deny_attribute'=>'style'));</code>
+<br />
+<br />
+  Permitting the <span class="term">style</span> 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> values. It does provide ways for the code-developer implementing htmLawed to do such checks through the <span class="term">$spec</span> argument, and through the <span class="term">hook_tag</span> parameter (see <a href="#s3.4.8">section 3.4.8</a> for more). Disallowing style completely and relying on CSS classes and stylesheet files is recommended.<br />
+<br />
+  If a value for a parameter auto-set through <span class="term">safe</span> is still manually provided, then that value can over-ride the auto-set value. E.g., with <span class="term">$config["safe"] = 1</span> and <span class="term">$config["elements"] = "*+script"</span>, <span class="term">script</span>, but not <span class="term">applet</span>, is allowed.<br />
+<br />
+  A page illustrating the efficacy of htmLawed's anti-XSS abilities with <span class="term">safe</span> set to <span class="term">1</span> against XSS vectors listed by <a href="http://ha.ckers.org/xss.html">RSnake</a> 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>  Using a hook function
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  If <span class="term">$config["hook"]</span> 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> before starting the main work (but after handling of characters, entities, HTML comments and <span class="term">CDATA</span> sections -- see code for function <span class="term">htmLawed()</span>).<br />
+<br />
+  The hook function also allows one to alter the <em>finalized</em> values of <span class="term">$config</span> and <span class="term">$spec</span>.<br />
+<br />
+  Note that the <span class="term">hook</span> parameter is different from the <span class="term">hook_tag</span> parameter (<a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+  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> website.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s3.8" id="s3.8"></a><span class="item-no">3.8</span>  Obtaining <em>finalized</em> parameter values
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  htmLawed can assign the <em>finalized</em> <span class="term">$config</span> and <span class="term">$spec</span> 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> value, <span class="term">spec</span>, with the <span class="term">$spec</span> 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> 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 />
+  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>  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 />
+  htmLawed does not remove certain characters that, though invalid, are nevertheless <em>discouraged</em> 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"><</span>, <span class="term">></span> and <span class="term">&</span> 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 />
+  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"><</span>, <span class="term">></span> and <span class="term">&</span> 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 />
+  An example (mixed HTML and PHP code in input text):<br />
+<br />
+
+<code class="code">    $text = preg_replace('`<\?php(.+?)\?>`sm', "\x83?php\\1?\x84", $text);</code>
+<br />
+
+<code class="code">    $processed = htmLawed($text);</code>
+<br />
+
+<code class="code">    $processed = preg_replace('`\x83\?php(.+?)\?\x84`sm', '<?php$1?>', $processed);</code>
+<br />
+<br />
+  This code will not work if <span class="term">$config["clean_ms_char"]</span> is set to <span class="term">1</span> (<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> to <span class="term">7</span>, and use of these characters as markers in the logic of hook functions may cause issues.)<br />
+<br />
+  Admins may also be able to use <span class="term">$config["and_mark"]</span> 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>  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>  Support
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  A careful reading of this documentation may provide an answer.<br />
+<br />
+  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>  Known issues
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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>  Change-log
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  (The release date for the downloadable package of files containing documentation, demo script, test-cases, etc., besides the <span class="term">htmLawed.php</span> file, may be updated without a change-log entry if the secondary files, but not htmLawed per se, are revised.)<br />
+<br />
+  <em>Version number - Release date. Notes</em><br />
+<br />
+  1.1.16 - 29 August 2013. Fix for a potential security vulnerability arising from specialy encoded space characters in URL schemes/protocols<br />
+<br />
+  1.1.15 - 11 August 2013. Improved tidying/prettifying functionality<br />
+<br />
+  1.1.14 - 8 August 2012. Fix for possible segmental loss of incremental indentation during <span class="term">tidying</span> when <span class="term">balance</span> 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 />
+  1.1.13 - 22 July 2012. Added feature allowing use of custom, non-standard attributes or custom rules for standard attributes<br />
+<br />
+  1.1.12 - 5 July 2012. Fix for a bug in identifying an unquoted value of the <span class="term">face</span> attribute<br />
+<br />
+  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 />
+  1.1.10 - 22 October 2011. Fix for a bug in the <span class="term">tidy</span> functionality that caused the entire input to be replaced with a single space; new parameter, <span class="term">$config["direct_list_nest"]</span> to allow direct descendance of a list in a list. (5 April 2012. Dual licensing from LGPLv3 to LGPLv3 and GPLv2+.)<br />
+<br />
+  1.1.9.5 - 6 July 2011. Minor correction of a rule for nesting of <span class="term">li</span> within <span class="term">dir</span><br />
+<br />
+  1.1.9.4 - 3 July 2010. Parameter <span class="term">schemes</span> now accepts <span class="term">!</span> 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> properties was not checked was fixed.<br />
+<br />
+  1.1.9.3 - 17 May 2010. Checks for correct nesting of <span class="term">param</span><br />
+<br />
+  1.1.9.2 - 26 April 2010. Minor fix regarding rendering of denied URL schemes<br />
+<br />
+  1.1.9.1 - 26 February 2010. htmLawed now uses the LGPL version 3 license; support for <span class="term">flashvars</span> attribute for <span class="term">embed</span><br />
+<br />
+  1.1.9 - 22 December 2009. Soft-hyphens are now removed only from URL-accepting attribute values<br />
+<br />
+  1.1.8.1 - 16 July 2009. Minor code-change to fix a PHP error notice<br />
+<br />
+  1.1.8 - 23 April 2009. Parameter <span class="term">deny_attribute</span> now accepts the wild-card <span class="term">*</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 />
+  1.1.7 - 11-12 March 2009. Attributes globally denied through <span class="term">deny_attribute</span> can be allowed element-specifically through <span class="term">$spec</span>; <span class="term">$config["style_pass"]</span> allowing letting through any <span class="term">style</span> value introduced; altered logic to catch certain types of dynamic crafted CSS expressions<br />
+<br />
+  1.1.3-6 - 28-31 January - 4 February 2009. Altered logic to catch certain types of dynamic crafted CSS expressions<br />
+<br />
+  1.1.2 - 22 January 2009. Fixed bug in parsing of <span class="term">font</span> attributes during tag transformation<br />
+<br />
+  1.1.1 - 27 September 2008. Better nesting correction when omitable closing tags are absent<br />
+<br />
+  1.1 - 29 June 2008. <span class="term">$config["hook_tag"]</span> and <span class="term">$config["tidy"]</span> introduced for custom tag/attribute check/modification/injection and output compaction/beautification; fixed a regex-in-$spec parsing bug<br />
+<br />
+  1.0.9 - 11 June 2008. Fix for a bug in checks for invalid HTML code-point entities<br />
+<br />
+  1.0.8 - 15 May 2008. Permit <span class="term">bordercolor</span> attribute for <span class="term">table</span>, <span class="term">td</span> and <span class="term">tr</span><br />
+<br />
+  1.0.7 - 1 May 2008. Support for <span class="term">wmode</span> attribute for <span class="term">embed</span>; <span class="term">$config["show_setting"]</span> introduced; improved <span class="term">$config["elements"]</span> evaluation<br />
+<br />
+  1.0.6 - 20 April 2008. <span class="term">$config["and_mark"]</span> introduced<br />
+<br />
+  1.0.5 - 12 March 2008. <span class="term">style</span> URL schemes essentially disallowed when $config <span class="term">safe</span> is on; improved regex for CSS expression search<br />
+<br />
+  1.0.4 - 10 March 2008. Improved corrections for <span class="term">blockquote</span>, <span class="term">form</span>, <span class="term">map</span> and <span class="term">noscript</span><br />
+<br />
+  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> directly inside <span class="term">table</span>; <span class="term">$config["safe"]</span> introduced<br />
+<br />
+  1.0.2 - 13 February 2008. Improved implementation of <span class="term">$config["keep_bad"]</span><br />
+<br />
+  1.0.1 - 7 November 2007. Improved regex for identifying URLs, protocols and dynamic expressions (<span class="term">hl_tag()</span> and <span class="term">hl_prot()</span>); no error display with <span class="term">hl_regex()</span><br />
+<br />
+  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>  Testing
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  To test htmLawed using a form interface, a <a href="htmLawedTest.php">demo</a> web-page is provided with the htmLawed distribution (<span class="term">htmLawed.php</span> and <span class="term">htmLawedTest.php</span> should be in the same directory on the web-server). A file with <a href="htmLawed_TESTCASE.txt">test-cases</a> 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>  Upgrade, & old versions
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  Upgrading is as simple as replacing the previous version of <span class="term">htmLawed.php</span> (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 />
+  <strong>Important</strong>  The following upgrades may affect the functionality of a specific htmLawed installation:<br />
+<br />
+  (1) From version 1.1-1.1.10 to 1.1.11 (or later), if a <span class="term">hook_tag</span> 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> function receives only the element name. The <span class="term">hook_tag</span> function therefore may have to be edited. See <a href="#s3.4.9">section 3.4.9</a>.<br />
+<br />
+  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>  Comparison with <span class="term">HTMLPurifier</span>
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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 />
+  *  does not support PHP versions older than 5.0 (HTMLPurifier dropped PHP 4 support after version 2)<br />
+<br />
+  *  is 15-20 times bigger (scores of files totalling more than 750 kb)<br />
+<br />
+  *  consumes 10-15 times more RAM memory (just including the HTMLPurifier files without calling the filter requires a few MBs of memory)<br />
+<br />
+  *  is expectedly slower<br />
+<br />
+  *  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> illegal)<br />
+<br />
+  *  lacks many of the extra features of htmLawed (like entity conversions and code compaction/beautification)<br />
+<br />
+  *  has poor documentation<br />
+<br />
+  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> 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>  Use through application plug-ins/modules
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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>  Use in non-PHP applications
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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>  Donate
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  A donation in any currency and amount to appreciate or support this software can be sent by <a href="http://paypal.com">PayPal</a> 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>  Acknowledgements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  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 />
+  Thank you!<br />
+
+</div>
+</div>
+<div class="section"><h2>
+<a name="s5" id="s5"></a><span class="item-no">5</span>  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>  Characters discouraged in XHTML
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  Characters represented by the following hexadecimal code-points are <em>not</em> invalid, even though some validators may issue messages stating otherwise.<br />
+<br />
+  <span class="term">7f</span> to <span class="term">84</span>, <span class="term">86</span> to <span class="term">9f</span>, <span class="term">fdd0</span> 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> 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>  Valid attribute-element combinations
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  Valid attribute-element combinations as per <a href="http://www.w3c.org">W3C</a> specs.<br />
+<br />
+  *  includes deprecated attributes (marked <span class="term">^</span>), attributes for the non-standard <span class="term">embed</span> element (marked <span class="term">*</span>), and the proprietary <span class="term">bordercolor</span> (marked <span class="term">~</span>)<br />
+  *  only non-frameset, HTML body elements<br />
+  *  <span class="term">name</span> for <span class="term">a</span> and <span class="term">map</span>, and <span class="term">lang</span> are invalid in XHTML 1.1<br />
+  *  <span class="term">target</span> is valid for <span class="term">a</span> in XHTML 1.1 and higher<br />
+  *  <span class="term">xml:space</span> is only for XHTML 1.1<br />
+<br />
+  abbr - td, th<br />
+  accept - form, input<br />
+  accept-charset - form<br />
+  accesskey - a, area, button, input, label, legend, textarea<br />
+  action - form<br />
+  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 />
+  alt - applet, area, img, input<br />
+  archive - applet, object<br />
+  axis - td, th<br />
+  bgcolor - embed, table^, tr^, td^, th^<br />
+  border - table, img^, object^<br />
+  bordercolor~ - table, td, tr<br />
+  cellpadding - table<br />
+  cellspacing - table<br />
+  char - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+  charoff - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+  charset - a, script<br />
+  checked - input<br />
+  cite - blockquote, q, del, ins<br />
+  classid - object<br />
+  clear - br^<br />
+  code - applet<br />
+  codebase - object, applet<br />
+  codetype - object<br />
+  color - font<br />
+  cols - textarea<br />
+  colspan - td, th<br />
+  compact - dir, dl^, menu, ol^, ul^<br />
+  coords - area, a<br />
+  data - object<br />
+  datetime - del, ins<br />
+  declare - object<br />
+  defer - script<br />
+  dir - bdo<br />
+  disabled - button, input, optgroup, option, select, textarea<br />
+  enctype - form<br />
+  face - font<br />
+  flashvars* - embed<br />
+  for - label<br />
+  frame - table<br />
+  frameborder - iframe<br />
+  headers - td, th<br />
+  height - embed, iframe, td^, th^, img, object, applet<br />
+  href - a, area<br />
+  hreflang - a<br />
+  hspace - applet, img^, object^<br />
+  ismap - img, input<br />
+  label - option, optgroup<br />
+  language - script^<br />
+  longdesc - img, iframe<br />
+  marginheight - iframe<br />
+  marginwidth - iframe<br />
+  maxlength - input<br />
+  method - form<br />
+  model* - embed<br />
+  multiple - select<br />
+  name - button, embed, textarea, applet^, select, form^, iframe^, img^, a^, input, object, map^, param<br />
+  nohref - area<br />
+  noshade - hr^<br />
+  nowrap - td^, th^<br />
+  object - applet<br />
+  onblur - a, area, button, input, label, select, textarea<br />
+  onchange - input, select, textarea<br />
+  onfocus - a, area, button, input, label, select, textarea<br />
+  onreset - form<br />
+  onselect - input, textarea<br />
+  onsubmit - form<br />
+  pluginspage* - embed<br />
+  pluginurl* - embed<br />
+  prompt - isindex<br />
+  readonly - textarea, input<br />
+  rel - a<br />
+  rev - a<br />
+  rows - textarea<br />
+  rowspan - td, th<br />
+  rules - table<br />
+  scope - td, th<br />
+  scrolling - iframe<br />
+  selected - option<br />
+  shape - area, a<br />
+  size - hr^, font, input, select<br />
+  span - col, colgroup<br />
+  src - embed, script, input, iframe, img<br />
+  standby - object<br />
+  start - ol^<br />
+  summary - table<br />
+  tabindex - a, area, button, input, object, select, textarea<br />
+  target - a^, area, form<br />
+  type - a, embed, object, param, script, input, li^, ol^, ul^, button<br />
+  usemap - img, input, object<br />
+  valign - col, colgroup, tbody, td, tfoot, th, thead, tr<br />
+  value - input, option, param, button, li^<br />
+  valuetype - param<br />
+  vspace - applet, img^, object^<br />
+  width - embed, hr^, iframe, img, object, table, td^, th^, applet, col, colgroup, pre^<br />
+  wmode - embed<br />
+  xml:space - pre, script, style<br />
+<br />
+  These are allowed in all but the shown elements:<br />
+<br />
+  class - param, script<br />
+  dir - applet, bdo, br, iframe, param, script<br />
+  id - script<br />
+  lang - applet, br, iframe, param, script<br />
+  onclick - applet, bdo, br, font, iframe, isindex, param, script<br />
+  ondblclick - applet, bdo, br, font, iframe, isindex, param, script<br />
+  onkeydown - applet, bdo, br, font, iframe, isindex, param, script<br />
+  onkeypress - applet, bdo, br, font, iframe, isindex, param, script<br />
+  onkeyup - applet, bdo, br, font, iframe, isindex, param, script<br />
+  onmousedown - applet, bdo, br, font, iframe, isindex, param, script<br />
+  onmousemove - applet, bdo, br, font, iframe, isindex, param, script<br />
+  onmouseout - applet, bdo, br, font, iframe, isindex, param, script<br />
+  onmouseover - applet, bdo, br, font, iframe, isindex, param, script<br />
+  onmouseup - applet, bdo, br, font, iframe, isindex, param, script<br />
+  style - param, script<br />
+  title - param, script<br />
+  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>  CSS 2.1 properties accepting URLs
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  background<br />
+  background-image<br />
+  content<br />
+  cue-after<br />
+  cue-before<br />
+  cursor<br />
+  list-style<br />
+  list-style-image<br />
+  play-during<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.4" id="s5.4"></a><span class="item-no">5.4</span>  Microsoft Windows 1252 character replacements
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  Key: <span class="term">d</span> double, <span class="term">l</span> left, <span class="term">q</span> quote, <span class="term">r</span> right, <span class="term">s.</span> single<br />
+<br />
+  Code-point (decimal) - hexadecimal value - replacement entity - represented character<br />
+<br />
+  127 - 7f - (removed) - (not used)<br />
+  128 - 80 - &#8364; - euro<br />
+  129 - 81 - (removed) - (not used)<br />
+  130 - 82 - &#8218; - baseline s. q<br />
+  131 - 83 - &#402; - florin<br />
+  132 - 84 - &#8222; - baseline d q<br />
+  133 - 85 - &#8230; - ellipsis<br />
+  134 - 86 - &#8224; - dagger<br />
+  135 - 87 - &#8225; - d dagger<br />
+  136 - 88 - &#710; - circumflex accent<br />
+  137 - 89 - &#8240; - permile<br />
+  138 - 8a - &#352; - S Hacek<br />
+  139 - 8b - &#8249; - l s. guillemet<br />
+  140 - 8c - &#338; - OE ligature<br />
+  141 - 8d - (removed) - (not used)<br />
+  142 - 8e - &#381; - Z dieresis<br />
+  143 - 8f - (removed) - (not used)<br />
+  144 - 90 - (removed) - (not used)<br />
+  145 - 91 - &#8216; - l s. q<br />
+  146 - 92 - &#8217; - r s. q<br />
+  147 - 93 - &#8220; - l d q<br />
+  148 - 94 - &#8221; - r d q<br />
+  149 - 95 - &#8226; - bullet<br />
+  150 - 96 - &#8211; - en dash<br />
+  151 - 97 - &#8212; - em dash<br />
+  152 - 98 - &#732; - tilde accent<br />
+  153 - 99 - &#8482; - trademark<br />
+  154 - 9a - &#353; - s Hacek<br />
+  155 - 9b - &#8250; - r s. guillemet<br />
+  156 - 9c - &#339; - oe ligature<br />
+  157 - 9d - (removed) - (not used)<br />
+  158 - 9e - &#382; - z dieresis<br />
+  159 - 9f - &#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>  URL format
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  An <em>absolute</em> URL has a <span class="term">protocol</span> or <span class="term">scheme</span>, a <span class="term">network location</span> or <span class="term">hostname</span>, and, optional <span class="term">path</span>, <span class="term">parameters</span>, <span class="term">query</span> and <span class="term">fragment</span> segments. Thus, an absolute URL has this generic structure:<br />
+<br />
+
+<code class="code">    (scheme) : (//network location) /(path) ;(parameters) ?(query) #(fragment)</code>
+<br />
+<br />
+  The schemes can only contain letters, digits, <span class="term">+</span>, <span class="term">.</span> and <span class="term">-</span>. Hostname is the portion after the <span class="term">//</span> and up to the first <span class="term">/</span> (if any; else, up to the end) when <span class="term">:</span> is followed by a <span class="term">//</span> (e.g., <span class="term">abc.com</span> in <span class="term">ftp://abc.com/def</span>); otherwise, it consists of everything after the <span class="term">:</span> (e.g., <span class="term">def@abc.com</span> in mailto:def@abc.com').<br />
+<br />
+  <em>Relative</em> URLs do not have explicit schemes and network locations; such values are inherited from a <em>base</em> URL.<br />
+
+</div>
+<div class="sub-section"><h3>
+<a name="s5.6" id="s5.6"></a><span class="item-no">5.6</span>  Brief on htmLawed code
+</h3><span class="totop"><a href="#peak">(to top)</a></span><br style="clear: both;" />
+<br />
+  Much of the code's logic and reasoning can be understood from the documentation above.<br />
+<br />
+  The <strong>output</strong> of htmLawed is a text string containing the processed input. There is no custom error tracking.<br />
+<br />
+  <strong>Function arguments</strong> for htmLawed are:<br />
+<br />
+  *  <span class="term">$in</span> - first argument; a text string; the <strong>input text</strong> to be processed. Any extraneous slashes added by PHP when <em>magic quotes</em> are enabled should be removed beforehand using PHP's <span class="term">stripslashes()</span> function.<br />
+<br />
+  *  <span class="term">$config</span> - second argument; an associative array; optional; named <span class="term">$C</span> within htmLawed code. The array has keys with names like <span class="term">balance</span> 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> (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> <span class="term">$config</span> is thus a filtered and possibly larger array.<br />
+<br />
+  *  <span class="term">$spec</span> - third argument; a text string; optional. The string has rules, written in an htmLawed-designated format, <strong>specifying</strong> element-specific attribute and attribute value restrictions. Function <span class="term">hl_spec()</span> is used to convert the string to an associative-array, named <span class="term">$S</span> within htmLawed code, for internal use. <em>Finalized</em> <span class="term">$spec</span> is thus an array.<br />
+<br />
+  <em>Finalized</em> <span class="term">$config</span> and <span class="term">$spec</span> are made <strong>global variables</strong> 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> values, the <span class="term">show_settings</span> parameter of <span class="term">$config</span> 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> attribute values for uniqueness, may be set. Unlike the other two variables, this one is not reset (or unset) post-processing.<br />
+<br />
+  Except for the main function <span class="term">htmLawed()</span> and the functions <span class="term">kses()</span> and <span class="term">kses_hook()</span>, htmLawed's functions are <strong>name-spaced</strong> using the <span class="term">hl_</span> prefix. The <strong>functions</strong> and their roles are:<br />
+<br />
+  *  <span class="term">hl_attrval</span> - checking attribute values against $spec<br />
+  *  <span class="term">hl_bal</span> - tag balancing<br />
+  *  <span class="term">hl_cmtcd</span> - handling CDATA sections and HTML comments<br />
+  *  <span class="term">hl_ent</span> - entity handling<br />
+  *  <span class="term">hl_prot</span> - checking a URL scheme/protocol<br />
+  *  <span class="term">hl_regex</span> - checking syntax of a regular expression<br />
+  *  <span class="term">hl_spec</span> - converting user-supplied $spec value to one used by htmLawed internally<br />
+  *  <span class="term">hl_tag</span> - handling tags<br />
+  *  <span class="term">hl_tag2</span> - transforming tags<br />
+  *  <span class="term">hl_tidy</span> - compact/beautify HTML<br />
+  *  <span class="term">hl_version</span> - reporting htmLawed version<br />
+  *  <span class="term">htmLawed</span> - main function<br />
+  *  <span class="term">kses</span> - main function of <span class="term">kses</span><br />
+  *  <span class="term">kses_hook</span> - hook function of <span class="term">kses</span><br />
+<br />
+  The last two are for compatibility with pre-existing code using the <span class="term">kses</span> script. htmLawed's <span class="term">kses()</span> basically passes on the filtering task to <span class="term">htmLawed()</span> function after deciphering <span class="term">$config</span> and <span class="term">$spec</span> from the argument values supplied to it. <span class="term">kses_hook()</span> is an empty function and is meant for being filled with custom code if the <span class="term">kses</span> script users were using one.<br />
+<br />
+  <span class="term">htmLawed()</span> finalizes <span class="term">$spec</span> (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> involves setting default values if an inappropriate or invalid one is supplied. This includes calling <span class="term">hl_regex()</span> 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> then removes invalid characters like nulls and <span class="term">x01</span> 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> with the help of <span class="term">hl_cmtcd()</span>. When retained, the <span class="term"><</span> and <span class="term">></span> characters identifying them, and the <span class="term"><</span>, <span class="term">></span> and <span class="term">&</span> characters inside them, are replaced with control characters (code-points <span class="term">1</span> to <span class="term">5</span>) till any tag balancing is completed.<br />
+<br />
+  After this <em>initial processing</em> <span class="term">htmLawed()</span> identifies tags using regex and processes them with the help of <span class="term">hl_tag()</span> --  a large function that analyzes tag content, filtering it as per HTML standards, <span class="term">$config</span> and <span class="term">$spec</span>. Among other things, <span class="term">hl_tag()</span> 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> rules using <span class="term">hl_attrval()</span>, and checks URL protocols using <span class="term">hl_prot()</span>. <span class="term">htmLawed()</span> 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"><</span>, <span class="term">></span> and <span class="term">&</span> characters inside <span class="term">pre</span>, <span class="term">script</span> and <span class="term">textarea</span> elements, and HTML comments and CDATA sections with control characters (code-points <span class="term">1</span> to <span class="term">5</span>, and <span class="term">7</span>).<br />
+<br />
+  htmLawed permits the use of custom code or <strong>hook functions</strong> 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> and <span class="term">$spec</span> 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> once the tag content is finalized (see <a href="#s3.4.9">section 3.4.9</a>).<br />
+<br />
+  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 '>' and ' ' 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 '>'. 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, ' ', '&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., '≪' 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 '-->'. 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 ']]>'. - - * For attribute values, character entities '<', '>' and '&' 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 '>'. 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., 'http' (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 'expression...'. 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 ' ' for non-breaking space), or alphanumeric like 'gt' (external or named entity; like ' ' for non-breaking space), or '#' followed by a number (decimal numeric entity; like ' ' for non-breaking space). Character entities referring to the soft-hyphen character (the '­' 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 '>', '<', '"', and '&' 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 '&'. - - *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 '<', are not affected). This allows one to distinguish, say, an '>' introduced by htmLawed and an '>' 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 <a href="">a.com</a>. - - 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'): - <-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a> - Output ('$config["comment"] = 1, $config["cdata"] = 2'): - <a href="home.htm"><![CDATA[x=&y]]>Home</a> - Output ('$config["comment"] = 2, $config["cdata"] = 2'): - <!-- home link --><a href="home.htm"><![CDATA[x=&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': - - <*> 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"] = 3': - - <*> Pseudo-tags <*> - <xml>Non-HTML tag xml</xml> - <p> - Disallowed tag p - </p> - <ul><li>OK</li></ul> - - The output with '$config["keep_bad"] = 6': - - <*> Pseudo-tags <*> - 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 - € - euro - 129 - 81 - (removed) - (not used) - 130 - 82 - ‚ - baseline s. q - 131 - 83 - ƒ - florin - 132 - 84 - „ - baseline d q - 133 - 85 - … - ellipsis - 134 - 86 - † - dagger - 135 - 87 - ‡ - d dagger - 136 - 88 - ˆ - circumflex accent - 137 - 89 - ‰ - permile - 138 - 8a - Š - S Hacek - 139 - 8b - ‹ - l s. guillemet - 140 - 8c - Œ - OE ligature - 141 - 8d - (removed) - (not used) - 142 - 8e - Ž - Z dieresis - 143 - 8f - (removed) - (not used) - 144 - 90 - (removed) - (not used) - 145 - 91 - ‘ - l s. q - 146 - 92 - ’ - r s. q - 147 - 93 - “ - l d q - 148 - 94 - ” - r d q - 149 - 95 - • - bullet - 150 - 96 - – - en dash - 151 - 97 - — - em dash - 152 - 98 - ˜ - tilde accent - 153 - 99 - ™ - trademark - 154 - 9a - š - s Hacek - 155 - 9b - › - r s. guillemet - 156 - 9c - œ - oe ligature - 157 - 9d - (removed) - (not used) - 158 - 9e - ž - z dieresis - 159 - 9f - Ÿ - 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 '>' and ' ' 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 '>'. 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, ' ', '&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, '≪' 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 '-->'. 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 ']]>'.
+
+ * For attribute values, character entities '<', '>' and '&' 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 '>'. 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., 'http' (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 'expression...'. 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 ' ' for non-breaking space), or alphanumeric like 'gt' (external or named entity; like ' ' for non-breaking space), or '#' followed by a number (decimal numeric entity; like ' ' for non-breaking space). Character entities referring to the soft-hyphen character (the '­' 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 '>', '<', '"', and '&' 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 '&'.
+
+ *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 '<', are not affected). This allows one to distinguish, say, an '>' introduced by htmLawed and an '>' 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 <a href="">a.com</a>.
+
+ 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'):
+ <-- home link --><a href="home.htm"><![CDATA[x=&y]]>Home</a>
+ Output ('$config["comment"] = 1, $config["cdata"] = 2'):
+ <a href="home.htm"><![CDATA[x=&y]]>Home</a>
+ Output ('$config["comment"] = 2, $config["cdata"] = 2'):
+ <!-- home link --><a href="home.htm"><![CDATA[x=&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':
+
+ <*> 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"] = 3':
+
+ <*> Pseudo-tags <*>
+ <xml>Non-HTML tag xml</xml>
+ <p>
+ Disallowed tag p
+ </p>
+ <ul><li>OK</li></ul>
+
+ The output with '$config["keep_bad"] = 6':
+
+ <*> Pseudo-tags <*>
+ 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 - € - euro
+ 129 - 81 - (removed) - (not used)
+ 130 - 82 - ‚ - baseline s. q
+ 131 - 83 - ƒ - florin
+ 132 - 84 - „ - baseline d q
+ 133 - 85 - … - ellipsis
+ 134 - 86 - † - dagger
+ 135 - 87 - ‡ - d dagger
+ 136 - 88 - ˆ - circumflex accent
+ 137 - 89 - ‰ - permile
+ 138 - 8a - Š - S Hacek
+ 139 - 8b - ‹ - l s. guillemet
+ 140 - 8c - Œ - OE ligature
+ 141 - 8d - (removed) - (not used)
+ 142 - 8e - Ž - Z dieresis
+ 143 - 8f - (removed) - (not used)
+ 144 - 90 - (removed) - (not used)
+ 145 - 91 - ‘ - l s. q
+ 146 - 92 - ’ - r s. q
+ 147 - 93 - “ - l d q
+ 148 - 94 - ” - r d q
+ 149 - 95 - • - bullet
+ 150 - 96 - – - en dash
+ 151 - 97 - — - em dash
+ 152 - 98 - ˜ - tilde accent
+ 153 - 99 - ™ - trademark
+ 154 - 9a - š - s Hacek
+ 155 - 9b - › - r s. guillemet
+ 156 - 9c - œ - oe ligature
+ 157 - 9d - (removed) - (not used)
+ 158 - 9e - ž - z dieresis
+ 159 - 9f - Ÿ - 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> </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=javascript:al <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: expression(alert('xss'));">xxx</a><br /> <strong>Bad IE7:</strong> <a href="xxx" style="background: expression(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/simplepie/README b/mod/simplepie/README index 24591c3e1..29a9f38aa 100644 --- a/mod/simplepie/README +++ b/mod/simplepie/README @@ -1,11 +1,11 @@ Widget Title ---------------------------- +=========== To change the widget title, edit the language file (en.php) and change the string 'simplepie:widget' from 'RSS Feed' to whatever you desire. Proxy Server ----------------------------- +=========== If your site is going through a proxy server to get to the feeds, you may want to increase the timeout on the feeds (though this is unlikely as the default timeout is 10 seconds). You can do this by editing @@ -14,6 +14,6 @@ uncomment the line $feed->set_timeout(20); HTML tags in feeds --------------------------- +================= The widget allows the following tags: `<a><p><br><b><i><em><del><pre><strong><ul><ol><li><img>`. -Other tags are stripped to avoid problems with the display of your site.
\ No newline at end of file +Other tags are stripped to avoid problems with the display of your site. diff --git a/mod/simplepie/actions/simplepie/save_group_feed.php b/mod/simplepie/actions/simplepie/save_group_feed.php new file mode 100644 index 000000000..8dc2ffd78 --- /dev/null +++ b/mod/simplepie/actions/simplepie/save_group_feed.php @@ -0,0 +1,14 @@ +<?php + +$group = get_entity((int)get_input('group_guid')); +$feed_url = get_input('feed_url'); + +if (!simplepie_is_url($feed_url)) { + register_error (elgg_echo("simplepie:invalid_url")); + forward(REFERER); +} + +if (!elgg_instanceof($group, 'group') || !$group->canEdit()) { + forward(REFERER); +} +$group->feed_url = $feed_url; diff --git a/mod/simplepie/languages/en.php b/mod/simplepie/languages/en.php index 07ff02905..a22316863 100644 --- a/mod/simplepie/languages/en.php +++ b/mod/simplepie/languages/en.php @@ -1,16 +1,14 @@ <?php
-
$english = array(
'simplepie:widget' => 'RSS Feed',
'simplepie:description' => 'Add an external blog to your profile',
'simplepie:notset' => 'Feed url is not set',
- 'simplepie:notfind' => 'Cannot find feed. Check the feed url.',
+ 'simplepie:notfound' => 'Cannot find feed. Check the feed url.',
'simplepie:feed_url' => 'Feed URL',
'simplepie:num_items' => 'Number of items',
'simplepie:excerpt' => 'Include excerpt',
'simplepie:post_date' => 'Include post date',
'simplepie:postedon' => 'Posted on',
+ 'simplepie:invalid_url' => 'Invalid url, copy it from the navigation bar please',
);
-
add_translation("en", $english);
-
diff --git a/mod/simplepie/languages/es.php b/mod/simplepie/languages/es.php new file mode 100644 index 000000000..d9bd5d2d6 --- /dev/null +++ b/mod/simplepie/languages/es.php @@ -0,0 +1,14 @@ +<?php
+$spanish = array(
+ 'simplepie:widget' => 'Enlace RSS',
+ 'simplepie:description' => 'Agregar un blog externo',
+ 'simplepie:notset' => 'Enlace RSS no configurado',
+ 'simplepie:notfound' => 'no se encontro el feed. Revisa el feed url.',
+ 'simplepie:feed_url' => 'Feed URL',
+ 'simplepie:num_items' => 'Numero de items',
+ 'simplepie:excerpt' => 'Incluir contenido',
+ 'simplepie:post_date' => 'Incluir fecha del post',
+ 'simplepie:postedon' => 'Posted on',
+ 'simplepie:invalid_url' => 'Url invalida, copiela desde la barra del navegador por favor',
+);
+add_translation("en", $english);
diff --git a/mod/simplepie/manifest.xml b/mod/simplepie/manifest.xml index 94b8a4b42..eff9681d2 100644 --- a/mod/simplepie/manifest.xml +++ b/mod/simplepie/manifest.xml @@ -2,7 +2,7 @@ <plugin_manifest xmlns="http://www.elgg.org/plugin_manifest/1.8">
<name>SimplePie RSS Feed Integration</name>
<author>Cash Costello</author>
- <version>1.8.0</version>
+ <version>1.8.1.1</version>
<category>widget</category>
<description>Provides a widget that pulls in RSS feeds</description>
<website>http://www.cashcostello.com/</website>
diff --git a/mod/simplepie/start.php b/mod/simplepie/start.php index 20adc7545..34a7ced3f 100644 --- a/mod/simplepie/start.php +++ b/mod/simplepie/start.php @@ -20,4 +20,14 @@ function simplepie_init() { $lib = elgg_get_plugins_path() . 'simplepie/vendors/simplepie.inc';
elgg_register_library('simplepie', $lib);
+
+ // Add group option
+ add_group_tool_option('rss', elgg_echo('simplepie:enablerss'), false);
+ elgg_extend_view('groups/tool_latest', 'simplepie/group_module');
+
+ elgg_register_action('simplepie/save_group_feed', elgg_get_plugins_path() . 'simplepie/actions/simplepie/save_group_feed.php');
+}
+
+function simplepie_is_url($url) {
+ return preg_match("/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i", $url);
}
diff --git a/mod/simplepie/views/default/forms/simplepie/save_group_feed.php b/mod/simplepie/views/default/forms/simplepie/save_group_feed.php new file mode 100644 index 000000000..a3f771586 --- /dev/null +++ b/mod/simplepie/views/default/forms/simplepie/save_group_feed.php @@ -0,0 +1,39 @@ +<?php +/** + * Simplepie feed reader widget settings + */ + +// set default value + +$url_label = elgg_echo("simplepie:feed_url"); +$url_textbox = elgg_view('input/text', array( + 'name' => 'feed_url', + 'id'=> 'feed_url', + 'value' => $vars['entity']->feed_url, +)); + +$group_field = elgg_view('input/hidden', array( + 'name' => 'group_guid', + 'value' => $vars['entity']->guid, +)); + +$save_button = elgg_view('input/submit', array( + 'value' => elgg_echo('save'), +)); + + + + +echo <<<HTML +<div> + <label for="feed_url">$url_label</label> + $url_textbox + $group_field +</div> +<div> + $save_button +</div> +HTML; + + + diff --git a/mod/simplepie/views/default/simplepie/group_module.php b/mod/simplepie/views/default/simplepie/group_module.php new file mode 100644 index 000000000..90c017a86 --- /dev/null +++ b/mod/simplepie/views/default/simplepie/group_module.php @@ -0,0 +1,109 @@ +<?php +/** + * Group simplepie:rss module + */ +elgg_load_library('simplepie'); + +$group = elgg_get_page_owner_entity(); + +if ($group->rss_enable != "yes") { + return true; +} + +elgg_push_context('widgets'); + +$allowed_tags = '<a><p><br><b><i><em><del><pre><strong><ul><ol><li><img>'; +$feed_url = $group->feed_url; +$content = ''; + +if ($group->canEdit()) { + $content .= elgg_view_form("simplepie/save_group_feed", array( + 'id' => 'simplepie-form', + 'class' => $feed_url ? 'hidden' : '', + ), $vars); +} + +if ($feed_url) { + +// get widget settings + $excerpt = true; + $post_date = true; + $num_items = 7; + $cache_location = elgg_get_data_path() . '/simplepie_cache/'; + if (!file_exists($cache_location)) { + mkdir($cache_location, 0777); + } + $feed = new SimplePie($feed_url, $cache_location); + + // doubles timeout if going through a proxy + //$feed->set_timeout(20); + // only display errors to profile owner + + $num_posts_in_feed = $feed->get_item_quantity(); + if (!$num_posts_in_feed) { + if (elgg_get_logged_in_user_guid() == elgg_get_page_owner_guid()) { + $content .= '<p>' . elgg_echo('simplepie:notfound') . '</p>'; + } + } + + // don't display more feed items than user requested + if ($num_items > $num_posts_in_feed) { + $num_items = $num_posts_in_feed; +} + + $feed_link = elgg_view('output/url', array( + 'href' => $feed->get_permalink(), + 'text' => $feed->get_title(), + )); + + // need to center + $content .= "<h2 class=\"simplepie-heading\">$feed_link</h2>"; + $content .= '<ul class="simplepie-list">'; + foreach ($feed->get_items(0, $num_items) as $item) { + $item_link = elgg_view('output/url', array( + 'href' => $item->get_permalink(), + 'text' => $item->get_title(), + )); + + if ($excerpt) { + $text = strip_tags($item->get_description(true), $allowed_tags); + $excerpt = elgg_get_excerpt($text); + } + + if ($post_date) { + $item_date_label = elgg_echo('simplepie:postedon'); + $item_date = $item->get_date('j F Y | g:i a'); + $post_date = "$item_date_label $item_date"; + } + + $content .= <<<HTML +<li class="mbm elgg-item"> + <h4 class="mbs">$item_link</h4> + <p class="elgg-subtext">$post_date</p> + <div class="elgg-content">$excerpt</div> +</li> +HTML; + } + $content .= "</ul>"; +} + +elgg_pop_context(); + +if (!$content) { + $content = '<p>' . elgg_echo('simplepie:none') . '</p>'; +} +if ($group->canEdit()) { + $edit = elgg_view('output/url', array( + 'href' => '#simplepie-form', + 'text' => elgg_echo('edit'), + 'rel' => 'toggle' + )); +} else { + $edit = false; +} + +echo elgg_view('groups/profile/module', array( + 'title' => elgg_echo('RSS Group'), + 'content' => $content, + 'all_link' => $edit, +)); 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 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 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 ' 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, , 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| |<\/?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| |<\/?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(" "));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(" ")); // 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 < > tag and placing the caret inside an empty LI is handled and should be handled by the selection logic + //li.appendChild(ed.dom.create(" ")); // 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> [\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> [\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*( )+/gi,/( |<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>"],[/ /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(/"/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(/ /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+\.( |\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*( |\u00a0)+\s*/,"")}else{r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^\s*\w+\.( |\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,[/ /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> [\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> [\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*( )+/gi,/( |<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>"],[/ /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(/"/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(/ /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+\.( |\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*( |\u00a0)+\s*/,"")}else{r=t.innerHTML.replace(/__MCE_ITEM__/g,"").replace(/^\s*\w+\.( |\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,[/ /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> [\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> [\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
[/ /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?" ":'<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?" ":'<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?" ":'<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 ? ' ' : '<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"> </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(/ | /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(/ | /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 == ’
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",{}," ")}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",{}," ")}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', {}, ' ');
}
-
+
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 Binary files differindex 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 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={'"':""","'":"'","<":"<",">":">","&":"&"};d={"<":"<",">":">","&":"&",""":'"',"'":"'"};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[^>]*>( | |\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={'"':""","'":"'","<":"<",">":">","&":"&"};d={"<":"<",">":">","&":"&",""":'"',"'":"'"};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[^>]*>( | |\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",{}," ");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> </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[^>]*>( | |\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', {}, ' ');
+ 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 Binary files differdeleted file mode 100644 index d0e8606a1..000000000 --- a/mod/twitter/graphics/thewire_speech_bubble.gif +++ /dev/null diff --git a/mod/twitter/graphics/twitter16px.png b/mod/twitter/graphics/twitter16px.png Binary files differdeleted file mode 100644 index de51c6953..000000000 --- a/mod/twitter/graphics/twitter16px.png +++ /dev/null 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; |